serve binds 127.0.0.1 by default and refuses a non-loopback --bind unless
authentication is configured. Set a bearer token with --token /
RUNEWARD_API_TOKEN, or point RUNEWARD_AUTHZ_FILE at a JSON store of named
principals (each with its own token, launch scope, optional approval-profile
scope, and admin flag) for multi-principal RBAC. When set, the token is required
on every request except /healthz and the static dashboard shell — pass it as
Authorization: Bearer <token> or an X-Runeward-Token header. The browser
obtains a short-lived, single-use scoped ticket for the terminal WebSocket, so
long-lived credentials never appear in URLs. Non-loopback listeners require TLS
via --tls-cert/--tls-key unless --allow-insecure-http explicitly
acknowledges a trusted TLS-terminating proxy; request bodies are capped at 16
MiB. See the
Security model.
Under RBAC, a non-admin principal sees and can act on only its tenant's
resources; admins see all. Different agent principals can share a tenant while
remaining independently attributable. OIDC is enabled with
RUNEWARD_OIDC_ISSUER and RUNEWARD_OIDC_AUDIENCE; signed Runeward claims map
to the same authorization model as local tokens.
Runs are durable, provider-neutral lineage records. They survive Citadel
teardown and include tenant, actor, Charter, agent/provider/model, optional
parent run, timestamps, and terminal status.
Opt-in GUI against /workspace via in-cell code-server. Requires
RUNEWARD_ENABLE_EXPERIMENTAL_IDE=1, Charter [ide] enabled = true, and an
IDE-capable target (ide or ide-agents) from deploy/Dockerfile.ide. Mint
kind=ide via POST /v1/tickets (or /ide-ticket), then open
/v1/citadels/{id}/ide?ticket=…. The ticket is single-use; a session cookie
covers subsequent asset loads. List/get may include ide / ide_agents.
Limits (summary): not per-keystroke policy; no Cursor/Claude Desktop/Codex
GUIs in-cell; no first-class GitHub Copilot (Open VSX). Full write-up:
Browser IDE.
Non-governance failures include a stable code: authentication_required,
authz_denied, not_found, conflict, rate_limited, invalid_request, or
internal_error. Policy decisions remain {"verdict":"deny",...} and approval
pauses remain {"verdict":"require-approval","approval_id":"..."}. SDKs expose
authorization and policy denials as different exception types.
Browser automation is experimental and disabled by default. Set
RUNEWARD_ENABLE_EXPERIMENTAL_BROWSER=1 only in a trusted deployment after
reviewing the security model.
AUTH=(-H"Authorization: Bearer $RUNEWARD_API_TOKEN")# omit when serving without a token: AUTH=()SB=$(curl-s"${AUTH[@]}"-XPOSTlocalhost:8080/v1/citadels-d'{"profile":"ns-auto"}'|jq-r.id)curl-s"${AUTH[@]}"-XPOST"localhost:8080/v1/citadels/$SB/shell/exec"-d'{"command":["echo","hi"]}'curl-s"${AUTH[@]}"-XPOST"localhost:8080/v1/citadels/$SB/usage"-d'{"tokens":1200,"cost_usd":0.03}'curl-s"${AUTH[@]}""localhost:8080/v1/chronicle/verify"curl-s"${AUTH[@]}"-XDELETE"localhost:8080/v1/citadels/$SB"