NK-WP-0056: founder decisions folded in; runtime split into T11 and T12
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run

Window-scoped write role (CCR-2026-0058 approved as amended), bind
self-rotation (pilot step is OPERATE on the one bind; svc-executor-pw
dropped), pilot without the PDP gate (not-evaluated), started event plus one
receipt per run, rapp Job in sso, owner workplans approved. New tasks: T11
core runner, T12 pilot operations and packaging; T06 depends on T12.

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:
tegwick 2026-10-31 15:42:30 +01:00
parent c9ab01e23e
commit cdf0d9bf25
11 changed files with 107 additions and 36 deletions

View file

@ -353,6 +353,8 @@
| task | NK-WP-0056-T08 | done | — | workplans/NK-WP-0056-bounded-executor-path.md |
| task | NK-WP-0056-T09 | done | — | workplans/NK-WP-0056-bounded-executor-path.md |
| task | NK-WP-0056-T10 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
| task | NK-WP-0056-T11 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
| task | NK-WP-0056-T12 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md |
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
| intake | NET-IN-0001 | open | — | intakes/intakes.md |

View file

@ -193,6 +193,26 @@ once Railiance declarations exist for it.
The receipt adds the outcome per step, check results, whether a revert ran, and the budget used. It
never holds a secret value.
Founder decision 2026-10-31, agreed with audit-core (AUDIT-WP-0018):
- When the executor consumes the admission, before its first step, it emits a separate
`execution.run.started` event, so a run that crashes before its receipt is still detectable.
- It then emits one `execution.attribution.receipt` per run:
- `target` and `action` at the admission level (`admission:<spec.id>`, `bounded-execution`);
- per-step detail in `steps[]`;
- `decision_refs` labelled `admission`, `admission_digest` and `approval`.
- Envelope ids are `run:<run_id>:started` and `run:<run_id>:receipt`.
**Policy decision point.** access-engine's gate (FLEX-WP-0040) has two decisions: `start` before
consume, and `execute-step` after it. The T06 pilot runs without the gate: its receipt records the
gate as `not-evaluated` and claims no allow. Any further admission needs the gate, or an exception
the founder records (founder decision 2026-10-31).
**Credentials during a run.** Write roles exist only during the admitted window: the platform creates
them when an admission is approved and deletes them after the receipt (CCR-2026-0058). A directory
bind rotates its own password, so no password-manager account is needed (founder decisions
2026-10-31).
## 7. The bind split as admissions
NK-WP-0056 tagged each step of the 2026-10-30 LLDAP bind split. The examples express them:

View file

@ -29,7 +29,7 @@ spec:
scope: openbao/platform/workloads/net-kingdom/directory-binds/svc-authelia
environments: &id001
- PRODUCTION
- mode: ADMINISTER
- mode: OPERATE
scope: lldap/people/svc-authelia
environments: *id001
- mode: OPERATE
@ -49,7 +49,7 @@ spec:
plan:
source:
repo: net-kingdom
commit: 43a78907593a
commit: c9ab01e23e92
files:
- path: tools/service-binds/bind-lane.py
sha256: 5b31b3567af07bba09530c6d3b0ed8b5c947df22bd13ac0442dc33c0d9b420c0
@ -69,13 +69,13 @@ spec:
- kv-advanced
revert: restore-kv
- id: set-directory
operation: lldap.user.set-password-from-kv
operation: lldap.user.change-own-password-from-kv
target: lldap:svc-authelia
consumer: authelia
class: delegable
mutates: true
requires:
mode: ADMINISTER
mode: OPERATE
scope: lldap/people/svc-authelia
checks_after:
- bind-signs-in
@ -115,13 +115,13 @@ spec:
mode: OPERATE
scope: openbao/platform/workloads/net-kingdom/directory-binds/svc-authelia
- id: restore-directory
operation: lldap.user.set-password-from-kv
operation: lldap.user.change-own-password-from-kv
target: lldap:svc-authelia
consumer: authelia
class: delegable
mutates: true
requires:
mode: ADMINISTER
mode: OPERATE
scope: lldap/people/svc-authelia
- id: redeliver
operation: eso.force-sync
@ -141,7 +141,7 @@ spec:
requires:
mode: OPERATE
scope: k8s/sso/deployment/authelia
plan_digest: sha256:47de5fcaa09fdadb4ea501e47f85fc12da5b6ce31b28c2ccad34a25ca9599d0f
plan_digest: sha256:36328096076c71ade582173092511be6aee546281ef5edb3c2acaadaf7c9e1fb
checks:
- id: kv-advanced
description: KV svc-authelia has exactly one new version, CAS on the previous one

