Skip to main content
Official Go SDK for Westyx Nexus - the centralised secrets, feature flags, and config service.

Highlights

  • Go 1.25+ - the core module has no dependencies at all; the AWS SDK is needed only to sign an STS request for the aws_iam provider, so that provider ships as its own module
  • Thread-safe TTL cache with atomic snapshot replacement and ETag/304 support
  • Background refresh - caller goroutines are never blocked on cache expiry
  • SSE live updates - started automatically by NewClient; propagates remote changes within milliseconds; falls back to TTL polling after 3 transport errors
  • Public-key vs secret-key access control - public-key clients get ErrPublicKeyRestricted from GetSecret
  • Sentinel errors - errors.Is(err, nexus.ErrUnauthorized) style, idiomatic Go
  • Workload Identity Federation (v0.2.0) - Kubernetes / AWS IRSA / GCP / Azure auto-detected; bearer-JWT auth with automatic refresh; AWS IAM (aws_iam) for ECS/Fargate/Lambda/plain-EC2 (v0.9.0); a developer credential so a laptop authenticates the same way (v0.14.0)
  • Stream observer hooks (v0.2.0) - opt-in structured callbacks for ops dashboards and load-test tooling
  • Service kind awareness (v0.2.0) - GetSecret blocked with ErrServiceKindMismatch on kind=frontend
  • 402 Payment Required handling (v0.2.0) - ErrBilling returned, background sync halted, cache served
  • AB Testing batch evaluation (v0.3.0) - EvaluateAB(ctx, keys, userID, attributes) for the AB Testing add-on; ErrABAddonNotAvailable on 403
  • File-type secrets (v0.3.0) - SecretTypeFile materialised as 0600 files inside a 0700 directory private to the client (nexus-secrets-* under the system temp directory), with SHA-256-derived names so neither the key name nor the value appears in the path; GetSecretFilePath(key) exposes the path; Close() removes the directory

What’s new in v0.16.1

  • A 402 Payment Required no longer suspends background refresh indefinitely. The client retries on its own every five minutes instead of requiring an explicit Sync call to notice the tenant’s invoices were settled.
  • The Go toolchain is pinned to 1.25.13, closing four disclosed standard-library CVEs. A build environment that pins an older Go image needs GOTOOLCHAIN=auto for this pin to actually take effect.

What’s new in v0.15.0

  • Printing a client or its config is safe. %v, %+v, %#v, %s, fmt.Print and every slog handler render a redacted form - the base URL, the key type, the WIF provider and how many configs, secrets and flags are cached - never the API key and never a secret value. Config and *Client implement fmt.Stringer, fmt.GoStringer and slog.LogValuer, which together cover every channel: fmt sends %#v to GoStringer rather than Stringer, and a handler resolves LogValuer before formatting. See Logging.
  • A redirect is refused rather than followed. net/http copies a custom header such as X-Nexus-API-Key across a redirect even when the domain changes, and the response that followed would become the cache. A Nexus endpoint does not redirect, and the error names the location it was sent to.
  • A secret operation is refused whenever the backend classifies the key as public, not only when it carries the wxp_ prefix. KeyType() reports the effective type.
  • SnapshotRevision() and Subscribe(fn) report configuration changes. SnapshotRevision counts contents replacements, so a 304 or a stream reconnect leaves it unchanged; Subscribe runs the callback after the new snapshot is visible, which StreamObserver.OnEvent cannot do - it fires before the sync it announces.
  • The OpenFeature provider emits PROVIDER_CONFIGURATION_CHANGED and drops its per-user evaluation cache when the configuration moves. openfeature.NewProvider reports an invalid option as an error where New panics.
  • Fixes: a very large Retry-After no longer overflows into “retry immediately”; a failed NewClient releases the connection it opened; a developer credential expiring inside the refresh window is refused with the renew command; and an entry written https://svc.westyx.dev:443 matches a client configured https://svc.westyx.dev.

