Route OpenBao configuration and provisioning away from the structure survey
Configuration intent (policies, auth roles) routes to the attended openbao-platform-admin-login lane and value provisioning to openbao-api-key custody; both fail closed when their front door is missing. The survey lane now points at ops-mason's read-only ops-mason-survey grant (MASON-IN-0004). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 327285@bnt-lap001 Assistant-Session: d6532cd1-7cd5-417b-baac-b841c9103a6b
This commit is contained in:
parent
4d912276e4
commit
139f77dd55
7 changed files with 115 additions and 11 deletions
|
|
@ -42,6 +42,7 @@
|
|||
| workplan | WARDEN-WP-0041 | finished | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
|
||||
| workplan | WARDEN-WP-0042 | finished | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
|
||||
| workplan | WARDEN-WP-0043 | ready | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
|
||||
| workplan | WARDEN-WP-0044 | ready | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
|
||||
| task | WARDEN-WP-ADHOC-2026-06-27-T01 | done | — | workplans/ADHOC-2026-06-27.md |
|
||||
| task | WARDEN-WP-ADHOC-2026-06-29-T01 | done | — | workplans/ADHOC-2026-06-29.md |
|
||||
| task | WARDEN-WP-ADHOC-2026-08-11-T01 | done | — | workplans/ADHOC-2026-08-11.md |
|
||||
|
|
@ -177,6 +178,10 @@
|
|||
| task | WARDEN-WP-0043-T01 | todo | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
|
||||
| task | WARDEN-WP-0043-T02 | wait | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
|
||||
| task | WARDEN-WP-0043-T03 | wait | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
|
||||
| task | WARDEN-WP-0044-T01 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
|
||||
| task | WARDEN-WP-0044-T02 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
|
||||
| task | WARDEN-WP-0044-T03 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
|
||||
| task | WARDEN-WP-0044-T04 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
|
||||
| intake | WARDEN-IN-0001 | closed | — | intakes/intakes.md |
|
||||
| intake | WARDEN-IN-0002 | closed | — | intakes/intakes.md |
|
||||
| intake | WARDEN-IN-0003 | closed | — | intakes/intakes.md |
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
# declares it, and is null where the field set has not been established --
|
||||
# null means unknown, never 'one field'.
|
||||
|
||||
generated_at: "2026-10-05T14:12:49Z"
|
||||
generated_at: "2026-10-07T22:52:56Z"
|
||||
source: ops-warden/registry/routing/catalog.yaml
|
||||
catalog_revision: "9084581461545e07d26fc29783c9fd79446e54c6"
|
||||
catalog_revision_date: "2026-10-05T16:12:48+02:00"
|
||||
catalog_dirty: false
|
||||
catalog_dirty: true
|
||||
high_risk_lane_count: 32
|
||||
concrete_path_count: 17
|
||||
|
||||
|
|
|
|||
|
|
@ -1430,25 +1430,25 @@ entries:
|
|||
title: OpenBao existing-structure survey for a construction plan (section 2)
|
||||
# Pointer only (WARDEN-WP-0042). Founder decision: ops-warden routes,
|
||||
# ops-mason surveys. No survey code and no token live in ops-warden; the
|
||||
# survey runs under ops-mason's own scoped ops-mason-build grant.
|
||||
# survey runs under ops-mason's read-only ops-mason-survey grant (MASON-IN-0004).
|
||||
risk: high
|
||||
workload_ref:
|
||||
applicability: not-applicable
|
||||
reason: "Attended builder survey under a founder-minted grant; reads OpenBao structure metadata, not a workload credential."
|
||||
need_keywords: [survey, structure survey, openbao structure, existing structure, existing policies, existing lane, auth roles, approle roles, policy list, auth list, secrets list, construction plan, construction plan section 2, plan-survey, ops-mason-build]
|
||||
reason: "Attended read-only survey under a founder-minted grant; reads OpenBao structure metadata, not a workload credential."
|
||||
need_keywords: [survey, structure survey, openbao structure, existing structure, existing policies, existing lane, auth roles, approle roles, policy list, auth list, secrets list, construction plan, construction plan section 2, plan-survey, ops-mason-survey]
|
||||
owner_repo: ops-mason
|
||||
subsystem: OpenBao structure metadata via ops-mason-build grant
|
||||
subsystem: OpenBao structure metadata via read-only ops-mason-survey grant
|
||||
warden_executes: false
|
||||
wiki_ref: wiki/playbooks/openbao-structure-survey.md#worker-checklist
|
||||
canon_ref: ops-mason/docs/construction-plan-format.md
|
||||
reviewed: "2026-10-05"
|
||||
reviewed: "2026-10-08"
|
||||
status: active
|
||||
delegation:
|
||||
mode: native
|
||||
intended_owner: ops-mason
|
||||
reviewed: "2026-10-05"
|
||||
reviewed: "2026-10-08"
|
||||
verified: source-read
|
||||
auth_method: "founder-minted scoped grant: ops-mason scripts/bao-session.sh grant <task> (policy ops-mason-build, 45m, display_name claude-<task>)"
|
||||
auth_method: "founder-minted scoped grant: ops-mason scripts/bao-session.sh grant <task> (policy ops-mason-survey, 45m, display_name claude-<task>)"
|
||||
exec_owner: ops-mason
|
||||
exec_command: "ops-mason/scripts/plan-survey.py --kv-path <PATH> --policy <POLICY> --approle <ROLE> --consumer <REPO> --keyword <WORD>"
|
||||
exec_capable: false
|
||||
|
|
|
|||
|
|
@ -199,6 +199,28 @@ class CatalogFreshness:
|
|||
}
|
||||
|
||||
|
||||
def _openbao_configuration_need(query: str) -> bool:
|
||||
"""Separate explicit control-plane configuration from metadata discovery.
|
||||
|
||||
This is a routing distinction, never approval. Exact lane IDs are handled
|
||||
first; reads and surveys continue through ordinary catalog ranking.
|
||||
"""
|
||||
text = query.lower().replace("-", " ")
|
||||
control = re.search(r"\b(?:polic(?:y|ies)|auth\s+roles?|token\s+roles?|"
|
||||
r"database\s*(?:/| )\s*config|secrets?\s+engine)\b", text)
|
||||
change = re.search(r"\b(?:configure|configuring|create|apply|install|update|"
|
||||
r"administration|administer)\b", text)
|
||||
return bool(re.search(r"\bopenbao\b", text) and control and change)
|
||||
|
||||
|
||||
def _openbao_provision_need(query: str) -> bool:
|
||||
"""Value provisioning belongs to custody, never the structure survey."""
|
||||
text = query.lower().replace("-", " ")
|
||||
return bool(re.search(r"\bopenbao\b", text)
|
||||
and re.search(r"\b(?:provision|provisioning|seed|seeding)\b", text)
|
||||
and re.search(r"\b(?:keys?|credentials?|secrets?|tokens?|values?)\b", text))
|
||||
|
||||
|
||||
@dataclass
|
||||
class Catalog:
|
||||
path: Path
|
||||
|
|
@ -232,6 +254,19 @@ class Catalog:
|
|||
scored = [(e.match_score(tokens), e) for e in pool]
|
||||
scored = [(s, e) for s, e in scored if s > 0]
|
||||
scored.sort(key=lambda pair: (-pair[0], pair[1].id))
|
||||
if _openbao_provision_need(query):
|
||||
custody = self.get("openbao-api-key")
|
||||
if custody is None or not custody.is_active or custody.lane != "secret":
|
||||
return []
|
||||
return ([custody] + [entry for _, entry in scored if entry.id != custody.id])[:limit]
|
||||
if _openbao_configuration_need(query):
|
||||
admin = self.get("openbao-platform-admin-login")
|
||||
# A missing/inactive administration front door must not fall back to
|
||||
# a survey ceremony or a secret-read transport.
|
||||
if admin is None or not admin.is_active or admin.lane != "login":
|
||||
return []
|
||||
ranked = [admin] + [entry for _, entry in scored if entry.id != admin.id]
|
||||
return ranked[:limit]
|
||||
return [e for _, e in scored[:limit]]
|
||||
|
||||
def stale(
|
||||
|
|
|
|||
60
tests/test_openbao_configuration_routing.py
Normal file
60
tests/test_openbao_configuration_routing.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""Configuration intent must never inherit a metadata survey's founder act."""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from warden.plan import build_plan
|
||||
from warden.routing.catalog import Catalog, load_catalog
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
NEED = ('attended OpenBao platform administration to configure read policies and auth roles '
|
||||
'for Coulomb and email backup delivery')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def catalog(monkeypatch):
|
||||
monkeypatch.setenv('WARDEN_POSTURE_CATALOG', str(ROOT / 'registry/policy/security-posture.yaml'))
|
||||
return load_catalog(ROOT / 'registry/routing/catalog.yaml')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('need', [NEED,
|
||||
'configure OpenBao policies and Kubernetes auth roles for backup delivery',
|
||||
'apply OpenBao read policy for an explicitly reviewed service account'])
|
||||
def test_configuration_requires_attended_admin_identity(catalog, need):
|
||||
plan = build_plan(need, catalog=catalog)
|
||||
assert plan.lane_id == 'openbao-platform-admin-login'
|
||||
assert plan.verdict == 'founder_required' and plan.founder_act.kind == 'oidc_login'
|
||||
assert all('--fetch' not in command and '--out' not in command for command in plan.commands)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('need', [
|
||||
'survey existing OpenBao policies and roles for backup delivery',
|
||||
'OpenBao read-only metadata survey policy approle consumer backup',
|
||||
'inspect OpenBao structure metadata policy approle'])
|
||||
def test_survey_remains_metadata_only(catalog, need):
|
||||
plan = build_plan(need, catalog=catalog)
|
||||
assert plan.lane_id == 'openbao-structure-survey'
|
||||
assert plan.founder_act.kind == 'approve'
|
||||
|
||||
|
||||
def test_explicit_lane_id_still_wins_and_missing_admin_fails_closed(catalog):
|
||||
assert catalog.find('openbao-structure-survey')[0].id == 'openbao-structure-survey'
|
||||
unavailable = Catalog(catalog.path, [entry for entry in catalog.entries
|
||||
if entry.id != 'openbao-platform-admin-login'])
|
||||
plan = build_plan(NEED, catalog=unavailable)
|
||||
assert plan.verdict == 'unroutable' and plan.lane_id is None
|
||||
assert not plan.commands
|
||||
|
||||
|
||||
def test_pair_provisioning_routes_to_custody_not_survey(catalog):
|
||||
need = "provision a Scaleway object storage backup publisher key pair into OpenBao custody"
|
||||
plan = build_plan(need, catalog=catalog)
|
||||
assert plan.lane_id == "openbao-api-key"
|
||||
assert plan.verdict == "founder_required"
|
||||
assert plan.founder_act.kind == "paste_once_provision"
|
||||
assert not any("plan-survey" in command for command in plan.commands)
|
||||
|
||||
|
||||
def test_missing_custody_frontdoor_fails_closed(catalog):
|
||||
unavailable = Catalog(catalog.path, [e for e in catalog.entries if e.id != "openbao-api-key"])
|
||||
assert build_plan("provision OpenBao backup key pair", catalog=unavailable).verdict == "unroutable"
|
||||
|
|
@ -652,6 +652,8 @@ def test_access_fetch_to_nonterminal_stdout_is_refused(tmp_path, monkeypatch):
|
|||
"warden.proxy.subprocess.run",
|
||||
lambda *a, **k: (_ for _ in ()).throw(AssertionError("fetch ran despite stdout guard")),
|
||||
)
|
||||
# This case tests the human stdout guard; the agent guard has its own tests.
|
||||
monkeypatch.setattr("warden.access.agent_read_boundary_identity", lambda: "")
|
||||
# CliRunner captures stdout (not a tty), so the guard trips without --unsafe-stdout.
|
||||
r = runner.invoke(app, ["access", "whynot-design-npm-publish", "--fetch"])
|
||||
assert r.exit_code == 6
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
Catalog: `openbao-structure-survey` (status `active`, pointer only)
|
||||
Owner: `ops-mason` — `scripts/plan-survey.py`, `docs/construction-plan-format.md` § 2
|
||||
Routing record: `WARDEN-WP-0042`; owner work: `ops-mason/MASON-WP-0008`
|
||||
Routing records: `WARDEN-WP-0042`, `WARDEN-WP-0043`; owner change: `ops-mason/MASON-IN-0004` (2026-10-07)
|
||||
|
||||
ops-warden routes this need and does nothing else. It holds no survey code and
|
||||
no OpenBao token; the survey runs under ops-mason's own scoped
|
||||
`ops-mason-build` grant, which reads structure metadata and is denied every
|
||||
`ops-mason-survey` read-only grant, which reads structure metadata and is denied every
|
||||
secret value.
|
||||
|
||||
## Worker checklist
|
||||
|
|
@ -22,3 +22,5 @@ secret value.
|
|||
|
||||
Anything the grant refuses is ops-mason's to resolve with the founder; do not
|
||||
reach for a broader session through ops-warden.
|
||||
|
||||
The builder grant was retired by MASON-IN-0004. Surveys cannot apply policies or auth roles; reviewed structure changes belong to railiance-platform's delegated metadata applier.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue