net-kingdom/docs/bounded-execution-contract-table.md
tegwick f85a22d245 NK-WP-0058: record where each owner decision was routed (message ids)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 943911@bnt-lap001
Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
2026-11-05 23:21:46 +01:00

40 KiB
Raw Blame History

Bounded execution: cross-repo contract table

NK-WP-0058-T01 (part 1), T09 (part 2) and T10 (part 3), as of 2026-11-05. NK-WP-0057-T01 takes the rows marked owner decision as inputs and does not inventory them again.

Each row is one interface on the bounded-execution path. Every claim cites the file that defines it, at these revisions: net-kingdom 23a99c1, rapp-bounded-executor 3ecd462, approval-engine f41a9ca, informed-decision 77161fe, audit-core 3157454, key-cape 82b471f, railiance-platform 31cf74d.

Marks.

  • keep: one owner, one source, so it only needs a contract test (T02).
  • merge: the contract is written down in more than one place. Keep the named source and derive or check the copies.
  • delete: the piece is redundant.
  • owner decision: the owner or the boundary is unclear. It goes to NK-WP-0057 or to the named owner.

Tested today. The levels are:

  • CI: runs in a Forgejo workflow on every change;
  • local: a test that needs a sibling checkout and skips without one;
  • manual: an exercise script someone has to run;
  • live: proven only by a founder-run step;
  • none.

Two facts frame this column:

  • net-kingdom CI (.forgejo/workflows/identity-journeys.yaml) tests a source tarball of net-kingdom alone, so every sibling-dependent test skips there.
  • rapp-bounded-executor has no CI workflow.

Part 1: the executor and its services

