Shipeasy
SDKsReferenceGo

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…

Generated from the SDK's own /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:

  • defaultConfigure does a one-shot fire-and-forget fetch in the background, then never refreshes. Ideal for short-lived / serverless processes.
  • Poll: trueConfigure does an initial fetch plus a periodic background refresh (default interval 30s, re-tuned from the edge's X-Poll-Interval header), so flags stay fresh without a redeploy. Use this for long-running servers.
  • NoInitialFetch: true — suppresses even the one-shot fetch (the init=false escape hatch). Ignored when Poll is 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 ClientGetFlag, 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 (GetFlagfalse, GetConfig(nil, false), Assign → a not-enrolled Assignment, GetKillswitchfalse). 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 any Override* values.
  • IsTrackingEnabled — usage telemetry / "any outside logging" only. Forced off whenever IsNetworkEnabled is 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):

  1. A native runtime env var, checked in order: SHIPEASY_ENV, then APP_ENV, then GO_ENV, then ENV. A value of production/prod (case-insensitive) ⇒ production; any other present value (development/staging/test/…) ⇒ not production.
  2. If none of those are set, fall back to the SDK's own Env option — which already defaults to "prod", so a real production deploy stays ON by default while Env: "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_..."
Was this page helpful?
Updated July 25, 2026

On this page