What’s new in v0.14.0

  • The developer WIF provider - the same code authenticates on a laptop and in a cluster. Run westyx dev setup --service=<name> once, and WIFConfig{Enabled: true} is the entire configuration on your machine. The SDK finds the session the Westyx CLI already obtained - $WESTYX_DEV_TOKEN first, then the CLI’s dev-credentials.json matched to your endpoint - and uses it directly as the bearer credential. No token exchange happens on this path; the CLI performed it. The credential is probed last, after every cloud provider, so a real workload credential always wins, and the file is re-read on every refresh so westyx dev setup takes effect without a restart.
  • A payload producer is told which service it is producing a credential for. ExchangePayloadFunc receives a nexus.ExchangeTarget carrying the client’s BaseURL host, so wifaws.ExchangePayload(ctx, wifaws.Config{}) needs no ServerID - the host signed into X-Nexus-Server-ID comes from the client that will send the request.
  • WIFConfig.Provider is a nexus.WIFProvider with Valid() and String(), the same closed domain the Nexus backend models. An unknown provider is reported by NewClient with the accepted set named.
  • An override that names its provider is logged as that provider rather than as custom.
  • Breaking, three one-line call shapes: a typed constant assigned to a string variable needs string(...); a custom ExchangePayload gains a nexus.ExchangeTarget parameter; wifaws.Config.ServerID is optional and a value disagreeing with the client’s host is refused. Provider: WIFProviderAWSIAM together with TokenSource is now rejected at construction - the two describe different kinds of credential.

What’s new in v0.13.0

  • File-type secrets are materialised into a directory private to the client. Each client creates its own 0700 directory (nexus-secrets-* under the system temp directory) lazily on the first file-type secret and writes the 0600 files inside it, so a file left behind by an abrupt exit sits where no other local user can enter. Close removes the directory with everything in it. The file name no longer carries an instance id - the directory’s random suffix keeps clients apart. If you logged or asserted on the previous flat os.TempDir() paths, read the path from GetSecretFilePath.
  • Close is documented as the only cleanup point for materialised secrets. Go runs no code on os.Exit, finalizers are not guaranteed to run before termination, and a library must not install a signal handler - so there is deliberately no exit backstop.
  • A Sync called on a closed client leaves nothing on disk.

What’s new in v0.12.0

  • Boolean flag evaluation is per-user. With a targeting key in the EvaluationContext, the OpenFeature provider resolves the flag through the AB Testing add-on, applying its rollout percentage and cohort rules to that identity. Without a targeting key it reads the synced snapshot, exactly as before.
  • FindFlag(key) returns the flag’s is_active state together with whether the flag is in the cache at all - a distinction GetFlag cannot express.
  • Breaking: EvaluateAB’s attributes is map[string]string, and an OpenFeature boolean evaluation of a flag the snapshot does not hold returns FLAG_NOT_FOUND rather than your default with a STATIC reason. A non-string attribute value was already rejected by the service with 400, so nothing that worked before stops working.

What’s new in v0.11.0

A correctness, security and packaging release. Several items are breaking; each is a one-line change at the call site, and the README carries a migration table.
  • The core module has no dependencies at all. The aws_iam WIF provider moved to gitlab.com/westyx/nexus/sdk/go/wifaws (breaking). Go has no optional dependencies, so while the SigV4 signer lived in the core module every consumer’s build carried the AWS SDK - including services that run nowhere near AWS. Every other provider is unchanged and still needs nothing.
  • The minimum is Go 1.25. The go directive is a hard floor for everyone who imports the module, transitively, and Go supports the two newest majors - so naming a patch release of the newest one excluded every Go 1.25 install. CI runs the suite on both.
  • Config numbers are json.Number, not float64 (breaking). float64 cannot represent an integer above 2^53 exactly, so an id or an amount in minor units came back altered with nothing to indicate it. See Config values.
  • New GetConfigAs[T] decodes a config value straight into your own type from the exact bytes the backend sent.
  • Config.Logger is a *slog.Logger (breaking). Records carry attributes plus a component field rather than pre-formatted text, so the output is aggregatable. Any backend works - zap, zerolog and logrus all provide an slog.Handler. See Logging.
  • The SSE stream has an idle deadline (StreamIdleTimeout, default 90 s = three missed keepalives), so a connection that is black-holed rather than closed is detected and reconnected instead of leaving a read blocked with no deadline.
  • New StreamConnected() reports whether live updates are arriving right now.
  • The stream outlives the context passed to NewClient (breaking). That context bounds construction; Close is what ends a client.
  • The background refresh backs off after a failure, so the retry rate is the SDK’s choice rather than a function of how often the application reads.
  • A panicking observer callback is contained and reported. In Go this is not cosmetic: a panic can only be recovered on the goroutine that raised it, and these callbacks run on a goroutine the SDK starts.
  • TTL and SSEReconnectCooldown are validated at construction, with the offending value named, rather than silently clamped at the point of use.
  • File-type secrets are created 0600, with the mode applied at creation rather than by a chmod afterwards, opened O_EXCL and O_NOFOLLOW; each client scopes its paths with an instance id so two clients never share a file.
  • Every response the SDK decodes is size-bounded, and govulncheck plus gosec, errorlint, bodyclose, noctx and contextcheck run in CI.