ID Interface Owner and source of truth Consumer Tested today 2026-11-04/05 failure Mark
E1 OpenBao: Kubernetes login and KV, three lanes railiance-platform: CCR-2026-0058, -0059, -0061 and openbao/policies/ executor (operations.py OpenBao); executor-lanes.py creates and deletes the write role CI: fake OpenBao. Local: tests/test_executor_lanes.py against the CCRs Write role left open overnight; window-close failed as bare LaneError merge
E2 KeyCape client_credentials token key-cape: src/internal/server/oidc/token.go. Client registration: net-kingdom sso-mfa/k8s/keycape/ with keycape-client.py executor (clients.py KeyCapeClientCredentials) CI: fakes. Manual: exercise_approval_identity.py (T19) against KeyCape source/image none (the request phase worked) keep; registration ownership is an owner decision
E3 approval-engine: create, get and consume approval-engine: approval_engine/api.py, store.py executor (clients.py ApprovalEngine, executor.py _check_approval) CI: fakes. Manual: T19 against approval-engine source platform-root approval refused as approval_entry; finding which field failed took diagnostic pod 4 keep
E4 informed-decision: memo intake (present_existing) informed-decision: informed_decision/intake_service.py, deploy/lifecycle/intake-submitters.json executor (clients.py InformedDecision, cli.py admission_memo) Local: test_approval_request.py, path hard-coded to /home/worsch/informed-decision Memo reached platform-root only, so one window was wasted (routing, E5) keep
E5 The approver's identity across repos None. It is defined four times (below) executor _check_approval, informed-decision routing, the founder none One wasted window: the admission named admin, routing went to platform-root merge; owner decision (NK-WP-0057)
E6 audit-core: event ingest audit-core: docs/event-envelope.md, audit_core/ingestion.py, sender registry. Receipt schema: net-kingdom canon/schemas/execution-attribution-receipt_v0.1.schema.json executor (clients.py AuditCore) CI: fakes. Manual: exercise_audit_sender.py (T15) against audit-core source. Receipt validator not in a workflow none (receipt accepted 2026-11-05) keep
E7 LLDAP: sign-in and self password change LLDAP upstream. The runtime in sso has no owner in docs/responsibility-map.md; its reference manifests are in net-kingdom sso-mfa/k8s/ executor (operations.py Directory, the image's lldap_set_password) CI: fakes. Nothing pins the image's tool version against the server none keep; runtime ownership is an owner decision
E8 Kubernetes API: ESO force-sync and Deployment restart API: railiance-cluster. Role content: net-kingdom sso-mfa/k8s/bounded-executor/rbac.yaml, copied to rapp manifests/rbac.yaml executor (operations.py KubeDelivery, KubeRestart) Make only: test_rbac.py needs no sibling, but no workflow runs it. Local: rapp test_ceiling_is_the_net_kingdom_source none in the run (ESO server-side apply was a founder step, T10) merge
E9 Network path of the executor pod rapp manifests/networkpolicy.yaml (egress). Ingress belongs to each target: net-kingdom netpol-sso.yaml (lldap, keycape), approval-engine e9e9f93, audit-core 7728fde, informed-decision :8081, OpenBao executor wait_all_reachable, make netcheck Local: rapp test_every_in_namespace_egress_is_admitted_by_net_kingdom. Live: make netcheck Two windows refused setup:URLError; four diagnostic pods; live approval-engine had a policy its file lacked keep; add a cross-owner ingress test
R1 Image build and pin chain net-kingdom builds (.forgejo/workflows/bounded-executor-image.yaml); rapp pins (Makefile IMAGE_DIGEST); the admission pins source files (plan.source.commit and file sha256) renderer (tools/render_job.py), the executor's checks none: commit to digest is recorded only in the rapp commit message A re-pin and a new admission after c2a0f2f, all by hand merge
R2 Runtime config, Job template and exit codes net-kingdom bounded-executor/cli.py (config keys in its docstring, EXIT). Copies: rapp examples/config.pilot.json, docs/runtime-contract.md, render_job.py allow-lists rapp renderer, pilot-run.sh, the founder Local: rapp test_packaging.py reads cli.py (skips without sibling); no rapp CI pilot-run.sh edits trust_satisfied into a temporary copy of the config merge

Row details

E1: OpenBao lanes

  • Contract:
    • address http://openbao.openbao.svc:8200;
    • login POST /v1/auth/kubernetes/login with the projected token, audience openbao, from /var/run/secrets/openbao/token;
    • one role per lane, with leases of at most 15 minutes, each revoked at exit:
      • write: net-kingdom-bind-executor-svc-authelia-write, KV platform/workloads/net-kingdom/directory-binds/svc-authelia, field LDAP_BIND_PASSWORD, cas_required;
      • journey: net-kingdom-bind-executor-syn-journey-read, KV .../synthetic-journey/syn-journey, fields PASSWORD, TOTP_SEED;
      • approval client: keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read, field CLIENT_SECRET, read once and revoked at once.
  • Window: the write role exists only between executor-lanes.py window-open and window-close.
  • Duplicates: role names, KV paths and fields appear in four places: the CCR files, executor-lanes.py LANES, rapp config.pilot.json and the rapp renderer's role allow-list. LANES also carries the write and journey policy text inline.
  • Proposal:
    • LANES reads names, paths and fields from the CCRs. The policy text is compared against railiance-platform's policy file, not kept as a second copy.
    • The renderer's allow-list is checked against the same source.
    • executor-lanes.py names the lane error's cause. On 2026-11-05 phase: identity, LaneError hid "no platform-admin login" (T03).

E2: KeyCape client

  • Contract:
    • POST http://keycape.sso.svc.cluster.local:8080/token with Host kc.coulomb.social, client_secret_basic, client nk-bind-executor-approval;
    • one scope per token (approval:create, approval:read, approval:consume);
    • the answer must be a bearer token with expires_in of at most 900;
    • a scope outside the list must be answered invalid_profile_usage.
  • Owners: KeyCape owns the token behaviour. The client's registration lives in net-kingdom sso-mfa/k8s/keycape/ (deployment env and keycape-client.py), and its case is security-cases/nk-bind-executor-approval.yaml (oidc-service-client).
  • Open:
    • excess-scope-refused is not yet proven against live KeyCape.
    • Owner decision: ADR-0015 moves runtime packages to rapp-*, but there is no KeyCape rapp, so the client registration sits in net-kingdom's reference manifests. Who owns registering a KeyCape client?

E3: approval-engine

  • Contract:
    • create: POST /v1/approvals answers 201. binding.actor must equal the token's sub (api.py); the executor sends human_control: true, pdp_path: false and required_count = the number of approvers.
    • read: GET /v1/approvals/{id}.
    • consume: POST /v1/approvals/{id}/consume with request_digest and decision_id.
    • the executor accepts an entry only if:
      • subject_id is a listed approver;
      • principal_type is human;
      • the assurance is aal2;
      • the subject is not the executor itself.
    • the deployed approval-engine records assurance but doesn't enforce it, so the executor checks aal2 itself.
  • Proposal: approval_entry names the failed field: subject, principal type or assurance. That alone would have replaced diagnostic pod 4.
  • Already answered by the owner: approval-engine replied on 2026-10-31 (message 756016f9, docs/decision-services-alignment-answer.md, e9e9f93). required_assurance is in source as schema v7: entries below the floor are refused with 403, the claim with assurance_unsatisfied, and consume with 409. It waits for a v7 image release and a founder-run rollout with a pre-v7 backup. The executor keeps its own aal2 check until then, and sends required_assurance once v7 is live. This is a dependency, not an open decision.

E4: informed-decision intake

  • Contract:
    • POST /v1/memos on informed-decision-workload.informed-decision.svc:8081;
    • the bearer is the projected ServiceAccount token, audience informed-decision-intake, checked by TokenReview;
    • the registry row is system:serviceaccount:sso:nk-bind-executor → submitter nk-bind-executor, memo class bounded-execution, review group net-kingdom-admins, present_existing.approval_actor service:nk-bind-executor, daily limit 10;
    • the receipt must echo memo_id, approval_id and approval_binding_digest;
    • cli.py --request exits 7 when the memo is not accepted. Resubmitting is idempotent.
  • Test gap: bounded-executor/tests/test_approval_request.py:216 hard-codes /home/worsch/informed-decision (T07: no hard-coded paths).

E5: approver identity, defined four times

Where Value on 2026-11-05 Owner
net-kingdom admissions/BEA-2026-0003.yaml spec.approval.approvers [admin] (a name) net-kingdom (prepare_admission.py)
rapp examples/config.pilot.json approver_subjects, approver_issuer admin → uid=admin,ou=people,dc=netkingdom,dc=local, https://kc.coulomb.social rapp-bounded-executor
informed-decision deploy/lifecycle/review-groups.json net-kingdom-admins → uid=admin,… (interim, decision c9bedd9c; it was platform-root until 77161fe) informed-decision
key-cape LLDAP adapter sub = the LDAP DN key-cape

Nothing checks that these four agree, and a mismatch shows only as a refused approval after a window has opened. Until NK-WP-0057 decides who routes: the T03 readiness check compares them. The admission's approver names resolve through the rapp config and must equal the routing recipient for the memo's review group.

E6: audit-core

  • Contract:
    • POST /v1/events with Idempotency-Key equal to the event's id, and all eight envelope fields;
    • answers: 202 accepted, 200 duplicate, 400 dead-lettered with a named reason, and 409 for a reused run id with a different body (a defect, never retried);
    • the bearer is the file /var/run/secrets/bounded-executor/audit-core, from sso/bounded-executor-tokens key audit-core (custody in part 2);
    • the source is checked against the sender registry;
    • receipt schema v0.1 and its EAR rules (tools/execution-attribution-receipt).
  • Proposal: fold the T15 exercise into the T02 suite, and run make execution-attribution-receipt-test in CI.

E7: LLDAP

  • Contract:
    • http://lldap.sso.svc.cluster.local:17170;
    • sign-in checks over its HTTP API;
    • the password change runs the image's /app/lldap_set_password with --base-url, --token, --username (OPAQUE).
  • Gap: nothing checks that the tool in the executor image is compatible with the running LLDAP server.
  • Owner decision: docs/responsibility-map.md names no owner for the LLDAP, Authelia or KeyCape runtimes in sso. SCOPE says the sso-mfa/k8s/ material is "migration inputs, not blanket authority".

E8: Kubernetes API

  • Contract:
    • force-sync: merge-patch metadata.annotations.force-sync on /apis/external-secrets.io/v1/namespaces/sso/externalsecrets/authelia-secrets, then poll its status;
    • restart: merge-patch spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] on Deployment authelia, then wait for the rollout;
    • Role: get/watch/patch on that Deployment and get/patch on that ExternalSecret. No Secret access.
  • Duplicate: the Role is written in net-kingdom and copied by hand into rapp (make ceiling-sync: "copy, never edit"). The compare test skips without the sibling checkout, and rapp has no CI.
  • Proposal: keep net-kingdom as the source, and make the compare test a T02 contract test that runs in CI.

