session 103: the lifecycle audit — one envelope, one gauntlet, a correlated view that never invents success (#104 #106 #107 #108 #105 #109) #120

Merged
larandar merged 12 commits from tango/103-lifecycle-audit into fastlane 2026-09-06 07:50:34 +00:00
Owner

Closes #103. Delivers #104, #106, #107, #108, #105, #109 as one review.

What lands

  • #104 — the envelope. Closed (deny_unknown_fields); every field an identifier, a generation, a timestamp, or the emitter's own outcome. EMITS says which layer may state which event type, required_fields says which identifiers each type must carry, and envelope.schema.json mirrors both — with a test holding the document to the code so the two cannot drift.
  • #106 — ingestion. A gauntlet in order: forbidden-value scan on the raw JSON → schema → declared emitter → signature → event-type authorization → append. The signature lives in the document, over a canonical payload, so a file ingested a week later is authenticated exactly as a delivery was. Chain and quarantine are open-append-fsync-close per write: an append failure is seen at append time and is an error, never an acknowledgement.
  • #107 — the correlation view. An ascent! program joining grant, lease, operation, token, principal, seat and Forge event. Out-of-order arrival converges on one chain without duplicating it.
  • #108 — the proofs. Replay, duplicate, restart and quarantine behaviour, through the real gauntlet and the real files.
  • #105 — drift. C01–C08, each one rule with the spec's discriminating negative as its body.
  • #109 — acceptance. The 25 production facts and the old-path exit conditions, rendered honestly: four items may be attested by naming a system of record, and no lifecycle-shaped fact can be attested.

The perimeter, which is the point

Jostoph observes and reports. grep -n 'ureq\|reqwest\|token_file\|/v1/internal\|/v1/identity/lease' src/lifecycle/*.rs is empty: no module under src/lifecycle/ constructs a client, and the only writes are two JSON-lines files. The registry's keys authenticate audit events and nothing else.

Evidence

cargo test: 126 passed (67 at baseline, 59 new). cargo clippy --all-targets -- -D warnings: clean. Both re-run independently by the run's queen.

End to end over the CLI, against a 78-event pilot chain (drone-17 under Teyla's seat: declare, provision, activate, issue, use, six one-axis negatives, rotation, successor use before cutover, predecessor invalidated on every layer within five minutes, successor use after, revocation pending → revoked; plus a second drone and a queen grant suspended and withdrawn):

  • ingest → {"appended":78}; ingest again → {"replayed":78};
  • 7 forbidden fixtures → quarantined, {"quarantined":7,"refused:schema":11,"refused:bad_signature":1}, chain unchanged at 78 lines;
  • grep -c for credential shapes over chain + quarantine → 0;
  • view → two chains, no missing layers, no contradictions, no duplicates;
  • drift → {"findings":[]};
  • acceptance → exit 1, {pass:20, missing:5}, and with four attestations {pass:20, attested:2, missing:3} — still accepted:false, because 16, 17 and 22 are lifecycle-shaped and cannot be attested.

What a reviewer must check

  1. Six components, not five: declaration is added beside the five runtime layers, because the required event names need an emitter for principal and grant declaration and no runtime component can be it.
  2. REASON_CODES adds delivery_uncertain, which the idempotency section names as a state — confirm that addition is wanted.
  3. The forbidden-value heuristic (≥32 base64url chars, mixed case plus a digit, never all-hex) — confirm it cannot bite a legitimate identifier. ULIDs are 26 chars; Kanidm and Forgejo ids are UUID, hex or numeric.
  4. TITLES[0..25] against the acceptance spec's own list: those strings are what flake-ops will read.
  5. Fact 11 reads "attributed mutation" as "attributed operation under the successor generation" — a read-only drone token cannot mutate. That reading is the subject of open decision D3.
  6. nix flake check was not run; no dependency was added.
Closes #103. Delivers #104, #106, #107, #108, #105, #109 as one review. ## What lands - **#104 — the envelope.** Closed (`deny_unknown_fields`); every field an identifier, a generation, a timestamp, or the emitter's own outcome. `EMITS` says which layer may state which event type, `required_fields` says which identifiers each type must carry, and `envelope.schema.json` mirrors both — with a test holding the document to the code so the two cannot drift. - **#106 — ingestion.** A gauntlet in order: forbidden-value scan on the raw JSON → schema → declared emitter → signature → event-type authorization → append. The signature lives **in the document**, over a canonical payload, so a file ingested a week later is authenticated exactly as a delivery was. Chain and quarantine are open-append-fsync-close per write: an append failure is seen at append time and is an error, never an acknowledgement. - **#107 — the correlation view.** An `ascent!` program joining grant, lease, operation, token, principal, seat and Forge event. Out-of-order arrival converges on one chain without duplicating it. - **#108 — the proofs.** Replay, duplicate, restart and quarantine behaviour, through the real gauntlet and the real files. - **#105 — drift.** C01–C08, each one rule with the spec's discriminating negative as its body. - **#109 — acceptance.** The 25 production facts and the old-path exit conditions, rendered honestly: four items may be *attested* by naming a system of record, and **no lifecycle-shaped fact can be attested**. ## The perimeter, which is the point Jostoph observes and reports. `grep -n 'ureq\|reqwest\|token_file\|/v1/internal\|/v1/identity/lease' src/lifecycle/*.rs` is **empty**: no module under `src/lifecycle/` constructs a client, and the only writes are two JSON-lines files. The registry's keys authenticate audit events and nothing else. ## Evidence `cargo test`: **126 passed** (67 at baseline, 59 new). `cargo clippy --all-targets -- -D warnings`: clean. Both re-run independently by the run's queen. End to end over the CLI, against a 78-event pilot chain (drone-17 under Teyla's seat: declare, provision, activate, issue, use, six one-axis negatives, rotation, successor use before cutover, predecessor invalidated on every layer within five minutes, successor use after, revocation pending → revoked; plus a second drone and a queen grant suspended and withdrawn): - ingest → `{"appended":78}`; ingest again → `{"replayed":78}`; - 7 forbidden fixtures → quarantined, `{"quarantined":7,"refused:schema":11,"refused:bad_signature":1}`, chain unchanged at 78 lines; - `grep -c` for credential shapes over chain + quarantine → **0**; - view → two chains, no missing layers, no contradictions, no duplicates; - drift → `{"findings":[]}`; - acceptance → exit 1, `{pass:20, missing:5}`, and with four attestations `{pass:20, attested:2, missing:3}` — still `accepted:false`, because 16, 17 and 22 are lifecycle-shaped and cannot be attested. ## What a reviewer must check 1. **Six components, not five**: `declaration` is added beside the five runtime layers, because the required event names need an emitter for principal and grant declaration and no runtime component can be it. 2. `REASON_CODES` adds `delivery_uncertain`, which the idempotency section names as a state — confirm that addition is wanted. 3. The forbidden-value heuristic (≥32 base64url chars, mixed case plus a digit, never all-hex) — confirm it cannot bite a legitimate identifier. ULIDs are 26 chars; Kanidm and Forgejo ids are UUID, hex or numeric. 4. `TITLES[0..25]` against the acceptance spec's own list: those strings are what flake-ops will read. 5. Fact 11 reads "attributed mutation" as "attributed operation under the successor generation" — a read-only drone token cannot mutate. That reading is the subject of open decision D3. 6. `nix flake check` was not run; no dependency was added.
One closed, non-secret record shape for the declaring authority, Kanidm,
OpenBao, Vedanta, the Forge credential adapter, and Forgejo. Event names
and field names are the ratified interfaces' own (`identity.lease.issued`,
`credential_token_id`, `principal_subject_uuid`, ...): an emitter written
against the contract emits this without translation.

Rulings made here, as code reviewed as code:

- EMITS: which layer may state which event. A Forgejo delivery cannot say
  a lease was issued; OpenBao cannot say Kanidm minted. The join across
  layers is Jostoph's to make from each layer's own words, which is why
  `component` is a required field and `outcome` is component-local.
- required_fields: the identifiers an event type must carry for its
  layer's evidence to be joinable, split into always / on success — a
  failed mint has no token to name.
- REASON_CODES: a failure names one of the declared error classes and its
  retryability must agree with that table.
- forbidden_paths: runs on the raw JSON before the typed parse. Secret-
  bearing key names at any depth (`credential`, `wrapping_token`, ...,
  with `*_token_id` and `credential_generation` as the identifier
  allow-list) and bearer-shaped values in any field (OpenBao token
  prefixes, JWS compact form, `Bearer `, PEM, long mixed-case
  high-entropy runs). A finding is a path and a shape, never a value.
- The envelope carries its own HMAC over a canonical payload (sorted keys,
  compact, `signature` removed — `jq -cS 'del(.signature)'` reproduces
  it), so a file ingested later is authenticated exactly as a delivery.
- The seat (assignment, tenure, holder UUID and persona) travels on the
  envelope: for a drone, Forgejo natively attributes use to the queen
  holder's account, and this is the field the audit joins back from.

fixtures/lifecycle/v1/ carries the same contract in example form for
every consumer: one valid fixture per required event type, invalid and
forbidden cases with an expectations manifest naming the refusal and the
value that must be absent from every finding, a 62-event pilot chain, and
a JSON Schema document a test holds to the code.

Refs #104
The gauntlet between a document and the chain, in an order that is the
contract: forbidden scan on the raw JSON, schema, authentication of the
claimed component against a declared emitter registry, authorization of
the event type (the registry may narrow what a layer owns, never widen
it), then durable append.

Rulings:

- A secret-shaped document is quarantined by path and digest before it is
  parsed as anything. The value is not typed, not logged, not echoed; the
  quarantine is a separate file so it can never be read as a lifecycle
  fact.
- An undeclared emitter, a bad signature, and a layer stating another
  layer's event each record no fact from the payload, in that order.
- "Appended" means these bytes are on this chain and nothing more: no
  downstream success is inferred. A failed append is an error, not an
  acknowledgement — the chain opens, writes, syncs, and closes per append
  so the failure is seen at append time.
- The same event id twice with the same content is a replay, answered as
  such and written nowhere; with different content it is a contradiction:
  the original stands, the quarantine records both digests and the paths
  that differ (names, not values), and the answer is a conflict.
- Reading the chain refuses on any corrupt line rather than skipping it:
  a view over a torn chain would invent evidence.

Surfaces: `POST /v1/lifecycle/events` on the listener when `serve` is
given the registry and the two logs, and `jostoph lifecycle ingest` for
files, both through the one Ingester. The fixture registry authenticates
every versioned fixture and the pilot chain through the real path.

Refs #106
`grant -> lease -> operation -> token -> resource event`, materialized
from the chain. The joins are Datalog because a join is a rule: an event
naming a token reaches the lease that token was issued under; an event
naming an operation reaches the lease it served; a Forgejo delivery that
names only the token it was presented with lands on the drone's chain
through that token; the declaring authority, which never names a lease,
reaches the chain through the grant.

Rulings:

- Every layer is listed under its own name with its own events and its
  own latest word. A layer with no event is `present: false` and stays
  so. There is no field in which to say "the lifecycle succeeded".
- One layer, one operation, two outcomes is a `contradiction` carrying
  both ids; so is one token with two generations and one grant version
  with two digests. Nothing is resolved; both are shown with provenance.
- `events` maps every id to its line on the chain and the emitter's own
  record locator, so every derived item points back past the view.
- The document is a function of the set: the chain in any order renders
  the same bytes (line numbers aside), which is what "converges" means.
- Contradictory duplicates from the quarantine are surfaced beside the
  chain's own contradictions; forbidden refusals are counted, not shown.

`jostoph lifecycle view` prints it and exits 1 while any chain has a
missing layer or a contradiction.

Refs #107
Tests only, through the real gauntlet, the real files, and the real view:

- a replay after a restart appends nothing, answers `replayed`, and
  leaves the view byte-identical;
- a restart between append and projection converges on the view a
  one-shot ingestion produces, with nothing dropped and nothing invented;
- a contradictory duplicate is answered as a conflict naming the paths
  that differ, recorded in the quarantine, surfaced by the view, and the
  original stands across a restart — offering it again is the same
  conflict, not a second record;
- malformed identifiers (whitespace, empty, over-long, a non-UUID, a
  non-sha256 digest, a date without a time) are refused by path before
  durability;
- every forbidden fixture is quarantined by path and its value is absent
  from the answer, the chain, and the quarantine; the quarantine record
  keeps identifiers, digest, and paths, nothing else.

Two things the proofs found and this commit fixes: the durable
timestamps were u128, which serde_json will write but not read back —
they are u64 now; and the pilot fixture's second drone had never used or
rotated its lease, so its Forgejo layer was honestly missing. The fixture
now carries a second drone through use, rotation to a distinct
generation, predecessor invalidation within the overlap, and expiry with
exact deletion, which is what the exit condition asks of it.

Refs #108
One `ascent!` rule per canonical class, each written so the acceptance
spec's discriminating negative is what the rule reads:

  C01 a minted token no OpenBao lease accounts for
  C02 OpenBao issued with a token Kanidm never minted, or Kanidm
      invalidated the current token of a lease nobody closed
  C03 a token used under a generation other than the one it was minted
      with, or one token carrying two generations
  C04 an event naming a grant version with a digest other than the one
      the declaring authority activated
  C05 the resource attributed use to an account that is neither the
      persona nor the seat holder (the holder's account IS the expected
      binding for a drone, and is not drift)
  C06 a resource allowed a token after its bound, or after a layer
      invalidated it
  C07 a resource allowed a token after OpenBao closed, or began closing,
      its lease
  C08 a suppressed or suspended principal with a lease no layer closed,
      naming the grant, the lease, and the token still standing
  revocation_pending: OpenBao reports pending and a layer that minted has
      not invalidated — the still-live layer is the finding's layer

Every finding carries the ids it was derived from and the owning
compensation path as text. Nothing here holds a client: there is no way
to invalidate, mint, activate, or widen from this module, and the pilot
chain — every class exercised by mutating it — reports clean.

Declared state is what the declaring authority and Kanidm said on the
chain. There is no second input, so nothing for the detector to prefer.

`jostoph lifecycle drift` prints the report and exits 1 on any finding.

Refs #105
The canonical acceptance spec's twenty-five facts and six exit
conditions, each rendered from the chain, the view, and the drift report
as pass, fail, pending, missing, contradictory, or attested — with the
event ids that earned it, or the source that would carry it.

Rulings:

- There is no code path that produces `pass` without listing evidence
  ids. A missing layer is `missing` and names the layer's event; a
  layer disagreeing with itself is `contradictory`; a partial
  revocation is `pending`.
- Four items are not lifecycle events by nature — the boundary probe
  (14), the pool manager's liveness and checkout rotation (20), the
  absence of Vedanta-facing callers, and break-glass handling. An
  out-of-band attestation naming a system-of-record locator renders them
  `attested`, never `pass`; an attestation without a locator is not one.
  Everything else — the successor tenure, the later session's new grant,
  every mutation — must arrive as the owning layer's own event.
- `accepted` requires every fact pass or attested; `exit.ready` requires
  `accepted` and every condition. Both are false on the pilot fixture,
  which names exactly what it lacks: a successor queen tenure (16, 17), a
  later session's grant (22), and the four attestations.
- "No operator-placed object" (23) is derived, not asserted: it passes
  when the owning layers' own events for principal, tenure, token,
  lease, and predecessor invalidation all passed — EMITS is what makes
  "owning layer" checkable.
- The report carries no credential value and no signature; the drift
  report rides along so a consumer has one document.

Also here: the test-only helpers gated behind `cfg(test)` and the
ascent-program allowances that `clippy --all-targets -D warnings` asked
for, so the branch is clean under the repository's own checks.

`jostoph lifecycle acceptance` prints the report and exits 0 only when
accepted.

Refs #109
One section naming the four verbs, the serve flags, the gauntlet every
document passes, and the perimeter: no module under src/lifecycle/ holds
a client with which to mint, rotate, revoke, invalidate, activate, or
alter membership.

Refs #103
larandar left a comment

Verdict: changes required.

  1. P1 — Unauthenticated Unicode panics ingestion. src/lifecycle/envelope.rs:770: t[..7] slices untrusted UTF-8 at an unchecked byte boundary. Reproduced through the built CLI with {"event_id":"éééé"}: exit 101, panic before authentication. Use a checked prefix or byte comparison.
  2. P1 — Quarantine persists a value the scanner identified as a credential. src/lifecycle/ingest.rs:219: the forbidden branch retains printable ASCII event IDs up to 128 bytes, even when that same value caused the denial. Reproduced with a disposable OpenBao-shaped marker: the exact marker appeared in the quarantine file. Secret-bearing key names also survive in finding paths/logs. Omit detected values and sanitize paths before persistence or logging.
  3. P1 — Acceptance fact 17 cannot pass. src/lifecycle/acceptance.rs:754: both possible branches are Missing or Fail; no valid evidence can yield Pass. Implement the positive successor-visibility/revocation predicate and test a fully accepted chain.
  4. P2 — Replay serialization is only per process. src/lifecycle/store.rs:102: two ingesters opened on the same chain keep independent seen maps and locks, allowing duplicate/contradictory IDs to be appended and acknowledged. Restart indexes the last duplicate. Enforce exclusive writer ownership or cross-process durable serialization.
  5. P2 — Some reports can misstate evidence. acceptance.rs:1154 matches emergency invalidation confirmations by lease/time/component without the exact requested token/operation; confirmations for older tokens can satisfy a current-token request. acceptance.rs:1209 filters full-lifetime drift by lease ID while C06 token findings use an empty lease ID. view.rs:320 orders timestamps lexically, so 10:00+02:00 can be selected over the later 09:00Z. Match exact target ownership, join token-scoped drift, require reconciliation interval evidence, and compare parsed timestamps.

Validation rerun: nix develop --command cargo test 126 passed, 0 failed; the CLI build also passed. The Unicode and quarantine reproductions ran against that binary. Clippy rerun was still in progress when this review was prepared.

Delivery record: the body describes delivering #104, #106, #107, #108, #105 and #109 but has a closing keyword only for #103. Add each delivered leaf’s closing line so the forge closes the delivered work as required by ratification.

Reviewed commit: 0b908a310473e5ba5da30f3b7943cdb8608bce4a.

Agent review performed by Codex at the user’s request. Posted through the PR author’s account; this records review evidence, not an independent collaborator approval or a new owner ruling.

**Verdict: changes required.** 1. **P1 — Unauthenticated Unicode panics ingestion.** `src/lifecycle/envelope.rs:770`: `t[..7]` slices untrusted UTF-8 at an unchecked byte boundary. Reproduced through the built CLI with `{"event_id":"éééé"}`: **exit 101, panic before authentication**. Use a checked prefix or byte comparison. 2. **P1 — Quarantine persists a value the scanner identified as a credential.** `src/lifecycle/ingest.rs:219`: the forbidden branch retains printable ASCII event IDs up to 128 bytes, even when that same value caused the denial. Reproduced with a disposable OpenBao-shaped marker: **the exact marker appeared in the quarantine file**. Secret-bearing key names also survive in finding paths/logs. Omit detected values and sanitize paths before persistence or logging. 3. **P1 — Acceptance fact 17 cannot pass.** `src/lifecycle/acceptance.rs:754`: both possible branches are Missing or Fail; no valid evidence can yield Pass. Implement the positive successor-visibility/revocation predicate and test a fully accepted chain. 4. **P2 — Replay serialization is only per process.** `src/lifecycle/store.rs:102`: two ingesters opened on the same chain keep independent seen maps and locks, allowing duplicate/contradictory IDs to be appended and acknowledged. Restart indexes the last duplicate. Enforce exclusive writer ownership or cross-process durable serialization. 5. **P2 — Some reports can misstate evidence.** `acceptance.rs:1154` matches emergency invalidation confirmations by lease/time/component without the exact requested token/operation; confirmations for older tokens can satisfy a current-token request. `acceptance.rs:1209` filters full-lifetime drift by lease ID while C06 token findings use an empty lease ID. `view.rs:320` orders timestamps lexically, so `10:00+02:00` can be selected over the later `09:00Z`. Match exact target ownership, join token-scoped drift, require reconciliation interval evidence, and compare parsed timestamps. Validation rerun: `nix develop --command cargo test` **126 passed, 0 failed**; the CLI build also passed. The Unicode and quarantine reproductions ran against that binary. Clippy rerun was still in progress when this review was prepared. Delivery record: the body describes delivering #104, #106, #107, #108, #105 and #109 but has a closing keyword only for #103. Add each delivered leaf’s closing line so the forge closes the delivered work as required by ratification. Reviewed commit: `0b908a310473e5ba5da30f3b7943cdb8608bce4a`. Agent review performed by Codex at the user’s request. Posted through the PR author’s account; this records review evidence, not an independent collaborator approval or a new owner ruling.
Author
Owner

Review verification update: the independent nix develop --command cargo clippy --all-targets -- -D warnings run completed successfully. The review findings and changes-required verdict remain unchanged.

Review verification update: the independent `nix develop --command cargo clippy --all-targets -- -D warnings` run completed successfully. The review findings and changes-required verdict remain unchanged.
envelope.rs sliced an untrusted string at a fixed byte offset (t[..7]) to
check for a "Bearer " prefix. That offset is not guaranteed to land on a
UTF-8 char boundary, so a multi-byte value long enough to pass the length
check but short of seven full characters (e.g. "éééé", reproduced via
{"event_id":"éééé"} through the built CLI) panicked the process — exit
101 — during the forbidden-value scan, before any authentication runs.

Compare the ASCII prefix over bytes instead of slicing the str, which
never panics regardless of char boundaries. Swept the rest of
src/lifecycle for the same class of unchecked slice on untrusted input;
the only other byte-index slicing in envelope.rs (the RFC 3339 parser)
only ever slices at positions proven to be ASCII, so it was not at risk.

Co-Authored-By: Teyla <[email protected]>
Two ways a caught secret survived its own quarantine:

- The forbidden branch kept the raw event_id (printable ASCII, <=128
  bytes) unconditionally, even when the event_id field was itself the
  value that tripped the scan. A disposable OpenBao-shaped marker set as
  event_id showed up verbatim in the quarantine file.
- forbidden_paths only scanned JSON *values* for bearer shapes; a value
  smuggled in as a *key* name instead sailed through the scan untouched
  and then appeared, still raw, in the schema-refusal path/why text and
  in the quarantine's finding paths and warn logs.

Fix: the quarantine branch now drops the event_id when its own path (or
its own shape) is what was flagged, and walk() now checks bearer_shape on
keys too, replacing a flagged key segment with `<redacted>` in the
finding's path instead of the key's raw text — closing the key-smuggling
gap and keeping every persisted/logged path a pointer, never a value.

Co-Authored-By: Teyla <[email protected]>
Fact 17 ("the successor holder managed existing leases within its
visibility and explicitly revoked wider ones without rewriting native
attribution") had a rewrite check (Fail) and an unconditional Missing —
no evidence on the chain could ever make it Pass.

Add the two positive predicates the title names: a successor-visibility
revocation (openbao: identity.lease.revoked, success, after the successor
tenure activates, naming a reason) for a lease that is wider than the
successor's scope, or continued native use of a lease that stayed within
it (forgejo: identity.resource.allowed, success, after the handover,
still attributed to the same holder). Either is Pass evidence naming both
the successor tenure and the corroborating event; neither appearing stays
Missing, and a rewritten native attribution is still Fail regardless.

Added a test building a chain with a genuine successor handover and an
explicit visibility-scoped revocation, proving fact 17 reaches Pass with
its two event ids as evidence — the pilot fixture deliberately does not
carry a successor tenure, so this fact was previously untestable as Pass
at all.

Co-Authored-By: Teyla <[email protected]>
Chain and Quarantine each kept their idempotency state in memory —
Chain's seen-map, Quarantine's write mutex — which is only ever
consistent within one process. Two ingesters opening the same chain
concurrently each believed themselves authoritative: both could append
or acknowledge a duplicate or a contradictory id, and a restart would
index whichever write happened to land last on disk.

Take an OS-level exclusive advisory lock (the newly stable
std::fs::File::try_lock) on the chain and quarantine files at open, held
for the life of the Chain/Quarantine value. A second opener on the same
path now fails loudly and immediately instead of racing; the lock
releases itself on drop, including on process exit or kill, so nothing
can wedge a chain closed after a crash.

Added a test opening a second Chain/Quarantine on paths a first still
holds and asserting both are refused, then showing the paths reopen
cleanly once the first is dropped.

Co-Authored-By: Teyla <[email protected]>
- acceptance.rs, the emergency-invalidate exit condition: a confirmation
  was matched by lease, component, and a later timestamp only — never
  the token or operation actually named in the request. A confirmation
  that invalidated an older, already-superseded generation could satisfy
  a request for the current one. Each layer names its own operation_id
  for its own action (the Forge adapter's revocation is a distinct
  operation from OpenBao's request), so the join that survives every
  layer is the lease-scoped credential_generation the request names.

- acceptance.rs, the reconciliation exit condition: filtered drift
  findings by `lease_id`, but C06 (a token accepted after it should no
  longer be) carries an empty lease_id by design — the finding is about
  the token, not the lease as a whole. A C06 finding on a lease's own
  token was invisible to this check no matter which lease the token
  belonged to. Now also joins by token id through the lease's own token
  set when a finding's lease_id is empty.

- view.rs, a layer's `latest` event: compared `occurred_at` as text.
  "10:00+02:00" sorts after "09:00Z" lexically despite being the earlier
  instant, so a layer's reported "last word" could be the wrong event.
  Now compares the parsed instant (`occurred_at_ms`), with the id as
  tie-break so the result is still a pure function of the set.

Each fix has a test that fails without it: a stale-generation
confirmation wrongly reaching Pass, a lease-scoped C06 finding wrongly
invisible to reconciliation readiness, and a lexically-earlier-looking
but instant-later event wrongly losing "latest."

Co-Authored-By: Teyla <[email protected]>
Author
Owner

All five findings fixed. Five commits on top of 0b908a31, pushed. 133 tests (was 126), clippy -D warnings clean, and the perimeter grep is still empty — re-run independently by the queen. Every fix has a test confirmed to fail on the parent commit.

Finding Commit
1 P1 — the Unicode panic before authentication 9c84cb2
2 P1 — the quarantine kept the credential it caught 6d39dc1
3 P1 — acceptance fact 17 could never pass b2288ea
4 P2 — replay serialization was per-process only 54523c3
5 P2 — three ways a report could misstate evidence 97de66d

1bearer_shape sliced a str at an unchecked byte boundary; it now compares raw bytes. {"event_id":"éééé"} panicked the CLI before authentication; it does not now.

2 was the worst of the five, and the fix is wider than the finding. The quarantine no longer retains the flagged event_id — and bearer-shaped JSON keys, not only values, are now caught and redacted before anything is persisted or logged. A module that exists to stop credentials reaching disk was writing them to disk.

3 — fact 17 now has a real positive predicate: explicit visibility-scoped revocation, or continued unrewritten use. Before, both branches led to Fail or Missing, so no evidence could ever satisfy it.

4Chain and Quarantine take an OS-level exclusive flock at open (stable File::try_lock, no new dependency), so a second concurrent owner is refused rather than silently interleaving.

5 — three separate defects: emergency-invalidation confirmations now join on exact credential_generation; C06's empty-lease_id findings are joined by token as well; and Layer::latest compares parsed timestamps, so 10:00+02:00 no longer beats the later 09:00Z.

Four things the worker flagged rather than settled

  1. Finding 5's join key is a reading, not a ruling. credential_generation for emergency-invalidation confirmations came from the one available fixture. Confirm it is the intended durable cross-layer join, because a wrong join key here misreports acceptance rather than failing loudly.
  2. Key redaction writes a bare <redacted> literal in the finding path. A digest would preserve correlation across events; the literal does not. Which is wanted?
  3. A second ingester now fails loudly at open, with no retry path. That is a deliberate operational posture and should be confirmed as the one you want.
  4. The PR body still closes only #103. Closing lines for #104, #106, #107, #108, #105 and #109 will be added to the description before merge.
**All five findings fixed.** Five commits on top of `0b908a31`, pushed. **133 tests** (was 126), clippy `-D warnings` clean, and the perimeter grep is still empty — re-run independently by the queen. Every fix has a test confirmed to fail on the parent commit. | | Finding | Commit | |---|---|---| | 1 | P1 — the Unicode panic before authentication | `9c84cb2` | | 2 | P1 — the quarantine kept the credential it caught | `6d39dc1` | | 3 | P1 — acceptance fact 17 could never pass | `b2288ea` | | 4 | P2 — replay serialization was per-process only | `54523c3` | | 5 | P2 — three ways a report could misstate evidence | `97de66d` | **1** — `bearer_shape` sliced a `str` at an unchecked byte boundary; it now compares raw bytes. `{"event_id":"éééé"}` panicked the CLI **before authentication**; it does not now. **2** was the worst of the five, and the fix is wider than the finding. The quarantine no longer retains the flagged `event_id` — and **bearer-shaped JSON *keys*, not only values, are now caught and redacted** before anything is persisted or logged. A module that exists to stop credentials reaching disk was writing them to disk. **3** — fact 17 now has a real positive predicate: explicit visibility-scoped revocation, or continued unrewritten use. Before, both branches led to Fail or Missing, so no evidence could ever satisfy it. **4** — `Chain` and `Quarantine` take an OS-level exclusive `flock` at open (stable `File::try_lock`, **no new dependency**), so a second concurrent owner is refused rather than silently interleaving. **5** — three separate defects: emergency-invalidation confirmations now join on exact `credential_generation`; C06's empty-`lease_id` findings are joined by token as well; and `Layer::latest` compares **parsed** timestamps, so `10:00+02:00` no longer beats the later `09:00Z`. ### Four things the worker flagged rather than settled 1. **Finding 5's join key is a reading, not a ruling.** `credential_generation` for emergency-invalidation confirmations came from the one available fixture. Confirm it is the intended durable cross-layer join, because a wrong join key here misreports acceptance rather than failing loudly. 2. **Key redaction writes a bare `<redacted>` literal** in the finding path. A digest would preserve correlation across events; the literal does not. Which is wanted? 3. **A second ingester now fails loudly at `open`, with no retry path.** That is a deliberate operational posture and should be confirmed as the one you want. 4. The PR body still closes only #103. Closing lines for #104, #106, #107, #108, #105 and #109 will be added to the description before merge.
larandar merged commit e151169cd8 into fastlane 2026-09-06 07:50:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Ting/Jostoph!120
No description provided.