Skip to content

Architecture

Nexus is one Go binary (nexus, Gin on :8080) backed by PostgreSQL. It has no controllers and no Custom Resources. Agents run in a separate runtime pool of Astromesh pods, which Nexus reaches over HTTP.

ComponentWhat it does
Dual authResolves the tenant from a Bearer JWT (with X-Tenant-ID) or an X-API-Key. A third credential, the per-run token, is accepted only on /api/v1/runs/*.
Agent registryStores specs and their append-only version history.
Invocation coreThe run path shared by the REST and WebSocket adaptors (below).
Operator planeTariffs, plans, subscriptions, cross-tenant reads and billing, behind the operator role. Every mutation is audited.
ConsoleA React SPA embedded in the binary and served at /. See Operations.
Background jobsThe stale-invocation sweep and the monthly period close, both inside nexus-api.
Runtime poolAstromesh pods pinned to one runtime version. Agents are held in memory; conversational memory lives in Redis.

Every run, synchronous or streamed, goes through the same prologue:

  1. Idempotency. An Idempotency-Key becomes the invocation id. A replayed key returns the existing invocation without dispatching; a key owned by another tenant is 409.
  2. Resolve and isolate. The agent is looked up inside the caller’s tenant only. An agent with no live version answers 422.
  3. Admit before executing. The invocation is registered against the tenant’s plan (and its subscription’s plan, if any) in one transaction: concurrency, requests per minute and credits for the period. A rejection is recorded with its reason. See Plans & Billing.
  4. Prepare the spec. metadata.name becomes t_<tenant>__<agent>, and so do the names of agents referenced as type: agent tools. max_tokens and max_iterations are clamped to the plan’s ceilings.
  5. Load. Nexus remembers the sha256 of the spec it last pushed per agent and serializes pushes with a per-agent lock. An unchanged spec is not pushed again, a restarted runtime (404) is reloaded, and referenced sub-agents are loaded too, up to 16 per invocation.
  6. Run. The runtime receives the query, the session id (namespaced as t_<tenant>__<agent>__<session>), the caller’s context, the tenant’s connection bundle and, when Herald is configured, a per-run token.
  7. Price and close. Per-model usage is priced against the tariffs in force when the run started. The invocation closes with its status, credits and costs.

The stale sweep marks any invocation still in_progress after --sweep-max-age (default 300 s) as interrupted, so a crashed run never holds a concurrency slot forever.

StatusMeaning
in_progressAdmitted and running.
okFinished; priced.
errorThe runtime or provider failed.
timeoutExceeded --run-timeout (default 120 s).
cancelledA streaming client closed its socket; charged zero credits.
rejectedRefused at admission; error_code holds the reason.
interruptedClosed by the stale sweep.

All state is in one PostgreSQL schema that nexus creates and migrates itself at boot. There is no migration job, and a second boot emits no DDL.

AreaTables
Identityusers (with is_operator), refresh_tokens, tenants, user_tenants, api_keys
Registryagents, agent_versions
Executioninvocations, invocation_model_usage, usage_counters
Pricingmodel_tariffs, plans
Subscriptionssubscriptions, subscription_counters, usage_events
Billingbilling_periods, charges
Connectionsconnections
Auditoperator_audit

Invariants the database enforces itself, not the application:

  • agent_versions, model_tariffs, plans and charges are append-only, guarded by triggers on UPDATE, DELETE and TRUNCATE. Changing a price files a new tariff row with a later effective_from.
  • Nothing is hard-deleted. Tenants, agents and API keys are soft-deleted, so a version keeps its author and a past invocation keeps its agent. Deleting a tenant revokes its keys, removes its owners’ access and cancels its active subscriptions in the same transaction.
  • A version’s checksum is sha256 of the spec as PostgreSQL stores it, so it can be recomputed from the row.

Publishing (POST/PUT /api/v1/agents) validates the spec against the astromesh/v1 Agent schema, accepts YAML or JSON, and appends a version with its number, date, author (the user, or the API key that wrote it) and checksum. An agent’s status is draft until it has a live version and active from then on.

Names are unique among a tenant’s live agents only, so a retired name can be reused. The by-id routes address a version history unambiguously; by-id is therefore not a valid agent name.

  • Every read and write is scoped by the tenant the credential resolved. The tenant never comes from a request body or, for connections and sends, from the path.
  • An API key reaches its own tenant only. A JWT reaches the tenants its user owns, selected with X-Tenant-ID; an operator’s JWT may name any tenant.
  • The run’s connection bundle is built from the database for the resolved tenant, never from anything the caller sent.
  • Provider and infrastructure costs never appear in customer-facing responses; they are on the operator plane only.
ComponentTechnology
APIGo 1.25, Gin
DatabasePostgreSQL 17 (JSONB)
ConsoleReact 19 + Vite, embedded with go:embed
Runtime poolThe astromesh image, version pinned in deploy/components/runtime-pool (0.63.0 at Nexus 0.26.4)
Conversation memoryRedis (allkeys-lru, no persistence)
DeployKustomize overlays, ArgoCD, ArgoCD Image Updater
Loggingzerolog