# OpenFGA Go SDK

A production-grade Go client library for [OpenFGA](https://openfga.dev), an open-source Fine-Grained Authorization (FGA) service. This SDK provides a dual-layer API: high-level methods for common patterns and low-level access for advanced use cases.

## Tech Stack

- **Language:** Go 1.25.0+
- **Module:** `github.com/openfga/go-sdk`
- **Key Dependencies:** OpenTelemetry (metrics), conc (parallelism), testify (testing)

## Project Structure

```
.
├── AGENTS.md                          # This file: universal AI agent onboarding
├── CLAUDE.md                          # Claude-specific rules reference
├── .claude/rules/                     # Detailed coding guardrails (Claude-specific)
├── .coderabbit.yaml                   # CodeRabbit code review configuration
├── Makefile                           # Build and test targets
├── go.mod / go.sum                    # Dependency management
├── errors.go                          # Error types & retry logic [HAND-WRITTEN]
├── configuration.go                   # SDK configuration [HAND-WRITTEN]
├── api_client.go                      # Low-level HTTP client setup [HAND-WRITTEN]
├── api_executor.go                    # Preferred API execution layer [HAND-WRITTEN]
├── streaming.go                       # Streaming response handler [AUTO-GENERATED]
├── utils.go                           # Helper functions [HAND-WRITTEN]
├── response.go                        # Response wrappers [HAND-WRITTEN]
├── model_*.go                         # Data models [AUTO-GENERATED - multiple files]
├── api_open_fga.go                    # Low-level OpenFGA API [AUTO-GENERATED, DEPRECATED in favor of api_executor]
├── client/
│   ├── client.go                      # High-level OpenFgaClient wrapper [HAND-WRITTEN]
│   └── *.go                           # Client methods (check, expand, etc.) [HAND-WRITTEN]
├── credentials/
│   └── credentials.go                 # Credential types (none, api_token, client_credentials) [HAND-WRITTEN]
├── oauth2/
│   ├── clientcredentials/             # OAuth2 client credentials flow [HAND-WRITTEN]
│   ├── token.go                       # Token refresh logic [HAND-WRITTEN]
│   └── transport.go                   # Bearer token injection [HAND-WRITTEN]
├── telemetry/
│   ├── configuration.go               # Metric configuration [HAND-WRITTEN]
│   ├── metrics.go                     # Metric recording [HAND-WRITTEN]
│   └── attributes.go                  # Telemetry attributes [HAND-WRITTEN]
├── internal/
│   ├── constants/constants.go         # SDK constants (batch limits, retries) [AUTO-GENERATED]
│   └── utils/retryutils/              # Retry backoff & header parsing [HAND-WRITTEN]
├── docs/                              # Auto-generated API reference (~90 markdown files)
├── example/                           # Independent example submodules
│   ├── openfga/                       # Basic usage example (separate go.mod)
│   ├── opentelemetry/                 # OpenTelemetry integration example (separate go.mod)
│   └── oauth2/                        # OAuth2 credentials example (separate go.mod)
└── .golangci.yaml                     # Linter config (excludes model_*.go)
```

## Code Generation

**Parts of this SDK are auto-generated.** The following files are generated by [OpenAPI Generator](https://openapi-generator.tech) from the [sdk-generator](https://github.com/openfga/sdk-generator) repo and **will be wiped on every regeneration**:

**Generated Files:**
- All `model_*.go` files (data model files)
- `api_open_fga.go` (low-level API client, deprecated)
- `streaming.go` (streaming response handler)
- `internal/constants/constants.go` (SDK constants)
- `docs/**` (~90 API reference markdown files)

**How to identify generated files:** Look for the header comment containing `NOTE: This file was auto generated by OpenAPI Generator ... DO NOT EDIT.`

**If you need to change generated files:** Contributors may modify generated files in this repo, but changes must be sourced from sdk-generator. Submit changes to https://github.com/openfga/sdk-generator first and link the sdk-generator PR in this repo's PR description. Unlinked changes will be overwritten on the next regeneration.

**Preferred pattern for new API methods:** Use `api_executor.go` (hand-written) rather than `api_open_fga.go` (generated). The executor pattern is newer and more maintainable.

## Architecture

### Dual-Layer Design

1. **Low-Level Layer** (`APIClient`, `APIExecutor`)
   - HTTP request marshaling, retry logic, error handling
   - Direct API responses (raw structs)
   - Use when you need fine-grained control

2. **High-Level Layer** (`OpenFgaClient` in `client/`)
   - Batch request splitting (50 tuples per batch max)
   - Parallel request execution (10 max parallel requests)
   - Credential injection and token refresh
   - Telemetry instrumentation
   - Use for standard operations (Check, BatchCheck, Expand, etc.)

### Package Purposes

- **Root package** (`openfga`): Core types, configuration, APIClient
- **`client/`**: High-level SDK wrapper, method implementations
- **`credentials/`**: Credential types and injection
- **`oauth2/`**: OAuth2 token flows and Bearer token transport
- **`telemetry/`**: OpenTelemetry metric recording
- **`internal/constants/`**: Batch limits (50 tuples), parallel request limits (10)
- **`internal/utils/retryutils/`**: Exponential backoff, Retry-After header parsing

## Development

```bash
# Format code
make fmt

# Run linter (gofmt + go vet + golangci-lint)
make lint

# Run all tests with coverage
make test

# Security scanning (gosec + govulncheck)
make security

# Run all checks (fmt + lint + test + security)
make check

# Build the SDK
go build -v ./...

# Tidy dependencies
go mod tidy

# Example apps (requires Docker for OpenFGA server)
cd example
make run-openfga  # Start OpenFGA server in Docker
make run          # Run example application
```

**Before every commit, run:** `make check`

## Verification

Every commit must pass:
1. `make fmt` — no formatting issues
2. `make lint` — no linting issues
3. `make test` — all tests passing
4. `make security` — no security vulnerabilities

## Key Patterns

### Credentials

Three credential types; configure via `ClientConfiguration`:
- **None** (default): No authentication
- **API Token**: Static bearer token via `Authorization` header
- **Client Credentials**: OAuth2 flow with automatic token refresh

```go
config := &client.ClientConfiguration{
    ApiUrl: "https://api.fga.example",
    Credentials: &credentials.Credentials{
        Method: credentials.CredentialsMethodClientCredentials,
        Config: &credentials.Config{
            ClientCredentialsApiTokenIssuer: "https://issuer.fga.example",
            ClientCredentialsClientId:       "client-id",
            ClientCredentialsClientSecret:   "client-secret",
        },
    },
}
```

### Streaming (StreamingChannel)

For APIs returning NDJSON streams (e.g., `StreamedListObjects`):

```go
channel, err := client.StreamedListObjects(ctx, storeId, request)
for {
    select {
    case result := <-channel.Results:
        // Process result
    case err := <-channel.Errors:
        // Handle error; loop terminates
        return err
    }
}
channel.Close()
```

**Critical:** Always drain both `Results` and `Errors` channels to prevent goroutine leaks.

### Retry Strategy

Automatic exponential backoff for retryable errors:
- **HTTP 429** (rate limit): Always retried
- **HTTP 5xx** (server error): Retried with backoff
- **HTTP 4xx** (client error): Never retried
- **Retry-After header:** Respected and capped at 1800 seconds

Custom retry params via `Configuration.RetryParams`.

### Telemetry (OpenTelemetry)

Metrics emitted for all client operations:
- `fga_client_credentials_request` (counter)
- `fga_client_request_duration` (histogram, ms)
- `fga_client_query_duration` (histogram, ms)
- `fga_client_http_request_duration` (histogram, ms)

**For unit tests:** Use `noop.NewMeterProvider()` to suppress metric export, not live OTel exporters.

## Gotchas

1. **Generated files get wiped.** Any edits to `model_*.go`, `api_open_fga.go`, `streaming.go`, `internal/constants/constants.go`, or `docs/*` will be lost on regeneration. Contributors may modify them, but must submit changes to [sdk-generator](https://github.com/openfga/sdk-generator) and link the PR.

2. **Token expiry is not exact.** OAuth2 tokens refresh 5–10 minutes early due to:
   - 300s threshold before expiry
   - 300s random jitter
   - Do not write time-based assertions in tests; mock token provider instead.

3. **StreamingChannel goroutine leak.** Channels must be fully drained before returning:
   ```go
   // WRONG: leaves goroutine running
   for result := range channel.Results { ... }

   // RIGHT: drain both channels
   for {
       select {
       case result := <-channel.Results:
       case err := <-channel.Errors: return err
       }
   }
   ```

4. **golangci-lint excludes generated code.** Model files are intentionally skipped by `.golangci.yaml` so lint errors in generated code don't fail CI. Do not remove this exclusion.

5. **Batch limits are constants.** `BatchCheck` splits into max 50-tuple batches with 10 parallel requests. Both are defined in `internal/constants/constants.go`:
   - `ClientMaxBatchSize = 50`
   - `ClientMaxMethodParallelRequests = 10`

6. **Deprecated config fields.** `ApiScheme` and `ApiHost` are deprecated; use `ApiUrl` instead. New code should set only `ApiUrl`.

7. **Example submodules are independent.** Each `example/*/go.mod` is a separate project with its own dependencies. They are not tested in CI.

## Contributing

1. Fork [github.com/openfga/go-sdk](https://github.com/openfga/go-sdk)
2. Create a branch: `git checkout -b feat/your-feature`
3. Make changes
4. Run `make check` to ensure all tests, lints, and security checks pass
5. Commit with a [conventional commit](https://www.conventionalcommits.org/) message (e.g., `feat: add method`, `fix: retry logic`)
6. Push and open a PR
7. See [CONTRIBUTING.md](./CONTRIBUTING.md) for full guidelines

## Resources

- [OpenFGA Documentation](https://openfga.dev/docs)
- [Go SDK Reference](https://pkg.go.dev/github.com/openfga/go-sdk)
- [OpenFGA Community](https://openfga.dev/community)
