Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0290b-3241-74c3-b868-6049545af836
325 lines
14 KiB
Python
325 lines
14 KiB
Python
"""Data model for routing catalog entries.
|
|
|
|
A `RouteEntry` is a pointer: it names the owner and the authoritative doc for a
|
|
credential need. Only the SSH lane (`warden_executes: true`) may carry an authored
|
|
`steps` block and a `cert_command` pattern — every other entry is identifiers and
|
|
pointers only (the no-double-source rule, enforced in `catalog.py`).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Optional
|
|
|
|
|
|
# Risk grade vocabulary (ADR-0007). Grades outside LOW_RISK_GRADES — including
|
|
# the "ungraded" default and any value from a newer catalog — are treated as
|
|
# high by is_high_risk, so the read-boundary fails safe in both directions.
|
|
LOW_RISK_GRADES = frozenset({"standard", "low", "accepted"})
|
|
GRADED_RISK = frozenset({"standard", "low", "accepted", "high", "critical"})
|
|
|
|
|
|
@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
|
|
|
|
|
|
# Risk classes for agent read-boundary (WARDEN-WP-0026 T04).
|
|
# high — recovery escrow, upload tokens, admin PATs, high-spend provider keys.
|
|
# Agent identities must not hold raw data-read (metadata/capabilities only).
|
|
# standard — ordinary workload secrets (ESO-fed, non-escrow); normal least-privilege.
|
|
VALID_RISK = ("standard", "high")
|
|
|
|
# Delegation modes (WARDEN-WP-0030). Absence of a block is implicit interim.
|
|
# native — intended owner already fronts the lane (route-primary / pointer)
|
|
# interim — ops-warden covers a gap; intended_owner + blocked_on required
|
|
# permanent — ops-warden is the designed owner of this front door (SSH only today)
|
|
VALID_DELEGATION_MODES = ("native", "interim", "permanent")
|
|
VALID_WORKLOAD_APPLICABILITY = ("applicable", "not-applicable")
|
|
|
|
IMPLICIT_DELEGATION_BLOCKED_ON = (
|
|
"unclassified — no delegation block; treat as a question, not a settlement"
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class Delegation:
|
|
"""Who should own this front door, and what is missing (WARDEN-WP-0030).
|
|
|
|
Pointer-layer only: names the intended owner and the blocker. Does not
|
|
restate how that owner will implement their front door.
|
|
"""
|
|
|
|
mode: str # native | interim | permanent
|
|
intended_owner: Optional[str] = None
|
|
blocked_on: Optional[str] = None
|
|
reviewed: Optional[str] = None
|
|
verified: Optional[str] = None
|
|
implicit: bool = False
|
|
|
|
#: How `reviewed` was established. The distinction exists because a date
|
|
#: bumped by editing the entry looks identical to one bumped by re-checking
|
|
#: the blocker, and on 2026-08-21 six lanes read as freshly reviewed when
|
|
#: only some had actually been re-verified (WARDEN-WP-0033-T05).
|
|
#:
|
|
#: `asked-and-waiting` deliberately does NOT count as verification: it is the
|
|
#: state the secrets-engine lanes sat in for ten days while looking fresh.
|
|
VERIFICATION_METHODS = (
|
|
"owner-confirmed", # the intended owner stated the blocker's status
|
|
"source-read", # re-derived from the owner's code, canon, or CCR
|
|
"asked-and-waiting", # a question is outstanding — NOT verification
|
|
"unverified", # carried forward without a check
|
|
)
|
|
#: Methods that mean the claim was actually re-established.
|
|
VERIFYING_METHODS = ("owner-confirmed", "source-read")
|
|
|
|
@property
|
|
def is_verified(self) -> bool:
|
|
"""True only when the blocker was re-established, not merely re-edited."""
|
|
return self.verified in self.VERIFYING_METHODS
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"mode": self.mode,
|
|
"intended_owner": self.intended_owner,
|
|
"blocked_on": self.blocked_on,
|
|
"reviewed": self.reviewed,
|
|
"verified": self.verified,
|
|
"is_verified": self.is_verified,
|
|
"implicit": self.implicit,
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class WorkloadReference:
|
|
"""Authoritative workload join for a catalog lane (WARDEN-WP-0032).
|
|
|
|
Managed deployables use the Repo Manager v1 ``rapp_id``/``name`` tuple.
|
|
Independently governed operational workloads use ``name`` plus an owner
|
|
declaration reference. An applicable lane whose owner has not published an
|
|
identity remains explicitly ``unknown``; it is never inferred from the
|
|
credential path or repository name.
|
|
"""
|
|
|
|
applicability: str # applicable | not-applicable
|
|
rapp_id: Optional[str] = None
|
|
name: Optional[str] = None
|
|
deployable: Optional[str] = None
|
|
declaration_ref: Optional[str] = None
|
|
reason: Optional[str] = None
|
|
unknown_reason: Optional[str] = None
|
|
|
|
@property
|
|
def resolution(self) -> str:
|
|
if self.applicability == "not-applicable":
|
|
return "not-applicable"
|
|
if self.unknown_reason:
|
|
return "unknown"
|
|
return "resolved"
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"applicability": self.applicability,
|
|
"rapp_id": self.rapp_id,
|
|
"name": self.name,
|
|
"deployable": self.deployable,
|
|
"declaration_ref": self.declaration_ref,
|
|
"reason": self.reason,
|
|
"unknown_reason": self.unknown_reason,
|
|
"resolution": self.resolution,
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class RouteEntry:
|
|
id: str
|
|
title: str
|
|
need_keywords: List[str]
|
|
owner_repo: str
|
|
subsystem: str
|
|
warden_executes: bool
|
|
wiki_ref: str
|
|
canon_ref: str
|
|
reviewed: str
|
|
status: str # "active" | "draft"
|
|
# Explicit workload applicability and authoritative join. Never inferred.
|
|
workload_ref: Optional[WorkloadReference] = None
|
|
# SSH lane only — None/empty for routed (non-executed) needs.
|
|
steps: List[str] = field(default_factory=list)
|
|
cert_command: Optional[str] = None
|
|
# Structured handoff (WP-0014) — optional, allowed on any lane. These are
|
|
# *templates and pointers* the `warden access` assist layer renders (and, for
|
|
# exec_capable lanes, proxies). They are NOT authored procedure prose and they
|
|
# never carry a secret value — only placeholders (`<...>`) and field names.
|
|
# Validation in catalog.py enforces the no-secret-material rule on every one.
|
|
auth_method: Optional[str] = None # how the caller authenticates to the owner
|
|
path_template: Optional[str] = None # owner-side path with `<...>` placeholders
|
|
fetch_command: Optional[str] = None # command skeleton run *as the caller*
|
|
exec_capable: bool = False # may `warden access --fetch/--exec` proxy it
|
|
policy_ref: Optional[str] = None # flex-auth check the fetch path runs first
|
|
# Proxy lane semantics (WP-0014 T4):
|
|
# "secret" — read a value (gated by flex-auth secret-read; caller must already
|
|
# be authenticated; value transits via inherit-stdout or child env).
|
|
# "login" — interactive auth bootstrap (OIDC/MFA). No secret-read gate (you have
|
|
# no identity yet), no caller-auth precheck (the point is to get one),
|
|
# run interactively as the caller; warden never captures the token.
|
|
# "ceremony" — attended owner operation such as Shamir seal/unseal. It is a
|
|
# pointer plus approval boundary, never an executable access lane.
|
|
lane: str = "secret"
|
|
# Owner-native exec front door (WP-0019). When `exec_owner` is set, that subsystem
|
|
# (e.g. secrets-engine) provides the PRIMARY way to run a secret-backed command; the
|
|
# catalog routes to it and keeps ops-warden's own --fetch/--exec proxy as a transparent
|
|
# fallback (route-primary, proxy-fallback). Pointers/templates only — never a value.
|
|
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
|
|
# Agent read-boundary risk class (WP-0026 T04). high → agents use wrap/out/exec only.
|
|
# Default is "ungraded", which FAILS SAFE: it is treated as high. Before
|
|
# ADR-0007 this defaulted to "standard", so a lane that simply omitted the
|
|
# field was silently placed outside the read-boundary (RISK-F-0003) — the
|
|
# control was never relaxed by decision, it was never reached.
|
|
risk: str = "ungraded" # "standard" | "high" | "ungraded"
|
|
# Delegation register (WP-0030). None → implicit interim with unknown owner.
|
|
delegation: Optional[Delegation] = None
|
|
|
|
@property
|
|
def is_active(self) -> bool:
|
|
return self.status == "active"
|
|
|
|
@property
|
|
def is_graded(self) -> bool:
|
|
"""False when this lane carries no explicit risk grade (ADR-0007)."""
|
|
return self.risk in GRADED_RISK
|
|
|
|
@property
|
|
def is_high_risk(self) -> bool:
|
|
"""True when this lane is on the agent raw-read deny list (WP-0026 T04).
|
|
|
|
Anything not explicitly graded low is high. An ungraded lane, or one
|
|
carrying a grade this version does not recognise, is treated as high
|
|
rather than waved through — ADR-0007: absence is not a grade.
|
|
"""
|
|
return self.risk not in LOW_RISK_GRADES
|
|
|
|
def risk_for_zone(
|
|
self,
|
|
*,
|
|
effective_zone: str = "unknown",
|
|
admission: str = "unknown",
|
|
synthetic_only: bool = False,
|
|
) -> str:
|
|
"""Resolve an absent grade using security-zones_v0.1 section 5.1.
|
|
|
|
Explicit grades always win. The sole lower default is a satisfied
|
|
``z0-experimental`` workload proven synthetic-only. Every other zone,
|
|
failed/unknown admission, and missing context fails safe to at least
|
|
``high``; z3 reports ``critical`` (which the read boundary treats as
|
|
high). Catalog CI still requires explicit grades, so this is the safe
|
|
runtime behavior for malformed or newer external catalogs.
|
|
"""
|
|
if self.is_graded:
|
|
return self.risk
|
|
if (
|
|
effective_zone == "z0-experimental"
|
|
and admission == "satisfied"
|
|
and synthetic_only
|
|
):
|
|
return "standard"
|
|
if effective_zone == "z3-critical" and admission == "satisfied":
|
|
return "critical"
|
|
return "high"
|
|
|
|
@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."""
|
|
return bool(self.exec_owner and self.exec_command)
|
|
|
|
@property
|
|
def effective_delegation(self) -> Delegation:
|
|
"""Declared delegation, or implicit interim with an unknown owner.
|
|
|
|
Absence of a ``delegation:`` block is a question (WP-0030), not a
|
|
settlement that ops-warden owns the front door.
|
|
"""
|
|
if self.delegation is not None:
|
|
return self.delegation
|
|
return Delegation(
|
|
mode="interim",
|
|
intended_owner=None,
|
|
blocked_on=IMPLICIT_DELEGATION_BLOCKED_ON,
|
|
reviewed=None,
|
|
implicit=True,
|
|
)
|
|
|
|
@property
|
|
def is_interim(self) -> bool:
|
|
"""True when this lane is a tracked gap (explicit or implicit)."""
|
|
return self.effective_delegation.mode == "interim"
|
|
|
|
@property
|
|
def has_handoff(self) -> bool:
|
|
"""True when structured assist fields are present (advisory richness)."""
|
|
return any((self.auth_method, self.path_template, self.fetch_command))
|
|
|
|
@property
|
|
def resolvable(self) -> bool:
|
|
"""True when `warden access --fetch` can run this lane with no further input.
|
|
|
|
A resolvable lane is active, exec_capable, and its fetch command (with the path
|
|
inlined) carries no unresolved ``<...>`` placeholder. Template lanes — like the
|
|
generic ``openbao-api-key`` or the ``<domain>``-parameterized login — are *not*
|
|
resolvable until an owner ships concrete names. Lets an automated caller know
|
|
whether ``--fetch`` will work *before* attempting it (whynot-design request).
|
|
"""
|
|
if not (self.is_active and self.exec_capable and self.fetch_command):
|
|
return False
|
|
blob = f"{self.fetch_command} {self.path_template or ''}"
|
|
return "<" not in blob and ">" not in blob
|
|
|
|
def match_score(self, tokens: List[str]) -> int:
|
|
"""Keyword-overlap score against need_keywords, title, and id.
|
|
|
|
Pure ranking helper — no I/O, no external calls.
|
|
"""
|
|
haystack = set(k.lower() for k in self.need_keywords)
|
|
haystack.update(self.id.lower().replace("-", " ").split())
|
|
haystack.update(self.title.lower().replace("-", " ").split())
|
|
score = 0
|
|
for tok in tokens:
|
|
t = tok.lower()
|
|
if t in haystack:
|
|
score += 2
|
|
elif any(t in h or h in t for h in haystack):
|
|
score += 1
|
|
return score
|