ops-warden/wiki/AccessRouting.md
tegwick 55f0f47a02
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
WARDEN-WP-0033-T05: split the stale cadences, and record how a blocker was verified
The 90-day --stale-days default on `warden route gaps` was not a loose threshold,
it was an inert one: the delegation register was created 2026-08-15, so it could
not have fired before November. It was inherited from the catalog pointer cadence
and applied to a claim with a completely different half-life.

Two changes. DEFAULT_BLOCKER_STALE_DAYS = 14 now governs interim blockers, while
DEFAULT_STALE_DAYS = 90 keeps governing pointer freshness -- "is this the right
owner and page" is quarterly, "has the owner answered" is not. 14 is calibrated
on blockers that actually cost something: ten days for the secrets-engine lanes,
one for RISK-F-0001, roughly fifty for FLEX-WP-0007.

The second change matters more. `reviewed` records when someone touched an entry,
which is indistinguishable from re-checking it -- six lanes read as freshly
reviewed today because I typed in them. `verified:` now says how the claim was
established, and asked-and-waiting explicitly does NOT count: that is the state
the secrets-engine blocker sat in for ten days while looking current. A lane in
that state is stale at zero days old, and key-cape-oidc-login proves it works.

8 of 14 interim lanes are honestly marked unverified rather than given a fresh
date they did not earn.

--fail-on-stale exits 3 for a cron or gate. No CI test on age: a date-triggered
failure breaks the build for whoever commits next instead of whoever owns the
blocker. The CI test is structural -- every interim lane must record how it was
verified -- so it fails on the commit that introduces the omission.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:26:10 +02:00

14 KiB

Access Routing — what ops-warden answers

Date: 2026-06-18

ops-warden issues short-lived SSH certificates, routes every other credential need to the subsystem that owns it, and assists with obtaining it through the warden access front door. This page states that role plainly so it cannot be misread as a desk that wraps the platform.

  • What ops-warden executes: the SSH certificate lane only (warden sign, cert_command, ops-ssh-wrapper).
  • What ops-warden answers: where a credential need belongs and who owns it — pointing at the owner's docs, never restating their procedure.
  • What ops-warden assists with: warden access renders the exact auth/path/command for any need and, for exec_capable lanes, proxies the fetch as the caller — a transparent, policy-gated, audited conduit that holds, caches, and logs nothing.
  • What ops-warden never does: own a secret store, establish identity, decide policy, open tunnels, or deploy hosts. The assist conduit uses your identity and owns none of these. See OperatorAccessAssist.md.

For the worker-facing decision tree see CredentialRouting.md; for component literacy see NetKingdomSecurityMap.md. This page is the steward's statement of role and boundary.


Issue vs route

Need Subsystem ops-warden role Who acts
SSH cert for host/ops access (adm/agt/atm) ops-warden Issue (warden sign) ops-warden signs; worker uses cert
API key / DB cred / dynamic lease OpenBao Route — point at path Worker calls OpenBao
"May I perform action X?" flex-auth (+ Topaz PDP) Route — point at policy Worker/PEP calls flex-auth
Login / OIDC token / MFA key-cape / Keycloak Route — point at IAM Profile Worker authenticates
Object-storage STS / S3 creds net-kingdom + flex-auth + OpenBao Route — point at vending path Worker follows NK-WP-0007
SSH tunnel / port forward ops-bridge Route — supply cert_command ops-bridge opens tunnel
Host principal / force-command railiance-infra Route — point at Ansible infra deploys host
OpenBao cluster init / unseal railiance-platform Route — point at ceremony platform operates

Only the first row is something ops-warden executes. Every other row is a pointer: ops-warden names the owner and the doc, and the worker acts on the owning system directly.

Assist layer (warden access). For routed rows, ops-warden goes beyond the pointer: it renders the exact auth method, path template, and command, and — where the catalog marks a lane exec_capable (today: OpenBao secret reads, key-cape login) — proxies the call as the caller. This does not change ownership: the secret stays in OpenBao, the decision stays in flex-auth, the identity stays in key-cape. ops-warden is a transparent conduit using the caller's identity, never a custodian of the value. The boundary that keeps this sound is in OperatorAccessAssist.md#the-conduit-vs-broker-boundary.


Anti-patterns (not coming to ops-warden)

