Operator Tokens
The operator token is a pre-shared secret that gates destructive-but-routine admin endpoints across services. Each service holds its own independent token; tokens are never shared across service boundaries.
Auth model
Requests carry the token in:
X-Operator-Tokenheader — executor, risk, and manager bot-lifecycle endpoints.Authorization: Bearer <token>header —POST /bff/emergency-flattenon gordon-manager.
| Condition | HTTP status | Code |
|---|---|---|
| Token not configured at startup | 503 | <SERVICE>_UNAUTHORIZED |
| Token missing or wrong | 401 | <SERVICE>_UNAUTHORIZED |
| Body missing or invalid | 400 | — |
| Success | 200 | — |
Fail-closed: if the GORDON_<SERVICE>_OPERATOR_TOKEN env var is absent or empty at startup, guarded endpoints return 503. There is no insecure fallback.
No oracle: missing-token and wrong-token responses are identical — the caller cannot distinguish which check failed.
Constant-time comparison: all implementations use constant-time equality to prevent timing side-channel attacks.
Token surfaces
| Token | Env var | Holder | Purpose |
|---|---|---|---|
| Manager operator token | GORDON_MANAGER_OPERATOR_TOKEN | gordon-console (server-side), gordon-manager | Primary browser-to-manager token. Console injects as X-Operator-Token on every BFF mutation. |
| Risk operator token | GORDON_RISK_OPERATOR_TOKEN | gordon-risk | Gates POST /emergency-flatten, POST /risk/resume, POST /bots/:id/pause on gordon-risk directly. |
| Executor operator token | GORDON_EXECUTOR_OPERATOR_TOKEN | gordon-executor | Gates POST /clear-quarantine, POST /flatten on gordon-executor. Manager-only forward token. |
| Manager→Risk forward token | GORDON_MANAGER__GORDON_RISK_OPERATOR_TOKEN | gordon-manager | Manager's token for proxying operator commands to gordon-risk. Set only on gordon-manager. |
BFF boundary note
The canonical v7 design has gordon-manager as the sole browser-facing token surface for write actions. Gordon-console holds only GORDON_MANAGER_OPERATOR_TOKEN. Manager re-authenticates to gordon-risk and gordon-executor using its own forward tokens.
Current drift (DP-12, open): gordon-console still holds GORDON_RISK_OPERATOR_TOKEN and calls POST /risk/resume directly on gordon-risk. The manager /bff/risk/resume proxy is designed but not yet wired. When DP-12 closes, GORDON_RISK_OPERATOR_TOKEN will be removed from the console's env.
Service isolation table
A compromised token for one service grants no access to any other service.
| Service | Env var | Vault key | Endpoints guarded |
|---|---|---|---|
| gordon-executor | GORDON_EXECUTOR_OPERATOR_TOKEN | vault_gordon_executor_operator_token | POST /clear-quarantine, POST /flatten |
| gordon-risk | GORDON_RISK_OPERATOR_TOKEN | vault_gordon_risk_operator_token | POST /emergency-flatten, POST /risk/resume, POST /bots/:id/pause |
| gordon-manager | GORDON_MANAGER_OPERATOR_TOKEN | vault_gordon_manager_operator_token | POST /bff/emergency-flatten, POST /bots/:id/clear-quarantine |
Production provisioning
Production values live in the Ansible vault:
homelab/group_vars/all/vault.yml
→ vault_gordon_executor_operator_token
→ vault_gordon_risk_operator_token
→ vault_gordon_manager_operator_tokenRotation procedure (repeat per service)
- Generate a new 256-bit random hex secret:bash
openssl rand -hex 32 - Update the relevant vault key.
- Run
ansible-playbook deploy.yml --tags gordon-<service>. - The service restarts with the new token — no DB changes required.
- Verify (example for executor):bash
curl -H "X-Operator-Token: <new>" http://srv-apps:8085/clear-quarantine # Should return 400 (missing body), not 401/503 - Repeat verification for each guarded endpoint on the rotated service.
Local development
Each service's docker-compose.yml sets a safe default:
| Service | Default dev value |
|---|---|
| gordon-executor | e2e-operator-token |
| gordon-risk | e2e-operator-token |
| gordon-manager | e2e-operator-token |
Override by setting the env var before docker compose up. E2E tests use these defaults; no secrets needed locally.
Error codes
| Service | Error code | Log level |
|---|---|---|
| gordon-executor | EXECUTOR_UNAUTHORIZED | warn |
| gordon-risk | RISK_UNAUTHORIZED | warn |
| gordon-manager | MANAGER_UNAUTHORIZED | warn |