WARDEN-WP-0026 T06: rotation guidance registry + warden rotate-guide
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

- routing model: RotationGuide (method rotate|re-establish, steps, owner,
  automatable), RouteEntry.rotation + has_rotation + vends_secret.
- catalog parser: validate rotation block; secret-material screen gains a
  prose-safe mode (high-entropy detector only) so authored steps aren't tripped
  by substrings like "s."/"exists.".
- CLI: `warden rotate-guide <id>` (human + --json); route show --json now
  carries has_rotation + rotation.
- scorecard: catalog_rotation_coverage — every active secret-vending lane must
  carry a rotation block (SSH/login/pointer lanes exempt). Promotion checklist
  criterion 9.
- data: rotation blocks for all 7 active vending lanes + the draft
  railiance-backup lane (re-establish: age keypair regen + re-encrypt).
- fix pre-existing collision: bare `npm` keyword on forgejo-admin -> forgejo-npm
  so "npm token" routes to the generic lane (restores test_access expectations).
- tests: rotation parse/coverage/prose-screen/CLI in tests/test_routing.py;
  scorecard count 6 -> 7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-07-16 14:40:30 +02:00
parent ac09f21ad3
commit c3eb59ea04
9 changed files with 385 additions and 12 deletions

View file

@ -11,6 +11,23 @@ from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class RotationGuide:
"""Structured-but-advisory renewal guidance for a lane (WARDEN-WP-0026 T06).
Held in the ops-warden registry, never in OpenBao. ``steps`` are authored
advisory prose (screened for secret material like every catalog string) they
tell an operator *how* to renew, they are not executed here. ``method`` is
``rotate`` (provider re-mints the same kind of credential) or ``re-establish``
(regenerate from source, e.g. a new age keypair + re-encrypt). ``automatable``
is a hint for a future Strand-B executable driver (WARDEN-WP-0027).
"""
method: str # "rotate" | "re-establish"
steps: List[str]
owner: str
automatable: bool = False
@dataclass
class RouteEntry:
id: str
@ -50,11 +67,31 @@ class RouteEntry:
exec_owner: Optional[str] = None # subsystem owning the native exec (e.g. secrets-engine)
exec_command: Optional[str] = None # e.g. "secrets-engine exec --catalog <id> -- <cmd>"
pointer_command: Optional[str] = None # e.g. "secrets-engine route <id> --json"
# Rotation / re-establishment guidance (WP-0026 T06) — advisory, no secret values.
rotation: Optional[RotationGuide] = None
@property
def is_active(self) -> bool:
return self.status == "active"
@property
def has_rotation(self) -> bool:
"""True when this lane carries renewal guidance (WP-0026 T06)."""
return self.rotation is not None
@property
def vends_secret(self) -> bool:
"""True when this lane hands back a rotatable static secret value.
Rotation guidance (WP-0026 T06) applies to these. It excludes the SSH lane
(short-lived certs renewal is re-issuance), ``login`` lanes (re-auth, no
stored value), and pure routing pointers with no secret path (tunnel,
principals, emission sinks, policy checks).
"""
if self.warden_executes or self.lane != "secret":
return False
return bool(self.path_template or self.fetch_command or self.exec_owner)
@property
def has_native_exec(self) -> bool:
"""True when an owner-native exec front door is the primary path for this lane."""