ops-warden does not own custody, identity, authorization, or transport — those belong to other subsystems. The assist layer (warden access) may proxy a call as the caller, but it never becomes the owner. Don't reach for a command that implies ownership:

Tempting command Why it's wrong Right path
warden secret / warden bao (as a store/vend) ops-warden owns no secret store and vends nothing OpenBao; to obtain as yourself, warden access <need> --fetch
warden login (as an identity owner) ops-warden does not establish identity key-cape / Keycloak; to run the login as yourself, warden access <login need> --fetch (login lane)
warden policy (as a decision) ops-warden does not decide authorization flex-auth makes the call; ops-warden only gates its own proxy on it
warden tunnel ops-warden does not manage transport ops-bridge

The distinction: a standing broker (warden's own secret-read token, a cache of values) is forbidden; a transparent conduit (warden access --fetch, caller's identity, nothing retained) is sanctioned. ops-warden authors step-by-step procedure for exactly one lane — SSH issuance — because it owns it. For everything else it carries a pointer (and, for exec_capable lanes, a conduit), not a fork of the owner's runbook. See the no-double-source rule in workplans/WARDEN-WP-0010-access-routing-charter.md and the conduit-vs-broker boundary in OperatorAccessAssist.md.


Interim custodianship

ops-warden works with, and never replaces or duplicates, the NetKingdom components that own identity, custody, authorization, and tenant/user lifecycle (INTENT.md §9). Covering a gap is legitimate. Silently becoming the owner of that gap is not.

The only lane ops-warden executes with its own authority is SSH issuance (ssh-cert-host-access). Every other execution position — including every warden access proxy — is interim by default. A catalog entry without a delegation: block is treated as interim with an unknown owner: absence is a question, not a settlement.

delegation.mode Meaning
permanent ops-warden is the designed owner of this front door (SSH only, today)
native the intended owner already fronts it; ops-warden routes (and may keep a proxy as fallback)
interim ops-warden covers the gap until intended_owner ships the missing front door named in blocked_on

Delegation targets — the components that should own a front door once they have one:

Target What they should front
secrets-engine owner-native secret-exec (secrets-engine exec --catalog <id>)
tenant-engine tenant/client secret custody and tenant-lane front door
user-engine end-user / account-lifecycle secrets that belong with user identity
railiance-platform OpenBao cluster, credential broker, platform workload procedure
flex-auth authorization decisions (already native — ops-warden only points)
key-cape identity login and client-credential protocol (OIDC, client_secret_basic)

Query the register with warden route gaps. An interim lane is retired by setting exec_owner / exec_command (the WP-0019 pattern) and flipping delegation.mode to native once the owner's front door is proven. Do not delete a working proxy on the way.


Routing lookup CLI (warden route)

Agents and operators query the pointer catalog directly instead of re-deriving routing from wiki prose. The command group is read-only — it never calls OpenBao, flex-auth, key-cape, or any other subsystem, and never returns secret material.

warden route list [--json] [--all] [--tag <keyword>]              # active-only unless --all
warden route list --stale [--stale-days 90] [--all] [--json]    # past review cadence
warden route gaps [--json] [--all]                              # interim register (owner + blocker)
warden route show <id> [--json]                                 # owner + pointers; SSH adds steps
warden route find "<free text need>" [--json] [--all]             # rank by keyword overlap

Agent-oriented examples:

# "I need an API key" — find the owner, get a pointer, act there yourself
warden route find "openrouter api key" --json
warden route show openbao-api-key --json
#   → {"warden_executes": false, "next_action": "next action on `railiance-platform` — see `wiki/CredentialRouting.md#routing-table`"}

# The one lane ops-warden executes: SSH. `show` appends the authored steps + cert pattern.
warden route show ssh-cert-host-access --json
#   → {"warden_executes": true, "cert_command": "warden sign <actor> --pubkey <path>", "steps": [...]}

show on a routed (non-SSH) need always ends with "next action on <owner_repo> — see <wiki_ref>" and never implies ops-warden performed anything. Draft scenarios (owner path not yet shipped) are hidden unless --all.


Audience notes

  • Human operators read this page and CredentialRouting.md to choose the right subsystem, then follow that subsystem's own docs.
  • Agents / CI read the machine-readable routing catalog (registry/routing/catalog.yaml) via warden route (above) so routing does not have to be re-derived from wiki prose each session.
  • Same truth, two shapes: humans read the wiki; agents read the catalog. The catalog references wiki sections by anchor so the two cannot drift apart — a test (tests/test_routing.py) fails CI if any wiki_ref anchor stops resolving.

