Contributing to the OpenFGA Go SDK
April 11, 2026 · View on GitHub
Thank you for considering contributing to the OpenFGA Go SDK! This guide will help you get set up and submit effective contributions.
Table of Contents
- Code of Conduct
- Prerequisites
- Getting Started
- Project Structure
- Generated vs Hand-Written Code
- Cross-SDK Consistency
- Development Workflow
- Writing Tests
- PR Requirements
- Adding New API Methods
- Working with Telemetry
- Security Considerations
- Getting Help
Code of Conduct
By participating and contributing to this project, you are expected to uphold our Code of Conduct.
Prerequisites
- Go: Latest two releases supported per Go's release policy. We target
(latest-1).0ingo.modand use the latest astoolchain. - golangci-lint: Used for linting (install)
- gosec and govulncheck: Used for security scanning
- make: For running common commands (
make check,make test, etc.)
Getting Started
- Fork github.com/openfga/go-sdk
- Clone your fork
- Create a branch:
git checkout -b feat/your-feature - Make your changes
- Run
make checkto verify everything passes - Push and open a PR
Useful commands
make fmt # Format code with gofmt
make lint # Format + go vet + golangci-lint
make test # Run all tests with race detection and coverage
make security # Run gosec and govulncheck
make check # All of the above
Run a single package's tests:
go test -race -v ./client/...
Project Structure
This SDK has a dual-layer architecture:
- Low-level layer (root package):
APIClient,APIExecutor,OpenFgaApigenerated models, error types - High-level layer (
client/package):OpenFgaClientwith fluent API, batch splitting, credential injection, telemetry
| Package | Purpose |
|---|---|
client/ | High-level SDK client (Check, BatchCheck, Expand, etc.) |
credentials/ | Authentication (none, API token, client credentials) |
oauth2/ | OAuth2 token flows and Bearer token transport |
telemetry/ | OpenTelemetry metric recording |
internal/constants/ | SDK constants (batch limits, retry defaults) |
internal/utils/retryutils/ | Exponential backoff, Retry-After header parsing |
Generated vs Hand-Written Code
Parts of this SDK are auto-generated from the sdk-generator repository using OpenAPI Generator.
Generated files (will be wiped on regeneration)
- All
model_*.gofiles (data model files) api_open_fga.go(deprecated in favor ofapi_executor.go)streaming.gointernal/constants/constants.go— these constants are generated from sdk-generator and are consistent across all OpenFGA SDKsdocs/**
You can identify generated files by the header comment: NOTE: This file was auto generated by OpenAPI Generator ... DO NOT EDIT.
The full list is tracked in .openapi-generator/FILES.
If you need to change a generated file
You may modify generated files in this repo, but changes must also be submitted to sdk-generator to persist:
- Submit a PR to openfga/sdk-generator with the template/spec changes
- Open your PR in this repo and link the sdk-generator PR in the description
- Without a linked sdk-generator PR, your changes will be overwritten on the next regeneration
This workflow ensures fixes propagate to all OpenFGA SDKs (Go, JS, Java, .NET, Python).
Hand-written files (safe to edit directly)
api_client.go,api_executor.go,configuration.go,errors.go,utils.go,response.goclient/**,credentials/**,oauth2/**,telemetry/**internal/utils/**- All
*_test.gofiles
Cross-SDK Consistency
OpenFGA maintains SDKs in multiple languages: Go, JS/TS, Java, .NET, and Python. These SDKs should behave consistently:
- Before implementing a feature or fix, check how the other SDKs handle it. Use them as reference to ensure your implementation matches the expected behavior and interface. When submitting your PR, note whether the change applies across SDKs.
- Public interfaces should match as closely as each language allows — same method names, same parameters, same defaults, same error behavior.
- Constants (batch limits, retry defaults, telemetry metric names, etc.) are generated from sdk-generator and must stay consistent across all SDKs.
- Behavioral contracts must be identical: which status codes are retried, how Retry-After headers are parsed, how token refresh works, how streaming channels behave.
If you discover a discrepancy between SDKs, please open an issue in openfga/sdk-generator so it can be tracked and resolved across all SDKs.
Development Workflow
Code style
- Format with
gofmt(notgoimports) — CI checks this - Lint with
golangci-lint runusing the repo's.golangci.yaml - Use
openfga.ToPtr[T]()for optional fields — the olderPtrString(),PtrBool(),PtrInt()helpers are deprecated - Propagate
context.Contextthroughout — never drop or ignore contexts - Use
github.com/sourcegraph/concfor concurrent batch operations
Fluent API pattern
All client methods follow this pattern:
response, err := fgaClient.Check(ctx).
Body(client.ClientCheckRequest{
User: "user:alice",
Relation: "viewer",
Object: "document:budget",
}).
Options(client.ClientCheckOptions{
AuthorizationModelId: openfga.ToPtr("01H..."),
}).
Execute()
Maintain this pattern when adding new methods.
Writing Tests
Every PR must include or update tests that exercise the changed code.
Test conventions
- Use
t.Parallel()at both the top-level test function and inside eacht.Run()subtest - Use table-driven tests with
t.Run()for multiple cases - Use
testifyfor assertions:requirefor fatal checks,assertfor non-fatal - Mock HTTP calls with
github.com/jarcoal/httpmock— never make real network calls - Use
httptest.NewServerfor streaming/NDJSON response tests - Never use
time.Sleep— use channels, contexts, or test helpers - All tests must be race-safe (CI runs with
-race)
Example
func TestMyFeature(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected string
}{
{"valid input", "foo", "bar"},
{"empty input", "", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
result := myFunction(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
PR Requirements
Title format
PR titles must follow Conventional Commits:
type(scope): description
Scope is optional. Examples:
feat: add StreamedListUsers methodfix(client): handle nil response in BatchCheckchore(deps): bump go.opentelemetry.io/otel
Allowed types:
| Type | Use for |
|---|---|
feat | New features |
fix | Bug fixes |
refactor | Code changes that don't fix bugs or add features |
perf | Performance improvements |
chore | Maintenance (use chore(docs), chore(ci), chore(test) for those areas) |
revert | Reverting previous changes |
release | Release PRs (e.g., release: v0.8.0) |
Checklist
Before submitting your PR:
-
make checkpasses (fmt + lint + test + security) - Tests added or updated for the changed code
- Documentation updated if the public interface or functionality changed (README, godoc comments, dedicated doc file if warranted)
- If generated files were modified, a linked sdk-generator PR is referenced in the description
- If telemetry metrics were added or changed, the changes are noted for the changelog
Adding New API Methods
New public methods that call the OpenFGA API must follow this checklist:
- Use the executor: All API methods must go through
api_executor.go— no custom HTTP logic. Client methods inclient/call API methods, but API methods must all use the executor. - Set
OperationNameinAPIExecutorRequest— this becomes thefga_client_request_methodtelemetry attribute. - Pass
storeIdthrough for metric attributes. - Add to
SdkClientinterface inclient/client.go. - Add telemetry attributes in
telemetry/attributes.goandtelemetry/configuration.goif the method introduces a new dimension (likebatch_check_sizefor BatchCheck). - Validate inputs: StoreId and AuthorizationModelId must be valid ULIDs (see
internal/utils/IsWellFormedUlidString). - Wrap errors using the typed error types from
errors.go— never return raw errors to users.
Use existing methods like Check, BatchCheck, or ListObjects in client/client.go as reference.
Prefer api_executor.go over api_open_fga.go — the latter is generated and deprecated.
Working with Telemetry
The SDK emits OpenTelemetry metrics for all client operations using the fga_client_* prefix.
Key rules
- High-cardinality attributes (e.g.,
url_full, unique per-request IDs) must be disabled by default inDefaultTelemetryConfiguration(). Some observability providers charge heavily for high cardinality. - No PII in metric attributes — no user IDs, tokens, or request bodies.
- Unit tests must use
noop.NewMeterProvider()— never live OTel exporters. - Document changes: any additions or modifications to metrics must be called out in the changelog on release.
Adding a new metric
- Define the metric in
telemetry/configuration.goas aMetricConfiguration - Add the recording method in
telemetry/metrics.go - Add attribute keys in
telemetry/attributes.goif needed - Test with noop provider
Security Considerations
This SDK handles user credentials. When working on security-sensitive areas (credentials/, oauth2/, errors.go, api_client.go, api_executor.go):
- Never log or include tokens, client secrets, or credentials in error messages
- Never expose sensitive headers in string representations or debug output
- OAuth2 token refresh must be thread-safe (
OpenFgaClientis shared across goroutines) - Always close HTTP response bodies in all code paths, including error paths
- Retry only on 429 and 5xx — never retry 4xx client errors (except 429)
- Respect
Retry-AfterandX-RateLimit-Resetheaders for backoff timing - Do not remove token expiry jitter (300s) — it prevents thundering herd against token issuers
Getting Help
- Questions or problems: Join the OpenFGA discussions or community
- Bug reports and feature requests: Open an issue in openfga/go-sdk
- Cross-SDK issues: Also report in openfga/sdk-generator and link the issues
- Security vulnerabilities: Do not use the public issue tracker. Follow the Responsible Disclosure Program