ops-warden/docs/adr/ADR-0005-implement-narrowly-route-broadly.md
tegwick 35aff380a3
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Lift ops-warden's binding rules into owned ADRs
Five rules that governed this repo lived in wiki prose, a workplan, and a
comment at the top of catalog.yaml. All were followed; none was addressable.
A reader outside ops-warden could not cite one, could not tell whether it was
current, and — the point of this change — could not tell whether it was ours
to change or someone else's that we merely obey.

  ADR-0001  The routing catalog is a pointer layer, never a second copy
  ADR-0002  ops-warden is a transparent conduit, never a secret broker
  ADR-0003  Cover gaps, but never silently own them
  ADR-0004  High-risk lanes refuse raw value streaming to agent sessions
  ADR-0005  Implement one lane narrowly, route everything else

Each carries owner: ops-warden, which is the load-bearing field. It says we
follow the rule AND we are responsible for changing it — by superseding ADR,
never an in-place edit. The failure this prevents runs both ways: a rule we own
mistaken for inherited canon never gets fixed, because we wait for an owner who
does not exist; inherited canon mistaken for ours gets quietly bent, and the
drift is invisible until it breaks a repo that trusted the canonical version.

Rules we follow but do not own — NetKingdom canon, the IAM profile, the
credential-management standard, the-custodian's ADR-001 workplan convention —
are cited, never copied into docs/adr/. Copying them would recreate exactly the
second-source-of-truth failure ADR-0001 exists to prevent. architecture.md also
now flags the three-digit/four-digit ADR-001 vs ADR-0001 collision, which is
itself an ours-versus-inherited confusion waiting to happen.

Publication verified rather than assumed: all five render through policy-nexus
tools/render.py, and owner reaches the reader in three places — the page eyebrow
(render.py:346), the index Owner column (build_site.py:123,137), and the
publication manifest. build_site.py:179 makes title/status/owner required, so
ownership cannot be dropped on the way out. policy-nexus publishes and never
writes back; docs/adr/ stays the source of truth.

Documentation adapted: SCOPE.md gains a Governing rules section and an orientation
entry; .claude/rules/architecture.md replaced its stub with the ADR index, the
owned-versus-inherited rule, and ADR-over-wiki precedence; finding-routing.md's
ADR-gap section closed; catalog.yaml's no-double-source header now cites ADR-0001
rather than the originating workplan.

uv run pytest -q → 338 passed, 4 deselected.

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

79 lines
3.2 KiB
Markdown

---
id: ops-warden-adr-0005
type: adr
title: "ADR-0005 — Implement one lane narrowly, route everything else"
domain: infotech
repo: ops-warden
status: accepted
version: "1.0"
revision: "1"
owner: ops-warden
binds: "ops-warden"
created: "2026-06-18"
updated: "2026-08-18"
last_reviewed: "2026-08-18"
review_interval: 6m
enforced_by: "SCOPE.md; registry/routing/catalog.yaml warden_executes"
supersedes: ""
successor: ""
---
# ADR-0005 — Implement one lane narrowly, route everything else
## Status
Accepted. The founding charter decision, taken 2026-06-18
(`history/2026-06-18-access-routing-intent-shift-assessment.md`).
## Context
ops-warden began as an SSH certificate manager. It then became the place workers
asked when they did not know where a credential came from — which is a real need,
and the obvious way to serve it is to start fetching credentials.
Down that path is a component that issues SSH certificates, vends API keys, brokers
tokens, and holds authority over all of them: a single point whose compromise is
total. NetKingdom's architecture deliberately separates identity (key-cape),
authorization (flex-auth), and secrets (OpenBao). A helpful front door that absorbed
all three would quietly undo that separation, one convenience at a time.
## Decision
**ops-warden executes exactly one lane with its own authority: SSH certificate
issuance for `adm`/`agt`/`atm` actors.** `warden_executes: true` appears on one
catalog entry and is expected to stay that way.
**For every other need it routes, and where the lane is `exec_capable` it may assist
by proxying as the caller** under `ADR-0002`. Routing is not a lesser service — it is
the service. Knowing which subsystem owns a need, and being right about it, is what
this repo sells.
**Scope growth is tested by ownership, not by usefulness.** "Would this be handy in
ops-warden?" is the wrong question and almost always answers yes. The right question
is "does ops-warden have the authority to own this, permanently?" If the answer is no,
the correct outcome is a pointer, or an `interim` cover recorded under `ADR-0003`.
## Consequences
**The blast radius stays bounded and known.** Compromising ops-warden yields the SSH
signing lane. That is worth defending well precisely because it is the only thing here.
**We say no to requests that would be easy to say yes to.** `warden secret`,
`warden login`, `warden bao`, `warden tunnel` do not exist and must not be invented;
the agent instructions name them as anti-patterns because agents keep reaching for
them. Each would be a day's work and a permanent widening.
**Being useful therefore depends on the pointers being right**, which is the whole
weight behind `ADR-0001`'s anchor enforcement and the catalog's review dates. A router
that routes wrongly is worse than no router.
**It leaves real gaps visible rather than filled.** Six workload lanes and three
tenant lanes are covered interim because secrets-engine and tenant-engine have not
shipped front doors. Under this ADR that is the correct state, tracked under
`ADR-0003`, and not a signal that ops-warden should absorb them.
## Related
- `SCOPE.md` — the issue-vs-route table
- `wiki/AccessRouting.md` — role and boundary
- `ADR-0001`, `ADR-0002`, `ADR-0003` — the three rules that follow from this one