Configuration
Call Configure once at process start. It stores the api key and the optional Attributes transform as a package global, and kicks off a background fetch so a…
/docs/ — also served raw at https://shipeasy-ai.github.io/sdk-go/pages/configuration.md.Configure — the front door
Call Configure once at process start. It stores the api key and the
optional Attributes transform as a package global, and kicks off a background
fetch so a later NewClient(user).GetFlag() resolves against real rules without
any explicit init.
shipeasy.Configure(shipeasy.Options{
APIKey: os.Getenv("SHIPEASY_SERVER_KEY"),
Attributes: func(u any) shipeasy.User {
acct := u.(*Account)
return shipeasy.User{"user_id": acct.ID, "plan": acct.Plan}
},
})Configure is first-config-wins (idempotent): the first call registers the
configuration and starts the fetch; subsequent calls are no-ops. After it runs,
build a cheap user-bound Client per request with NewClient(user).
For the full Options field table see Installation.
The Attributes transform & identity default
Attributes func(any) shipeasy.User maps your user value (any shape) to the
Shipeasy attribute map used for every evaluation. It is applied once in
NewClient(user) and the result is cached on the bound Client.
If you omit it, the identity transform is used: a shipeasy.User (or a
map[string]any) passed to NewClient is used as-is; nil becomes an empty
map; any other type degrades to an empty map (unidentified user) with a warning.
init / poll vs one-shot
You never start the fetch yourself — Configure owns the fetch lifecycle, and
two Options fields choose its shape:
- default —
Configuredoes a one-shot fire-and-forget fetch in the background, then never refreshes. Ideal for short-lived / serverless processes. Poll: true—Configuredoes an initial fetch plus a periodic background refresh (default interval 30s, re-tuned from the edge'sX-Poll-Intervalheader), so flags stay fresh without a redeploy. Use this for long-running servers.NoInitialFetch: true— suppresses even the one-shot fetch (theinit=falseescape hatch). Ignored whenPollis true.
// Long-running server that wants live updates:
shipeasy.Configure(shipeasy.Options{
APIKey: os.Getenv("SHIPEASY_SERVER_KEY"),
Poll: true,
})Change listeners — OnChange
When polling is on (Poll: true), register a callback fired after a background
poll loads new data (a 200, not a 304). It returns a cancel func:
cancel := shipeasy.OnChange(func() {
log.Println("flags/experiments changed; re-render or warm caches")
})
defer cancel()OnChange requires Configure(Options{Poll: true}) — no poll runs otherwise, so
the listener never fires. A panicking listener is recovered and logged so it
can't take down the poll loop.
Fail-safe reads & the LogLevel option
Reads never panic into your request path. Every runtime read on the bound
Client — GetFlag, GetFlagOr, GetFlagDetail, GetConfig, GetConfigOr,
GetKillswitch, Universe(name).Assign() — is wrapped so that even an
unexpected panic is caught, logged, and the read returns its documented safe
default (GetFlag → false, GetConfig → (nil, false), Assign → a
not-enrolled Assignment, GetKillswitch → false). You never need a
recover() around a read. (Setup mistakes still fail loudly — NewClient(user)
before Configure panics on purpose.)
Options.LogLevel controls the SDK's own log verbosity. The SDK logs its
fire-and-forget failures (a failed Track/exposure/see() POST, a poll
error, or a recovered panic) with a [shipeasy] prefix. Levels, from quietest
to loudest, are silent, error, warn, info, debug; a message at level L
is printed only when your configured level is at least L. The default is warn;
an empty or unrecognized value also resolves to warn.
shipeasy.Configure(shipeasy.Options{
APIKey: os.Getenv("SHIPEASY_SERVER_KEY"),
LogLevel: "silent", // fully quiet the SDK's background chatter in prod
})SDK self-monitoring
When one of those last-resort guards actually fires — a bug on Shipeasy's side,
not yours — the SDK also reports that internal error to Shipeasy's own
project so we can track and fix SDK bugs across every app the SDK runs in. It
is a dedicated, baked-in destination (a public client-key ingest credential),
entirely separate from your See() reporting: internal errors never land in your
project or Errors tab. The report carries only the error itself plus a stable,
deduped consequence (subject = the guarded operation, e.g. GetFlag), is
rate-limited, and is fire-and-forget — it can never slow down or break a read. It
is on by default and always off in test/offline mode. Opt out entirely with
DisableInternalErrorReporting: true:
shipeasy.Configure(shipeasy.Options{
APIKey: os.Getenv("SHIPEASY_SERVER_KEY"),
DisableInternalErrorReporting: true, // suppress the SDK self-monitoring channel
})Environment-derived egress defaults
The SDK is quiet by default outside production. Two *bool options control
outbound traffic, and both default ON in production and OFF in every other
environment, so an app that embeds the SDK never phones home from a dev machine
or CI unless it opts in:
IsNetworkEnabled— the master switch. When off the SDK is fully offline: it makes no outbound request at all (flag/experiment/config fetch,Track, exposure logging, internal error reports, and usage telemetry). Reads return your in-code defaults and anyOverride*values.IsTrackingEnabled— usage telemetry / "any outside logging" only. Forced off wheneverIsNetworkEnabledis off.
Both are *bool so an explicit false is distinguishable from "unset" (nil):
nil uses the environment-derived default; a non-nil value always wins.
How "production" is decided (is_production_env, mirrors every Shipeasy SDK):
- A native runtime env var, checked in order:
SHIPEASY_ENV, thenAPP_ENV, thenGO_ENV, thenENV. A value ofproduction/prod(case-insensitive) ⇒ production; any other present value (development/staging/test/…) ⇒ not production. - If none of those are set, fall back to the SDK's own
Envoption — which already defaults to"prod", so a real production deploy stays ON by default whileEnv: "dev"stays quiet.
Behaviour change (0.15.0): before this release the SDK always made network
calls and always sent usage telemetry (unless DisableTelemetry: true). Now, in a
non-production environment, it is offline by default. To restore the old
always-on behaviour, either set a production env var or pass the switch
explicitly:
// Option A — declare the runtime production for egress:
// export SHIPEASY_ENV=production
//
// Option B — force it on regardless of environment:
netOn := true
shipeasy.Configure(shipeasy.Options{
APIKey: os.Getenv("SHIPEASY_SERVER_KEY"),
IsNetworkEnabled: &netOn, // make every outbound request (fetch/Track/…) again
IsTrackingEnabled: &netOn, // and re-enable usage telemetry
})To go the other way — force the SDK fully offline even in production — pass
IsNetworkEnabled: &off (with off := false). Test/offline configs
(ConfigureForTesting / ConfigureForOffline) are always offline regardless of
these options.
Env-var convention
The SDK authenticates with your project's server key. Read it from the environment — never hard-code it:
export SHIPEASY_SERVER_KEY="sk_server_..."