RPF-WP-0069: answer NK-WP-0056-T02; add workload-kv-write-next-version; CCR-2026-0058 (proposed)
All checks were successful
CCR intake gate / gate (push) Successful in 11s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Owner answer for the bounded executor (hub 9d08288d): one Kubernetes role per
lane for sso/nk-bind-executor; new contract-only request type for a CAS
next-version write (no create, metadata or delete, 15m, agents fenced);
CCR-2026-0058 drafted as proposed, not approved; audit sender in the
CCR-2026-0021/0022 shape once audit-core admits it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 994874@bnt-lap001
Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
This commit is contained in:
codex 2026-10-31 14:21:35 +01:00
parent c5f9770c97
commit 8b57d270de
8 changed files with 553 additions and 0 deletions

View file

@ -48,6 +48,7 @@
| workplan | RPF-WP-0066 | proposed | — | workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md |
| workplan | RPF-WP-0067 | active | — | workplans/RPF-WP-0067-netkingdom-identity-receipt-and-metadata-answers.md |
| workplan | RPF-WP-0068 | active | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| workplan | RPF-WP-0069 | active | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md |
| task | RPF-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
| task | RPF-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
@ -225,6 +226,12 @@
| task | RPF-WP-0068-T02 | done | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0068-T03 | wait | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0068-T04 | todo | — | workplans/RPF-WP-0068-email-connect-platform-smtp.md |
| task | RPF-WP-0069-T01 | done | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T02 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T03 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T04 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T05 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| task | RPF-WP-0069-T06 | wait | — | workplans/RPF-WP-0069-bounded-executor-lanes.md |
| intake | RPF-IN-0001 | closed | — | intakes/intakes.md |
| intake | RPF-IN-0002 | open | — | intakes/intakes.md |
| intake | RPF-IN-0003 | open | — | intakes/intakes.md |

View file