E9: network path

  • Contract:
    • the executor pod is labelled app.kubernetes.io/name=bounded-executor in namespace sso;
    • egress per the rapp policy;
    • each target admits it on its own port: approval-engine and audit-core on 8080, informed-decision on 8081, KeyCape and LLDAP through netpol-sso.yaml, and OpenBao on 8200;
    • the executor waits up to 30 s for all five endpoints. A setup failure names the unreachable one (since c2a0f2f).
  • Gap: the ingress side lives in five repos, and only the sso part is compared. The live approval-engine policy differed from its file.
  • Proposal: a T02 test that renders every owner's ingress policy for this label, plus a live comparison in the T03 readiness check.

R1: image and pins

  • Chain:
    1. a net-kingdom commit;
    2. the Forgejo image workflow builds a digest;
    3. the digest is pinned by hand in rapp's Makefile IMAGE_DIGEST, with the source commit only in the rapp commit message;
    4. separately, prepare_admission.py --commit pins the admission to source file sha256s (plan.source.files).
  • Gap: no record links the digest to the commit to the admission pins, so "the admission's pins match the pinned image" cannot be checked.
  • Proposal:
    • the image workflow writes a value-free pin record (commit, file sha256s, digest);
    • rapp pins that record;
    • the renderer refuses when the admission's file hashes are not the image's (T03).

