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_iamprovider, 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
ErrPublicKeyRestrictedfromGetSecret - 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); adevelopercredential 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) -
GetSecretblocked withErrServiceKindMismatchonkind=frontend - 402 Payment Required handling (v0.2.0) -
ErrBillingreturned, background sync halted, cache served - AB Testing batch evaluation (v0.3.0) -
EvaluateAB(ctx, keys, userID, attributes)for the AB Testing add-on;ErrABAddonNotAvailableon 403 - File-type secrets (v0.3.0) -
SecretTypeFilematerialised as0600files inside a0700directory 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
Synccall 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=autofor 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.Printand everysloghandler 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.Configand*Clientimplementfmt.Stringer,fmt.GoStringerandslog.LogValuer, which together cover every channel:fmtsends%#vtoGoStringerrather thanStringer, and a handler resolvesLogValuerbefore formatting. See Logging. - A redirect is refused rather than followed.
net/httpcopies a custom header such asX-Nexus-API-Keyacross 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()andSubscribe(fn)report configuration changes.SnapshotRevisioncounts contents replacements, so a 304 or a stream reconnect leaves it unchanged;Subscriberuns the callback after the new snapshot is visible, whichStreamObserver.OnEventcannot do - it fires before the sync it announces.- The OpenFeature provider emits
PROVIDER_CONFIGURATION_CHANGEDand drops its per-user evaluation cache when the configuration moves.openfeature.NewProviderreports an invalid option as an error whereNewpanics. - Fixes: a very large
Retry-Afterno longer overflows into “retry immediately”; a failedNewClientreleases the connection it opened; a developer credential expiring inside the refresh window is refused with the renew command; and an entry writtenhttps://svc.westyx.dev:443matches a client configuredhttps://svc.westyx.dev.
What’s new in v0.14.0
- The
developerWIF provider - the same code authenticates on a laptop and in a cluster. Runwestyx dev setup --service=<name>once, andWIFConfig{Enabled: true}is the entire configuration on your machine. The SDK finds the session the Westyx CLI already obtained -$WESTYX_DEV_TOKENfirst, then the CLI’sdev-credentials.jsonmatched 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 sowestyx dev setuptakes effect without a restart. - A payload producer is told which service it is producing a credential for.
ExchangePayloadFuncreceives anexus.ExchangeTargetcarrying the client’sBaseURLhost, sowifaws.ExchangePayload(ctx, wifaws.Config{})needs noServerID- the host signed intoX-Nexus-Server-IDcomes from the client that will send the request. WIFConfig.Provideris anexus.WIFProviderwithValid()andString(), the same closed domain the Nexus backend models. An unknown provider is reported byNewClientwith 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
stringvariable needsstring(...); a customExchangePayloadgains anexus.ExchangeTargetparameter;wifaws.Config.ServerIDis optional and a value disagreeing with the client’s host is refused.Provider: WIFProviderAWSIAMtogether withTokenSourceis 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
0700directory (nexus-secrets-*under the system temp directory) lazily on the first file-type secret and writes the0600files inside it, so a file left behind by an abrupt exit sits where no other local user can enter.Closeremoves 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 flatos.TempDir()paths, read the path fromGetSecretFilePath. Closeis documented as the only cleanup point for materialised secrets. Go runs no code onos.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
Synccalled 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’sis_activestate together with whether the flag is in the cache at all - a distinctionGetFlagcannot express.- Breaking:
EvaluateAB’sattributesismap[string]string, and an OpenFeature boolean evaluation of a flag the snapshot does not hold returnsFLAG_NOT_FOUNDrather than your default with aSTATICreason. A non-string attribute value was already rejected by the service with400, 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_iamWIF provider moved togitlab.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
godirective 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, notfloat64(breaking).float64cannot 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.Loggeris a*slog.Logger(breaking). Records carry attributes plus acomponentfield rather than pre-formatted text, so the output is aggregatable. Any backend works - zap, zerolog and logrus all provide anslog.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;Closeis 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.
TTLandSSEReconnectCooldownare 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, openedO_EXCLandO_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
govulncheckplusgosec,errorlint,bodyclose,noctxandcontextcheckrun in CI.
What’s new in v0.10.1
- Config reads are deep-copied -
GetConfigandGetAllConfigsreturn a deep copy of object/array config values, so mutating a returned nestedmap/slice can no longer corrupt the shared cache or race a background refresh. Scalars are unaffected. - Clean shutdown -
Closecancels all background work (TTL refresh, WIF session refresh) and waits for it to finish before removingfile-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.ymlplus a pinnedgolangci-lintstage covering the root andopenfeature/modules.
What’s new in v0.10.0
- Version alignment across the Westyx Nexus SDK suite.
What’s new in v0.9.0
aws_iamWIF 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 STSGetCallerIdentityrequest (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 signedX-Nexus-Server-IDis your service’s ownBaseURLhost - a captured request is valid for that one service only, and there is nothing to configure. See Workload identity.- Azure Workload Identity (AKS) - the
azureprovider now prefers the projected federated token file ($AZURE_FEDERATED_TOKEN_FILE) before falling back to IMDS; auto-detection probes the file too. - Security hardening -
NewClientrejects plain-httpBaseURLs (loopback excepted); the Azure IMDS path refuses the generic default audience (must beapi://<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_iampayload 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.Clientand implements the OpenFeatureFeatureProviderinterface.EvaluationContextis 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 theRunStreamgoroutine started byNewClient; 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,DeleteSecretVersionfor programmatic secret management. secret keys only; public keys getErrPublicKeyRestrictedimmediately 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 yourBaseURLfromnexus.westyx.devto<slug>.westyx.dev. - Quarantine handling (v0.4.0) - new
429quarantine response pauses sync until expiry.OnQuarantined(reason, expiresAt)observer callback added. Retry-After-aware SSE 429 handling -RunStreamnow sleeps the indicated delay (clamped[5 s, 5 min]) and reconnects automatically when the server returns429 Too Many Requestswith a parseableRetry-Afterheader. The reconnect does NOT count toward the transport-failure threshold.
Module
The module is served directly viaproxy.golang.org - there is no separate package registry.