What’s new in v0.10.1

  • Config reads are deep-copied - GetConfig and GetAllConfigs return a deep copy of object/array config values, so mutating a returned nested map/slice can no longer corrupt the shared cache or race a background refresh. Scalars are unaffected.
  • Clean shutdown - Close cancels all background work (TTL refresh, WIF session refresh) and waits for it to finish before removing file-secret temp files; a closed client never starts a new background sync, and no orphaned secret files are left on disk.
  • Lint enforced in CI - committed .golangci.yml plus a pinned golangci-lint stage covering the root and openfeature/ modules.

What’s new in v0.10.0

  • Version alignment across the Westyx Nexus SDK suite.

What’s new in v0.9.0

  • aws_iam WIF provider (AWS IAM Caller Identity) - authenticates non-EKS AWS compute (ECS/Fargate, Lambda, plain EC2) that has IAM credentials but no OIDC token. The SDK SigV4-signs an STS GetCallerIdentity request (never sent to AWS) and posts it to /v1/auth/token-exchange; Nexus replays it against a pinned STS endpoint to prove your IAM role. The signed X-Nexus-Server-ID is your service’s own BaseURL host - a captured request is valid for that one service only, and there is nothing to configure. See Workload identity.
  • Azure Workload Identity (AKS) - the azure provider now prefers the projected federated token file ($AZURE_FEDERATED_TOKEN_FILE) before falling back to IMDS; auto-detection probes the file too.
  • Security hardening - NewClient rejects plain-http BaseURLs (loopback excepted); the Azure IMDS path refuses the generic default audience (must be api://<client-id>); the token-exchange response read is bounded; AWS auto-detection stats the IRSA token file instead of trusting the env var.
  • WIF test suite - per-provider token-source tests, auto-detect dispatch, the Azure audience guard, and the signed aws_iam payload shape.

What’s new in v0.8.0

  • OpenFeature provider - new openfeature/ sub-module (gitlab.com/westyx/nexus/sdk/go/openfeature). Wraps an initialized *nexus.Client and implements the OpenFeature FeatureProvider interface. EvaluationContext is ignored - Nexus has no per-user targeting. See OpenFeature integration.

What’s new in v0.5.1

  • Security improvements - exception messages contain only status codes; file-type secret paths are fully hashed so key names are never visible on the filesystem.
  • Close() cancels the stream - Close() now stops the RunStream goroutine started by NewClient; no need to cancel the parent context separately.
  • CI improvements - tests run on merge requests; publish is restricted to main-branch tags.

What’s new in v0.5.0

  • Write API - SetSecret, DeleteSecret, DeleteSecretVersion for programmatic secret management. secret keys only; public keys get ErrPublicKeyRestricted immediately without a network call.
  • ErrRateLimited - new sentinel for HTTP 429 on write endpoints.

What’s new in v0.4.0

  • Path prefix update - API endpoints moved from /api/v1/ to /v1/; update your BaseURL from nexus.westyx.dev to <slug>.westyx.dev.
  • Quarantine handling (v0.4.0) - new 429 quarantine response pauses sync until expiry. OnQuarantined(reason, expiresAt) observer callback added.
  • Retry-After-aware SSE 429 handling - RunStream now sleeps the indicated delay (clamped [5 s, 5 min]) and reconnects automatically when the server returns 429 Too Many Requests with a parseable Retry-After header. The reconnect does NOT count toward the transport-failure threshold.

Module

The module is served directly via proxy.golang.org - there is no separate package registry.
Latest release: v0.16.1 (2026-08-30 - a 402 Payment Required throttles background refresh to a five-minute retry instead of suspending it indefinitely).

Quick start

Pages