R2: config, Job and exit codes

  • Source: the config keys and exit codes are defined in cli.py: 0 completed, 3 reverted, 4 refused, 5 unknown, 6 not in custody, and 7 for a request whose memo was not accepted.
  • Copies:
    • rapp docs/runtime-contract.md (prose);
    • config.pilot.json (values);
    • render_job.py allow-lists (token paths, role names);
    • pilot-run.sh sets trust_satisfied by editing a temporary copy of the config.
  • Proposal:
    • a versioned config schema file in net-kingdom, which the renderer validates against;
    • the exit codes stated once, in that file;
    • trust_satisfied becomes a run input recorded in the state file (T04), not a config edit.

Summary for part 1

  • keep: E2, E3, E4, E6, E7, E9. Each needs its T02 contract test. The T15 and T19 exercises become CI tests.
  • merge:
    • E1: lane names and policy text, from the CCRs;
    • E5: the approver, compared until it has one source;
    • E8: the RBAC copy;
    • R1: the pin record;
    • R2: the config schema.
  • delete: nothing yet. The candidates are the hand copies named under merge, once they are derived or checked.
  • owner decision (to NK-WP-0057-T01 or the named owner):
    • E5: who owns the approver and routing;
    • E2: KeyCape client registration with no KeyCape rapp;
    • E7: the LLDAP, Authelia and KeyCape runtime owner in sso;
    • E3 is not open: approval-engine enforces required_assurance from v7, which waits for its release.
  • Transparency findings for T03:
    • executor-lanes.py lane errors without a cause;
    • approval_entry without the failed field.
  • Hard-coded path for T07: test_approval_request.py:216.

Part 2: the approval, case and custody chain

NK-WP-0058-T09, as of 2026-11-05. It uses the same revisions as part 1, plus access-engine 535bab7.

