Cross-tenant operator endpoints on top of the M7.2-D DatabasePool primitives:
GET /api/v1/admin/tenants — list tenant DBs
DELETE /api/v1/admin/tenants/{tenant_id} — drop (GDPR delete)
Auth is a static bearer (ADMIN_API_TOKEN env), explicitly NOT a Keycloak JWT. The whole point of these endpoints is to operate across tenants, and a customer JWT always carries a single tenant_id, which would be a semantic conflict. Constant-time comparison so byte-level timing probes don't work.
Design
ADMIN_API_TOKEN env on the agent. When unset, the admin routes aren't mounted at all (404 rather than 401) — an operator who hasn't opted in can't even fingerprint the surface.
Admin sub-router is built in start_api_server when the token is configured, then merged into the main router with its own require_admin_token middleware.
compliance-core::auth gains a PUBLIC_PREFIXES list. Paths under /api/v1/admin/ bypass require_jwt_auth so the customer JWT path and the admin token path never collide.
require_tenant_status passes through naturally — admin requests carry no TenantContext.
Files
File
What
compliance-core/src/auth.rs
PUBLIC_PREFIXES list + prefix-aware skip in require_jwt_auth
compliance-core/src/config.rs
admin_api_token + tenant_registry_url on AgentConfig (the latter is for the upcoming scheduler→registry PR — adding now to avoid bumping the config shape twice)
compliance-agent/src/config.rs
Env wiring for both
compliance-agent/src/api/handlers/admin.rs (new)
list_tenant_dbs, drop_tenant_db, require_admin_token middleware, constant-time tokens_eq with a small test
compliance-agent/src/api/server.rs
Conditional admin sub-router + merge
Test fixtures
Two new None fields in both AgentConfig constructions
cargo test -p compliance-agent --lib — 229 pass (+1 new for tokens_eq)
Manual after deploy: with ADMIN_API_TOKEN=secret set on the agent, curl -H "Authorization: Bearer secret" $AGENT/api/v1/admin/tenants returns a JSON list. Without the env, the same call returns 404.
Production
Set ADMIN_API_TOKEN in orca-infra (per-tenant secret, NOT committed) when ready to expose. Without it, the routes literally don't exist on the binary.
Long-term: replace the static bearer with a dedicated admin realm in Keycloak. Token rotation is currently an env change + restart; revocation responsiveness is zero. Acceptable for ops-only endpoints but should harden before any customer-facing admin UI.
## Summary
Cross-tenant operator endpoints on top of the M7.2-D `DatabasePool` primitives:
- `GET /api/v1/admin/tenants` — list tenant DBs
- `DELETE /api/v1/admin/tenants/{tenant_id}` — drop (GDPR delete)
**Auth is a static bearer (`ADMIN_API_TOKEN` env), explicitly NOT a Keycloak JWT.** The whole point of these endpoints is to operate across tenants, and a customer JWT always carries a single `tenant_id`, which would be a semantic conflict. Constant-time comparison so byte-level timing probes don't work.
## Design
- `ADMIN_API_TOKEN` env on the agent. **When unset, the admin routes aren't mounted at all (404 rather than 401)** — an operator who hasn't opted in can't even fingerprint the surface.
- Admin sub-router is built in `start_api_server` when the token is configured, then merged into the main router with its own `require_admin_token` middleware.
- `compliance-core::auth` gains a `PUBLIC_PREFIXES` list. Paths under `/api/v1/admin/` bypass `require_jwt_auth` so the customer JWT path and the admin token path never collide.
- `require_tenant_status` passes through naturally — admin requests carry no TenantContext.
## Files
| File | What |
|---|---|
| `compliance-core/src/auth.rs` | `PUBLIC_PREFIXES` list + prefix-aware skip in `require_jwt_auth` |
| `compliance-core/src/config.rs` | `admin_api_token` + `tenant_registry_url` on `AgentConfig` (the latter is for the upcoming scheduler→registry PR — adding now to avoid bumping the config shape twice) |
| `compliance-agent/src/config.rs` | Env wiring for both |
| `compliance-agent/src/api/handlers/admin.rs` (new) | `list_tenant_dbs`, `drop_tenant_db`, `require_admin_token` middleware, constant-time `tokens_eq` with a small test |
| `compliance-agent/src/api/server.rs` | Conditional admin sub-router + merge |
| Test fixtures | Two new `None` fields in both AgentConfig constructions |
## Test plan
- [x] `cargo fmt --all -- --check` clean
- [x] `cargo clippy --workspace --exclude compliance-dashboard -- -D warnings` clean
- [x] `cargo test -p compliance-core --lib` — 7 pass
- [x] `cargo test -p compliance-agent --lib` — 229 pass (+1 new for `tokens_eq`)
- [ ] Manual after deploy: with `ADMIN_API_TOKEN=secret` set on the agent, `curl -H "Authorization: Bearer secret" $AGENT/api/v1/admin/tenants` returns a JSON list. Without the env, the same call returns 404.
## Production
- Set `ADMIN_API_TOKEN` in `orca-infra` (per-tenant secret, NOT committed) when ready to expose. Without it, the routes literally don't exist on the binary.
- **Long-term**: replace the static bearer with a dedicated admin realm in Keycloak. Token rotation is currently an env change + restart; revocation responsiveness is zero. Acceptable for ops-only endpoints but should harden before any customer-facing admin UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds two cross-tenant operator endpoints on top of the M7.2-D
DatabasePool primitives:
- GET /api/v1/admin/tenants → list tenant DBs
- DELETE /api/v1/admin/tenants/{tenant_id} → drop (GDPR delete)
Auth is a static bearer (ADMIN_API_TOKEN env), explicitly NOT a
Keycloak JWT — the whole point is to operate across tenants and a
customer JWT always carries a single tenant_id, which would be a
semantic conflict. Comparison is constant-time to avoid byte-level
timing probes.
Design
- ADMIN_API_TOKEN env on the agent. When unset, the admin routes
aren't mounted at all (404 rather than 401). An operator who
hasn't opted in can't fingerprint the surface.
- Admin sub-router is built in start_api_server when the token is
configured, then merged into the main router with its own
require_admin_token middleware.
- compliance-core::auth gains a PUBLIC_PREFIXES list. Paths under
/api/v1/admin/ bypass require_jwt_auth so the customer JWT path
and the admin token path never collide.
- require_tenant_status passes through naturally — admin requests
carry no TenantContext.
Files
- compliance-core/src/auth.rs — PUBLIC_PREFIXES + prefix-aware skip.
- compliance-core/src/config.rs — admin_api_token + tenant_registry_url
fields on AgentConfig. tenant_registry_url is added now so the
scheduler→registry PR doesn't have to bump the config shape again.
- compliance-agent/src/config.rs — env wiring for both.
- compliance-agent/src/api/handlers/admin.rs (new) — list_tenant_dbs,
drop_tenant_db, require_admin_token middleware, tokens_eq helper
with a small test.
- compliance-agent/src/api/server.rs — conditional admin sub-router
+ merge.
- Test harness fixtures updated for the two new config fields.
Test plan
- cargo fmt --all clean
- cargo clippy --workspace --exclude compliance-dashboard
-- -D warnings clean
- cargo test -p compliance-core --lib — 7 pass
- cargo test -p compliance-agent --lib — 229 pass (+1 new for
tokens_eq)
Production
- Set ADMIN_API_TOKEN in orca-infra (per-secret, NOT committed) when
ready to expose these endpoints. Without the env, the routes
literally don't exist on the binary.
- Long-term: replace the static bearer with a dedicated admin realm
in Keycloak. Token rotation is just an env change + restart for
now; revocation responsiveness is zero.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Cross-tenant operator endpoints on top of the M7.2-D
DatabasePoolprimitives:GET /api/v1/admin/tenants— list tenant DBsDELETE /api/v1/admin/tenants/{tenant_id}— drop (GDPR delete)Auth is a static bearer (
ADMIN_API_TOKENenv), explicitly NOT a Keycloak JWT. The whole point of these endpoints is to operate across tenants, and a customer JWT always carries a singletenant_id, which would be a semantic conflict. Constant-time comparison so byte-level timing probes don't work.Design
ADMIN_API_TOKENenv on the agent. When unset, the admin routes aren't mounted at all (404 rather than 401) — an operator who hasn't opted in can't even fingerprint the surface.start_api_serverwhen the token is configured, then merged into the main router with its ownrequire_admin_tokenmiddleware.compliance-core::authgains aPUBLIC_PREFIXESlist. Paths under/api/v1/admin/bypassrequire_jwt_authso the customer JWT path and the admin token path never collide.require_tenant_statuspasses through naturally — admin requests carry no TenantContext.Files
compliance-core/src/auth.rsPUBLIC_PREFIXESlist + prefix-aware skip inrequire_jwt_authcompliance-core/src/config.rsadmin_api_token+tenant_registry_urlonAgentConfig(the latter is for the upcoming scheduler→registry PR — adding now to avoid bumping the config shape twice)compliance-agent/src/config.rscompliance-agent/src/api/handlers/admin.rs(new)list_tenant_dbs,drop_tenant_db,require_admin_tokenmiddleware, constant-timetokens_eqwith a small testcompliance-agent/src/api/server.rsNonefields in both AgentConfig constructionsTest plan
cargo fmt --all -- --checkcleancargo clippy --workspace --exclude compliance-dashboard -- -D warningscleancargo test -p compliance-core --lib— 7 passcargo test -p compliance-agent --lib— 229 pass (+1 new fortokens_eq)ADMIN_API_TOKEN=secretset on the agent,curl -H "Authorization: Bearer secret" $AGENT/api/v1/admin/tenantsreturns a JSON list. Without the env, the same call returns 404.Production
ADMIN_API_TOKENinorca-infra(per-tenant secret, NOT committed) when ready to expose. Without it, the routes literally don't exist on the binary.🤖 Generated with Claude Code
Adds two cross-tenant operator endpoints on top of the M7.2-D DatabasePool primitives: - GET /api/v1/admin/tenants → list tenant DBs - DELETE /api/v1/admin/tenants/{tenant_id} → drop (GDPR delete) Auth is a static bearer (ADMIN_API_TOKEN env), explicitly NOT a Keycloak JWT — the whole point is to operate across tenants and a customer JWT always carries a single tenant_id, which would be a semantic conflict. Comparison is constant-time to avoid byte-level timing probes. Design - ADMIN_API_TOKEN env on the agent. When unset, the admin routes aren't mounted at all (404 rather than 401). An operator who hasn't opted in can't fingerprint the surface. - Admin sub-router is built in start_api_server when the token is configured, then merged into the main router with its own require_admin_token middleware. - compliance-core::auth gains a PUBLIC_PREFIXES list. Paths under /api/v1/admin/ bypass require_jwt_auth so the customer JWT path and the admin token path never collide. - require_tenant_status passes through naturally — admin requests carry no TenantContext. Files - compliance-core/src/auth.rs — PUBLIC_PREFIXES + prefix-aware skip. - compliance-core/src/config.rs — admin_api_token + tenant_registry_url fields on AgentConfig. tenant_registry_url is added now so the scheduler→registry PR doesn't have to bump the config shape again. - compliance-agent/src/config.rs — env wiring for both. - compliance-agent/src/api/handlers/admin.rs (new) — list_tenant_dbs, drop_tenant_db, require_admin_token middleware, tokens_eq helper with a small test. - compliance-agent/src/api/server.rs — conditional admin sub-router + merge. - Test harness fixtures updated for the two new config fields. Test plan - cargo fmt --all clean - cargo clippy --workspace --exclude compliance-dashboard -- -D warnings clean - cargo test -p compliance-core --lib — 7 pass - cargo test -p compliance-agent --lib — 229 pass (+1 new for tokens_eq) Production - Set ADMIN_API_TOKEN in orca-infra (per-secret, NOT committed) when ready to expose these endpoints. Without the env, the routes literally don't exist on the binary. - Long-term: replace the static bearer with a dedicated admin realm in Keycloak. Token rotation is just an env change + restart for now; revocation responsiveness is zero. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>