View file

@ -43,7 +43,7 @@ spec:
plan:
source:
repo: net-kingdom
commit: 43a78907593a
commit: c9ab01e23e92
files:
- path: sso-mfa/k8s/lldap/bind-split.py
sha256: 4decc3bbe65120ff6c4cef5613b9a41a60168ae64ecd4c1380f61a2fcd37b6b3
@ -93,7 +93,7 @@ spec:
requires:
mode: OPERATE
scope: openbao/platform/workloads/net-kingdom/directory-binds
plan_digest: sha256:be02f6850b329e2e2262bc720a70f881a146183ff70fb3ac20ae72e3a5ae70e8
plan_digest: sha256:139e7b874d8d8ba9419f141bf9e10341a9a0057bb75fa47eee32c2ee19b396ce
checks:
- id: accounts-read-only
description: each account exists in lldap_strict_readonly only and cannot write

View file

@ -46,7 +46,7 @@ spec:
plan:
source:
repo: net-kingdom
commit: 43a78907593a
commit: c9ab01e23e92
files:
- path: sso-mfa/k8s/lldap/bind-split.py
sha256: 4decc3bbe65120ff6c4cef5613b9a41a60168ae64ecd4c1380f61a2fcd37b6b3
@ -73,7 +73,7 @@ spec:
scope: k8s/sso
checks_after:
- split-shape
plan_digest: sha256:c07dd58e6581049441d512edb60753bfe7102e3c89ea28d55081141373844d0d
plan_digest: sha256:0459999b93bb8aee0dacb1b36ea95d114ac74d0ed775095f3ee5dc2ee66203cd
checks:
- id: split-shape
description: every consumer binds as its own svc- account; no service binds as admin

View file

@ -43,7 +43,7 @@ spec:
plan:
source:
repo: net-kingdom
commit: 43a78907593a
commit: c9ab01e23e92
files:
- path: sso-mfa/k8s/lldap/bind-split.py
sha256: 4decc3bbe65120ff6c4cef5613b9a41a60168ae64ecd4c1380f61a2fcd37b6b3
@ -93,7 +93,7 @@ spec:
requires:
mode: OPERATE
scope: k8s/sso/deployment/lldap
plan_digest: sha256:60578e013b23fd3d68fead65956c939cde1eb9230d9f9fdbe1aaa677e8e0de11
plan_digest: sha256:5f51601ce96a07da5fa56b5b00c2066c572e3b19a4677b1a16cee3178295cae0
checks:
- id: ld-admin-administers
description: ld-admin signs in and administers; admin keeps lldap_admin membership

View file

@ -42,7 +42,7 @@ spec:
plan:
source:
repo: net-kingdom
commit: 43a78907593a
commit: c9ab01e23e92
files:
- path: sso-mfa/k8s/lldap/bind-split.py
sha256: 4decc3bbe65120ff6c4cef5613b9a41a60168ae64ecd4c1380f61a2fcd37b6b3
@ -104,7 +104,7 @@ spec:
requires:
mode: OPERATE
scope: k8s/sso/deployment/lldap
plan_digest: sha256:33063248bed00c84b640a37344a4c9e52285e76cacb68b92d08872cd9b7c77db
plan_digest: sha256:139f0ca7564184290c7cb1f6b20bdb9807db98e5609a79083ac6f7aea13bb50c
checks:
- id: ld-admin-administers
description: ld-admin signs in and administers; admin keeps lldap_admin membership

View file

