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
| Component | Technology |
|---|---|
| Router | gin |
| Database | Postgres (jackc/pgx) — all state |
| Cache | Redis — Better Auth session cache; no pub/sub |
| Auth | Better Auth session cookie (better-auth.session_token) + wrd_ API keys |
| Kubernetes | Writes 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-RPCThere'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.
| Variable | Default | Description |
|---|---|---|
APP_ENV | development | production tightens validation (HTTPS-only cluster/store URLs) |
HTTP_ADDR | :8080 | Public API listen |
WARDN_INTERNAL_ADDR | — | Separate listener for /api/v1/internal/* — admit only operator pods via NetworkPolicy |
POSTGRES_URL | postgres://wardn:wardn@localhost:5432/wardn?sslmode=disable | Required |
REDIS_URL | redis://localhost:6379/0 | Session cache |
BETTER_AUTH_SECRET | — | Required — verifies signed session cookies; must match the platform app |
WARDN_CREDENTIALS_KEY | falls back to BETTER_AUTH_SECRET | AES-256-GCM key sealing kubeconfigs + store credentials; set explicitly in prod |
SESSION_COOKIE_NAME | better-auth.session_token | Cookie name to read |
CORS_ALLOWED_ORIGINS | http://localhost:3002 | Comma-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_INTERVAL | 60s | Follow-policy tag re-resolution cadence |
OPERATOR_ADDR | localhost:8081 | Operator address (metrics) |
WARDN_SHELL_IMAGE | wardn-shell:dev | Toolbox image for shell pods (kubectl + CLI) |
WARDN_SHELL_NAMESPACE | wardn-system | Where shell pods + SAs live |
WARDN_SHELL_TOKEN_TTL | 1h | WARDN_TOKEN TTL minted into shell pods |
WARDN_SHELL_DEADLINE_SECS | 3600 | Pod activeDeadlineSeconds |
TERMINAL_IDLE_TIMEOUT / TERMINAL_MAX_DURATION / TERMINAL_MAX_PER_USER | 15m / 1h / 5 | Terminal session bounds |
WARDN_API_URL | — | API base URL reachable from inside clusters — injected into shell pods |
MCP_RATE_LIMIT_RPM | 120 | MCP calls/min per credential (0 disables) |
HTTP_*_TIMEOUT | 15s/30s/120s/30s | Server 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.
| Group | Routes | Perm pattern |
|---|---|---|
/healthz, /readyz | liveness/readiness | public |
/auth | me, sessions, can-i, api-keys | session or wrd_ key (sessions endpoint is session-only) |
/apps | list/create/get/status/history/events/sync/deploy/rollback/promote/clone/restart/approve/scale/freeze/preview/adopt | deployment:* |
/clusters | CRUD, test, {name}/namespaces, {name}/shell | cluster:* |
/teams | GET, POST | team:read/team:manage |
/audit | GET | audit:read |
/secret-stores | providers, CRUD, test, {id}/test, {id}/references | secretstore:read/manage (session-only) |
/registries | CRUD, test, {id}/test | registry:* |
/webhooks | outbound incident webhooks — CRUD, test | webhook:* |
/incidents | POST emit | webhook:manage |
/remediations | GET, POST {id}/approve, POST {id}/reject | remediation:approve |
/mcp | JSON-RPC 2.0 — tools scoped by the caller's key | mcp:read |
/internal | GET /internal/clusters/{name}/kubeconfig | WARDN_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:
| Table | Purpose |
|---|---|
users, accounts, sessions, orgs, teams, org_members, team_members | Better Auth schema (owned by the platform app, read by the API) |
role_bindings | Scoped elevations — subject, role, scope, scope_id |
api_keys | wrd_ hashes with role/scope ceiling + expiry |
clusters | Registered clusters — kubeconfig column is the sealed blob |
secret_stores | Store rows — credentials sealed; status, last_tested_at, credentials_expire_at |
registries | Container registries for registryRef |
webhooks | Outbound incident-webhook configs |
incidents, remediations | Agent proposals + approval records |
audit_log | Org-scoped audit entries |