How this stays aligned

NetKingdom security architecture is canonical in net-kingdom. ops-warden tracks it: when canon changes, the wiki section is updated and the catalog pointer (wiki_ref + canon_ref) follows. ops-warden never overrides canon and never silently forks it.

Report drift via a custodian workplan or a State Hub message to ops-warden.


Drift review cadence

Every catalog entry carries a reviewed: date (YYYY-MM-DD) — the last time an ops-warden steward confirmed the pointer still matches net-kingdom canon and the owner repo's shipped path.

Cadence Action
Quarterly (default 90 days) Run warden route list --stale — reconcile every listed entry against canon
On canon change When net-kingdom security docs change, review affected canon_ref entries immediately
On owner ship When an owning repo merges a new OpenBao path or playbook, promote draftactive and bump reviewed
On agent confusion If warden route find misses a common query, add need_keywords or a playbook — do not restate owner procedure in the catalog
Fortnightly (default 14 days) Run warden route gaps — re-check each blocked_on against the intended owner; flip to native when their front door exists

Two cadences, because they are two different claims

A catalog pointer and an interim blocker both carry a reviewed: date, and for a while they shared one 90-day threshold. They should not.

Claim Question Default Where
Pointer freshness Is this still the right owner and page? 90 days warden route list --stale
Interim blocker Has the intended owner answered / can they front this yet? 14 days warden route gaps

A pointer genuinely is a quarterly question. A blocker is not: it is a claim about another repo's state at a date, and this estate invalidates those in days. RISK-F-0001 invalidated an ops-warden blocker in one. The secrets-engine lanes carried one for ten while it was answerable from that repo's source the whole time. A FLEX-WP-0007 blocker was repeated by two repos for about seven weeks after that workplan read finished.

Sharing 90 days did not make the blocker check loose, it made it inert: the delegation register was created 2026-08-15, so the threshold could not have fired before November and never had.

Reviewed is not verified

The more important half. A reviewed: date records when someone last touched the entry, which looks identical to re-checking it. So every interim lane also carries verified:, saying how the claim was established:

Value Meaning Counts as verification
owner-confirmed the intended owner stated the blocker's status yes
source-read re-derived from the owner's code, canon, or a CCR yes
asked-and-waiting a question is outstanding no
unverified carried forward without a check no

asked-and-waiting deliberately does not reset the clock. A lane asked today reads as reviewed today, and that is precisely how a blocker stays fresh-looking while nobody answers it. warden route gaps flags such a lane as stale at zero days old.

Re-check the blocker; do not bump the date. Bumping reviewed without re-establishing the claim is the failure this section exists to prevent, and it is cheap to avoid — most of these are answerable by reading the owner's repo.

warden route gaps                      # 14-day blocker cadence, plus unverified lanes
warden route gaps --stale-days 30      # looser threshold
warden route gaps --fail-on-stale      # exit 3 — for a cron job or a gate

Stale check (operators and agents)

# Entries not reviewed in the last 90 days (default threshold)
warden route list --stale

# Include draft scenarios in the stale report
warden route list --stale --all

# Custom threshold (e.g. monthly review)
warden route list --stale --stale-days 30 --json

For each stale entry:

  1. Open canon_ref in net-kingdom — confirm ownership and vocabulary unchanged.
  2. Open wiki_ref in this repo — update the playbook section if canon moved.
  3. Confirm the owner path still exists (anti-stale rule: unshipped paths stay draft).
  4. Bump reviewed: in registry/routing/catalog.yaml to today's date.
  5. Run uv run pytest tests/test_routing.py — anchor resolution must still pass.

CI enforces structural drift (every wiki_ref anchor resolves; no-double-source rule). The quarterly cadence catches semantic drift CI cannot detect — canon moved but anchors still resolve.


See also

  • CredentialRouting.md — worker decision tree and routing table
  • NetKingdomSecurityMap.md — component literacy
  • INTENT.md — steward mission ("issue SSH, route the rest"); §9 interim custodianship
  • workplans/WARDEN-WP-0030-delegation-register.md — delegation register
  • workplans/WARDEN-WP-0010-access-routing-charter.md — charter + no-double-source rule
  • net-kingdom/docs/platform-identity-security-architecture.md — platform canon