KeyCape takes KEYCAPE_LLDAP_BIND_PW (key-cape KEY-WP-0046). ESO creates the dedicated Secret keycape-lldap-bind. bind-lane setup reads the current bindPW from keycape-config and requires the svc-keycape bindDN; verify compares the delivered Secret, and the config value while it is present. directory-bind-rollout gains clear-password, which runs only with the service bind and the delivered env (switch restores). deployment.yaml pins image main-e2b5fa1 and adds the env. Nothing is applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 896183@bnt-lap001 Assistant-Session: 3a18c01a-3205-4d02-94db-9efff25649a2
14 KiB
| id | type | title | domain | repo | status | flavor | owner | topic_slug | created | updated | related | state_hub_workstream_id | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| NK-WP-0056 | workplan | Bounded executor path: founder admits, executors run | infotech | net-kingdom | proposed | design | claude | netkingdom | 2026-10-30 | 2026-10-30 |
|
5bc74ef8-136e-5157-9a7f-da765dbfc977 |
NK-WP-0056 — Bounded executor path
Intake from the founder's direction on 2026-10-30, before the live LLDAP bind split: top-level decisions and guardrails stay with the founder; execution below that is delegated to automation with limited expense budgets and risk control, and the security architecture is the means. Founder-attended runbooks are a transitional state.
SI-15 (canon/standards/service-identity-privilege_v0.1.md) already names the golden path prepare →
admit → execute → verify → retain and allows "a bounded executor under the admission" at step 3.
Today preparation and verification are strong, admission is prose (a hub decision) and execution falls
to the founder, because no executor exists that has its own identity, its own secret delivery and an
authority ceiling the founder can admit and bound. SI-14 adds that an interactive agent session holds
everything its tools reach, so it cannot be that executor until a session contract exists
(glas-harness GLAS-WP-0018, proposed).
Why the bind split needed the founder (2026-10-30 run, step by step)
| Runbook step | Needed the founder because | Class | Delegable once |
|---|---|---|---|
| Setup: decrypt custody copy | custody is a personal SOPS/age copy | delegable | service credentials live in OpenBao (T04) |
| 1 prepare custody (generate passwords) | writes the personal custody copy | delegable | executor generates into OpenBao (T04) |
| 2 plan, 4 verify, 10 verify | sign in as the human admin |
delegable | read-only executor directory identity (T03) |
3 create svc-* accounts |
needs lldap_admin (LLDAP has no narrower grant that can create users) |
root-class | admitted per run; executor runs it (T03) |
| 5-8 switch one consumer | cluster-admin kubeconfig; secret values; founder sign-in check | delegable | scoped service account (T03), OpenBao/ESO (T04), synthetic MFA journey (T05) |
| 9 change LLDAP's configured super-admin | changes who administers the directory | root-class | admitted per run; executor runs it |
10 change admin's own password; seal ld-admin in the password safe |
the founder's own credential and crisis custody | human-intrinsic | never: stays with the founder |
| Afterwards: restore drill on the next generation | admin operates drills |
delegable | executor identity with the drill's read scope |
Root-class means the founder still admits each run, but no longer types it. The run also showed why
personal custody does not scale: the custody copy had drifted from live (AUTHELIA_KEYCAPE_CLIENT_SECRET)
and step 6 had to be done by a one-off path.
Admission contract
id: NK-WP-0056-T01
status: todo
priority: high
assignee: claude-code
state_hub_task_id: "99df0ee2-2c17-55f2-9f3e-4277da52e01c"
Draft a NetKingdom standard for a bounded-execution admission: the founder approves, at aal2, an
exact plan by digest with a validity window, a scope ceiling (MODE @ SCOPE grants, SI terms), a
blast-radius rule (for example one consumer at a time), a stop-and-revert policy tied to named machine
checks, a risk class, and an expense budget (wall time, attempts, compute/egress cost). The executor
consumes the admission once. Map it to SI-15, the Playbook Capability Contract's trust-state
requirements and the execution-attribution receipt (NK-WP-0040). Include a validator and synthetic
fixtures.
Done when: the standard is proposed with a schema, a validator, and the bind-split steps expressed as admissions in its examples.
Owner agreement on the components
id: NK-WP-0056-T02
status: todo
priority: high
assignee: claude-code
needs_human: true
intervention_note: "Founder chooses the executor runtime owner; owner answers are agreements, not agent decisions."
depends_on: [NK-WP-0056-T01]
state_hub_task_id: "7263417b-b227-54f7-9edc-3e7f97cf132f"
Ask the owners which part each carries: approval-engine (the admission as its consumable approval object, plan digest and budget as bound fields, single consume), access-engine (reads the admission as an input claim), secrets-engine and railiance-platform (executor credential delivery through OpenBao), glas-harness (GLAS-WP-0018 session contract, so a harness-launched agent can be an executor), audit-core (receipt holder, as in NK-WP-0040). The founder decides who builds the executor runtime itself.
Done when: each component has a named owner and an answer, and the founder has chosen the executor runtime owner.
Executor identity and authority ceiling
id: NK-WP-0056-T03
status: todo
priority: high
assignee: claude-code
depends_on: [NK-WP-0056-T02]
state_hub_task_id: "592231dd-1981-57a6-a0e8-5d6fc474c6e7"
Declare the executor as a WORKLOAD (or harness-launched AGENT) principal in declarations/ with
grants no wider than the pilot: a Kubernetes service account whose Role names the sso Secrets,
ConfigMaps and Deployments it may patch or restart; a read-only directory bind for plan and verify;
lldap_admin only for root-class steps under an admission. Extend tools/service-identity to check
it.
Done when: the declaration validates and the Role is prepared in source with tests, not applied.
Service credentials into OpenBao
id: NK-WP-0056-T04
status: todo
priority: high
assignee: claude-code
needs_human: true
intervention_note: "Founder runs the custody reconciliation and approves moving service binds out of personal custody."
depends_on: [NK-WP-0056-T02]
state_hub_task_id: "fd439c0c-9b5e-562c-9bfb-48dd0fb2c570"
With railiance-platform, move the LLDAP service binds (LLDAP_SVC_*_PASS) and the Authelia secrets
from the personal custody copy into OpenBao paths delivered by ESO, so that neither a person nor an
agent handles the values in a rotation. First reconcile the stale custody
AUTHELIA_KEYCAPE_CLIENT_SECRET with the live value (founder-run, value-free comparison as in the
2026-10-30 evidence). Root-class material (ld-admin, unseal shares) stays in founder custody.
Done when: the four service binds are ESO-delivered from OpenBao, and the custody copy no longer needs to be decrypted for a bind rotation.
2026-10-30: reconciliation tool prepared: sso-mfa/bootstrap/reconcile-keycape-client-secret.py
(value-free; accepts the live KeyCape value only if it matches Authelia's live hash, writes custody through
rotation-env.py, no cluster writes; tests/test_reconcile_keycape_client_secret.py). Founder ran it
2026-10-30: live KeyCape value matches Authelia's hash; custody did not match before, was updated and matches
after (value-free output); custody re-sealed and shredded. The reconciliation step is done; the ESO move remains.
2026-10-30, pilot svc-provisioner prepared (source only, nothing applied): railiance-platform
CCR-2026-0052 (proposed) and policy net-kingdom-svc-provisioner-bind-eso; sso-mfa/k8s/identity-provisioner/bind-eso.yaml
(ServiceAccount, SecretStore, ExternalSecret with creationPolicy: Merge into the existing Secret, password only);
tools/service-binds/bind-lane.py setup|verify (approved sha256-bound CCR, attended platform-admin, custody must equal
live, coding-agent fence, KV CAS=0, value-free receipts); tests/test_service_bind_lane.py (7). Next: owner approval of
the CCR, then the founder-run setup, ESO apply and verify. Authelia, KeyCape and privacyIDEA follow one at a time.
2026-10-30, pilot live and verified: CCR-2026-0052 approved; founder-run setup, ESO apply and verify passed;
svc-provisioner now binds with the ESO-delivered value (evidence).
Finding: ESO targets need template.metadata: {} or the admission guard refuses the Secret update. Remaining:
svc-authelia, svc-keycape, svc-privacyidea.
2026-10-31, svc-authelia lane prepared (source only, nothing applied): railiance-platform CCR-2026-0053
(proposed) and policy net-kingdom-svc-authelia-bind-eso; sso-mfa/k8s/authelia/bind-eso.yaml (Merge of
ldap_password only into authelia-secrets, which also holds the session, storage and OIDC keys). bind-lane.py
gains per-lane Secret keys: Authelia keeps the bind user in authelia-config, so setup refuses unless the live
configuration binds uid=svc-authelia, and it decodes only the bind key. Authelia reads the file at start-up
only, so the identical projection needs no restart; a later rotation does. 12 lane tests. Next: owner approval
and binding confirmation, then the founder-run setup, ESO apply, verify and an Authelia restart with a login.
2026-10-31, svc-authelia live and verified: CCR-2026-0053 approved and binding confirmed; founder-run setup,
ESO apply (Ready first time), verify and Authelia restart passed, and an admin portal sign-in with OTP worked
(evidence). Two of four binds are ESO-delivered.
Remaining: svc-keycape (bind inside sso/keycape-config), svc-privacyidea (resolver password stored in the
privacyIDEA database, not a Secret key).
2026-10-31, svc-keycape design proposal (for founder decision): KeyCape's server reads lldap.bindPW only
from config.yaml inside sso/keycape-config (internal/config/config.go); unlike client secrets (secretRef: env:... via secretKeyRef) it has no environment indirection. ESO Merge cannot own one field inside a YAML
document, and rendering all of config.yaml from OpenBao would move every KeyCape value at once. Proposed:
- key-cape (owner change, new KEY-WP):
config.LoadtakesKEYCAPE_LLDAP_BIND_PW(the namelldap-exportalready uses). Env set andbindPWempty: use env. Both set and equal: use it and log thatbindPWis redundant. Both set and different: refuse to start. This allows a staged switch with a clean rollback. Image through the owner pipeline;check-configreports which source is used, without the value. - NetKingdom: CCR-2026-0054 and a
svc-keycapelane; ESO creates a new dedicated Secretkeycape-lldap-bind(creationPolicy: Owner, keypassword), so no Secret has two writers. Setup compares custody with the livebindPWinkeycape-config. - Deployment: add the env from
secretKeyRef keycape-lldap-bind/passwordwith the new image; values are equal, so behaviour is unchanged. Verify, restart, admin login. - Remove
bindPWfromkeycape-config(directory-bind-rollout.py --operation clear-password, guarded on uid/resourceVersion);create-secrets.pystops writing it. Rollback at any step: re-add the value or drop
2026-10-31, founder decision: environment override, implemented by claude-code in key-cape. Done in source:
key-cape e2b5fa1 (KEY-WP-0046-T01; image main-e2b5fa1 = sha256:9e082dca…6f8d, built by the Forgejo workflow;
its acceptance job also fails on the previous commit a1ab8ff). NetKingdom: CCR-2026-0054 (proposed),
sso-mfa/k8s/keycape/bind-eso.yaml (dedicated Secret keycape-lldap-bind, creationPolicy: Owner),
bind-lane.py svc-keycape (setup reads bindPW from keycape-config and requires bindDN = svc-keycape;
verify reads the dedicated Secret and requires it to equal bindPW while present), directory-bind-rollout.py --operation clear-password (only with the service bind and the delivered env; switch restores), and
deployment.yaml pinned to the new image with the KEYCAPE_LLDAP_BIND_PW env. The new image also carries
key-cape 67a0869..e2c5a94 (online token status, inactive while tokenStatus is unset) and identity-facts.
Founder order: approve, setup, ESO apply, verify, deployment apply (image + env), login, clear-password, restart, login.
the env.
Synthetic verification instead of a founder sign-in
id: NK-WP-0056-T05
status: todo
priority: medium
assignee: claude-code
depends_on: [NK-WP-0056-T04]
state_hub_task_id: "bb511d1e-6d5d-56d1-9030-2e0d94006a16"
A synthetic test user with an OTP seed held in OpenBao, and a journey that signs in through Authelia and KeyCape with MFA and resolves through privacyIDEA. Reuse the identity-provider journey acceptance. The executor runs it as the check between consumers and reverts on failure.
Done when: the journey runs unattended, and fails closed against a deliberately broken bind on a synthetic directory.
Pilot: unattended bind rotation under one admission
id: NK-WP-0056-T06
status: todo
priority: high
assignee: claude-code
needs_human: true
intervention_note: "Founder signs the admission for the single live pilot rotation."
depends_on: [NK-WP-0056-T01, NK-WP-0056-T03, NK-WP-0056-T04, NK-WP-0056-T05]
state_hub_task_id: "6c3939f8-23a9-5f70-a3b1-94f4b291706b"
Rotate svc-authelia's password with no person in the loop beyond the admission: generate into
OpenBao, set in LLDAP, let ESO deliver, restart, run the synthetic journey, revert on failure, emit a
receipt to audit-core. Rehearse on a synthetic directory first, then one live run under a
founder-signed admission.
Done when: the live rotation completes or reverts on its own, within its budget, with a verified receipt, and the founder's only action was the admission.
Budget and risk enforcement
id: NK-WP-0056-T07
status: todo
priority: medium
assignee: claude-code
depends_on: [NK-WP-0056-T06]
state_hub_task_id: "a87c5edb-7604-5eba-9261-b1ed2ed16655"
Enforce the admission's budget and blast radius at run time (attempt and time limits, a kill switch the founder holds, refusal when a check is missing), and feed outcomes and drift into posture feedback (NK-WP-0031) so that recurring admissions can be reviewed in aggregate rather than one by one.
Done when: a run that exceeds its budget or scope stops and reverts in a synthetic test, and the pilot's receipts appear in a posture-feedback report.