ID Interface Owner and source of truth Consumer Tested today 2026-11-04/05 failure Mark
C1 CCR lifecycle ↔ net-kingdom lane tooling railiance-platform: credential-change-requests/CCR-2026-0058, -0059, scripts/credential-change.py tools/service-binds/executor-lanes.py, which imports three platform helper modules; pilot-run.sh Local: tests/test_executor_lanes.py copies railiance-platform/scripts window-close failed as bare LaneError (identity phase) merge; owner decision on a supported interface
C2 Case-type bytes ↔ broker admission net-kingdom security-case-types/*/type.yaml (canon). Pin: railiance-platform scripts/keycape_client_secret_generate.py ACKNOWLEDGED_TYPES (sha256) railiance-platform credential broker none in net-kingdom. The README errata warns against edits none (both hashes match today) keep; add a net-kingdom guard test
C3 Case status ↔ broker gate net-kingdom security-cases/*.yaml (status, acceptance); canon canon/standards/security-case-types_v0.1.md broker (case_not_materialized), founder runbooks Local: the broker case check on a synthetic copy none. The status is committed by hand from a founder message keep (who materializes: tracked in MASON-WP-0010-T03)
C4 KeyCape client secret delivery railiance-platform: CCR-2026-0060 and -0061, manifests/nk-bind-executor-eso.yaml, template keycape-issuer-eso.yaml net-kingdom sso-mfa/k8s/keycape/deployment.yaml env KEYCAPE_NK_BIND_EXECUTOR_APPROVAL_CLIENT_SECRET; keycape-client.py env_secret_name(); executor E1 lane railiance-platform tests/test_nk_bind_executor_custody.py, test_keycape_client_secret_generate.py; net-kingdom sso-mfa/k8s/keycape/test_keycape_client.py; no cross test none (custody verify passed 2026-11-04) merge
C5 audit-core sender bearer custody railiance-platform: CCR-2026-0063 (path platform/workloads/net-kingdom/bounded-executor/audit-sender, field AUDIT_TOKEN), ESO to sso/bounded-executor-tokens key audit-core. Seed: audit-core scripts/seed-nk-bounded-executor-sender.py (single seeder) rapp templates/job.yaml mount; executor audit_core.token_file; railiance-platform nk_bind_executor_custody.py verify audit-core tests/test_seed_nk_bounded_executor_sender.py; railiance-platform custody tests; no cross test While being prepared: competing seeders in two repos and two Secret names. Settled in RPF-WP-0069 and AUDIT-WP-0018-T04; one stale copy remains merge; delete the stale design text
C6 Executor's submitter profile Two owners, one fact. informed-decision deploy/lifecycle/intake-submitters.json and access-engine examples/informed-decision-present-existing-review/policy.md present_existing_profiles (v14) informed-decision intake; access-engine decisions; executor executor_identity (rapp config) none across the three none merge; owner decision on which is the source
C7 Memo routing ↔ who can see it informed-decision deploy/lifecycle/review-groups.json (the recipient). access-engine policy v14 recipient_ok: list only when review_recipient == subject.id the founder in informed-decision; executor _check_approval (E5) none The memo was visible only to platform-root, so one window was wasted owner decision (NK-WP-0057; this is E5's routing half)

C1: CCR lifecycle and the lane tooling

  • Contract:
    • executor-lanes.py acts only under both CCRs, approved by platform-owner and net-kingdom-owner with bound_claims_confirmed: true;
    • each CCR is passed as the sha256 of its file (--write-ccr-sha256, --journey-ccr-sha256), computed by sha256sum in pilot-run.sh;
    • the identity phase requires an attended platform-admin token from auth/netkingdom/, never root.
  • Gap: executor-lanes.py puts railiance-platform/scripts on sys.path and imports helpers from three scripts, state_hub_preflight_lane, repair_eso_kubernetes_auth and telemetry_smtp_delivery. None of them was written as an interface for another repo, so a refactor there breaks the window commands without a failing test in either CI.
  • Owner decision (railiance-platform): offer a supported entry point for role create, delete and read, under a CCR digest, or accept these helpers as a versioned interface. Either way T02 adds a test that fails in the consumer.

C2: case-type hash pins

  • Contract: the broker admits oidc-browser-relying-party@1.1 (f705c64e…) and oidc-service-client@0.2 (cd238693…) only when the net-kingdom type.yaml bytes hash to these values. A wording fix in net-kingdom silently breaks the mint.
  • Proposal: a net-kingdom CI test that compares each acknowledged type.yaml with a pin list kept in net-kingdom. That list is checked against the broker's ACKNOWLEDGED_TYPES once owner source can reach CI (T02).

C3: case status and the broker

  • Contract: the broker refuses unless the case is status: materialized with acceptance: {at, record, by: [{role: owner}, {role: founder}]}.
  • Consistent with the canon: materialization is the OpenBao structure only, never values (security-case-types_v0.1.md, line 61). Committing materialized after go-live steps 0–3 and before the mint is therefore correct; the mint and verification lead to live. An earlier draft of this row said otherwise.
  • Gap:
    • the status is committed by hand by claude-code after the founder reports steps 0–3. No receipt links them; the T04 state file could record that link.
    • the canon names ops-mason as the materializer, but railiance-platform's go-live steps did it here by hand. That isn't a new question: ops-mason's case build path (ops-mason build --case, MASON-WP-0010-T03) exists and waits on railiance-platform's structural evidence. ops-mason reviewed and test-rendered this case on 2026-10-31 (message 91e2205f). Tracked there; no new routing.

C4: KeyCape client secret delivery

  • Chain:
    1. the broker mints into platform/workloads/keycape/nk-bind-executor-approval (CLIENT_SECRET);
    2. ESO (CCR-2026-0060) delivers it to sso/keycape-nk-bind-executor-approval-client key client-secret;
    3. KeyCape reads it from env KEYCAPE_NK_BIND_EXECUTOR_APPROVAL_CLIENT_SECRET;
    4. the executor reads the same KV through CCR-2026-0061.
  • Duplicates: the Secret name follows a naming convention written twice: net-kingdom keycape-client.py env_secret_name() and railiance-platform's ESO template. The KV path appears in the CCRs, the rapp config and the custody tool.
  • Proposal: one test computes the convention from both sides for every registered client. The executor's KV path is checked against CCR-2026-0061 (T02).

C5: audit sender custody

  • Contract:
    • audit-core's seeder mints the bearer CAS 0 into the CCR-2026-0063 path, merges the registry row nk-bounded-executor with compare-and-set, force-syncs and probes;
    • railiance-platform adds only the coding-agent fence and verify;
    • ESO delivers to sso/bounded-executor-tokens key audit-core;
    • the rapp Job mounts it at /var/run/secrets/bounded-executor/audit-core.
  • Settled 2026-11-04: one seeder (RPF-WP-0069 "One seeder"; AUDIT-WP-0018-T04, audit-core 3157454). The Secret moved from sso/nk-bind-executor-audit key audit-token to sso/bounded-executor-tokens key audit-core.
  • Still duplicated or stale:
    • railiance-platform docs/credential-lane-designs/bounded-executor.md:85-90 still names sso/nk-bind-executor-audit key audit-token. That is a removal or correction request to railiance-platform.
    • audit-core's seeder hard-codes /home/worsch/railiance-platform/...CCR-2026-0063...yaml and the cluster UID (seed-nk-bounded-executor-sender.py:58-69), for T07.
    • the names appear in four repos (CCR, seeder, ESO manifest, rapp Job) and no test compares them.

C6: the executor's submitter profile

The executor is described as one requester profile in three places:

Where Fields
informed-decision intake-submitters.json system:serviceaccount:sso:nk-bind-executor → nk-bind-executor, class bounded-execution, group net-kingdom-admins, present_existing.approval_actor service:nk-bind-executor, limit 10
access-engine policy v14 present_existing_profiles service:nk-bind-executor → intake_submitter nk-bind-executor, memo_class bounded-execution, approval_action bounded-execution
rapp config.pilot.json executor_identity service:nk-bind-executor, runtime_principal system:serviceaccount:sso:nk-bind-executor

The policy says its profiles are "owner-authored here, not generated". That was a deliberate access-engine choice (GH-DEC-2026-026), so it isn't changed locally. Owner decision: whether informed-decision's registry or access-engine's policy is the source, with the other checked against it. Until then, T02 compares the three.

C7: routing and visibility

  • informed-decision maps the review group to exactly one recipient (intake_service.py:189).
  • access-engine's list allows a viewer only if they are that recipient (policy.md recipient_ok).
  • The two rules together mean the routing table decides who can approve, without the admission seeing it. On 2026-11-04 the admission named admin, routing named platform-root, and the policy then hid the memo from admin.
  • This is NK-WP-0057's core question. The T03 readiness check compares the admission's approver with the routing recipient before a window opens.

Summary for part 2

  • keep:
    • C2, with a net-kingdom guard test for the acknowledged type.yaml bytes;
    • C3.
  • merge:
    • C1: lane tooling on platform internals;
    • C4: the KeyCape secret naming convention and the KV path;
    • C5: audit custody names in four repos;
    • C6: the submitter profile in three places.
  • delete: the stale Secret name in railiance-platform docs/credential-lane-designs/bounded-executor.md (an owner request).
  • owner decision:
    • C1: a supported platform interface for window roles (railiance-platform);
    • C6: the source of the submitter profile (informed-decision and access-engine);
    • C7: routing and visibility (NK-WP-0057, together with E5).
  • For T07: the hard-coded path and cluster UID in audit-core's seeder.

Part 3: operations and the founder

NK-WP-0058-T10, as of 2026-11-05. It uses the same revisions as parts 1 and 2. Sources for the founder steps: sso-mfa/k8s/bounded-executor/T06-PILOT-RUNBOOK.md, railiance-platform docs/credential-lane-designs/nk-bind-executor-go-live.md, audit-core docs/bounded-executor-receipts.md, and the evidence docs/evidence/2026-11-04-nk56-t06-go-live/, 2026-11-04-nk56-t06-pilot/ and 2026-11-05-nk56-t06-pilot/.

ID Interface Owner and source of truth Consumer Tested today 2026-11-04/05 failure Mark
O1 Argo CD ↔ manual applies railiance-platform argocd/railiance01/applications/ (pins, no automated sync). Every other object is applied by hand with kubectl (table below) founder runbooks; agents writing apply commands none: no drift check between Argo pin, repo file and live Argo pin for informed-decision older than the live release, so a sync would have reverted it; Argo-managed admission guard refused client-side ESO apply; a live approval-engine policy missing from its file owner decision (railiance-platform); merge (drift check in T03, convention in T07)
O2 warden lanes ↔ child scripts ops-warden: warden access <lane> --exec (silent, one command, fails on output, revokes on every path). Children: railiance-platform and audit-core silent scripts the founder; seven lane invocations in the go-live runbook none across repos Seed r1 refused token_env_must_be_unset: warden strips BAO_TOKEN and VAULT_TOKEN, not OPENBAO_TOKEN keep; owner request to ops-warden
O3 Platform-admin session: lane vs shell Two patterns: ops-warden lanes (railiance-platform, audit-core) and an interactive bao login -method=oidc -path=netkingdom role=platform-admin in the shell (net-kingdom executor-lanes.py, pilot-run.sh, T06 runbook 6c) executor-lanes.py attended_platform_admin() none 2026-11-05: window-close failed phase: identity, LaneError in a shell without a login merge into one pattern
O4 Cluster access routes Three in one runbook: kubectl --kubeconfig ~/.kube/config-railiance01 (admin); keycape-client.py via portal-client-rollout.py hard-coding /home/worsch/.kube/config-hosteurope with --server https://127.0.0.1:16444 (cluster UID pinned); the agent's default context (NetworkPolicy reads only) founder, agents none none directly; costs founder setup merge (T07: no hard-coded kubeconfigs)
F1 Founder ↔ runbook steps net-kingdom T06 runbook, railiance-platform go-live, audit-core founder runbook the founder live only about 50 copy-paste rounds; values carried by hand between steps (table below) merge into T04's commands
F2 Founder identity in the browser informed-decision UI at aal2; the identity model (admin for production, platform-root kept out) the founder; executor _check_approval none The first approval came as uid=platform-root, the identity signed in at that moment, and was refused keep the refusal; add identity hygiene to T04

O1: which controller manages each object the path writes

Object Written by Managed by Note
informed-decision deploy/lifecycle (incl. review-groups.json, intake-submitters.json ConfigMaps) founder release (2026-11-01), routing change (2026-11-05) Argo CD, pinned revision, manual sync The pin lagged live until railiance-platform 31cf74d; a sync before that would have reverted the present-existing release
reject-secret-last-applied admission policy — Argo CD (secret-annotation-guard) Refuses Secrets carrying last-applied; ESO propagates the annotation from a client-side apply
OpenBao ClusterSecretStores (shared) — Argo CD (openbao-secretstore)
sso ESO objects (manifests/nk-bind-executor-eso.yaml) founder, kubectl apply --server-side manual First apply was client-side and failed (runbook fixed, railiance-platform 9a2495d)
approval-engine and audit-core ingress policies founder, one object each manual Live approval-engine also has informed-decision-review-ingress, not in its file
KeyCape Deployment env and client registry founder (rollout pause, patch, keycape-client.py apply) manual net-kingdom sso-mfa/k8s/keycape/deployment.yaml updated afterwards to match live
rapp standing objects (ServiceAccount, Role, RoleBinding, NetworkPolicy) founder, make deploy manual
Per-run Job and ConfigMap founder, make request, make run manual (kubectl create)
OpenBao write role founder, executor-lanes.py window-open / window-close manual Left open overnight; T11 confirmed no use

Owner decision (railiance-platform, with railiance-cluster): which objects on this path Argo should own. The alternative is for each runbook to state the managing controller before it writes. Until that's decided, T07's convention ("check the managing controller before writing apply commands") and T03's readiness check (compare each Argo pin with live) cover the gap.

O2 and O3: platform-admin sessions

  • The ops-warden lane contract:
    • one reviewed command per invocation;
    • the child must be silent and write its own receipt;
    • output fails the lane, and the token is revoked on every path;
    • the caller passes BAO_ADDR, VAULT_ADDR and, where needed, KUBECONFIG.
  • Owner request to ops-warden: strip OPENBAO_TOKEN too, or every token variable by pattern (T07).
  • The second pattern: net-kingdom's window commands assume a token in the shell instead. They run several commands, and pilot-run.sh keeps that session open across a whole run.
  • Proposal: run executor-lanes.py window-open, window-close and the T03 readiness check as warden lanes. They already write their own receipts and print one line. pilot-run.sh then opens one lane per command and needs no shell login.

F1: the founder's steps, 2026-11-04/05

Step Authority it needs Value carried by hand Value-free result Safe to repeat
1 owner ingress, two objects admin kubeconfig none kubectl output; make netcheck columns yes
2 accept the case the founder in State Hub decision id the decision yes
3.0–3.7 custody go-live platform-admin through 7 warden lanes; admin kubeconfig for ESO none, but the case is committed materialized by claude-code between 3.3 and 3.5, after a pull one receipt per step mostly; the mint is create-only
4 KeyCape bind and register admin kubeconfig; the keycape-client.py route (O4); docker, to extract check-config before.uid and before.resource_version from inspect into apply registry count, check-config, rollout status no: one restart, in a quiet minute
5 admission none (claude-code prepares) window start the file yes, as a new file
6 setup none trust_satisfied edited into a temporary config by an inline Python line none yes
6a request admin kubeconfig the approval id, into 6d outcome: presented yes (idempotent memo)
6b approve admin at aal2 in the browser none none in the shell —
6c window open platform-admin shell login (O3); CCR sha256s the two CCR sha256s, receipt numbers window_opened refuses if already open
6d run admin kubeconfig approval id; Job name the result line in the log no: one consume
6e window close platform-admin shell login receipt number window_closed yes
6f evidence the agent reads audit-core by correlation id — — yes

There are about 45 commands across three runbooks, with five kinds of values carried by hand: UID and resource version, approval id, CCR digests, receipt numbers and trust_satisfied. That fits the reported ~50 rounds. T04's state file removes the carried values: ready, request, run and status read and write it, and the receipt numbers come from the tool.

F2: the browser identity

The approval was refused correctly, because the admission names admin and the founder's identity model keeps platform-root out of production. But the founder learned which identity was signed in only from the refusal, after the window had opened. T04 records the hygiene: a browser profile per identity, and a check of the signed-in subject and OTP freshness before approving. The T03 check confirms that the memo's recipient is the admission's approver.

Statuses the agent had to ask the founder for (input to T05)

The agent's default context can read NetworkPolicies in sso, approval-engine, audit-core, informed-decision and openbao. It can't read Jobs, pods, pod logs, Deployments or ConfigMaps (kubectl auth can-i, 2026-11-05). ExternalSecret read is not checked: the repository's secret-read guard blocks the question.

Status Where Readable by the agent today Proposal for T05
Run and request Job status and result line sso Jobs, pod logs (value-free by contract) no grant get on Jobs and pods, get on pod logs of app.kubernetes.io/name=bounded-executor pods
Diagnostic pod output sso pods, logs no covered by the same grant; T03's --preflight-only should make diagnostic pods unnecessary
ExternalSecret Ready (KeyCape client, bounded-executor-tokens) sso ExternalSecrets unknown get on ExternalSecrets in sso (status only, no Secret access)
KeyCape and Authelia rollout status sso Deployments no get on Deployments in sso
Live ingress policies NetworkPolicies yes none needed
informed-decision registry ConfigMaps vs Argo pin informed-decision ConfigMaps, Argo Application no get on those ConfigMaps (non-secret), or read the Argo Application status
Approval entries approval-engine GET /v1/approvals/{id} no (needed diagnostic pod 4) the executor's approval_entry names the failed field (T03); otherwise an operator read scope
OpenBao write role present OpenBao auth/kubernetes/role/... no founder-only: OpenBao read under platform-admin; T03 and T06 report it value-free
OpenBao audit log openbao-0 file no founder-only (exec); audit forwarding (NK-WP-0055-T11) is the durable path
audit-core receipt in custody audit-core operator read through the run result's receipt_not_in_custody flag keep: the result line already carries it

Summary for part 3

  • keep:
    • O2: the warden lane contract;
    • F2: the executor's refusal of the wrong identity.
  • merge:
    • O1: drift check and controller convention;
    • O3: one platform-admin pattern, warden lanes for the window commands;
    • O4: one cluster access route, no hard-coded kubeconfigs;
    • F1: carried values move to T04's state file.
  • owner decision: O1, which objects Argo owns on this path (railiance-platform with railiance-cluster).
  • owner request: O2, ops-warden strips every token variable.
  • For T05: the status table above. The minimal grant is read-only get on Jobs, pods, executor pod logs, Deployments and ExternalSecrets in sso, plus the informed-decision registry ConfigMaps. It excludes Secrets, exec and kubeconfigs. The founder-only rows stay founder-only.
  • For T07: portal-client-rollout.py and three other tools hard-code kubeconfigs (config-hosteurope, /etc/rancher/k3s/k3s.yaml).

Routing of the owner decisions (2026-11-05)

Row Question Routed to Reference
E5 + C7 Approver and routing; the routing table decides who can approve NK-WP-0057-T01 workplan inputs
E2 Who registers KeyCape clients, with no KeyCape rapp NK-WP-0057-T01 workplan inputs
E7 Runtime owner of LLDAP, Authelia and KeyCape in sso NK-WP-0057-T01 workplan inputs
C1 A supported platform interface for window roles railiance-platform message f6198285
O1 Which objects on the path Argo CD owns railiance-platform (with railiance-cluster) message f6198285
C5 Correct the stale Secret name in bounded-executor.md:85-90 railiance-platform message f6198285
O2 Strip every token variable in warden access --exec; name the variable in the refusal ops-warden message 4acb653c
C6 Which copy of the executor's present-existing profile is the source informed-decision and access-engine messages fa7e25d3, df5aa613
E3 required_assurance not routed: answered 2026-10-31 (756016f9); waits for approval-engine v7 release —
C3 Who materializes not routed: tracked in ops-mason MASON-WP-0010-T03 (91e2205f) —