sync.RWMutex, and lets multiple goroutines call GetFeatureConfig concurrently without contention. The client integrates with Go’s context package so you can control its lifetime cleanly.
Requirements
Go 1.22.10 or later.Installation
Quick start
Store your API key in an environment variable and read it with
os.Getenv("FOFF_API_KEY"). Never hard-code it in source files.Configuration
Configure the client using theconfig.Config struct.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
APIKey | string | Yes | Your Foff API key. | |
BaseURL | string | Yes | Base URL of the Foff API. Use https://foff.twospoon.ai/live. | |
Scope | string | Yes | The scope to fetch configs for (e.g. production, staging). | |
PollingInterval | uint32 | No | 10 | How often, in seconds, to poll for config updates. |
Validation
NewClient validates the config before creating the client. It returns an error if APIKey, BaseURL, or Scope is empty.
Polling interval normalization
The polling interval is clamped to safe bounds automatically before polling starts.Creating a client
NewClient performs the following steps in order:
Normalize the polling interval
Clamps
PollingInterval to the range 10–3600 seconds (unless it is 0).Fetch all configs (blocking)
Makes an HTTP
GET request to {BaseURL}/api/v1/scopes/{scope}/configs and populates the in-memory cache.The initial fetch is blocking. If the Foff API is unreachable or returns an error at startup,
NewClient returns that error immediately. Check the error and handle it at startup so your process fails fast rather than serving stale or empty configs.Resolving feature configs
GetFeatureConfig(featureName string, orderedHierarchy []string) interface{}
Returns the config value for a feature, resolved against a hierarchy. The call reads from the in-memory cache — no network request is made.
Hierarchy resolution algorithm
The SDK resolves from most-specific to least-specific:- It checks the full combination first —
org-1 + team-a + user-123. - It then tries shorter prefixes —
org-1 + team-a, thenorg-1. - If no override matches, it returns the feature’s
"default"value. - If the feature does not exist at all, it returns
nil.
Thread safety
All public methods onClient are safe for concurrent use. The internal cache is protected by a sync.RWMutex, so multiple goroutines can call GetFeatureConfig simultaneously without contention or data races.
Polling
WhenPollingInterval is greater than 0, the SDK polls GET {BaseURL}/api/v1/scopes/{scope}/configs in the background and replaces the in-memory cache on each successful response. The default polling interval is 10 seconds.
- Polling errors are silently ignored. The SDK continues serving the last successfully fetched config.
- The polling goroutine respects the context passed to
NewClient. Cancelling that context stops polling. - Calling
c.Close()also stops the polling goroutine and releases all resources.
Recommended polling intervals
| Use case | Interval |
|---|---|
| Near-real-time | 10s |
| Standard | 30–60s |
| Low-traffic / batch | 300–600s |
Closing the client
Callc.Close() when your application shuts down to stop the background polling goroutine:
defer c.Close() immediately after a successful NewClient call is the recommended pattern. It ensures the goroutine is always stopped, even if your main function returns early.