Updated by fix-consistency on 2026-10-11: - workplan status: backlog → active Assistant: claude-code Assistant-Model: opus Assistant-Process: 1370019@bnt-lap001 Assistant-Session: d462dd63-16de-4189-ae70-b9cd6566bf87
345 lines
17 KiB
Markdown
345 lines
17 KiB
Markdown
---
|
|
id: FLEX-WP-0031
|
|
type: workplan
|
|
title: "The decision record has a declared emission guarantee and nothing that delivers it"
|
|
domain: infotech
|
|
repo: flex-auth
|
|
status: active
|
|
flavor: residual
|
|
owner: claude
|
|
topic_slug: netkingdom
|
|
planning_priority: P1
|
|
planning_order: 310
|
|
related_workplans:
|
|
- FLEX-WP-0030
|
|
- FLEX-WP-0019
|
|
created: "2026-09-21"
|
|
updated: "2026-10-10"
|
|
state_hub_workstream_id: "84f5d9fe-b4c9-584a-b964-efe3e48af095"
|
|
---
|
|
|
|
# FLEX-WP-0031 — Deliver the decision-record emission guarantee
|
|
|
|
`GH-DEC-2026-018` ruled that flex-auth is the §4 source of evidence for the
|
|
decision record, and that it is not conforming on §11 until it declares and
|
|
delivers a per-event-class emission guarantee. The declaration is published
|
|
(`cadence.yaml`, `FLEX-WP-0030-T07`). Nothing emits: the decision record reaches
|
|
consumers only in the `/v1/check` response, no sender named flex-auth or
|
|
`access-engine` is registered with `audit-core`, and there is no outbox,
|
|
heartbeat or reconciliation count. This plan closes declared gap **G2**
|
|
(`docs/conformance/security-layer-conformance.md`, review 2026-10-19).
|
|
|
|
Bound, stated up front so no argument rests on more: heartbeat and
|
|
reconciliation detect loss, outage, drain failure and accident. Neither detects
|
|
a compromised flex-auth suppressing a record and its own count together.
|
|
|
|
## 1. Decide emission atomicity
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "2dbc225f-d762-537b-9a24-ab2b17fc2fa2"
|
|
```
|
|
|
|
`GH-DEC-2026-018` left open whether decision-record emission must be atomic with
|
|
the decision (§9.4), pending the class inventory. It exists now. Decide whether
|
|
a rare load-bearing decision (`deny`) may be returned before its record is
|
|
committed to the outbox, and record the answer as a `FLEX-DEC`. Gate: decided,
|
|
with the latency cost stated.
|
|
|
|
Done 2026-09-23: `FLEX-DEC-2026-018`. Every decision is released only after its
|
|
record commits to the local outbox. When the commit fails, restrictive effects
|
|
(`deny`, `redact`, `not_applicable`) are still released and counted as
|
|
`released_uncommitted`, while `allow` and `audit_only` are withheld (503).
|
|
Withholding a deny would turn it into an error that an open-stance consumer
|
|
treats as proceed. The measured cost is about 1.1 ms per durable append on
|
|
railiance01. The ruling puts two requirements on T03: the outbox needs a PVC,
|
|
and the node disk is at 84 %.
|
|
|
|
## 2. Register flex-auth as an audit-core sender
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T02
|
|
status: wait
|
|
blocked_on: message-from:audit-core
|
|
blocking_reason: "GH-WP-0004-T12 refused the load-bearing failure-path exception (GH-DEC-2026-024). AUDIT-IN-0006 sender rows and governed token delivery remain pending."
|
|
priority: high
|
|
state_hub_task_id: "89661908-ca9a-5e4a-a0a5-62d1a9e02568"
|
|
```
|
|
|
|
Open an intake with `audit-core` (worked examples `AUDIT-IN-0002`,
|
|
`AUDIT-IN-0003`): sender registration, `evidence_kind`, `heartbeat_classes` per
|
|
class exactly as `cadence.yaml` publishes them, and a token lane routed via
|
|
`warden route find`. audit-core has said it accepts the classification as
|
|
supplied and will not infer it. Gate: sender registered; no secret in any file.
|
|
|
|
Requested 2026-09-23 (hub message `6044ed35`). The request asks for six senders,
|
|
one per pin (`flex-auth.<pin>`), because each pin can only reconcile its own
|
|
count. It also asks for `tenants: ["*"]` with a justification,
|
|
`may_read: true` for `GET /v1/reconciliation`, and per-class `heartbeat_classes`
|
|
at 86400 s for the four rare classes. It asks audit-core to rule on whether
|
|
FLEX-DEC-2026-018's failure-path exception counts as a `completeness_trade`,
|
|
which `senders.py` forbids for a load-bearing source. warden route has no
|
|
catalog lane for audit-core sender tokens, and audit-core was asked to name one.
|
|
Waiting on audit-core.
|
|
|
|
2026-09-27: audit-core replied (`AUDIT-IN-0006`, thread `6044ed35`), accepted
|
|
with corrections: `may_read` must be `false`, not `true` (a writer already
|
|
counts its own sources without it); `tenants: ["*"]` accepted on the stated
|
|
justification; per-class heartbeats and the `cadence.yaml` correction accepted
|
|
(done, see T05); each registration must also carry an `emission_cadence`
|
|
declaration (info-tech-canon wire schema 0.1, contract digest
|
|
`b08b4d95fc4b0bd3`) with `allow` as expected-rate, or its rate is only
|
|
declared and not evaluated. Two envelope corrections land before submission:
|
|
the heartbeat event needs its own `correlation_id` (no decision id exists for
|
|
it), and `occurred_at` needs an explicit offset. The atomicity ruling
|
|
(whether the `FLEX-DEC-2026-018` failure-path release is a `completeness_trade`)
|
|
is referred to `gate-house` by audit-core, not decided here — flex-auth agreed
|
|
in reply to wait for that referral. The token lane is an attended OpenBao mint
|
|
in the founder's terminal (same path `tenant-engine` used,
|
|
`AUDIT-WP-0010-T02`); `ops-warden` has no catalog lane for it yet and will add
|
|
one when registration opens. flex-auth acknowledged all of this in reply.
|
|
Still `wait`: the mint needs the founder, and `ops-warden`'s catalog entry is
|
|
not yet in place.
|
|
|
|
## 3. Durable outbox and the release rule
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T03
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "a59603d5-8954-5b05-b1a0-b143c82e439b"
|
|
```
|
|
|
|
Split on 2026-09-23 under the per-task budget. T05 and T06 carry the rest of
|
|
the original scope, and the original gate is divided between the three.
|
|
|
|
Done 2026-09-23:
|
|
|
|
- `internal/emission`: a durable outbox with one fsync per commit. A batch
|
|
commits once. Each event carries exactly the eight fields audit-core's
|
|
`normalize()` requires.
|
|
- Event ids are random and the decision id travels as `correlation_id`.
|
|
Decision ids are content digests, so two identical checks share one, and
|
|
idempotency would merge them.
|
|
- A torn tail is cut on open, and a failed commit is cut back to the last
|
|
committed size. Committed counts per class are rebuilt on restart.
|
|
- The engine applies `FLEX-DEC-2026-018` in `Check`, `BatchCheck` and
|
|
`ListAllowed`. A withheld decision answers 503.
|
|
- `serve --outbox-dir --emission-source` enables it, and `GET /v1/emission`
|
|
reports committed and `released_uncommitted` counts per class.
|
|
- `api.DecisionEffects()` lists the effect vocabulary. A source-parsing test
|
|
fails on any constant it misses, and a second test fails unless the
|
|
load-bearing classes in `cadence.yaml` equal that vocabulary.
|
|
- `make test` (race) passes, and a local serve smoke test committed and counted
|
|
one deny and one allow. Nothing is deployed and no chart changed.
|
|
|
|
## 5. Implement heartbeat and drain to audit-core
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T05
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "14bd6648-11da-53d7-a512-027005bd4772"
|
|
```
|
|
|
|
2026-10-04: split implementation from native delivery acceptance (new T08).
|
|
`internal/emission/delivery.go` implements daily per-class `audit-core.heartbeat`
|
|
events with independent correlation IDs and offset timestamps, persisted in
|
|
our durable outbox. Restart rebuilds each class's last heartbeat from the log.
|
|
Heartbeats expose committed and released-uncommitted counts; a known failure
|
|
changes the assertion to `released-uncommitted-finding`.
|
|
|
|
The asynchronous drain sends the eight-field envelope with event-ID
|
|
idempotency, reads a mounted token file per event for rotation, refuses
|
|
redirects, and advances a fsynced/renamed cursor only on 200 or 202. Every other
|
|
response, transport failure or cursor-write failure retains the event. A
|
|
bounded drain runs each minute, without holding the decision lock during HTTP.
|
|
`GET /v1/emission` exposes pending depth, oldest pending timestamp and lag-bound
|
|
exceedance (100 events / 1h). No response body or token is logged.
|
|
|
|
`serve --audit-url <explicit /v1/events endpoint> --audit-token-file <mount>`
|
|
enables the worker only with an outbox. Both default empty, and no pin enables
|
|
it. Local tests cover per-class cadence, restart, rejection, stable replay,
|
|
ambiguous acceptance, token rotation and cursor durability. The current outbox
|
|
retains acknowledged log entries; retention/compaction is not implemented, so
|
|
capacity planning must include cumulative log growth.
|
|
|
|
## 6. Reconciliation, profile check, and storage
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T06
|
|
status: wait
|
|
depends_on: [FLEX-WP-0031-T02]
|
|
blocked_on: message-from:audit-core
|
|
blocking_reason: "Receiver counts use accepted_at, while local counts use commit time; audit-core must supply compatible window semantics or durable acceptance timestamps before a valid comparator can be implemented."
|
|
priority: high
|
|
state_hub_task_id: "bf92a951-3b69-59dd-8907-f6748c91a264"
|
|
```
|
|
|
|
2026-09-27: reconciliation compare needs `audit-core`'s `GET /v1/reconciliation`,
|
|
which does not exist until T02's sender is registered. The profile checker
|
|
(`net-kingdom/tools/emission-cadence-profile`) is runnable locally but needs a
|
|
`--contract-schema` for the decision-record cadence contract that is not yet
|
|
published in this repo or referenced by any sibling's invocation found —
|
|
inventing one here would be exactly the kind of schema flex-auth should
|
|
publish deliberately, not improvise for a validation run. The PVC chart change
|
|
is explicitly a production change needing the founder's go-ahead and was left
|
|
undone. Stays `wait`.
|
|
|
|
- Compare committed counts per class and window with audit-core's
|
|
`GET /v1/reconciliation`. Divergence is a finding, and undrained events count
|
|
as lag, not divergence.
|
|
- Validate `cadence.yaml` with the net-kingdom emission-cadence profile checker,
|
|
supplying the inventory as `--rare-load-bearing`/`--load-bearing`.
|
|
- Add an optional PVC to the chart for the outbox, sized against the node disk
|
|
(84 % on 2026-09-23). Rolling it out to any pin is a production change and
|
|
needs the founder's go-ahead.
|
|
|
|
2026-10-04: storage/profile preparation is now complete as T07. The remaining
|
|
comparator is independently blocked by receiver time semantics, not just a
|
|
missing registration: audit-core counts accepted_at in half-open windows,
|
|
while our log timestamps local commits. Ingest returns status/reference only.
|
|
A receiver-approved matching basis or stable acceptance-time receipts (also
|
|
on duplicates) is required before implementing a comparison. Do not widen
|
|
may_read to fetch archive payloads as a workaround. T02 and T08 still gate
|
|
native custody/rollout evidence; see the dependency record for exact returns.
|
|
|
|
## 4. Close G2
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T04
|
|
status: wait
|
|
depends_on: [FLEX-WP-0031-T08, FLEX-WP-0031-T06]
|
|
blocking_reason: "G2 needs T08 native heartbeat/drain and silence-finding evidence plus T06 reconciliation; local tests cannot close the delivery gap."
|
|
priority: medium
|
|
state_hub_task_id: "423b3090-1b72-58fd-9353-5c907c7683bb"
|
|
```
|
|
|
|
2026-09-27: closing G2 requires the silence-finding gate, which requires T05's
|
|
sender and T06's reconciliation to actually exist. Both are blocked on T02
|
|
(external, `audit-core`). Stays `wait`.
|
|
|
|
Change `cadence.yaml` `state` to emitting, move G2 out of the gap table with the
|
|
evidence, and tell `gate-house`, `audit-core` and `kings-guard`. Gate: a silence
|
|
finding is observed on a deliberately withheld heartbeat in a non-production
|
|
run.
|
|
|
|
## 7. Prepare persistent storage and validate the cadence contract
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T07
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "151fe609-ba68-506b-9c2c-9fdb0b149eef"
|
|
```
|
|
|
|
Split from T06 on 2026-10-04; its historical schema/storage blockers above are
|
|
superseded by this implementation. `charts/flex-auth` has optional outbox PVC,
|
|
mount, source and flags, defaults disabled, a one-replica constraint and
|
|
Recreate strategy to avoid two writers during rollout. It supports an existing
|
|
claim. Rendering is tested with caller auth enabled and disabled. No values pin
|
|
or live deployment is changed; 1Gi is a configurable preparation value, not an
|
|
assertion of available node space.
|
|
|
|
Published `docs/contracts/decision-emission-cadence.json` against the actual
|
|
InfoTechCanon wire schema and NetKingdom profile. All five decision classes
|
|
are explicitly supplied; contract/profile assessment passes. The generic
|
|
expected-rate schema forbids reconciliation on that entry and the profile
|
|
forbids duplicate classes, so allow's additional reconciliation guarantee is
|
|
in the `extensions.flex-auth` namespace. Receiver acceptance of that mapping
|
|
remains part of T02; a profile pass does not establish observer delivery.
|
|
|
|
## 8. Admit and exercise native heartbeat/drain delivery
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T08
|
|
status: wait
|
|
priority: high
|
|
depends_on: [FLEX-WP-0031-T02]
|
|
needs_human: true
|
|
blocking_reason: "Needs admitted sender/token mounts, founder approval of exact image/PVC/network rollout, and audit-core observer evidence; no production delivery is enabled."
|
|
state_hub_task_id: "b3b52b30-c73d-5a90-9935-d98afe5a9eaf"
|
|
```
|
|
|
|
Remaining acceptance split from T05: audit-core's owner must return admitted
|
|
exact per-pin sources, scope, heartbeat classes and cadence mapping under
|
|
AUDIT-IN-0006. Platform must provide governed attended token custody and
|
|
mount references, never values. The founder must approve exact immutable image,
|
|
PVC capacity and rollout; network owners must admit the decision pins to the
|
|
receiver. Record native 202 and idempotent 200 delivery, restart cursor/lag
|
|
behaviour, and an audit-core silence finding from a separately authorized
|
|
non-production withheld-heartbeat exercise. No test engagement/window is
|
|
created by this workplan. T06 still owns the compatible-time reconciliation
|
|
implementation and its native acceptance. See
|
|
`docs/blocked-task-dependencies-2026-10-04.md` for the concrete dependency gates.
|
|
|
|
2026-10-04 review: workplan stays blocked. Local heartbeat/drain and storage
|
|
preparation no longer wait on another repository, but admission, statutory
|
|
ruling, valid reconciliation time basis and approved native acceptance do.
|
|
`cadence.yaml` remains `declared-not-yet-emitting` and G2 remains open.
|
|
|
|
## 9. Enforce the accepted mandatory-emission ruling in source
|
|
|
|
```task
|
|
id: FLEX-WP-0031-T09
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "02a8d006-cc70-5a0e-b773-6924f05540ed"
|
|
```
|
|
|
|
2026-10-10: implemented `GH-DEC-2026-024` §3, the accepted refusal of
|
|
load-bearing failure-path release. This corrects the source release behavior
|
|
described under T01/T03; those dated entries remain historical evidence of
|
|
`FLEX-DEC-2026-018`, not the current implementation. No new human decision is
|
|
needed to implement the accepted doctrine in source.
|
|
|
|
With a required recorder attached, a failed durable append now withholds every
|
|
effect, including deny/redact/not_applicable. Single checks, restriction-only
|
|
batches, list-allowed and application readback retain the existing
|
|
`ErrRecordNotCommitted` / HTTP 503 availability representation. No replacement
|
|
allow or deny is manufactured. Uncommitted results are not inserted into
|
|
Explain's history, and no released-uncommitted counter is incremented. Existing
|
|
historical outbox counters and their reporting remain readable. Standalone
|
|
operation without an attached recorder remains unchanged; no source change
|
|
enables production emission or claims that an unconfigured pin is conformant.
|
|
|
|
Tests inject commit failure across the complete five-effect vocabulary, check
|
|
that Explain cannot expose uncommitted results and can expose committed ones,
|
|
exercise actual restrictive single/batch/list policy paths, and verify
|
|
restrictive enforcement and readback HTTP 503. Affected package suites pass:
|
|
`go test ./internal/decision ./internal/emission ./internal/readback
|
|
./cmd/access-engine ./pkg/api`.
|
|
|
|
This source fix is not deployed. Consumer outage behavior remains governed by
|
|
each declared PEP stance: a consumer with an open-for-scope stance can treat
|
|
unavailable evaluation as proceed, so the exact rollout must review those
|
|
stances rather than claim that a 503 enforces denial. T02 native sender/custody,
|
|
T06 compatible reconciliation windows, T08 exact approved rollout and silence
|
|
proof, and T04 G2 closure remain unfinished. `cadence.yaml` remains declared,
|
|
and the emission completeness gap remains open until native acceptance.
|
|
|
|
|
|
### T08 independent chart delivery preparation — 2026-10-11
|
|
|
|
Prepared inert chart `auditDelivery` configuration for existing admitted per-pin
|
|
Secret/key directory mount, explicit /v1/events endpoint and narrow receiver/DNS
|
|
egress. Supports existing `serve` pins only; unsupported readback flags refuse.
|
|
No pin defaults, native rollout, credential or sender registration changes.
|
|
Renderer tests pass for auth on/off, safe mounts/scoped peers, disabled defaults
|
|
and invalid/partial configurations. Mandatory-commit/emission/readback/HTTP/API
|
|
Go suites pass. Exact remaining image/PVC/custody/network/receiver and same-trial
|
|
native receipts are in `docs/audit-delivery-rollout-preparation.md`.
|
|
T08 remains wait; T06 receiver time basis and T04 native G2 acceptance remain open.
|
|
|
|
|
|
T08 preparation revision 2: independent Platform review found fractional YAML/
|
|
JSON receiver ports could survive integer truncation and render invalid Kubernetes
|
|
NetworkPolicy. Corrected integer-shape guard plus 22 YAML/JSON rejection cases
|
|
and integer boundary rendering passed. Original stage retained; no native change.
|
|
|
|
> 2026-10-11: demoted to `flavor: residual` / `backlog` by founder-authorized triage — off the
|
|
> critical path (identity → authz → secrets → SecurityCase execution). Tasks are unchanged;
|
|
> promote back when demand or risk appears. See the-custodian
|
|
> `history/20261011-netkingdom-security-progress-assessment.md`.
|