Skip to main content
Spendra ships an on-prem package at infra/helm/spendra. The supported baseline is customer-managed Kubernetes with external Postgres, Supabase-compatible dashboard Auth, customer ingress/TLS, customer secret management, and customer observability/SIEM. Spendra does not bundle Postgres, an ingress controller, a secrets manager, a service mesh, Prometheus, or a SIEM. The chart deploys only the Spendra web, API/gateway, worker, and operational jobs.

Architecture

Deploy these components:
  • spendra-web: Next.js dashboard.
  • spendra-api: Fastify management API and OpenAI-compatible gateway.
  • spendra-worker: Graphile Worker background processor.
  • External Postgres 16, operated by the customer with HA, backups, PITR, and monitoring.
  • Supabase-compatible Auth/JWKS for dashboard sessions.
  • Customer ingress, TLS, optional mTLS/service mesh, secrets manager, log pipeline, and metrics scraper.
Recommended production starting point:

Required secret

Create one Kubernetes Secret and pass its name as secrets.existingSecret. Minimum keys:
Common optional keys include SENTRY_DSN, OPENROUTER_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, Azure OpenAI settings, Vertex AI settings, SMTP/notification provider secrets, and provider-account secret references. For production, manage this Secret through the customer secrets manager. External Secrets Operator, Vault Secrets Operator, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, and sealed-secret workflows are all acceptable as long as Kubernetes receives the expected environment variable names.

Build environment-specific images

Build the web image separately for each environment. The dashboard browser bundle reads NEXT_PUBLIC_* values at Next.js build time, so a web image built for integration must not be promoted unchanged into production. Use environment-specific tags such as 0.1.0-int and 0.1.0-prod:
The API and worker image contents are environment-neutral, but tag all three images with the same release/environment tag because the chart uses one image.tag value. Runtime endpoints, database credentials, provider secrets, and operational tokens come from secrets.existingSecret.

Install

Render and inspect the manifests before installing:
Install:
The chart fails rendering if secrets.existingSecret is not set. The Secret must include DATABASE_URL; production API startup rejects missing DATABASE_URL instead of falling back to an in-memory store. The chart runs database migrations, Graphile Worker migrations, and database smoke checks as Helm hooks by default. db:seed is disabled by default for on-prem. It inserts demo Acme data and a demo API key for local/demo environments, so it must not run in enterprise production installs. For an isolated demo environment only, explicitly set both jobs.seed.enabled=true and jobs.seed.allowDemoData=true; the seed hook is install-only and does not run on upgrades.

Ingress, TLS, and mTLS

Enable ingress with customer-owned hostnames and TLS secrets:
Terminate public TLS at ingress or load balancer. If mTLS is required, enforce it in the customer ingress, API gateway, or service mesh. Spendra containers do not terminate mTLS directly. Do not expose /metrics publicly. Scrape it only from the private observability plane with SPENDRA_METRICS_BEARER_TOKEN.

Catalog updates and air-gapped mode

By default, the worker syncs model and tool catalogs from upstream sources on the configured schedule. Disable online sync in air-gapped deployments:
Create a bundle in a connected environment:
Import it inside the air-gapped environment after migrations:
Imported rows are upserted and catalog sync metadata is recorded with source offline_bundle.

Upgrade and rollback

Before every upgrade:
  1. Confirm Postgres backups and PITR are healthy.
  2. Capture a restore point or backup according to customer policy.
  3. Render the Helm diff or rendered manifests.
  4. Run the upgrade during a maintenance window if schema changes are destructive or large.
Upgrade:
Application rollback uses Helm revision rollback:
Schema rollback is not automatic. Normal fixes use forward migrations. If a destructive migration must be undone, restore Postgres from PITR/snapshot to the pre-upgrade restore point and then redeploy the matching application revision.

Post-install acceptance

Verify:
  • API /health returns successfully.
  • API /metrics is reachable only from the private scraper with the metrics bearer token.
  • Dashboard login reaches onboarding or the active organization.
  • Worker jobs are present and processing.
  • A governed OpenAI-compatible request creates a settled ledger entry.
  • Audit records are written for key, policy, budget, role, and organization changes.
  • Catalog sync is either healthy online or imported from an offline bundle.