@ -0,0 +1,128 @@
id: CCR-2026-0058
kind: credential-change-request
schema_version: 1
request_type: workload-kv-write-next-version
title: NetKingdom bounded executor writes the next svc-authelia bind version (pilot)
status: proposed
created: '2026-10-31'
updated: '2026-10-31'
requester:
agent: claude-code
reason: 'NK-WP-0056-T02 owner request (hub 9d08288d), for NK-WP-0056-T06: the bounded
executor service:nk-bind-executor rotates the svc-authelia LLDAP bind unattended
under one founder admission (Bounded Execution Admission v0.1, proposed). The
executor must write the next KV version of the bind with CAS and read it back for
its checks and revert. Drafted by the railiance-platform worker; not approved.'
review:
required: true
required_approvers:
- platform-owner
- net-kingdom-owner
comments: []
target:
domain: infotech
tenant: platform
workload: net-kingdom-bind-executor
environment: production
purpose: Let only the executor ServiceAccount sso/nk-bind-executor read and write the
next version of platform/workloads/net-kingdom/directory-binds/svc-authelia, with
CAS on the current version; no create, metadata, delete, destroy, list or sibling
path
admission:
standard: 'net-kingdom canon/standards/bounded-execution-admission_v0.1.md (proposed)'
executor_identity: service:nk-bind-executor
example: net-kingdom examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml
note: The OpenBao role does not evaluate the admission. The admission (approval-engine
consume, window, plan digest) is enforced by the executor and its owners; this
request bounds what a compromised or unadmitted executor can reach to one value.
openbao:
mount: platform
kv_path: platform/workloads/net-kingdom/directory-binds/svc-authelia
fields:
- LDAP_BIND_PASSWORD
policy_name: net-kingdom-bind-executor-svc-authelia-write
policy_file: openbao/policies/net-kingdom-bind-executor-svc-authelia-write.hcl
write:
mode: next-version
cas_required: true
create: false
metadata_read: false
delete: false
token_self_lifecycle: true
auth:
method: kubernetes
mount: kubernetes
role: net-kingdom-bind-executor-svc-authelia-write
audience: openbao
ttl: 15m
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
policies:
- net-kingdom-bind-executor-svc-authelia-write
bound_claims:
service_account_names:
- nk-bind-executor
service_account_namespaces:
- sso
bound_claims_confirmed: false
access_frontdoor:
type: platform-owner
catalog_id: net-kingdom-bind-executor-svc-authelia-write
readiness: pending-review
resolvable: false
agent_fence:
data_and_metadata_denied: true
note: The svc-authelia data and metadata paths are already denied to coding agents
since CCR-2026-0053; setup re-checks the deny before the role is created.
lifecycle:
deactivate: Delete the role, then the policy; the KV entry and the ESO reader lane
(CCR-2026-0053) are unchanged. The bind falls back to founder-run rotation.
rotate: Executor reads the current version N, sets the new password in LLDAP, writes
version N+1 with options.cas=N; ESO projects it, Authelia restarts, the synthetic
journey proves sign-in. Revert writes the prior value as version N+2 with options.cas=N+1.
compromised: Delete the role first (stops new logins; existing tokens expire within
15m, or revoke by accessor), then reset svc-authelia in LLDAP and rotate attended
as in CCR-2026-0053.
delivery:
surface: native-workload-login
namespace: sso
service_account: nk-bind-executor
reader: 'Executor runtime (net-kingdom source, rapp-* package per ADR-0015, NK-WP-0056-T03);
logs in with its projected ServiceAccount token, audience openbao; no Secret is
created by this request'
risk:
classification: medium
notes:
- A standing write role on the svc-authelia bind is new; until now only the attended
platform-admin wrote this path. A compromised executor ServiceAccount can replace
the bind value (Authelia sign-in outage on next restart) but cannot read any other
path, create paths, delete versions or read metadata.
- cas_required on the key makes every write state the version it replaces; a racing
or replayed write is refused by the engine, not by the executor.
- The executor also reads the current value (needed for CAS and revert). svc-authelia
is a read-only directory bind (lldap_strict_readonly).
- The role is a separate role from the ESO reader (CCR-2026-0053) and from the journey
read lane (NK-WP-0056-T10); policies are never combined in one role.
- Node root, ESO administration, cluster admins able to mint tokens for sso/nk-bind-executor
and platform-admin remain inside this credential boundary.
verification:
positive:
- Exact policy and role read back; login as sso/nk-bind-executor (audience openbao)
gets only this policy with a lease of at most 15m
- Write of the next version with options.cas equal to the current version succeeds
in a rehearsal on a synthetic sibling-free test path or under the T06 admission;
the value is compared in memory only
negative:
- Write without options or with a stale cas is refused
- Create of a new sibling path, metadata read, delete, destroy, undelete and list
are refused
- Wrong ServiceAccount, wrong namespace and wrong audience logins are denied
- Coding-agent boundary denies data and metadata of the path
activation_conditions:
- Explicit platform-owner and net-kingdom-owner approval of this exact request.
- ServiceAccount sso/nk-bind-executor exists from the reviewed rapp-* package
(bound_claims_confirmed then set to true by the platform-owner).
- Attended platform-admin (aal2) session sets cas_required on the key and applies
policy and role from a reviewed commit.
- Positive and negative results recorded in value-free receipts.

View file

