291 lines
14 KiB
Python
291 lines
14 KiB
Python
|
|
"""Draft the unsigned 2026-10-03 provisioning-window sitting (hub decision D1, CUST-WP-0075).
|
||
|
|
|
||
|
|
Five candidate lanes were reviewed against owner source. Only lanes whose sole
|
||
|
|
missing step is a human act get a memo; the others are recorded as excluded in
|
||
|
|
the batch index with the owner-source reason. One question per memo. No secret
|
||
|
|
values. Does not submit, present, create approvals or bind.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
|
|
|
||
|
|
from prepare_compact_batches import REVIEW_GROUP, _memo, _sha # noqa: E402
|
||
|
|
|
||
|
|
from informed_decision.memo import Scope # noqa: E402
|
||
|
|
from informed_decision.records import dumps, memo_from # noqa: E402
|
||
|
|
|
||
|
|
SITTING = "2026-10-03-provisioning-window"
|
||
|
|
ROOT = Path(__file__).resolve().parents[1] / "docs" / "batches" / SITTING
|
||
|
|
SITTING_ID = "infd-sitting-2026-10-03-provisioning-window"
|
||
|
|
BATCH = "provisioning"
|
||
|
|
HUB_DECISION = "62aa95e4-3ccc-4867-8806-5e3db005b483"
|
||
|
|
|
||
|
|
MEMOS = [
|
||
|
|
{
|
||
|
|
"id": "infd-20261003-p01",
|
||
|
|
"workplan": "RPF-WP-0035",
|
||
|
|
"task": "RPF-WP-0035-T06",
|
||
|
|
"hub_task_prefix": "68bff751",
|
||
|
|
"question": (
|
||
|
|
"Accept the passed 2026-09-13 scoped delivery proof for the CCR-2026-0019 "
|
||
|
|
"secrets-engine approval-client reader as that reader's native delivery acceptance?"
|
||
|
|
),
|
||
|
|
"brief": (
|
||
|
|
"Reader 1 (CCR-2026-0019, applied 2026-09-14) already ran its scoped delivery proof "
|
||
|
|
"under the attended reader lane and the receipts say passed. RPF-WP-0035-T06 waits only "
|
||
|
|
"on a human accepting that proof. Accepting runs nothing, delivers no credential and "
|
||
|
|
"changes no OpenBao object. It does not close T06 by itself."
|
||
|
|
),
|
||
|
|
"highlight": (
|
||
|
|
"Known gaps you accept: the wrong-group human login is untested, and the read check used a "
|
||
|
|
"nonexistent approval id (HTTP 404 proves authenticated routing, not a usable claim). The "
|
||
|
|
"proof is from 2026-09-13 and is not re-run. Accepting does not close RPF-WP-0035-T06: the "
|
||
|
|
"CCR-2026-0018 coordinated disablement still needs KeyCape and Approval Engine confirmation, "
|
||
|
|
"and the file-delivered secret's removal depends on operator hygiene."
|
||
|
|
),
|
||
|
|
"target": Scope(
|
||
|
|
"credential-lane",
|
||
|
|
"ccr:CCR-2026-0019-delivery-proof",
|
||
|
|
"secrets-engine approval-client reader, scoped delivery proof",
|
||
|
|
"prod",
|
||
|
|
),
|
||
|
|
"binding": {
|
||
|
|
"action": "accept",
|
||
|
|
"actor": "informed-decision",
|
||
|
|
"principal": "railiance-platform",
|
||
|
|
"purpose": "Accept the 2026-09-13 scoped delivery proof for the CCR-2026-0019 secrets-engine approval-client reader",
|
||
|
|
"target": {
|
||
|
|
"id": "ccr:CCR-2026-0019-delivery-proof",
|
||
|
|
"type": "credential-lane",
|
||
|
|
"system": "railiance-platform",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"packet": """# RPF-WP-0035-T06 — accept the CCR-2026-0019 scoped delivery proof
|
||
|
|
|
||
|
|
Blocking workplan: RPF-WP-0035 (railiance-platform)
|
||
|
|
Blocking task: RPF-WP-0035-T06 (hub prefix 68bff751)
|
||
|
|
Hub decision: 62aa95e4-3ccc-4867-8806-5e3db005b483 (D1, attended provisioning window)
|
||
|
|
Design: railiance-platform `docs/credential-lane-designs/keycape-approval-client-side-readers.md`
|
||
|
|
Request: railiance-platform `credential-change-requests/CCR-2026-0019-secrets-engine-approval-client-read.yaml` (status `applied`)
|
||
|
|
|
||
|
|
## Question
|
||
|
|
Accept the passed 2026-09-13 scoped delivery proof for the CCR-2026-0019
|
||
|
|
secrets-engine approval-client reader as that reader's native delivery acceptance?
|
||
|
|
|
||
|
|
## One act
|
||
|
|
Record a human acceptance of a proof that has already run. Nothing executes
|
||
|
|
because of this memo. No OpenBao, KeyCape or cluster object changes.
|
||
|
|
|
||
|
|
## What already ran (reference only)
|
||
|
|
Both under the attended reader lane, never the platform-admin lane:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
warden access secrets-engine-approval-client-login --exec -- \\
|
||
|
|
python3 /home/worsch/railiance-platform/scripts/approval-client-reader-preflight.py
|
||
|
|
warden access secrets-engine-approval-client-login --exec -- \\
|
||
|
|
/home/worsch/secrets-engine/.venv/bin/python -B \\
|
||
|
|
/home/worsch/railiance-platform/scripts/approval-client-delivery-check.py
|
||
|
|
```
|
||
|
|
|
||
|
|
## Non-secret receipts (railiance-platform `docs/evidence/`)
|
||
|
|
- `2026-09-14-ccr0019-operator-binding.json` — role/policy apply and readback
|
||
|
|
- `2026-09-14-ccr0019-reader-preflight.json` — `status: passed`, `secret_values_read: false`
|
||
|
|
- `2026-09-14-ccr0019-delivery-check.json` — `status: passed`, `approval_mutations: false`, `cleanup: true`
|
||
|
|
|
||
|
|
## Positive evidence the owner expects
|
||
|
|
- Exact read of `platform/data|metadata/workloads/secrets-engine/approval-client`
|
||
|
|
- Private delivery of the existing version-1 value; private runtime file removed
|
||
|
|
- `approval:read` token reaches Approval Engine authenticated (HTTP 404 on a fresh id)
|
||
|
|
- Warden session exited 0 after self-revocation and helper cleanup
|
||
|
|
|
||
|
|
## Negative evidence the owner expects
|
||
|
|
- Denied: sibling `approval-engine/operator-client`, parent metadata/listing,
|
||
|
|
an unrelated workload path, policy read, role read
|
||
|
|
- `approval:consume` token refused on the read route (HTTP 403); no real approval consumed
|
||
|
|
|
||
|
|
## Known gaps (required acknowledgement)
|
||
|
|
- Wrong-group human login untested.
|
||
|
|
- The 404 read proves routing, not a usable claim on a real approval.
|
||
|
|
- File delivery outlives the reading process; removal is operator hygiene.
|
||
|
|
|
||
|
|
## Rollback / revocation (CCR-2026-0019 lifecycle)
|
||
|
|
- Deactivate: detach policy `workload-kv-read-secrets-engine-approval-client` from
|
||
|
|
role `secrets-engine-approval-client-workload-kv-read`; disable the ops-warden
|
||
|
|
catalog entry. The KeyCape verifier lane is unaffected.
|
||
|
|
- Compromise: KeyCape disables the `secrets-engine-approval` registration first,
|
||
|
|
then the value is rotated (shared with CCR-2026-0017) and the policy detached.
|
||
|
|
|
||
|
|
## Not decided here
|
||
|
|
- CCR-2026-0018 retention or coordinated disablement (KeyCape + Approval Engine
|
||
|
|
+ Platform). T06 stays open until both owners confirm that sequence.
|
||
|
|
- Any rotation, reseed, `approval:consume` grant, or CCR-2026-0020 (cancelled).
|
||
|
|
|
||
|
|
## Must not
|
||
|
|
- Display or paste the client secret
|
||
|
|
- Re-run the proof as a substitute for this acceptance
|
||
|
|
- Treat acceptance as authority for any other lane in the D1 window
|
||
|
|
""",
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
EXCLUDED = [
|
||
|
|
{
|
||
|
|
"workplan": "MASON-WP-0007",
|
||
|
|
"task": "MASON-WP-0007-T05",
|
||
|
|
"owner_repo": "ops-mason",
|
||
|
|
"reason": (
|
||
|
|
"Half the act has no reviewed command. The policy + Kubernetes-auth role script "
|
||
|
|
"railiance-platform scripts/openbao-tenant-engine-audit-core-sender-eso-attended.sh exists "
|
||
|
|
"(silent, idempotent, exit 3 on drift), but the row-7 wrapped seed that copies the tenant-engine "
|
||
|
|
"sender token from the registry to platform/workloads/tenant-engine/audit-core-sender has no "
|
||
|
|
"script: railiance-platform says it has no helper and the plan only names 'the user-engine "
|
||
|
|
"migration technique'. A placeholder child must not be approved. Also MASON-WP-0007-T01 "
|
||
|
|
"(metadata survey: path unused, no conflicting names) is still todo, and ESO's right to create "
|
||
|
|
"serviceaccounts/token in namespace tenant-engine is unverified (MASON-WP-0007-T06)."
|
||
|
|
),
|
||
|
|
"ready_when": (
|
||
|
|
"ops-mason or railiance-platform commits a reviewed silent seed helper, T01 survey is recorded, "
|
||
|
|
"then one memo covers policy/role apply plus seed."
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"workplan": "RPF-WP-0035",
|
||
|
|
"task": "RPF-WP-0035-T02",
|
||
|
|
"owner_repo": "railiance-platform",
|
||
|
|
"reason": (
|
||
|
|
"Design docs/credential-lane-designs/secrets-engine-service-jwt.md is 'proposed, not provisioned'. "
|
||
|
|
"Its 2026-09-27 return supplies inputs (openbao/auth/keycape-services-config.json, "
|
||
|
|
"openbao/auth/secrets-engine-jwt-role.json, openbao/policies/secrets-engine-login-self.hcl) but "
|
||
|
|
"no reviewed child command and says 'Do not execute a placeholder command'. Live KeyCape client "
|
||
|
|
"registration and protected client-side custody remain unverified, and first-lane apply is "
|
||
|
|
"gated on SECRETS-WP-0007-T04. The earlier sitting memo infd-20260914-c02 for this task was "
|
||
|
|
"created 2026-09-15 and expired unbound 2026-09-16."
|
||
|
|
),
|
||
|
|
"ready_when": (
|
||
|
|
"railiance-platform accepts the design, commits the reviewed silent install/verify child, and "
|
||
|
|
"records KeyCape registration + custody."
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"workplan": "RPF-WP-0035",
|
||
|
|
"task": "RPF-WP-0035-T08",
|
||
|
|
"owner_repo": "railiance-platform",
|
||
|
|
"reason": (
|
||
|
|
"The attended first-provision already happened. factory-audit-senders-review.md 'Current "
|
||
|
|
"execution status': both version-1 credentials provisioned (seed receipt "
|
||
|
|
"2026-09-11-factory-audit-sender-seed.json) and delivered (2026-09-11-factory-audit-delivery-live.json, "
|
||
|
|
"delivered_and_scope_verified_pending_producer_acceptance); CCR-2026-0021/0022 applied; 'Do not "
|
||
|
|
"repeat provisioning or delivery'. What remains is owner acceptance (independent archive readback, "
|
||
|
|
"audit bearer-revocation), not an attended OpenBao act. The task's blocking_reason is stale."
|
||
|
|
),
|
||
|
|
"ready_when": "railiance-platform re-qualifies T08 to its real remaining owner acceptance work.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"workplan": "RAPPCOREHUB-WP-0003",
|
||
|
|
"task": "RAPPCOREHUB-WP-0003-T04",
|
||
|
|
"owner_repo": "rapp-core-hub",
|
||
|
|
"reason": (
|
||
|
|
"No credential change request, design or provisioning script exists for a dedicated "
|
||
|
|
"read:repository Forgejo identity for the core-hub publisher (the only read:repository lane, "
|
||
|
|
"CCR-2026-0014, belongs to policy-nexus and must not be reused). The task also needs Repo "
|
||
|
|
"Manager to fix the identity-canon registry identity and publish a corrected image before the "
|
||
|
|
"123/0 snapshot can pass."
|
||
|
|
),
|
||
|
|
"ready_when": (
|
||
|
|
"The Forgejo identity/custody owner drafts the CCR and silent provisioning child; Repo Manager "
|
||
|
|
"ships the identity-canon correction."
|
||
|
|
),
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
directory = ROOT / BATCH
|
||
|
|
directory.mkdir(parents=True, exist_ok=True)
|
||
|
|
ordinal = []
|
||
|
|
intents = []
|
||
|
|
for n, spec in enumerate(MEMOS, start=1):
|
||
|
|
packet_path = directory / f"{spec['id']}.packet.md"
|
||
|
|
packet_path.write_text(spec["packet"], encoding="utf-8")
|
||
|
|
digest = _sha(spec["packet"])
|
||
|
|
memo = _memo(
|
||
|
|
memo_id=spec["id"],
|
||
|
|
question=spec["question"],
|
||
|
|
brief=spec["brief"],
|
||
|
|
target=spec["target"],
|
||
|
|
packet_id=spec["id"] + "-packet",
|
||
|
|
packet_hash=digest,
|
||
|
|
highlight=spec["highlight"],
|
||
|
|
workplan=spec["workplan"],
|
||
|
|
task=spec["task"],
|
||
|
|
)
|
||
|
|
if memo_from(json.loads(dumps(memo))).question != spec["question"]:
|
||
|
|
raise RuntimeError("round-trip failed")
|
||
|
|
memo_path = directory / f"{spec['id']}.memo.json"
|
||
|
|
memo_path.write_text(dumps(memo) + "\n", encoding="utf-8")
|
||
|
|
ordinal.append(
|
||
|
|
{
|
||
|
|
"n": n,
|
||
|
|
"memo_id": spec["id"],
|
||
|
|
"workplan": spec["workplan"],
|
||
|
|
"task": spec["task"],
|
||
|
|
"hub_task_prefix": spec["hub_task_prefix"],
|
||
|
|
"question": spec["question"],
|
||
|
|
"memo": memo_path.name,
|
||
|
|
"packet": packet_path.name,
|
||
|
|
"packet_hash": digest,
|
||
|
|
"required_highlight": f"{spec['id']}-h1",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
intents.append({"memo_id": spec["id"], "create_client": "informed-decision-sitting-requester", "binding": spec["binding"]})
|
||
|
|
index = {
|
||
|
|
"kind": "informed-decision-batch",
|
||
|
|
"id": f"infd-batch-{SITTING}",
|
||
|
|
"review_group": REVIEW_GROUP,
|
||
|
|
"status": "draft-unsigned",
|
||
|
|
"submitted": False,
|
||
|
|
"one_question_per_memo": True,
|
||
|
|
"approve_all_forbidden": True,
|
||
|
|
"agent_disposition_forbidden": True,
|
||
|
|
"hub_decision": HUB_DECISION,
|
||
|
|
"ordinal": ordinal,
|
||
|
|
"excluded": EXCLUDED,
|
||
|
|
}
|
||
|
|
(directory / "index.json").write_text(json.dumps(index, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||
|
|
create = {
|
||
|
|
"kind": "informed-decision-approval-create-intents",
|
||
|
|
"sitting_id": SITTING_ID,
|
||
|
|
"status": "not-posted",
|
||
|
|
"posted": False,
|
||
|
|
"requester_client": "informed-decision-sitting-requester",
|
||
|
|
"human_control": True,
|
||
|
|
"pdp_path": False,
|
||
|
|
"required_count": 1,
|
||
|
|
"note": "Intents only. Digests are computed by approval-engine at create; do not invent them here.",
|
||
|
|
"intents": intents,
|
||
|
|
}
|
||
|
|
(ROOT / "approval-create-intents.json").write_text(json.dumps(create, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||
|
|
sitting = {
|
||
|
|
"kind": "informed-decision-sitting",
|
||
|
|
"id": SITTING_ID,
|
||
|
|
"review_group": REVIEW_GROUP,
|
||
|
|
"principal": "pending-human-session",
|
||
|
|
"status": "draft-unsigned",
|
||
|
|
"hub_decision": HUB_DECISION,
|
||
|
|
"batches": [index["id"]],
|
||
|
|
"memo_count": len(MEMOS),
|
||
|
|
"excluded_count": len(EXCLUDED),
|
||
|
|
"bind_path": "create approval (attended), Flex Auth exact-record admission, load store, human accept",
|
||
|
|
"note": "Unsigned draft. Live bind substitutes pending-human-session with the authenticated key-cape subject.",
|
||
|
|
}
|
||
|
|
(ROOT / "sitting.json").write_text(json.dumps(sitting, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||
|
|
print(f"Wrote {len(MEMOS)} unsigned memo(s), {len(EXCLUDED)} excluded lane(s) under {ROOT}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|