Accept both backup readers and deliver the email-connect pair.
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s

Record founder acceptance on CCR-2026-0036 and CCR-2026-0037, install the reader roles, and admit only the email-connect node delivery. Treat a missing OpenBao token period as non-periodic.

Assistant: grok
Assistant-Session: 01a11cdb-f9fe-7011-947c-936e72007e54
This commit is contained in:
codex 2026-10-09 00:05:35 +02:00
parent d217c806ca
commit 1900e24f80
8 changed files with 252 additions and 21 deletions

View file

@ -4,7 +4,7 @@
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "coulomb-social primary archive node-delivery reader",
"status": "draft",
"status": "applied",
"created": "2026-10-08",
"updated": "2026-10-08",
"requester": {
@ -17,7 +17,22 @@
"platform-owner",
"coulomb-social-owner"
],
"comments": []
"comments": [
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (platform-owner)",
"decision": "approved",
"comment": "Founder accepted this reader on 2026-10-08. One read policy, ServiceAccount backup-primary-delivery in namespace coulomb-social, audience openbao. Node delivery is not admitted.",
"source_ref": "founder-acceptance-2026-10-08"
},
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (coulomb-social-owner)",
"decision": "approved",
"comment": "Founder accepted the coulomb-social reader record. Node delivery of its pair was not requested.",
"source_ref": "founder-acceptance-2026-10-08"
}
]
},
"target": {
"domain": "communication",
@ -59,13 +74,13 @@
"coulomb-social"
]
},
"bound_claims_confirmed": false
"bound_claims_confirmed": true
}
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "coulomb-social-backup-primary-delivery",
"readiness": "pending-review",
"readiness": "applied-pending-verify",
"resolvable": false
},
"delivery": {
@ -88,7 +103,7 @@
"endpoint": "https://s3.nl-ams.scw.cloud",
"bucket": "railiance-platform-pg-backup",
"prefix": "platform-pg/application-archives/coulomb-social/",
"identity": "separate IAM application and key; provider identity not created",
"identity": "IAM application coulomb-social-backup-primary 681d00bf-b965-4323-b657-97bfde423511; key only in OpenBao; bucket policy limits the prefix and withholds object delete",
"retained_delete": false
},
"risk": {
@ -113,6 +128,18 @@
"Provider denies sibling/native prefixes, retained object/version deletion and IAM/bucket administration.",
"Draft/unconfirmed/source-changed contract, wrong cluster, symlinks, hard links, insecure modes, changed KV version, unconfirmed token revocation and different existing file refuse delivery."
],
"evidence": [
{
"at": "2026-10-08T12:55:00Z",
"actor": "grok",
"kind": "delegated_metadata_apply",
"result": "passed",
"details": [
"Attended platform-admin login wrote workload-kv-read-coulomb-social-backup-primary and auth/kubernetes/role/coulomb-social-backup-primary-workload-kv-read.",
"Readback bound backup-primary-delivery in coulomb-social, one policy, ttl 300, token_max_ttl 300, token_explicit_max_ttl 300, token_num_uses 4, token_no_default_policy true, token_type service. Node delivery remains unadmitted."
]
}
],
"activation_conditions": [
"Named owner reviews and confirmed native reader bindings, applied policy/role with live positive/negative evidence, provider prefix boundary proof and explicit node execution admission.",
"Separate platform-owned attended pair provision with initial KV CAS=0, no partial/second single-field PUT; retain only version metadata. Current reader does not create or rotate values.",

View file

@ -4,7 +4,7 @@
"schema_version": 1,
"request_type": "workload-kv-read",
"title": "email-connect primary archive node-delivery reader",
"status": "draft",
"status": "applied",
"created": "2026-10-08",
"updated": "2026-10-08",
"requester": {
@ -17,7 +17,22 @@
"platform-owner",
"email-connect-owner"
],
"comments": []
"comments": [
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (platform-owner)",
"decision": "approved",
"comment": "Founder accepted this reader on 2026-10-08. One read policy, ServiceAccount backup-primary-delivery in namespace email-connect, audience openbao.",
"source_ref": "founder-acceptance-2026-10-08"
},
{
"at": "2026-10-08T12:40:00Z",
"reviewer": "User (email-connect-owner)",
"decision": "approved",
"comment": "Founder accepted email-connect node delivery of this backup pair. Retention, escrow, capture and the timer stay separate.",
"source_ref": "founder-acceptance-2026-10-08"
}
]
},
"target": {
"domain": "communication",
@ -59,13 +74,13 @@
"email-connect"
]
},
"bound_claims_confirmed": false
"bound_claims_confirmed": true
}
},
"access_frontdoor": {
"type": "platform-owner",
"catalog_id": "email-connect-backup-primary-delivery",
"readiness": "pending-review",
"readiness": "applied-pending-verify",
"resolvable": false
},
"delivery": {
@ -78,17 +93,17 @@
"directory_mode": "0700",
"procedure": "scripts/application_backup_credentials.py",
"admission": {
"node_executor_approved": false,
"provider_scope_verified": false,
"reader_boundary_verified": false,
"source_ref": null
"node_executor_approved": true,
"provider_scope_verified": true,
"reader_boundary_verified": true,
"source_ref": "backup-reader-boundary-2026-10-08"
}
},
"provider": {
"endpoint": "https://s3.nl-ams.scw.cloud",
"bucket": "railiance-platform-pg-backup",
"prefix": "platform-pg/application-archives/email-connect/",
"identity": "separate IAM application and key; provider identity not created",
"identity": "IAM application email-connect-backup-primary 0eb0f958-77a4-4c70-9aa7-14ba46411415; key only in OpenBao; bucket policy limits the prefix and withholds object delete",
"retained_delete": false
},
"risk": {
@ -113,6 +128,28 @@
"Provider denies sibling/native prefixes, retained object/version deletion and IAM/bucket administration.",
"Draft/unconfirmed/source-changed contract, wrong cluster, symlinks, hard links, insecure modes, changed KV version, unconfirmed token revocation and different existing file refuse delivery."
],
"evidence": [
{
"at": "2026-10-08T12:55:00Z",
"actor": "grok",
"kind": "delegated_metadata_apply",
"result": "passed",
"details": [
"Attended platform-admin login wrote workload-kv-read-email-connect-backup-primary and auth/kubernetes/role/email-connect-backup-primary-workload-kv-read.",
"Readback bound backup-primary-delivery in email-connect, one policy, ttl 300, token_max_ttl 300, token_explicit_max_ttl 300, token_num_uses 4, token_no_default_policy true, token_type service."
]
},
{
"at": "2026-10-08T13:05:00Z",
"actor": "grok",
"kind": "reader_boundary",
"result": "passed",
"details": [
"Live email-connect ServiceAccount login had one reader policy, 300 second lease, four uses, null identity policies and KV version 2 with both field lengths only.",
"Sibling data, metadata, parent list, unrelated write, the coulomb-social account, and a wrong audience were denied. The token was revoked. Provider prefix proof already denied foreign prefixes and object delete."
]
}
],
"activation_conditions": [
"Named owner reviews and confirmed native reader bindings, applied policy/role with live positive/negative evidence, provider prefix boundary proof and explicit node execution admission.",
"Separate platform-owned attended pair provision with initial KV CAS=0, no partial/second single-field PUT; retain only version metadata. Current reader does not create or rotate values.",

View file

@ -239,7 +239,7 @@ def retrieve(native, application, expected_version):
info.get("policies") == [policy]
and not info.get("identity_policies")
and 0 < info.get("ttl", 0) <= 300
and info.get("period", 0) == 0
and (info.get("period") or 0) == 0
and 0 < info.get("explicit_max_ttl", 0) <= 300
and 0 < info.get("num_uses", 0) <= 4,
"reader_scope_mismatch",

View file

@ -0,0 +1,134 @@
#!/bin/sh
# Install both backup-primary reader policies and Kubernetes auth roles.
# Silent: warden access rejects any child output. Receipts stay on absolute paths.
set -eu
export BAO_ADDR="${BAO_ADDR:-http://127.0.0.1:18200}"
export VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:18200}"
receipt_dir=/home/worsch/.local/state/railiance/receipts
umask 077
mkdir -p "$receipt_dir"
stage="$receipt_dir/backup-primary-readers.stage"
: >"$stage"
python3 - "$stage" <<'PY'
import importlib.util
import json
import subprocess
import sys
from pathlib import Path
stage = Path(sys.argv[1])
root = Path("/home/worsch/railiance-platform")
spec = importlib.util.spec_from_file_location(
"credential_change", root / "scripts/credential-change.py"
)
cc = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cc)
refs = [
"CCR-2026-0036-coulomb-social-backup-primary.yaml",
"CCR-2026-0037-email-connect-backup-primary.yaml",
]
safe_role_keys = (
"audience",
"bound_service_account_names",
"bound_service_account_namespaces",
"policies",
"token_explicit_max_ttl",
"token_max_ttl",
"token_no_default_policy",
"token_num_uses",
"token_policies",
"token_type",
"ttl",
)
def run(argv):
result = subprocess.run(argv, cwd=root, capture_output=True)
if result.returncode != 0:
stage.write_text(f"exit {result.returncode} step {argv[1]}\n", encoding="utf-8")
raise SystemExit(result.returncode or 1)
return result.stdout.decode()
receipts = []
for name in refs:
ccr = json.loads((root / "credential-change-requests" / name).read_text(encoding="utf-8"))
if ccr.get("status") != "approved" or ccr["openbao"]["auth"].get("bound_claims_confirmed") is not True:
stage.write_text(f"{ccr.get('id')} not approved for apply\n", encoding="utf-8")
raise SystemExit(44)
blockers = cc.applier_readiness_blockers(ccr)
if blockers:
stage.write_text(ccr["id"] + " blocked\n", encoding="utf-8")
raise SystemExit(45)
openbao = ccr["openbao"]
auth = openbao["auth"]
policy_path = root / openbao["policy_file"]
run(["bao", "policy", "write", openbao["policy_name"], str(policy_path)])
installed = run(["bao", "policy", "read", openbao["policy_name"]])
if cc.normalized_policy_body(installed) != cc.normalized_policy_body(policy_path.read_text(encoding="utf-8")):
stage.write_text(openbao["policy_name"] + " readback differs\n", encoding="utf-8")
raise SystemExit(46)
payload = cc.auth_payload(ccr)
role_file = Path(f"/home/worsch/.local/state/railiance/receipts/{auth['role']}.payload.json")
role_file.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
role_file.chmod(0o600)
run(["bao", "write", f"auth/{auth['mount']}/role/{auth['role']}", f"@{role_file}"])
raw = run(["bao", "read", "-format=json", f"auth/{auth['mount']}/role/{auth['role']}"])
data = json.loads(raw).get("data", {})
observed = {key: data.get(key) for key in safe_role_keys if key in data}
names = data.get("bound_service_account_names")
namespaces = data.get("bound_service_account_namespaces")
policies = data.get("token_policies") or data.get("policies")
audience = data.get("audience") or data.get("audiences")
problems = []
if names != payload["bound_service_account_names"]:
problems.append("names")
if namespaces != payload["bound_service_account_namespaces"]:
problems.append("namespaces")
if policies != payload["policies"].split(","):
problems.append("policies")
if audience not in (payload.get("audience"), [payload.get("audience")]):
problems.append("audience")
if data.get("token_no_default_policy") is not True:
problems.append("no_default")
if data.get("token_num_uses") != 4:
problems.append("uses")
if data.get("token_type") != "service":
problems.append("type")
if data.get("ttl") not in (300, "5m", "300s"):
problems.append("ttl")
if data.get("token_max_ttl") not in (300, "5m", "300s"):
problems.append("max_ttl")
if data.get("token_explicit_max_ttl") not in (300, "5m", "300s"):
problems.append("explicit_max_ttl")
if problems:
stage.write_text(
json.dumps({"role": auth["role"], "problems": problems, "observed": observed}, indent=2)
+ "\n",
encoding="utf-8",
)
raise SystemExit(47)
receipts.append(
{
"id": ccr["id"],
"policy": openbao["policy_name"],
"role": auth["role"],
"policies": policies,
"bound_service_account_names": names,
"bound_service_account_namespaces": namespaces,
"ttl": data.get("ttl"),
"token_max_ttl": data.get("token_max_ttl"),
"token_explicit_max_ttl": data.get("token_explicit_max_ttl"),
"token_num_uses": data.get("token_num_uses"),
"token_no_default_policy": data.get("token_no_default_policy"),
"token_type": data.get("token_type"),
}
)
out = Path("/home/worsch/.local/state/railiance/receipts/backup-primary-readers.json")
out.write_text(json.dumps({"status": "installed", "readers": receipts}, indent=2) + "\n", encoding="utf-8")
out.chmod(0o600)
PY
if [ -s "$stage" ]; then
exit 48
fi
exit 0

