Prepare offline attended provisioning checklists and route remaining custody gates
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
This commit is contained in:
parent
7c1a5c2516
commit
46eb8bea9e
6 changed files with 221 additions and 4 deletions
13
docs/attended-provisioning-window.md
Normal file
13
docs/attended-provisioning-window.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Attended provisioning preparation contract
|
||||
|
||||
Owner: railiance-platform, RPF-WP-0050. Origin: CUST-WP-0075 P2/P3.
|
||||
|
||||
`python3 scripts/credential-change.py window-checklist --window-file <metadata.yaml> --workplan-root <owner/workplans>` joins open non-residual task blocks carrying the exact decision UUID to explicit CCR, lane and owner-receipt contracts. Repeat `--workplan-root` for each repo in scope. Roots are the authoritative file snapshot; Hub qualifiers are independently verified before any attended window. No implicit completeness claim exists for omitted roots.
|
||||
|
||||
The metadata manifest has `decision_id`, `date` (YYYY-MM-DD), and `entries`; each entry has `task_id`, `ccr`, `lane_doc`, `receipt_contract`. The three file references are relative to this platform repo and must exist; the receipt contract names the owner’s required positive/negative/revocation/cleanup evidence, never values. Empty manifests/decisions, missing joins or any non-approved CCR produce exit 2 and NOT READY. Duplicate IDs and stale task joins are refused. Applied CCRs are never replay candidates. Arbitrary CCR content is never printed.
|
||||
|
||||
Each item must additionally bind the exact reviewed revision/digest, expected result, native preflight, abort owner, verification and rollback in the existing CCR/supervision receipt. REVIEW PREPARED is a source-join result, not execution permission. Upstream owner gates and live readiness must be checked again in the approved dated window. The Custodian owns cadence; this renderer schedules nothing and executes nothing. A resolved packet calls for owner re-check, not unconditional task completion.
|
||||
|
||||
## P3 draft for founder ruling
|
||||
|
||||
The current rule remains attended value custody. Choose explicitly between (a) batched attended custody, (b) response-wrapped issuer delivery with one-time unwrap, and (c) a scoped MFA writer through a silent contained helper. Review exact actor/path/audience, TTL/single use, audit boundary, child-output suppression, crash cleanup, replay refusal and rollback before adopting b/c. Wrapped tokens are credentials and stay out of files, Hub, chat and logs. No option grants broad write or changes existing lanes. RPF-WP-0050-T02 is routed to D3; a generic D3 resolution still requires a specific value-path disposition, so lack of an explicit answer does not unblock it.
|
||||
|
|
@ -2240,6 +2240,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
)
|
||||
applier_apply.set_defaults(func=command_applier_apply)
|
||||
|
||||
from provisioning_window import command_window
|
||||
window = sub.add_parser("window-checklist", help="Render an offline attended-window preparation checklist; never execute")
|
||||
window.add_argument("--window-file", required=True)
|
||||
window.add_argument("--workplan-root", action="append", required=True)
|
||||
window.set_defaults(func=command_window)
|
||||
|
||||
runbook = sub.add_parser(
|
||||
"runbook",
|
||||
help="Render or execute the attended operator apply/verify runbook",
|
||||
|
|
|
|||
80
scripts/provisioning_window.py
Normal file
80
scripts/provisioning_window.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
"""Offline, metadata-only attended-window checklist; never executes a CCR."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
from uuid import UUID
|
||||
import yaml
|
||||
|
||||
|
||||
def task_sources(roots, decision_id):
|
||||
selected = {}
|
||||
for root in roots:
|
||||
for path in sorted(Path(root).glob("*.md")):
|
||||
text = path.read_text()
|
||||
front = re.match(r"---\n(.*?)\n---", text, re.S)
|
||||
meta = yaml.safe_load(front.group(1)) if front else {}
|
||||
if meta and (meta.get("flavor") == "residual" or meta.get("status") in {"finished", "archived"}):
|
||||
continue
|
||||
for block in re.findall(r"```task\s*\n(.*?)\n```", text, re.S):
|
||||
task = yaml.safe_load(block)
|
||||
if str(task.get("decision_id", "")) == decision_id and task.get("status") not in {"done", "cancel"}:
|
||||
task_id = task["id"]
|
||||
if task_id in selected:
|
||||
raise ValueError("duplicate task id: " + task_id)
|
||||
selected[task_id] = task
|
||||
return selected
|
||||
|
||||
|
||||
def local_file(repo, value):
|
||||
path = (repo / value).resolve()
|
||||
if not path.is_relative_to(repo.resolve()) or not path.is_file():
|
||||
raise ValueError("missing or outside-repository preparation file")
|
||||
return path
|
||||
|
||||
|
||||
def render_window(window_file, roots, repo):
|
||||
window = yaml.safe_load(Path(window_file).read_text())
|
||||
decision = str(UUID(str(window["decision_id"])))
|
||||
date = str(window["date"])
|
||||
# A dated manifest is preparation, never a scheduled event or approval.
|
||||
if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", date):
|
||||
raise ValueError("window date must be YYYY-MM-DD")
|
||||
tasks = task_sources(roots, decision)
|
||||
entries = window.get("entries", [])
|
||||
by_task = {}
|
||||
for entry in entries:
|
||||
tid = entry["task_id"]
|
||||
if tid in by_task:
|
||||
raise ValueError("duplicate window task")
|
||||
by_task[tid] = entry
|
||||
stale = set(by_task) - set(tasks)
|
||||
if stale:
|
||||
raise ValueError("manifest tasks absent from current decision: " + ", ".join(sorted(stale)))
|
||||
lines = ["# Attended provisioning preparation: " + date,
|
||||
"Decision: " + decision,
|
||||
"Preparation only. Founder window approval and each exact CCR revision remain required."]
|
||||
ready = bool(tasks)
|
||||
for index, tid in enumerate(sorted(tasks), 1):
|
||||
entry = by_task.get(tid)
|
||||
if not entry:
|
||||
lines.append(f"{index}. {tid}: NOT READY — missing CCR/lane/receipt join.")
|
||||
ready = False
|
||||
continue
|
||||
ccr = yaml.safe_load(local_file(repo, entry["ccr"]).read_text())
|
||||
local_file(repo, entry["lane_doc"])
|
||||
local_file(repo, entry["receipt_contract"])
|
||||
eligible = ccr.get("status") == "approved"
|
||||
ready &= eligible
|
||||
label = "REVIEW PREPARED" if eligible else "NOT READY (CCR is not approved; never replay applied custody)"
|
||||
# Deliberately avoid rendering arbitrary CCR contents or secret-bearing values.
|
||||
lines.extend([f"{index}. {tid}: {label}",
|
||||
" CCR: " + entry["ccr"], " Lane: " + entry["lane_doc"],
|
||||
" Owner receipt contract: " + entry["receipt_contract"],
|
||||
" Revalidate exact digest, upstream gates and native readiness before attended execution."])
|
||||
lines.append("Checklist complete: " + str(ready).lower())
|
||||
return "\n".join(lines) + "\n", ready
|
||||
|
||||
|
||||
def command_window(args):
|
||||
output, ready = render_window(args.window_file, args.workplan_root, Path(__file__).resolve().parents[1])
|
||||
print(output, end="")
|
||||
return 0 if ready else 2
|
||||
55
tests/test_provisioning_window.py
Normal file
55
tests/test_provisioning_window.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import importlib.util
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import yaml
|
||||
|
||||
SPEC = importlib.util.spec_from_file_location("window", Path(__file__).resolve().parents[1] / "scripts/provisioning_window.py")
|
||||
module = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(module)
|
||||
D = "62aa95e4-3ccc-4867-8806-5e3db005b483"
|
||||
|
||||
class WindowTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.root = Path(self.tmp.name)
|
||||
self.plans = self.root / "workplans"
|
||||
self.plans.mkdir()
|
||||
self.task = "```task\nid: EX-WP-0001-T01\nstatus: wait\ndecision_id: " + D + "\n```\n"
|
||||
(self.plans / "plan.md").write_text(self.task)
|
||||
for name in ("lane.md", "receipt.md"):
|
||||
(self.root / name).write_text("non-secret contract")
|
||||
self.window = {"decision_id": D, "date": "2026-10-03", "entries": [{"task_id": "EX-WP-0001-T01", "ccr": "ccr.yaml", "lane_doc": "lane.md", "receipt_contract": "receipt.md"}]}
|
||||
self.ccr("approved")
|
||||
def ccr(self, status):
|
||||
(self.root / "ccr.yaml").write_text(yaml.safe_dump({"status": status, "secret": "NEVER_PRINT_THIS"}))
|
||||
def render(self):
|
||||
p = self.root / "window.yaml"
|
||||
p.write_text(yaml.safe_dump(self.window))
|
||||
return module.render_window(p, [self.plans], self.root)
|
||||
def test_approved_join_and_no_values(self):
|
||||
output, ready = self.render()
|
||||
self.assertTrue(ready)
|
||||
self.assertNotIn("NEVER_PRINT_THIS", output)
|
||||
def test_applied_custody_cannot_replay(self):
|
||||
self.ccr("applied")
|
||||
self.assertFalse(self.render()[1])
|
||||
def test_missing_join_stays_not_ready(self):
|
||||
self.window["entries"] = []
|
||||
self.assertFalse(self.render()[1])
|
||||
def test_stale_join_refused(self):
|
||||
(self.plans / "plan.md").write_text(self.task.replace("status: wait", "status: done"))
|
||||
with self.assertRaises(ValueError): self.render()
|
||||
def test_duplicate_authority_refused(self):
|
||||
(self.plans / "duplicate.md").write_text(self.task)
|
||||
with self.assertRaises(ValueError): self.render()
|
||||
def test_outside_contract_refused(self):
|
||||
self.window["entries"][0]["lane_doc"] = "../outside.md"
|
||||
with self.assertRaises(ValueError): self.render()
|
||||
def test_residual_not_selected(self):
|
||||
(self.plans / "plan.md").write_text("---\nflavor: residual\n---\n" + self.task)
|
||||
self.window["entries"] = []
|
||||
self.assertFalse(self.render()[1])
|
||||
|
||||
if __name__ == "__main__": unittest.main()
|
||||
|
|
@ -71,7 +71,7 @@ issuer/audience/subject/claims and unrelated secret access fail, bounded TTL
|
|||
and revocation are proven, the consumer explicitly opts into the verified
|
||||
contract, and rollback/cleanup receipts contain no secret material.
|
||||
|
||||
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
|
||||
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
|
||||
|
||||
2026-10-03 (CUST-WP-0075): gate check. SECRETS-WP-0007-T04 is done (2026-09-27), so the gate named in the design is open. It is not the last one: the design says to confirm the exact live client registration and protected client-side custody before writing, and positive-login proof needs a real KeyCape service JWT. KeyCape's contract (`openbao-service-auth-contract.md`) and KEY-WP-0009 claim source registration only; the live registration of `secrets-engine-openbao` and its client-credential custody are "a separate approved custody action" that no workplan task owns yet (SECRETS-WP-0008-T06 waits on this task). Re-qualified with `blocked_on: message-from:key-cape`; key-cape was asked for the owning task id, which will replace it as `depends_on`. The design stays proposed and no child command was written. Not approvable in D1 yet.
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ only the exact matrix, negative/expiry/revocation checks pass, custody is seeded
|
|||
through the separate writer, and the verified route has a safe handoff receipt.
|
||||
The unattended adapter remains a separate demand and gets no operator session.
|
||||
|
||||
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
|
||||
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
|
||||
|
||||
## Accept the needed signing lane and deliver it to the owning runtime
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ receipts to HFACT-WP-0001-T03, SECRETS-WP-0009-T03 and APPROVAL-WP-0002-T01/T05.
|
|||
Audit receiver/sender custody remains AUDIT-WP-0009-T09 and must not be folded
|
||||
into this client-identity grant.
|
||||
|
||||
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
|
||||
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
|
||||
|
||||
## Admit the two factory audit-sender custody lanes
|
||||
|
||||
|
|
@ -440,6 +440,7 @@ into this client-identity grant.
|
|||
id: RPF-WP-0035-T08
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "ab85ae8a-12a0-4945-ae64-1d3d86d5b26f"
|
||||
blocking_reason: "Provisioned and delivered 2026-09-11 (CCR-2026-0021/0022 applied); do not repeat. Receiver rollout with evidence_kind and native producer acceptance passed 2026-09-11. Remaining human acceptance: independent archive readback and revocation of the interim audit bearer tokens."
|
||||
priority: high
|
||||
assignee: railiance-platform
|
||||
|
|
@ -469,7 +470,7 @@ Approval Engine's missing namespace remains its owner prerequisite.
|
|||
[Preparation evidence](../docs/evidence/2026-09-11-factory-audit-custody.json).
|
||||
Do not fold these senders into CCR-2026-0019 or the cancelled CCR-2026-0020.
|
||||
|
||||
2026-10-03: human gate routed to hub decision 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1: Attended OpenBao provisioning window: one session clears the pending credential lanes) by CUST-WP-0075; resolving that decision raises C-40 here.
|
||||
2026-10-03: the historical D1 routing was withdrawn because provision/delivery already completed. Owner routing now uses decision `ab85ae8a-12a0-4945-ae64-1d3d86d5b26f`, specifically independent archive readback and coordinated interim audit bearer revocation acceptance. `needs_human` and the wait remain; resolving a decision requires owner receipt re-check, not automatic completion.
|
||||
|
||||
## Dependency review — 2026-09-06
|
||||
|
||||
|
|
|
|||
62
workplans/RPF-WP-0050-attended-provisioning-preparation.md
Normal file
62
workplans/RPF-WP-0050-attended-provisioning-preparation.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
id: RPF-WP-0050
|
||||
type: workplan
|
||||
title: "Prepare generated attended windows and explicit value-custody choice"
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: active
|
||||
owner: codex
|
||||
topic_slug: platform-services
|
||||
created: "2026-10-03"
|
||||
updated: "2026-10-03"
|
||||
flavor: implementation
|
||||
---
|
||||
|
||||
Origin: Custodian proposal f84637fa-2355-4971-a2dc-7f1fd01710a8, CUST-WP-0075 P2/P3. Platform accepts bounded preparation; Custodian owns cadence, founder owns custody policy and live authorization. No recurring event is enabled.
|
||||
|
||||
## Render offline decision-task-CCR preparation checklist
|
||||
|
||||
```task
|
||||
id: RPF-WP-0050-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Implemented `credential-change.py window-checklist` and `scripts/provisioning_window.py`. Reads authoritative open non-residual task blocks and explicit CCR/lane/receipt contracts. Missing/stale joins and non-approved/applied CCRs prevent a complete checklist. No execution, scheduling, credential reads or printing arbitrary CCR contents. Contract: `docs/attended-provisioning-window.md`. Synthetic regression suite verifies actual decision joins and replay refusal. This completes the bounded source renderer, not a real attended session.
|
||||
|
||||
## Obtain exact founder value-custody choice
|
||||
|
||||
```task
|
||||
id: RPF-WP-0050-T02
|
||||
status: wait
|
||||
priority: high
|
||||
needs_human: true
|
||||
decision_id: "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa"
|
||||
blocking_reason: "Founder must explicitly choose batched attended custody, response-wrapped issuer delivery, or scoped MFA silent writer, and approve its exact boundary. Generic D3 resolution without this disposition is insufficient."
|
||||
```
|
||||
|
||||
P3 options and required review boundaries are drafted in the contract. Existing attended custody remains in force. No delivery helper, delegated value role or policy change is admitted by preparation.
|
||||
|
||||
## Bind a real dated window and owner receipt manifests
|
||||
|
||||
```task
|
||||
id: RPF-WP-0050-T03
|
||||
status: wait
|
||||
priority: medium
|
||||
blocked_on: message-from:the-custodian
|
||||
blocking_reason: "Custodian must return the reviewed cadence/window date and current owner task scope before a real metadata manifest is assembled; exact founder execution authorization remains separate."
|
||||
```
|
||||
|
||||
Generate a checklist from all named authoritative roots, verified Hub decision joins and owner CCR/lane/receipt mappings; revalidate each exact revision, upstream acceptance, native readiness and rollback. Applied custody such as RPF-WP-0035-T08 is excluded. Each session returns exact receipts for each owner; missing receipts leave waits intact. Do not activate schedules in this workplan.
|
||||
|
||||
## Accept platform seam for catalog lane scaffold proposal
|
||||
|
||||
```task
|
||||
id: RPF-WP-0050-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
P4 accepted for platform-side source review and bounded scaffolding after Warden returns its contract. Catalog entry/scaffold never supplies credentials, reviews, admission or live authority. Keep owner acceptance explicit before implementation.
|
||||
|
||||
2026-10-03: Platform source review accepts the delivered Warden seam at `ops-warden/wiki/CatalogLaneScaffoldContract.md` (`ac112d8`). T04 is now a ready source implementation task for the platform-owned rendering and schema fixture. Warden T02 may progress independently; no mutual owner-return wait remains. This is source contract acceptance, not credential admission or production authorization.
|
||||
Loading…
Add table
Add a link
Reference in a new issue