Highlights
- Async-first -
tokio+reqwest, TLS viarustls, so no OpenSSL system dependency - Cache reads stay synchronous - they read in-memory state, so there is no
.awaitto write and nothing to block on blockingfeature - the same surface synchronously for applications with no runtime of their own; it works even inside another runtimetracingdiagnostics - spans and structured events, a no-op until you install a subscriber, and no credential is ever recorded- Clone is cheap (
Arcbump) - pass clones freely to tasks and to axum/actix handlers - Thread-safe TTL cache - one snapshot shared by every clone; concurrent reads on a stale snapshot trigger a single coalesced refresh
- ETag/304 support - nothing is downloaded when nothing has changed
- SSE live updates via
run_stream()- atokiotask with a connect deadline and an idle deadline that resets on every byte; falls back to TTL polling after 3 transport errors and retries on a schedule - Public-key vs secret-key access control - public-key clients get
PublicKeyRestrictedbefore any network call, and both secret accessors refuse afrontend-kind service - Owner-only file-type secrets - materialised
0600into a private0700directory per client; removed onclose()or when the last clone is dropped - Pattern-match on
NexusError- idiomatic enum variants,#[non_exhaustive], no string matching - Workload Identity Federation - Kubernetes / AWS IRSA / GCP / Azure auto-detected, plus AWS IAM (
aws_iam) for ECS/Fargate/Lambda/plain EC2 behind a feature flag, adeveloperprovider for local machines, and a custom credential source for everything else - Write API -
set_secret,delete_secret,delete_secret_version(secret key only) - A/B testing -
evaluate_ab;AbAddonNotAvailableon 403 - OpenFeature - the separate
westyx-nexus-openfeaturecrate wraps an existing client
What’s new in v0.14.0-beta.2
- Three declared dependency floors raised to versions that actually build.
serde_jsonto1.0.25,tempfileto3.10.0, and the dev-onlymockitoto1.3.0- a-Z minimal-versionsbuild now runs every pipeline, so a declared floor cannot silently stop building again.
What’s new in v0.14.0-beta.1
- Breaking: secrets are
SecretString.get_secretreturns asecrecy::SecretString, read with.expose_secret()(both re-exported from the crate root). The value is zeroised on drop - so a replaced cache snapshot leaves no plaintext in freed memory - andDebugrenders it as[REDACTED]. - Credential-bearing requests refuse redirects. The API and stream clients set
redirect::Policy::none(), so a 3xx never resends the API key or the WIF session token to another host. - The public-key guard honours the backend’s classification. A key the backend reports as public is refused secret access whether or not it carries the
wxp_prefix - either signal is sufficient. - The
developercredential is matched on host and port, so two local services on the same host but different ports resolve to their own sessions.
What’s new in v0.13.0-beta.1
- A typed provider vocabulary.
WifProvideris a#[non_exhaustive]enum implementingDisplayandFromStr, so a provider read out of an environment variable is validated where it is read rather than deep inside a session refresh. - Breaking: one
credentialfield.WifConfig.providerandWifConfig.token_sourcecollapse intocredential: WifCredential, so a provider and a custom source cannot both be configured. See Workload identity for the one-line migration per break. - An async credential source that can express a whole exchange body.
WifCredentialSourceproduces either an OIDC token or the complete token-exchange payload, so a credential that is not an OIDC token fits without a change to the SDK.blocking_token_sourceadapts an existing synchronous closure. - The
developerprovider. On a machine wherewestyx dev setuphas run, the SDK sends the Nexus session token the CLI already obtained. It performs no token exchange and is probed last, so a real cloud credential always wins. - Contradictions rejected at construction, naming the fix:
aws_regionoutsideaws_iam, andaws_iamwithout theaws-iamcargo feature. - Printing a
NexusConfigor aWifConfigrenders the API key asset/unset.
What’s new in v0.12.0-beta.1
- The OpenFeature provider resolves boolean flags per user. An evaluation context carrying a targeting key selects a per-user evaluation through the A/B Testing add-on, so rollout percentages and cohort rules apply. One request covers every flag in the snapshot, results are memoised per user for 30 seconds, and concurrent evaluations for the same user coalesce into one call. See OpenFeature.
NexusProviderOptions-targeting_ttl,max_targeting_keysandaddon_suppression, each validated when the provider is built.find_flagon the blocking client, matching the async one: a disabled flag and an absent one are distinguishable on both surfaces.- Breaking:
evaluate_abtakes&HashMap<String, String>for its attributes on both clients. The service compares attribute values witheq,neqandin, so a string map is what a cohort rule can act on. Values built withjson!("pro")become"pro".to_string(). - Through the provider, a whole number written
5.0resolves as5. JSON has one number type; a fractional value resolves asTYPE_MISMATCHwith your default rather than being truncated, and values beyond the float range keep their exact value. - A composite config value containing a JSON null resolves as
TYPE_MISMATCH. The OpenFeature value model has no null, so such a value cannot be represented there; read it as raw JSON through the wrapped client.
What’s new in v0.11.0-beta.2
The largest rework in the crate’s history. It breaks the public API; Async migration lists the one-line change per break.- Async-first client.
create,sync,set_secret,delete_secret,delete_secret_versionandevaluate_abareasync fn. Cache reads stay synchronous, andspawn_blockingwrappers are no longer needed. blockingfeature for applications with no runtime of their own.tracinginstrumentation with structured fields on create, sync, the token exchange and the stream loop.- Owner-only file-type secrets. Each client materialises them into its own private
0700directory, and each file is created0600at creation viacreate_new+O_NOFOLLOWplus an atomic rename. Two clients never share a path, and a secret removed on the server is removed from disk. - Cleanup runs while the stream is active. The SSE task now holds only a weak reference, so dropping the last clone reaches the cleanup.
- Stream deadlines - a connect deadline plus an idle deadline that resets on every byte, keepalive comments included.
- One status-to-error mapping shared by every endpoint;
evaluate_abreportsRateLimitedon 429. - Bounded response bodies everywhere, with a size error instead of a truncated document.
- Coalesced refreshes - concurrent reads on a stale snapshot trigger one request, and snapshots cannot apply out of order.
- Billing back-off instead of a halt, cleared by the next successful sync.
- Configuration validated once, at construction, naming the option and the list index.
auth_expiringforces a refresh, and a 401 forces one refresh plus one retry.NexusError::TransportreplacesHttp, so the HTTP client is no longer part of the semver contract; the enum is#[non_exhaustive]and gainedConfigandResponseTooLarge.- Exact RFC 3339 date arithmetic, so a quarantine expiry between March and December of a leap year is no longer a day early, and an absurd expiry is clamped to 24 hours.
- OpenFeature provider - see OpenFeature.
- Edition 2024,
thiserror 2, and minimum supported Rust version 1.88, measured and verified by a CI job.
What’s new in v0.10.1-beta.1
- Public-key error message no longer mentions the retired
pk_prefix - the error surfaced when a public-key client attempts a secret operation reads “cannot read or write secrets with a public key”.
What’s new in v0.9.0-beta.1
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 own endpoint host - a captured request is valid for that one service only. Behind the optionalaws-iamcargo feature, off by default. See Workload identity.- Azure IMDS path - the
azureprovider prefers the projected federated token file ($AZURE_FEDERATED_TOKEN_FILE, AKS) and otherwise fetches an IMDS managed-identity token, requiring an explicitapi://<client-id>audience. - Security hardening - plain-http endpoints rejected (loopback excepted); a failed WIF refresh fails closed; token-exchange 400/403 map to
BadRequest/Forbidden; the slugHostheader is sent on the exchange; metadata and exchange reads are bounded;expires_inis clamped.
What’s new in v0.8.0-beta.2
- WIF GCP fix - the
gcpprovider fetches a real Google-signed OIDC identity token from the GCE metadata server. It previously read theGOOGLE_APPLICATION_CREDENTIALSkey file, which is not a JWT and was rejected by the token exchange. Auto-detection was aligned with the actual token sources.
What’s new in v0.5.1-beta.1
- Security - response bodies are no longer embedded in error messages;
SecretNotFoundtruncates the key to 128 characters; file-secret paths carry an opaque hash rather than the key name; WIF token files are whitespace-trimmed. - SSE -
flag.toggledtriggers a re-sync, and a429carrying a quarantine body pauses reconnects untilexpires_at.
Latest release
v0.14.0-beta.2 on crates.io. The crate is on the beta track: the public surface is settling release by release, and graduating to stable is a decision to take once it has held still for one.Documentation
Installation
Add the crate, pick your features, and check the Rust version floor.
Async migration
One line per break when moving from v0.10.x.
Configuration
Every option, its default, and what is rejected at construction.
API reference
Every method, with the errors it can return.
Caching behaviour
TTL, ETag, coalesced refreshes, and cleanup.
SSE live updates
Deadlines, reconnection, and the control events.
Workload identity
Keyless authentication on Kubernetes, AWS, GCP and Azure.
OpenFeature
The provider crate and its resolution mapping.