@ -52,17 +52,17 @@ rotation = doc(
"Pilot shape for NK-WP-0056-T06: generate a new bind password into OpenBao, set it in LLDAP, let ESO deliver it, restart Authelia, prove sign-in with the synthetic journey; revert on any failed check.",
"medium", {"not_before": "2026-11-03T09:00:00Z", "not_after": "2026-11-03T11:00:00Z"},
[{"mode": "OPERATE", "scope": KV, "environments": P},
{"mode": "ADMINISTER", "scope": "lldap/people/svc-authelia", "environments": P},
{"mode": "OPERATE", "scope": "lldap/people/svc-authelia", "environments": P},
{"mode": "OPERATE", "scope": "k8s/sso/externalsecret/authelia-secrets", "environments": P},
{"mode": "OPERATE", "scope": "k8s/sso/deployment/authelia", "environments": P}],
{"unit": "consumer", "consumers": ["authelia"], "max_concurrent": 1},
[LANE, JOURNEY],
[step("generate", "openbao.kv.write-next-version", "openbao:" + KV[len("openbao/"):], "authelia", "delegable", True, "OPERATE", KV, ["kv-advanced"], "restore-kv"),
step("set-directory", "lldap.user.set-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "ADMINISTER", "lldap/people/svc-authelia", ["bind-signs-in"], "restore-directory"),
step("set-directory", "lldap.user.change-own-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "OPERATE", "lldap/people/svc-authelia", ["bind-signs-in"], "restore-directory"),
step("deliver", "eso.force-sync", "k8s:sso/externalsecret/authelia-secrets", "authelia", "delegable", True, "OPERATE", "k8s/sso/externalsecret/authelia-secrets", ["eso-synced"], "redeliver"),
step("restart", "k8s.rollout-restart", "k8s:sso/deployment/authelia", "authelia", "delegable", True, "OPERATE", "k8s/sso/deployment/authelia", ["synthetic-journey"], "restart-again")],
[step("restore-kv", "openbao.kv.write-previous-value-as-next-version", "openbao:" + KV[len("openbao/"):], "authelia", "delegable", True, "OPERATE", KV),
step("restore-directory", "lldap.user.set-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "ADMINISTER", "lldap/people/svc-authelia"),
step("restore-directory", "lldap.user.change-own-password-from-kv", "lldap:svc-authelia", "authelia", "delegable", True, "OPERATE", "lldap/people/svc-authelia"),
step("redeliver", "eso.force-sync", "k8s:sso/externalsecret/authelia-secrets", "authelia", "delegable", True, "OPERATE", "k8s/sso/externalsecret/authelia-secrets"),
step("restart-again", "k8s.rollout-restart", "k8s:sso/deployment/authelia", "authelia", "delegable", True, "OPERATE", "k8s/sso/deployment/authelia")],
[check("kv-advanced", "KV svc-authelia has exactly one new version, CAS on the previous one", LANE, 30),

View file

@ -95,7 +95,10 @@ class RuleTests(unittest.TestCase):
{"mode": "GOVERN", "scope": "lldap", "environments": ["PRODUCTION"]}))))
# whole-directory ADMINISTER for a delegable step at medium risk
def widen(s):
s["scope_ceiling"][1]["scope"] = "lldap"
s["scope_ceiling"][1].update(mode="ADMINISTER", scope="lldap")
for step in s["plan"]["steps"] + s["plan"]["revert_steps"]:
if step["requires"]["scope"].startswith("lldap/"):
step["requires"]["mode"] = "ADMINISTER"
self.assertIn("BEA-5", rules(mutate(ROTATION, widen)))
def test_bea6_least_privilege(self):

View file

@ -31,9 +31,10 @@ identities:
principal_type: WORKLOAD
conventions: [executor]
executor_subject: "service:nk-bind-executor"
purpose: "OpenBao Kubernetes-auth role for sso/nk-bind-executor: write the next version of the svc-authelia bind (CAS) and read it for checks; lane shape pending secrets-engine and railiance-platform answers (T02)"
purpose: "OpenBao Kubernetes-auth role for sso/nk-bind-executor (CCR-2026-0058, approved, window-scoped): write the next version of the svc-authelia bind (CAS) and read it; holding the current value is what lets the executor change the bind's own password in LLDAP"
grants:
- {mode: OPERATE, scope: openbao/platform/workloads/net-kingdom/directory-binds/svc-authelia, activation: APPROVED, environments: [PRODUCTION], note: "KV data read and CAS write; 15 min TTL"}
- {mode: OPERATE, scope: openbao/platform/workloads/net-kingdom/directory-binds/svc-authelia, activation: APPROVED, environments: [PRODUCTION], note: "KV data read and CAS write; 15 min TTL; role exists only in the admitted window (CCR-2026-0058)"}
- {mode: OPERATE, scope: lldap/people/svc-authelia, activation: APPROVED, environments: [PRODUCTION], note: "self-rotation: signs in as the bind with the KV value and changes its own password (hub 3d0c8f86)"}
interactive_login: false
second_factor: not_applicable
custody: [openbao-lease]
@ -41,20 +42,6 @@ identities:
owner: "railiance-platform (OpenBao role) for NetKingdom"
rotation: {path: "OpenBao lease per run; no standing secret", coupling: [bounded-executor]}
status: planned
- name: svc-executor-pw
principal_type: SERVICE
conventions: [executor]
executor_subject: "service:nk-bind-executor"
purpose: "LLDAP account in lldap_password_manager: set svc-authelia's password from KV. LLDAP cannot narrow it to one account (it can reset any non-admin user); each admission narrows it to the accounts it names"
grants:
- {mode: ADMINISTER, scope: lldap/people, activation: APPROVED, environments: [PRODUCTION], note: "lldap_password_manager; admission ceiling narrows to lldap/people/<account>"}
interactive_login: false
second_factor: not_applicable
custody: [openbao-lease]
consumers: [{name: bounded-executor, kind: service}]
owner: "NetKingdom directory and User Engine owners"
rotation: {path: "Its own password held in OpenBao; lane shape pending secrets-engine (T02)", coupling: [bounded-executor]}
status: planned
- name: svc-executor-read
principal_type: SERVICE
conventions: [bind]

View file

@ -121,6 +121,18 @@ NetKingdom proved on the pinned LLDAP; audit-core holds receipts plus a `run.sta
creates the rapp, the image is built in net-kingdom, one Job per admission; glas-harness: agent executors after
GLAS-WP-0018-T04.
**2026-10-31, founder decisions after the answers** (hub c29c6eda, 3d0c8f86, 19afd15e):
- The executor's OpenBao write role is window-scoped. CCR-2026-0058 was amended, approved and pushed.
- The bind rotates its own password, so `svc-executor-pw` is dropped and RPF-WP-0069-T06 is cancelled.
- The pilot runs without the policy gate (`not-evaluated`); later admissions need the gate or a recorded exception.
- Packaging is `rapp-bounded-executor`, one Job per admission in `sso`, with the image built in net-kingdom.
- Receipts are a started event plus one receipt per run.
- These owner workplans are approved: APPROVAL-WP-0007, FLEX-WP-0040, SECRETS-WP-0017, RPF-WP-0069, AUDIT-WP-0018 and
GLAS-WP-0018. glas-harness owns agent executors later.
Runtime work is split into T11 (core runner) and T12 (pilot operations and packaging).
## Executor identity and authority ceiling
```task
@ -396,6 +408,53 @@ check between consumers; failure triggers the revert path.
**Done when:** the journey runs unattended against live with no person involved, and fails closed
against a deliberately broken bind on a synthetic directory (T08).
## Executor runtime: core runner
```task
id: NK-WP-0056-T11
status: todo
priority: high
assignee: claude-code
depends_on: [NK-WP-0056-T01, NK-WP-0056-T03]
```
The executor itself, in net-kingdom (founder decision 6d1adce9):
- consume one admission through the approval-engine carrier (independent GET, digest check, aal2 check, consume once);
- refuse outside the window or ceiling;
- run the pinned steps one consumer at a time, with checks after each mutating step, settle times, revert on
failure, the budget (wall time, attempts) and an `on_unknown: stop`;
- emit `execution.run.started` and the receipt (PDP `not-evaluated` for the pilot).
Tested against in-process fakes for the carrier, operations and audit sink.
**Done when:** the runner passes its tests, including revert on a failed check, budget exhaustion, a stale or
reused approval, and a step outside the ceiling. Every refusal must happen before any effect.
## Executor runtime: pilot operations and packaging
```task
id: NK-WP-0056-T12
status: todo
priority: high
assignee: claude-code
depends_on: [NK-WP-0056-T11, NK-WP-0056-T08]
```
The pilot's operations:
- OpenBao read and CAS write of the next version;
- LLDAP self-rotation of the bind;
- ExternalSecret force-sync and its status check;
- Deployment restart and rollout check;
- the synthetic journey.
Run them on the T08 synthetic stack, including a revert. Add the Containerfile and image workflow in
net-kingdom. The `rapp-bounded-executor` package is created with `rmgr rapp wrap`, and the founder creates the
Forgejo remote. It runs one Job per admission in `sso` with an egress-only NetworkPolicy.
**Done when:** a synthetic rotation completes and a deliberately failing one reverts, with receipts; the image builds.
## Pilot: unattended bind rotation under one admission
```task
@ -405,7 +464,7 @@ 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, NK-WP-0056-T10]
depends_on: [NK-WP-0056-T01, NK-WP-0056-T03, NK-WP-0056-T04, NK-WP-0056-T05, NK-WP-0056-T10, NK-WP-0056-T12]
state_hub_task_id: "6c3939f8-23a9-5f70-a3b1-94f4b291706b"
```