View file

@ -166,7 +166,7 @@ def test_atomic_private_delivery_and_identical_resume(application, directory, ca
@pytest.mark.parametrize(
"change",
["default", "identity", "unbounded", "uses", "version", "destroyed", "fields", "injection"],
["default", "identity", "unbounded", "uses", "period", "version", "destroyed", "fields", "injection"],
)
def test_scope_and_payload_failures_revoke_without_publishing(change, directory):
path, fd = directory
@ -179,6 +179,8 @@ def test_scope_and_payload_failures_revoke_without_publishing(change, directory)
native.info["data"]["explicit_max_ttl"] = 0
elif change == "uses":
native.info["data"]["num_uses"] = 0
elif change == "period":
native.info["data"]["period"] = 60
elif change == "version":
native.record["data"]["metadata"]["version"] = 2
elif change == "destroyed":
@ -193,6 +195,14 @@ def test_scope_and_payload_failures_revoke_without_publishing(change, directory)
assert not (path / "primary.env").exists() and not list(path.glob("delivery-v*.json"))
def test_absent_period_still_delivers(directory):
_path, fd = directory
native = FakeNative()
native.info["data"]["period"] = None
receipt = d.deliver(approved(), "source-hash", "coulomb-social", 1, native, fd, os.getuid())
assert receipt["outcome"] == "created"
def test_revocation_failure_does_not_publish_or_disclose(directory, capsys, monkeypatch, tmp_path):
path, fd = directory
ccr = approved()

View file

@ -47,12 +47,13 @@ def identity():
@pytest.mark.parametrize("application", sorted(custody.PROFILES))
def test_preview_reads_draft_contract_and_writes_nothing(application, capsys, monkeypatch):
def test_preview_reads_recorded_contract_and_writes_nothing(application, capsys, monkeypatch):
monkeypatch.setattr(custody, "provision", lambda *_: pytest.fail("preview provisioned"))
assert custody.main(["--application", application]) == 0
receipt = json.loads(capsys.readouterr().out)
recorded = json.loads(custody.ccr_path(application).read_text(encoding="utf-8"))
assert receipt["status"] == "preview"
assert receipt["ccr_status"] == "draft"
assert receipt["ccr_status"] == recorded["status"]
assert receipt["writer_admitted"] is True
assert receipt["openbao_write"] is False
assert receipt["provider_keys"] == "not_created"

View file

@ -28,6 +28,13 @@ def test_owner_yaml_updates_export_without_node_yaml_dependency(application, tmp
ccr = yaml.safe_load(source.read_text())
ccr["status"] = "applied"
ccr["openbao"]["auth"]["bound_claims_confirmed"] = True
ccr["review"]["comments"] = []
ccr["delivery"]["admission"] = {
"node_executor_approved": False,
"provider_scope_verified": False,
"reader_boundary_verified": False,
"source_ref": None,
}
candidate = tmp_path / "reviewed.yaml"
candidate.write_text(yaml.safe_dump(ccr))
raw = e.render(candidate, application)

View file

@ -140,10 +140,10 @@ is not ledger capture/recovery coverage; CSOC-WP-0010-T04/T05 own that residual.
```task
id: RPF-WP-0053-T05
status: wait
status: progress
priority: high
needs_human: true
intervention_note: "EMAIL-WP-0007 has tested capture/recovery tools; platform must admit its separate email-connect primary prefix/caller, retention/capacity, independent receipts and escrow before first live capture/upload or timer activation."
intervention_note: "email-connect primary.env is delivered from the accepted reader. Retention, capacity, independent receipts, escrow and the first capture remain open. Coulomb node delivery was not requested."
state_hub_task_id: "3f744ce6-28e4-5496-8d21-7231271a7bc8"
```
@ -179,7 +179,7 @@ and SMTP/sender/caller admission remain unchanged by source preparation.
```task
id: RPF-WP-0053-T06
status: progress
status: done
priority: high
state_hub_task_id: "ce950b3c-1ca6-5b01-bf2a-d958cafaed25"
```
@ -247,3 +247,18 @@ version or node file was created. Review:
docs/credential-lane-designs/application-backup-pair-custody-2026-10-08.md.
T05 stays wait for owner admission of the writer, prefix and escrow. T06 stays
progress until that attended act.
## 2026-10-08 — Reader acceptance and email delivery
Founder acceptance is recorded on CCR-2026-0036 and CCR-2026-0037 with both
owner reviews, source ref founder-acceptance-2026-10-08, and confirmed
Kubernetes bindings. Attended platform-admin login installed both read policies
and roles: one policy, backup-primary-delivery, matching namespace, ttl 300,
four uses, no default policy, service token. Both ServiceAccounts exist with
automount disabled. Live proof: each account read only its own version 2 pair
(lengths 20 and 36), sibling read denied, email metadata/list/write denied,
cross-namespace and wrong-audience login denied, tokens revoked. Email delivery
admission is true. Coulomb delivery admission stays false and
/etc/coulomb-social-backup was not created. email-connect primary.env was
created root:root 0600 under /etc/email-connect-backup mode 0700, outcome
created, readback equal, KV version 2. No capture, upload, escrow or timer.