diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 1618daf..5556ae8 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -49,6 +49,7 @@ | workplan | FLEX-WP-0037 | finished | — | workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md | | workplan | FLEX-WP-0038 | active | — | workplans/FLEX-WP-0038-wave2-resource-right-sizing.md | | workplan | FLEX-WP-0039 | proposed | — | workplans/FLEX-WP-0039-core-pdp-production-readiness.md | +| workplan | FLEX-WP-0040 | proposed | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | | task | FLEX-WP-ADHOC-2026-10-03-T01 | done | — | workplans/ADHOC-2026-10-03.md | | task | FLEX-WP-ADHOC-2026-10-03-T02 | wait | — | workplans/ADHOC-2026-10-03.md | | task | FLEX-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md | @@ -238,6 +239,12 @@ | task | FLEX-WP-0039-T02 | todo | — | workplans/FLEX-WP-0039-core-pdp-production-readiness.md | | task | FLEX-WP-0039-T03 | todo | — | workplans/FLEX-WP-0039-core-pdp-production-readiness.md | | task | FLEX-WP-0039-T04 | todo | — | workplans/FLEX-WP-0039-core-pdp-production-readiness.md | +| task | FLEX-WP-0040-T01 | wait | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | +| task | FLEX-WP-0040-T02 | wait | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | +| task | FLEX-WP-0040-T03 | todo | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | +| task | FLEX-WP-0040-T04 | todo | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | +| task | FLEX-WP-0040-T05 | todo | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | +| task | FLEX-WP-0040-T06 | wait | — | workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md | | intake | FLEX-IN-0001 | closed | — | intakes/intakes.md | | intake | FLEX-IN-0002 | closed | — | intakes/intakes.md | | intake | FLEX-IN-0003 | open | — | intakes/intakes.md | diff --git a/docs/bounded-execution-admission-pdp.md b/docs/bounded-execution-admission-pdp.md new file mode 100644 index 0000000..c9c5f9e --- /dev/null +++ b/docs/bounded-execution-admission-pdp.md @@ -0,0 +1,97 @@ +# Bounded Execution Admission and the PDP — access-engine owner answer + +Answer to NetKingdom NK-WP-0056-T02 (owner request +`net-kingdom/docs/proposals/2026-10-31-nk56-t02-owner-requests.md`, section +"access-engine"), for the proposed standard +`net-kingdom/canon/standards/bounded-execution-admission_v0.1.md` (BEA v0.1). +Date: 2026-10-31. Status: owner answer and proposal. The founder and the named +owners decide; nothing here is enabled or deployed. + +Follow-on work: `workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md`. + +## What exists today (from source) + +| Fact | Evidence | +| --- | --- | +| access-engine is the estate's only PDP and validates approvals as input claims; it never owns, mutates or consumes the approval object | `SCOPE.md` (One-liner, Out of Scope: approval object is approval-engine's) | +| A typed task-execution path exists on `/v1/check`. It requires TokenReview caller mapping, a fixed owner-installed Approval reader (`GET /v1/approvals/{UUID}/claim`, no consume) and original/fresh claim correspondence before policy runs | `docs/trusted-service-task-source.md`, `internal/taskexecution/approval_source.go`, `internal/taskexecution/service_input.go` | +| That path is hard-wired to one consumer: subject `secrets-engine`, transport principal `system:serviceaccount:secrets-engine:secrets-engine`, contract `SECRETS-EXEC-20261011-v1`, operation `metadata-apply-ccr`, exactly six OpenBao operations | `examples/secrets-engine-service-task-execution-v2/policy_package.md` (status `proposed`) | +| The v2 policy requires `approval.consumed == false` at decision time | same file, `candidate_matches` | +| The older secrets-engine `apply` rule ignores `execution_contract` content: an allow there is not exact task authority | `internal/policy/secrets_execution_gap_test.go` (gap characterization test) | +| The Approval claim adapter carries id, native binding and PDP digest; it carries no approver assurance level | `third_party/approvalfact` (pinned copy of `approval-engine/adapters/go/approvalfact/claim.go`) | +| Human callers on the typed path must present `aal2` + MFA from key-cape, at most 300 s old; that is the caller's assurance, not the approver's | `internal/taskexecution/identity.go` | +| No chart, runtime admission or credential custody is enabled for the typed path | `docs/trusted-service-task-source.md` | + +So the mechanics a BEA gate needs exist in one consumer-specific shape. There +is no `bounded-execution` action, no namespace for admissions, no caller mapping +for `service:nk-bind-executor`, and no evaluator for BEA's `scope_ceiling` or +`plan.steps`. + +## Q1 — Should a PDP decision gate executor operations? + +**Answer: yes, in scope, recommended for production; not required for the pilot.** + +A decision "executor X may run operation O on target T under admission A" is +the kind of decision this repository exists to make (SCOPE: estate's only PDP), +and reading the admission as an approval claim through approval-engine matches +the existing boundary (approval-engine owns the object, access-engine validates +it as input). It adds what local enforcement cannot: an independent evaluator +outside the executor's own code, a signed decision record per step with policy +version and request digest, and a single place to revoke or narrow executor +authority across admissions. + +Two shape points we propose (they change the request NetKingdom sketched): + +1. **Two decisions, not one per step against an unconsumed claim.** The current + typed path demands `consumed == false`. BEA consumes once *before* the first + step (BEA-C6/C7). Proposed: + - `start` — before consume: claim valid, unconsumed, approver human/`aal2`, + executor == admission executor, now inside `window`, admission digest + matches the claim's target. ALLOW is the precondition for the consume call. + - `execute-step` — after consume: claim consumed exactly once, by this + executor, inside the window; the step id, operation, target and required + `mode/scope` match the digested plan and lie inside `scope_ceiling`. + This needs approval-engine to expose `consumed_by` and `consumed_at` in the + claim (question for approval-engine, alongside NK's question 3). +2. **The PDP never consumes.** Consume stays the executor's call to + approval-engine, as today (`approval_source.go`: "no policy, retry, consume + or effect lives here"). + +## Q2 — What is needed, who owns it, timeline + +| Item | Status | Proposal | +| --- | --- | --- | +| Action vocabulary `start`, `execute-step` on resource type `bounded-execution-admission`, system `net-kingdom` | needs change | access-engine adds; vocabulary only, no allow | +| Typed path generalised from the single secrets-engine mapping to an owner-installed caller map (first entry `system:serviceaccount:sso:nk-bind-executor` → `service:nk-bind-executor`) | needs change | access-engine; keeps the existing secrets-engine behaviour byte-identical | +| Admission input: the executor submits the admission document; access-engine recomputes its digest and requires equality with the claim's `target.admission_digest` | needs change | access-engine; canonical digest rule must match NK's BEA digest definition | +| `execution_contract` coverage: evaluate step id/operation/target/mode/scope against `plan.steps` and `scope_ceiling`, plus window and blast-radius (`max_concurrent`) | needs change | in the new pack; the old `apply` gap (see test) is not reused and stays characterised | +| Approver assurance `aal2` (BEA-C4) | **missing input** | approval-engine must carry approver assurance in the claim; access-engine will not infer it | +| `consumed_by` / `consumed_at` in the claim | **missing input** | approval-engine | +| Policy pack `net-kingdom.bounded-execution` | proposal | **Pack mechanics and fixtures: access-engine. Semantic content (what a ceiling admits): NetKingdom as BEA standard owner, reviewed by gate-house for authority ceilings.** Founder to confirm owner | +| Deployment of a pin serving the pack, caller trust, Approval reader credential | gated | founder/platform admission, as for every pin; not in this workplan's authority | + +**Timeline (proposal).** Source work (vocabulary, generalised caller map, +pack with fixtures, DENY-by-default until inputs exist) can be done in about one +to two weeks of repository work once approval-engine answers the claim-field +questions. It lands as `proposed`, evaluated only in tests. A live gate follows +FLEX-WP-0039 (per-pin readiness and policy import) and a founder rollout +decision. Realistic: source-ready mid/late November 2026, live after the pilot. +It does not block the pilot window in the BEA example (2026-11-03). + +## Q3 — Pilot with local enforcement only + +**Answer: acceptable, with conditions.** + +- The executor's validator (BEA-C1..C7 against the carrier) plus a namespaced + Kubernetes Role and narrowly scoped OpenBao/LLDAP credentials are the + enforcement for T06. That is a consumer-side PEP and is the consumer's to own. +- The pilot must not claim PDP gating. Its receipt should state the PDP + decision as `not-evaluated` (no decision id), so later evidence cannot be + mistaken for a flex-auth ALLOW. +- Do not call the existing secrets-engine `apply` rule or the generic + `/v1/check` as a stand-in: the former ignores `execution_contract`, the latter + refuses task execution. +- When the gate goes live, the executor's stance on PDP unavailability is + fail-closed (declared by the consumer, per SCOPE "Out of Scope: fail-open"). +- Revisit after the pilot: the second admission should not run without the PDP + gate unless the founder records an explicit exception. diff --git a/workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md b/workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md new file mode 100644 index 0000000..0f4ff72 --- /dev/null +++ b/workplans/FLEX-WP-0040-bounded-execution-pdp-gate.md @@ -0,0 +1,113 @@ +--- +id: FLEX-WP-0040 +type: workplan +title: "Bounded execution PDP gate: decide executor steps under an approved admission" +domain: infotech +repo: access-engine +status: proposed +flavor: implementation +owner: access-engine +created: "2026-10-31" +updated: "2026-10-31" +related: [NK-WP-0056, FLEX-WP-0039, FLEX-WP-0031] +--- + +# FLEX-WP-0040 — Bounded execution PDP gate + +NetKingdom's proposed Bounded Execution Admission v0.1 (NK-WP-0056) lets the +founder approve, at `aal2`, an exact digested admission that a bounded executor +consumes once. Owner answer: `docs/bounded-execution-admission-pdp.md`. PDP +gating is in scope and recommended for production; the T06 pilot may run on +local enforcement without it. + +Today the typed task path (`docs/trusted-service-task-source.md`) serves only +the secrets-engine v2 contract, requires an unconsumed claim, and the Approval +claim carries no approver assurance. Everything below lands as `proposed` +source, evaluated in tests only. No deployment, caller trust or credential is +admitted by this workplan. + +## 1. Settle the claim inputs with approval-engine + +```task +id: FLEX-WP-0040-T01 +status: wait +priority: high +``` + +Wait: approval-engine answers to NK-WP-0056-T02 (binding to +`{admission_id, admission_digest}`, approver assurance `aal2`, consume scope) +plus our two additions: `consumed_by` and `consumed_at` in the claim. Record +the agreed claim fields here. Gate: written claim field list with owner ack. + +## 2. Pack owner and semantic review + +```task +id: FLEX-WP-0040-T02 +status: wait +priority: medium +``` + +Wait: founder confirms the proposal — access-engine owns pack mechanics and +fixtures; NetKingdom (BEA owner) owns what a ceiling admits; gate-house reviews +authority-ceiling semantics. Gate: decision recorded. + +## 3. Generalise the typed path caller map + +```task +id: FLEX-WP-0040-T03 +status: todo +priority: medium +``` + +Replace the single hard-wired secrets-engine mapping with an owner-installed +caller map; first new entry `system:serviceaccount:sso:nk-bind-executor` → +`service:nk-bind-executor`. Gate: existing secrets-engine v2 tests unchanged +and passing; unknown service accounts refuse; generic `/v1/check` still refuses +task execution. + +## 4. Vocabulary and admission digest + +```task +id: FLEX-WP-0040-T04 +status: todo +priority: medium +depends_on: [FLEX-WP-0040-T01] +``` + +Add actions `start` and `execute-step` on resource type +`bounded-execution-admission` (system `net-kingdom`). The executor submits the +admission document; access-engine recomputes its digest by NK's BEA digest rule +and requires equality with the claim's admission digest. Gate: digest fixtures +shared with NetKingdom's validator agree byte-for-byte. + +## 5. Policy pack `net-kingdom.bounded-execution` (DENY by default) + +```task +id: FLEX-WP-0040-T05 +status: todo +priority: medium +depends_on: [FLEX-WP-0040-T02, FLEX-WP-0040-T03, FLEX-WP-0040-T04] +``` + +`start`: claim valid and unconsumed, approver HUMAN at `aal2` and not the +executor, executor equals admission executor, now inside `window`. +`execute-step`: claim consumed once by this executor inside the window; step +id/operation/target/mode/scope match the digested `plan.steps` and lie inside +`scope_ceiling`; `max_concurrent` respected. Fixtures from +`net-kingdom/examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml` +including out-of-ceiling, wrong executor, expired window, replayed consume and +missing assurance. Gate: pack validates, all fixtures pass, the existing +`apply` gap test stays as characterisation. + +## 6. Live gate readiness + +```task +id: FLEX-WP-0040-T06 +status: wait +priority: low +depends_on: [FLEX-WP-0040-T05, FLEX-WP-0039-T01] +``` + +Wait: FLEX-WP-0039 readiness surface and a founder rollout decision for a pin +serving the pack, with the executor's declared fail-closed stance. Gate: +founder-approved rollout; no live change from this repo's agents.