Server

Wardn API server — architecture, configuration, RBAC, and complete API reference

Server

The Wardn server is the central API layer — it validates Better Auth sessions and wrd_ API keys, enforces org + scoped RBAC, writes App CRs to the management cluster, stores sealed credentials, and hosts the MCP endpoint.

Architecture

ComponentTechnology
Routergin
DatabasePostgres (jackc/pgx) — all state
CacheRedis — Better Auth session cache; no pub/sub
AuthBetter Auth session cookie (better-auth.session_token) + wrd_ API keys
KubernetesWrites App CRs to WARDN_MGMT_KUBECONFIG per-org namespaces
┌──────────┐     ┌──────────┐     ┌──────────┐
│ Platform │────▶│  Server  │────▶│ Postgres │   orgs/teams/bindings
│  (Next)  │     │ (Go API) │     │ (state)  │   clusters/stores/audit
└──────────┘     └────┬─────┘     └──────────┘
                      │
                      ├──▶ Redis — session cache
                      ├──▶ Mgmt cluster — App CRs in org-* namespaces
                      ├──▶ Operator — /internal/clusters/{name}/kubeconfig
                      └──▶ MCP clients — /mcp JSON-RPC

There's no SSE/Redis pub/sub — the UI polls GET /status and the app list.

Configuration

Everything is env-var driven (server/internal/config/config.go), .env file supported, real env wins.

VariableDefaultDescription
APP_ENVdevelopmentproduction tightens validation (HTTPS-only cluster/store URLs)
HTTP_ADDR:8080Public API listen
WARDN_INTERNAL_ADDR—Separate listener for /api/v1/internal/* — admit only operator pods via NetworkPolicy
POSTGRES_URLpostgres://wardn:wardn@localhost:5432/wardn?sslmode=disableRequired
REDIS_URLredis://localhost:6379/0Session cache
BETTER_AUTH_SECRET—Required — verifies signed session cookies; must match the platform app
WARDN_CREDENTIALS_KEYfalls back to BETTER_AUTH_SECRETAES-256-GCM key sealing kubeconfigs + store credentials; set explicitly in prod
SESSION_COOKIE_NAMEbetter-auth.session_tokenCookie name to read
CORS_ALLOWED_ORIGINShttp://localhost:3002Comma-separated allowlist
WARDN_MGMT_KUBECONFIG—Management cluster kubeconfig; empty disables the apps surface (handlers → 503)
WARDN_INTERNAL_TOKEN—Bearer the operator presents on /internal/*; empty disables those endpoints (503)
WARDN_TAG_POLL_INTERVAL60sFollow-policy tag re-resolution cadence
OPERATOR_ADDRlocalhost:8081Operator address (metrics)
WARDN_SHELL_IMAGEwardn-shell:devToolbox image for shell pods (kubectl + CLI)
WARDN_SHELL_NAMESPACEwardn-systemWhere shell pods + SAs live
WARDN_SHELL_TOKEN_TTL1hWARDN_TOKEN TTL minted into shell pods
WARDN_SHELL_DEADLINE_SECS3600Pod activeDeadlineSeconds
TERMINAL_IDLE_TIMEOUT / TERMINAL_MAX_DURATION / TERMINAL_MAX_PER_USER15m / 1h / 5Terminal session bounds
WARDN_API_URL—API base URL reachable from inside clusters — injected into shell pods
MCP_RATE_LIMIT_RPM120MCP calls/min per credential (0 disables)
HTTP_*_TIMEOUT15s/30s/120s/30sServer timeouts

BETTER_AUTH_SECRET must match the platform frontend. WARDN_CREDENTIALS_KEY should differ in production — rotate by re-sealing rows.

API Surface

All routes are under /api/v1 except /healthz, /readyz, and /mcp.

GroupRoutesPerm pattern
/healthz, /readyzliveness/readinesspublic
/authme, sessions, can-i, api-keyssession or wrd_ key (sessions endpoint is session-only)
/appslist/create/get/status/history/events/sync/deploy/rollback/promote/clone/restart/approve/scale/freeze/preview/adoptdeployment:*
/clustersCRUD, test, {name}/namespaces, {name}/shellcluster:*
/teamsGET, POSTteam:read/team:manage
/auditGETaudit:read
/secret-storesproviders, CRUD, test, {id}/test, {id}/referencessecretstore:read/manage (session-only)
/registriesCRUD, test, {id}/testregistry:*
/webhooksoutbound incident webhooks — CRUD, testwebhook:*
/incidentsPOST emitwebhook:manage
/remediationsGET, POST {id}/approve, POST {id}/rejectremediation:approve
/mcpJSON-RPC 2.0 — tools scoped by the caller's keymcp:read
/internalGET /internal/clusters/{name}/kubeconfigWARDN_INTERNAL_TOKEN bearer

Full request/response shapes live in the API Reference pages.

RBAC

Org roles (Owner/Admin/Contributor/Viewer) plus role_bindings rows for scoped elevation. See RBAC for the matrix.

Database Schema

Auto-migrated on startup. The meaningful tables:

TablePurpose
users, accounts, sessions, orgs, teams, org_members, team_membersBetter Auth schema (owned by the platform app, read by the API)
role_bindingsScoped elevations — subject, role, scope, scope_id
api_keyswrd_ hashes with role/scope ceiling + expiry
clustersRegistered clusters — kubeconfig column is the sealed blob
secret_storesStore rows — credentials sealed; status, last_tested_at, credentials_expire_at
registriesContainer registries for registryRef
webhooksOutbound incident-webhook configs
incidents, remediationsAgent proposals + approval records
audit_logOrg-scoped audit entries

Running Locally

On this page