@ -0,0 +1,107 @@
# Bounded executor: OpenBao lanes and audit sender (owner answer, NK-WP-0056-T02)
Owner answer from railiance-platform to NetKingdom's request
(net-kingdom `docs/proposals/2026-10-31-nk56-t02-owner-requests.md` §railiance-platform;
State Hub message `9d08288d`). Basis: Bounded Execution Admission v0.1 (proposed) and its pilot
example `bind-rotation-svc-authelia.yaml`. Workplan: `workplans/RPF-WP-0069-bounded-executor-lanes.md`.
Everything here is source and proposals. No role, policy, Secret or KV value was created or read.
CCR-2026-0058 is `proposed`; approvals and the founder's choices below stay open.
## Q1. Kubernetes auth role for `sso/nk-bind-executor`: which request types?
**Answer: one role per lane, all bound to the one ServiceAccount, two request types.**
The existing `workload-kv-read` contract requires a role to carry exactly its own policy
(`scripts/credential-change.py` `validate_workload_kv_read`, "openbao.auth.policies must contain exactly
the policy used by the auth method"; applier check "must contain only openbao.policy_name"). We keep that
rule. The executor logs in once per lane with the same projected ServiceAccount token (audience
`openbao`); each token carries one policy and lives at most 15 minutes. A combined role would make every
lane's review depend on every other lane.
| Executor need | Request type | Status |
| --- | --- | --- |
| Write the next version of `platform/workloads/net-kingdom/directory-binds/svc-authelia` (CAS) and read it for checks/revert (T06) | `workload-kv-write-next-version` (**new**, `scripts/workload_kv_write_next_version.py`) | Type in source; CCR-2026-0058 `proposed` |
| Read `platform/workloads/net-kingdom/synthetic-journey/syn-journey` (`PASSWORD`, `TOTP_SEED`) for the journey (T10) | `workload-kv-read`, native workload login (`token_self_lifecycle: true`, `metadata_read: false`), as the native reader proof in CCR-2026-0053 | Not drafted; see below |
| LLDAP credential to set `svc-authelia`'s password | `workload-kv-read` of a new custody entry, after secrets-engine answers its Q2 | Not drafted |
| audit-core sender bearer (receipts) | `workload-kv-read` with ESO delivery, as CCR-2026-0021/0022 | Not drafted; see Q2 |
### The new type `workload-kv-write-next-version`
Prepared the way `attended-service-credential-delivery` was added: module
`scripts/workload_kv_write_next_version.py`, tests `tests/test_workload_kv_write_next_version.py`, dispatch in
`scripts/credential-change.py` (validate, plan, operator-commands, runbook). It is **contract-only**: there
is no delegated applier (it is not in `LANE_MODULES`). The attended platform-admin applies policy and role
from a reviewed commit, like CCR-2026-0052..0055.
The contract refuses anything wider than:
- one exact `platform/workloads/...` path on mount `platform`; no wildcard, no sibling;
- policy (generated by `policy_hcl`, artifact `openbao/policies/<policy>.hcl`):
`read` + `update` on `platform/data/<path>` only, `required_parameters = ["options"]`,
`allowed_parameters = {data, options}`; plus `lookup-self`/`revoke-self`. No `create` (a missing path is
refused, so the executor cannot mint new entries), no `metadata/`, `delete/`, `destroy/`, `undelete/`, list;
- key-level `cas_required = true`, set by the attended platform-admin at setup. The engine then refuses
every write that does not name the version it replaces; races and replays fail closed in OpenBao, not in
executor code. (`required_parameters = ["options"]` alone cannot force `options.cas`; the key setting does.)
- Kubernetes auth: one ServiceAccount, one namespace, audience `openbao`, `ttl`/`token_max_ttl`/
`token_explicit_max_ttl` = 15m, `token_no_default_policy: true`, exactly one policy;
- `access_frontdoor.resolvable: false`; `agent_fence.data_and_metadata_denied: true` (coding agents fenced);
- `admission` names the standard and executor identity; full `verification` and `lifecycle`; explicit
approval by every `review.required_approvers` role from `approved` onward.
Revert (`restore-kv` in the example) is a further next-version write of the prior value with CAS. Reading
an older version is a `read` on `data/<path>?version=N`, which the policy already grants; no extra
capability is needed.
**What the role does not do.** OpenBao does not evaluate the admission. The role is standing: a compromised
`nk-bind-executor` ServiceAccount can replace the svc-authelia bind (sign-in outage on Authelia's next
restart) but reach nothing else. The admission window, consume-once and plan digest are enforced by the
executor, approval-engine and access-engine. If that is not enough, the founder can choose a
window-scoped role instead (created by the attended platform-admin at the window start, removed after);
that removes the "unattended" property of T06 for the platform side. Recommendation: standing role for the
pilot, because the reachable damage is one read-only directory bind already covered by an attended
recovery path (CCR-2026-0053 `lifecycle.compromised`).
### Journey secrets (T10)
CCR-2026-0057 is `attended-service-credential-delivery` with `runtime_access: false`; its contract forbids
any role or policy on that path. A `workload-kv-read` request for the executor therefore changes
CCR-2026-0057's stated boundary. It must say so, cite CCR-2026-0057, and be approved by both
platform-owner and net-kingdom-owner. CCR-2026-0057's attended procedure stays valid. Shape: role
`net-kingdom-bind-executor-syn-journey-read`, policy `read` on the data path only, native login, 15m.
Draft it when T10 is scheduled and the ServiceAccount exists.
## Q2. Can the executor pod receive an ESO-projected audit-core sender bearer?
**Yes, with the CCR-2026-0021/0022 shape, once audit-core admits the sender.** The receipt is an event to
audit-core; per RPF-WP-0067-T01 the wire is `POST /v1/events` to
`http://audit-core.audit-core.svc.cluster.local:8080`, with a per-sender bearer from the registry at
`platform/workloads/audit-core/senders`. `Idempotency-Key` is the envelope `id` and `correlation_id` is the
`run_id`. The envelope `source` equals the sender name.
Shape (as `docs/credential-lane-designs/factory-audit-senders-review.md`):
- custody `platform/workloads/net-kingdom/bounded-executor/audit-sender` (`AUDIT_TOKEN`), generated CAS=0 in
the attended platform process and merged into the registry as one exact sender (proposed name
`nk-bounded-executor`, `tenant:platform`, write true, read false, `evidence_kind: load-bearing`,
`secret_policy: redact`); no existing sender changes;
- exact-path policy, Kubernetes role for `external-secrets/external-secrets` only, ClusterSecretStore
restricted to namespace `sso`; ExternalSecret into `sso/nk-bind-executor-audit` key `audit-token`,
mounted as a file in the executor pod;
- coding-agent fence on the new data and metadata paths.
The bearer lets the executor append its own receipts only. It is not an OpenBao identity. The ESO
manifests belong in the `rapp-*` package, as in CCR-2026-0053, which points to net-kingdom
`bind-eso.yaml`. Prerequisites: audit-core says yes to its Q1 (net-kingdom request §audit-core) and
names the sender. Then a `workload-kv-read` CCR follows here.
## Open items for the founder and other owners
1. Review and approve or change CCR-2026-0058 (platform-owner, net-kingdom-owner). It stays `proposed`.
2. Standing vs window-scoped write role (recommendation: standing for the pilot).
3. Accept that a journey-read CCR widens CCR-2026-0057's `runtime_access: false`.
4. secrets-engine: the LLDAP credential lane shape (its Q2) before a custody CCR can be drafted.
5. audit-core: sender admission and name, before the sender CCR.
6. railiance-apps / rapp package: the ServiceAccount `sso/nk-bind-executor` must exist before
`bound_claims_confirmed` can be set.

View file

@ -0,0 +1,16 @@
path "platform/data/workloads/net-kingdom/directory-binds/svc-authelia" {
capabilities = ["read", "update"]
required_parameters = ["options"]
allowed_parameters = {
"data" = []
"options" = []
}
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}
path "auth/token/revoke-self" {
capabilities = ["update"]
}

View file

@ -520,6 +520,8 @@ def validate_ccr(path: Path) -> tuple[dict[str, Any], list[str], list[str]]:
errors.extend(owner.validate_contract(ccr))
elif request_type == "attended-service-credential-delivery":
errors.extend(attended_service_module().validate_contract(ccr))
elif request_type == "workload-kv-write-next-version":
errors.extend(write_next_version_module().validate_contract(ccr))
elif request_type in LANE_MODULES:
errors.extend(lane_module(ccr).validate(ccr, REPO_DIR))
else:
@ -728,6 +730,8 @@ def render_plan(ccr: dict[str, Any]) -> str:
return "Use docs/credential-lane-designs/railiance-clock-host-delivery.md through the attended owner procedure. No workload policy or auth role is created."
if ccr.get("request_type") == "attended-service-credential-delivery":
return attended_service_module().procedure_note(ccr)
if ccr.get("request_type") == "workload-kv-write-next-version":
return write_next_version_module().procedure_note(ccr)
openbao = ccr["openbao"]
auth = openbao["auth"]
payload = auth_payload(ccr)
@ -761,6 +765,8 @@ def render_operator_commands(ccr: dict[str, Any]) -> str:
return "Use docs/credential-lane-designs/railiance-clock-host-delivery.md through the attended owner procedure. No workload policy or auth role is created."
if ccr.get("request_type") == "attended-service-credential-delivery":
return attended_service_module().procedure_note(ccr)
if ccr.get("request_type") == "workload-kv-write-next-version":
return write_next_version_module().procedure_note(ccr)
openbao = ccr["openbao"]
auth = openbao["auth"]
auth_path = f"auth/{auth['mount']}/role/{auth['role']}"
@ -850,6 +856,15 @@ def attended_service_module():
return module
def write_next_version_module():
"""Owner contract for workload-kv-write-next-version (bounded executor, NK-WP-0056)."""
import importlib.util
spec = importlib.util.spec_from_file_location("workload_kv_write_next_version", REPO_DIR / "scripts/workload_kv_write_next_version.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def lane_module(ccr: dict[str, Any]):
"""Lazy-load the applier module for a lane request type, or None."""
name = LANE_MODULES.get(ccr.get("request_type"))
@ -1132,6 +1147,8 @@ def runbook_readiness_blockers(ccr: dict[str, Any]) -> list[str]:
return ["use the attended owner procedure in docs/credential-lane-designs/railiance-clock-host-delivery.md"]
if ccr.get("request_type") == "attended-service-credential-delivery":
return ["use the attended procedure: " + attended_service_module().procedure_note(ccr)]
if ccr.get("request_type") == "workload-kv-write-next-version":
return ["use the attended procedure: " + write_next_version_module().procedure_note(ccr)]
blockers: list[str] = []
status = ccr.get("status")
if status not in RUNBOOK_ALLOWED_STATUSES:

View file

@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""workload-kv-write-next-version CCRs: a workload writes the next version of one KV entry.
NetKingdom NK-WP-0056 (bounded executor, hub request 9d08288d). A non-human
executor rotates one existing value under a founder admission: it reads the
current version, writes the next version with CAS on that version, and may
revert by writing the prior value as a further version. It never creates a
path, reads metadata, deletes, destroys or lists.
The contract is source-only: validation, the exact policy HCL and an attended
procedure note. The policy, Kubernetes auth role and key-level ``cas_required``
are applied by the attended platform-admin from a reviewed commit, as with
attended-service-credential-delivery. Nothing here calls OpenBao or Kubernetes,
or reads, prints or accepts a secret value.
"""
from __future__ import annotations
import re
from typing import Any
REQUEST_TYPE = "workload-kv-write-next-version"
DOC = "docs/credential-lane-designs/bounded-executor.md"
APPROVED = {"approved", "applied", "verified", "active"}
KV_RE = re.compile(r"^platform/workloads/[a-z0-9][a-z0-9-]*(/[a-z0-9][a-z0-9-]*)+$")
NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,62}$")
FIELD_RE = re.compile(r"^[A-Z][A-Z0-9_]*$")
OPENBAO_KEYS = {"mount", "kv_path", "fields", "policy_name", "policy_file", "write", "auth"}
WRITE = {"mode": "next-version", "cas_required": True, "create": False, "metadata_read": False,
"delete": False, "token_self_lifecycle": True}
TTL = "15m"
AUTH_FIXED = {"method": "kubernetes", "mount": "kubernetes", "audience": "openbao", "ttl": TTL,
"token_max_ttl": TTL, "token_explicit_max_ttl": TTL, "token_no_default_policy": True}
DISALLOWED_POLICIES = {"root", "default", "platform-admin"}
def policy_hcl(ccr: dict[str, Any]) -> str:
"""Exact policy: read and update of one data path; no create, metadata, delete or list."""
openbao = ccr["openbao"]
suffix = openbao["kv_path"][len("platform/"):]
return (f'path "platform/data/{suffix}" {{\n'
' capabilities = ["read", "update"]\n'
' required_parameters = ["options"]\n'
' allowed_parameters = {\n "data" = []\n "options" = []\n }\n'
"}\n\n"
'path "auth/token/lookup-self" {\n capabilities = ["read"]\n}\n\n'
'path "auth/token/revoke-self" {\n capabilities = ["update"]\n}\n')
def _approvals(ccr: dict[str, Any]) -> set[str]:
comments = (ccr.get("review") or {}).get("comments") or []
return {str(c.get("reviewer", "")).removeprefix("User (").removesuffix(")")
for c in comments if c.get("decision") == "approved"}
def validate_contract(ccr: dict[str, Any]) -> list[str]:
errors: list[str] = []
if ccr.get("request_type") != REQUEST_TYPE:
errors.append("unsupported write-next-version contract")
openbao = ccr.get("openbao")
if not isinstance(openbao, dict):
return errors + ["openbao must be an object"]
extra = set(openbao) - OPENBAO_KEYS
if extra:
errors.append("openbao declares unsupported keys: " + ", ".join(sorted(extra)))
kv_path = str(openbao.get("kv_path", ""))
if openbao.get("mount") != "platform" or not KV_RE.match(kv_path):
errors.append("openbao.kv_path must be one exact platform/workloads/... path on mount platform")
fields = openbao.get("fields")
if not isinstance(fields, list) or not fields or not all(isinstance(f, str) and FIELD_RE.match(f) for f in fields):
errors.append("openbao.fields must list upper-case field names")
policy = openbao.get("policy_name")
if not isinstance(policy, str) or not NAME_RE.match(policy) or policy in DISALLOWED_POLICIES:
errors.append("openbao.policy_name must be a dedicated lower-case policy name")
elif openbao.get("policy_file") != f"openbao/policies/{policy}.hcl":
errors.append("openbao.policy_file must be openbao/policies/<policy_name>.hcl")
if openbao.get("write") != WRITE:
errors.append("openbao.write must be next-version with cas_required, no create, metadata or delete, "
"and token self-lifecycle")
auth = openbao.get("auth")
if not isinstance(auth, dict):
errors.append("openbao.auth must be an object")
else:
for key, value in AUTH_FIXED.items():
if auth.get(key) != value:
errors.append(f"openbao.auth.{key} must be {value}")
if not NAME_RE.match(str(auth.get("role", ""))):
errors.append("openbao.auth.role must be a dedicated role name")
if auth.get("policies") != [policy]:
errors.append("openbao.auth.policies must contain exactly openbao.policy_name")
claims = auth.get("bound_claims") or {}
for key in ("service_account_names", "service_account_namespaces"):
values = claims.get(key)
if not (isinstance(values, list) and len(values) == 1 and NAME_RE.match(str(values[0]))):
errors.append(f"openbao.auth.bound_claims.{key} must name exactly one value")
admission = ccr.get("admission")
if not isinstance(admission, dict) or not admission.get("standard") or not admission.get("executor_identity"):
errors.append("admission must name the admission standard and executor identity")
if (ccr.get("access_frontdoor") or {}).get("resolvable") is not False:
errors.append("runtime credential frontdoor must remain disabled")
if (ccr.get("agent_fence") or {}).get("data_and_metadata_denied") is not True:
errors.append("coding agents must be fenced from the data and metadata paths")
if ccr.get("status") in APPROVED:
approved = _approvals(ccr)
for role in (ccr.get("review") or {}).get("required_approvers") or []:
if role not in approved:
errors.append("explicit role approval missing: " + role)
for section, keys in {"verification": ["positive", "negative", "activation_conditions"],
"lifecycle": ["deactivate", "rotate", "compromised"]}.items():
for key in keys:
if not (ccr.get(section) or {}).get(key):
errors.append(f"{section}.{key} required")
return errors
def procedure_note(ccr: dict[str, Any]) -> str:
openbao = ccr["openbao"]
return (f"Attended platform-admin applies policy {openbao['policy_name']} and Kubernetes role "
f"{openbao['auth']['role']} from a reviewed commit, sets cas_required on {openbao['kv_path']} "
f"and proves the negatives ({DOC}). The workload may only write the next version with CAS.")

View file

@ -0,0 +1,60 @@
"""workload-kv-write-next-version contract (NetKingdom NK-WP-0056 bounded executor); no values."""
import copy
import importlib.util
from pathlib import Path
import yaml
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("wkw", ROOT / "scripts/workload_kv_write_next_version.py")
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
CCR = yaml.safe_load((ROOT / "credential-change-requests/CCR-2026-0058-net-kingdom-bind-executor-svc-authelia-write.yaml").read_text())
def test_checked_in_request_is_valid():
assert m.validate_contract(CCR) == []
def test_policy_artifact_matches_contract():
path = ROOT / CCR["openbao"]["policy_file"]
assert path.read_text() == m.policy_hcl(CCR)
hcl = m.policy_hcl(CCR)
assert '"read", "update"' in hcl and "create" not in hcl and "metadata" not in hcl
assert 'required_parameters = ["options"]' in hcl
def test_write_shape_cannot_be_widened():
for change in ({"cas_required": False}, {"create": True}, {"metadata_read": True}, {"delete": True},
{"mode": "any"}):
ccr = copy.deepcopy(CCR); ccr["openbao"]["write"].update(change)
assert m.validate_contract(ccr), change
ccr = copy.deepcopy(CCR); ccr["openbao"]["kv_path"] = "platform/workloads/net-kingdom/*"
assert m.validate_contract(ccr)
ccr = copy.deepcopy(CCR); ccr["openbao"]["metadata_paths"] = ["x"]
assert m.validate_contract(ccr)
def test_auth_is_one_serviceaccount_one_policy_15m():
for key, value in (("ttl", "1h"), ("token_no_default_policy", False), ("audience", "x")):
ccr = copy.deepcopy(CCR); ccr["openbao"]["auth"][key] = value
assert m.validate_contract(ccr), key
ccr = copy.deepcopy(CCR); ccr["openbao"]["auth"]["policies"].append("other")
assert "openbao.auth.policies must contain exactly openbao.policy_name" in m.validate_contract(ccr)
ccr = copy.deepcopy(CCR); ccr["openbao"]["auth"]["bound_claims"]["service_account_names"].append("x")
assert m.validate_contract(ccr)
def test_fence_frontdoor_and_admission_required():
ccr = copy.deepcopy(CCR); ccr["agent_fence"]["data_and_metadata_denied"] = False
assert "coding agents must be fenced from the data and metadata paths" in m.validate_contract(ccr)
ccr = copy.deepcopy(CCR); ccr["access_frontdoor"]["resolvable"] = True
assert "runtime credential frontdoor must remain disabled" in m.validate_contract(ccr)
ccr = copy.deepcopy(CCR); del ccr["admission"]
assert m.validate_contract(ccr)
def test_approved_stage_needs_every_required_approval():
ccr = copy.deepcopy(CCR); ccr["status"] = "approved"
ccr["review"]["comments"] = [{"reviewer": "platform-owner", "decision": "approved"}]
assert m.validate_contract(ccr) == ["explicit role approval missing: net-kingdom-owner"]

View file

@ -0,0 +1,99 @@
---
id: RPF-WP-0069
type: workplan
title: "Bounded executor lanes: write-next-version type, journey read and audit sender (NK-WP-0056)"
domain: financials
repo: railiance-platform
status: active
flavor: implementation
owner: claude
topic_slug: railiance
created: "2026-10-31"
updated: "2026-10-31"
related:
- NK-WP-0056
- NK-WP-0040
- RPF-WP-0067
---
# RPF-WP-0069 — Bounded executor lanes (NK-WP-0056-T02 owner answer)
Receiving workplan for NetKingdom's owner request (State Hub message
`9d08288d-e866-4e1c-9c0b-f1cc0968bd4d`; net-kingdom
`docs/proposals/2026-10-31-nk56-t02-owner-requests.md`). Owner answer:
`docs/credential-lane-designs/bounded-executor.md`. No live, cluster or OpenBao action is
part of T01; every CCR stays `proposed` until its approvers act.
## Answer the request and add the write-next-version request type
```task
id: RPF-WP-0069-T01
status: done
priority: high
```
Answer doc written; new contract-only request type `workload-kv-write-next-version`
(`scripts/workload_kv_write_next_version.py`, `tests/test_workload_kv_write_next_version.py`,
dispatch in `scripts/credential-change.py`); CCR-2026-0058 drafted `proposed` with policy artifact
`openbao/policies/net-kingdom-bind-executor-svc-authelia-write.hcl`.
## Review CCR-2026-0058 (founder gate)
```task
id: RPF-WP-0069-T02
status: wait
priority: high
needs_human: true
```
platform-owner and net-kingdom-owner approve or change CCR-2026-0058. The founder chooses
standing or window-scoped write role (recommendation: standing for the pilot). Binding is
confirmed only after `sso/nk-bind-executor` exists from the reviewed rapp-* package.
## Attended setup and verification of CCR-2026-0058
```task
id: RPF-WP-0069-T03
status: wait
priority: high
```
After T02: an attended platform-admin (aal2) session sets `cas_required` on the svc-authelia key,
re-checks the coding-agent fence, applies policy and role, and records value-free positive and
negative receipts. This needs an owner setup script modelled on net-kingdom `bind-lane.py`
setup/verify; write it once approval exists. The founder runs it.
## Journey-secret read lane for the executor (NK-WP-0056-T10)
```task
id: RPF-WP-0069-T04
status: wait
priority: medium
```
Draft a `workload-kv-read` CCR (native login, data read only, 15m) for
`platform/workloads/net-kingdom/synthetic-journey/syn-journey`, stating that it widens
CCR-2026-0057's `runtime_access: false`. Waits for T10 scheduling and the ServiceAccount.
## Executor audit-core sender (NK-WP-0040)
```task
id: RPF-WP-0069-T05
status: wait
priority: medium
```
After audit-core admits the sender and names it, draft a `workload-kv-read` CCR in the
CCR-2026-0021/0022 shape: custody `platform/workloads/net-kingdom/bounded-executor/audit-sender`,
ESO into `sso/nk-bind-executor-audit`, ClusterSecretStore restricted to `sso`.
## LLDAP credential custody for the executor
```task
id: RPF-WP-0069-T06
status: wait
priority: medium
```
After secrets-engine answers the lane shape for the executor's LLDAP credential (its Q2),
draft the custody CCR here.