Nexus 0.2.3 is the release that makes us comfortable pointing teams at Nexus for production use. Not because it adds the most features, but because it fixes the things that needed to be right before we could say that: a proper internal architecture, observable metrics on every credential operation, a set of silent-but-real Broker bugs, and client SDKs that make the integration surface usable from every language agents are actually built in.
This post covers everything that shipped across the 0.2.1, 0.2.2, and 0.2.3 cycle, from the commit history up.
The Service Layer Refactor
Before this release, the Broker's business logic lived inside its HTTP handlers. The OAuth consent flow, the token exchange, the credential retrieval — all of it was tangled into the handler functions that also handled request parsing and response writing. That made the code hard to test, impossible to mock, and fragile whenever we needed to change behavior across multiple endpoints consistently.
PR #57 extracted all of that into a formal ConnectionService interface with a concrete implementation backed by typed repositories:
type ConnectionService interface {
CreateConsentSpec(ctx context.Context, req CreateConsentRequest) (*ConsentSpecResponse, error)
ExchangeCodeForTokens(ctx context.Context, state, code, errorParam, errorDesc string) (string, bool, error)
GetToken(ctx context.Context, connectionID uuid.UUID) (map[string]interface{}, string, error)
GetTokenByWorkspaceAndProvider(ctx context.Context, workspaceID, providerName string) (map[string]interface{}, string, error)
SaveCredential(ctx context.Context, state string, credentials map[string]interface{}) (string, error)
Refresh(ctx context.Context, connectionID uuid.UUID) (*RefreshResponse, error)
}
The handlers now call the service. The service calls the repositories. The repositories talk to PostgreSQL. Each layer has a defined interface, which means each layer can be tested independently. The refactor shipped with 306 new unit tests for ConnectionService alone, plus an architecture_test.go that enforces the layer boundaries at the Go compiler level — if a handler ever directly imports a repository package again, the test fails.
The numbers: 1,228 lines added, 1,341 lines deleted. The handler files got dramatically shorter. The service layer got the logic they had been carrying for months.
Five New Prometheus Metrics
When we looked at what Nexus was actually exporting to Prometheus before this release, the picture was embarrassing. Token retrieval was partially instrumented, but token refresh was completely invisible. API key credential captures had no signal. And the provider label on oauth_token_get_total was hardcoded to the string "unknown" for every request.
PR #58 fixed all of it. Five new metrics now cover the full credential lifecycle:
oauth_token_refreshes_total(counter,status={success,error}) — every on-demand token refresh, success and failure separatelyoauth_refresh_duration_seconds(histogram) — end-to-end duration of the token refresh flow, giving you P50/P95/P99 on the most latency-sensitive operation in the Brokeroauth_credential_captures_total(counter,status={success,error}) — API key and Basic Auth credential captures through theSaveCredentialhandlernexus_connections_total(gauge,status) — live count of connections by status (active,pending,attention_required), backed by a background poller that queries the database every 30 secondsnexus_db_operation_duration_seconds(histogram,repo,method) — per-method database latency for everyConnectionRepositoryandTokenRepositoryoperation, implemented as instrumented decorator wrappers that add zero coupling to the repository implementations
The provider label fix on oauth_token_get_total is worth calling out specifically. GetToken now fetches the provider name from the database and uses it as the label value. Every dashboard that was showing provider="unknown" for every token retrieval will now show the actual provider name on upgrade.
Critical Broker Fixes
Four bugs that had been in the Broker since before 0.2.0 are fixed in this release.
Silent JSON serialization failures. json.Marshal errors in the service layer were being swallowed. A serialization failure would produce an empty response body with a 200 status code. The call site would see a successful HTTP response and an empty payload, with no error to surface. Fixed: json.Marshal errors now propagate correctly and return a 500 with an error body.
Context not propagated to outbound HTTP. All outbound HTTP requests from the Broker to OAuth providers and OIDC discovery endpoints were being made without the request context attached. That meant request cancellation and deadlines from the caller were silently ignored. Every outbound call now passes the context from the inbound request.
Double-slash URL construction. Token URLs were being built by string concatenation in some paths, producing URLs like https://login.salesforce.com//services/oauth2/token when the base URL had a trailing slash. Fixed using url.JoinPath throughout.
OIDC discovery with missing token endpoint. During OIDC auto-discovery, if the discovery document was missing the token_endpoint field, the Broker would proceed with an empty string as the token URL and fail later with a cryptic network error. Fixed: TokenEndpoint is now validated immediately after discovery, and the connection request fails with a clear error if the endpoint is absent.
There is also a typed ServiceError type that carries an HTTP status code through the service layer. Before this, service layer errors were being translated to 500s regardless of their actual cause. A connection_not_found error now correctly returns 404. An authentication failure returns 401. The right status code reaches the SDK caller.
The Gateway Resolve Endpoint
A new endpoint, GET /v1/resolve, is now wired end-to-end through the Gateway. It accepts a workspace ID and provider name and returns the current valid token for that combination. This is the endpoint the SDKs call in the MCP token resolution flow — the pattern where a stateless MCP server resolves a credential per-invocation rather than managing a persistent connection.
The endpoint was partially implemented in earlier versions but not wired to a live Broker route. It is now complete, tested with an integration smoke test against a live Gateway, and documented in the SDK reference.
Three SDKs for Go, Python, and TypeScript
The SDK work is the most visible part of this release, but it sits on top of the Broker fixes and the service layer refactor. Getting the architecture right first meant the SDKs have a stable API surface to call against.
The Go SDK gains AuthenticatedHTTPClient, a standard *http.Client whose custom RoundTripper resolves a token from the Gateway on every request and injects it with the correct strategy. Pass it to any library that accepts an http.Client and your agent is authenticated without touching credential values in application code.
The Python SDK is a new standalone package extracted from the internal jarviscore.nexus module. Zero runtime dependencies, pure standard library. It ships authenticated_session, which returns a requests.Session-compatible object that resolves and caches tokens transparently. The TokenCache evicts tokens before their actual expiry to prevent serving stale credentials in high-throughput agent runtimes.
The TypeScript SDK replaces nexus-mcp-adapter entirely. It is an ESM-first package with a createFetcher method that returns a fetch-compatible function pre-bound to a workspace and provider. It includes a TokenManager that handles caching and TTL eviction. All internal logging goes to console.error, which is the only correct choice for a library that runs inside MCP stdio servers — writing to stdout corrupts the JSON-RPC stream.
All three SDKs handle multi-strategy credential injection. When the Gateway returns a credential, it includes a strategy field describing how to apply it: OAuth2 bearer token, custom header injection with a specified header name, query parameter, or API key. The SDK reads the strategy and applies it. Your agent code never contains a switch statement over auth strategy types.
pip install nexus-sdk # Python, zero deps
npm install @dromos/nexus-sdk # TypeScript ESM
go get github.com/Prescott-Data/nexus-framework/nexus-sdk@latest
Release Pipeline
An automated GitHub Actions release workflow is now in place. On version tag push, the workflow validates the tag against the VERSION file, runs all tests across Go, Python, and TypeScript, and produces release artifacts. The VERSION file is the single source of truth: 0.2.3.
Upgrade Notes
The service layer refactor is internal and requires no changes to deployments or integrations. The new Prometheus metrics are additive — existing dashboards continue to work, and the corrected provider label on oauth_token_get_total will require updating any alert rules that were filtering on provider="unknown".
If you are running the Go SDK, pull the latest tag. The Python and TypeScript SDKs are new packages with no prior version to migrate from.
The full changelog, SDK documentation, and API reference are at nexus.developers.prescottdata.io. Source and release on GitHub.