Observe all drift on the identity plane, and alert what Vedanta cannot resolve #11

Open
opened 2026-08-22 15:48:58 +00:00 by agent.odin · 2 comments
Owner

First required delivery

Keep both directions of the existing set comparison together, plus the held-slot alert producer required by Ting#2 Q2. Prove a real persisted, addressed alert against flake-ops#470; detection can proceed before transport, but alert acceptance requires that real surface. Broader adapters move to #55. The approved rationalization corrects the obsolete Jostoph-mutation and queen-mints-principal wording below without reopening the observation mandate.

Shared vocabulary

This record uses Proposal, Design, Tasks, and Specification Delta.

Proposal

Why

Drift detection has had no concrete case to be accepted against. It has two now, and both arrived by themselves rather than being constructed.

Declared-removed but still present. sa-freki and sa-geri are declared entities for personas that have been ruled removed. lar.ad/flake-ops#169 and #135 both state it: "The Pack roster is Saga, Sköll, Ratatoskr, Völundr, Starkaðr, and Heiðrún. Freki and Geri are dropped; Hati remains a sub-agent." Their SOULs were always placeholders — modules/users/fenrisulfr/agents/*/SOUL.md says "SLOT, NOT VOICE … standing room" — but their directory entities are real and reconciled today by impl/forge-identity/kanidm. Nothing in the declarative path can remove them, by construction: nixops-kanidm's provider.nu:27 states it outright — "NEVER PRUNES, NEVER DESTROYS. Schema v0 has no destroy" — and the trait is add-and-repair, so "a persona removed from the roster keeps its entity — removal is a revocation, which is runtime business".

Declared but never present. sa-odin's membership of agents was in the roster, evaluated into this impl's group_membership member list, and had simply never reached the directory. This is the more expensive direction. A surviving entity is a revocation debt — bad, but inert; the wolves sat for weeks harming nothing. A missing membership is a live outage that presents four layers downstream: it surfaced as curl: (22) ... 400 in a nushell overlay, and the body kanidm actually sent ({"error":"access_denied"}) was being discarded by the caller. Cost: an evening, and every persona token exchange refused throughout.

Scope

Observation of drift across the identity plane, and an alert wherever Vedanta cannot resolve what it observed. Not the roster edit itself (lar.ad/flake-ops#135), not entity creation, and not principal disposition — see the ruling below.

Design

Owner ruling (2026-09-03), Q1 — the observation mandate. Creation of permanent identities is the provider's fief, as previously ruled. The tension that leaves — something must still notice what the declarative path structurally cannot fix — is settled by a standing rule:

Vedanta is in charge of observing ALL drift on the identity plane, and MUST raise an alert where resolution is out of her scope.

This is broader than the case that produced it. Consequences:

  • Observation is unbounded within the identity plane; authority to act is not. Vedanta observes every divergence between declared and actual identity state, in both directions, regardless of which component owns the fix.
  • Detection stays here, because Vedanta's reconcile projection already holds both sets — the roster as declaration, the directory as observation — and computing the comparison in either order is nearly free where the sets already are.
  • Disposition routes out, unchanged. Entity creation belongs to the provider. Jostoph independently audits and never rotates, disables, or retires a principal; declaration and lifecycle disposition follow the exact canonical provider/private-effector boundary. Vedanta may invalidate only exact resource-credential token IDs through its private validated command boundary. Observing drift confers no power to resolve it.
  • The alert is a duty, not a courtesy. Out-of-scope drift that Vedanta observes and does not raise is a defect in Vedanta, equal in kind to failing to detect it. Silent tolerance of a known divergence is the failure mode this rule exists to forbid.
  • Both directions are in scope by construction. Declared-but-absent is drift on the identity plane, so it is observed here rather than in a sibling record — settling Q2. The two directions are the same set comparison in opposite order; separate scenarios preserve the distinct failure classes.

The forge half is currently vacuous for the wolves, and acceptance must say so. Neither freki nor geri has a Forgejo account — GET /users/freki and GET /users/geri both return nothing, and the only agent accounts on jo.et0.pw are agent.odin, agent.omo, agent.teyla and codexo. A test that passes because there was never anything to observe is not evidence that observation works. The forge half needs a subject that actually has an account.

Neither direction catches hand-minting, worth recording: sa-odin was minted by hand during the 2026-08-23 incident rather than through the mint path, whose kanidm-sa step does "service account + group membership" as one act. Hand-minting is what split the account from its membership. Drift detection is the net; that hole is upstream of it.

Specification Delta

Requirement: drift is observed in both directions

Scenario: the wolves leave the roster

  • GIVEN sa-freki and sa-geri exist in kanidm, carrying the agents group
  • AND the roster (flake.users.*.agents) no longer declares either
  • WHEN drift detection runs
  • THEN both are reported as drift, named individually
  • AND a second run reports them again, because Vedanta cannot resolve them

Scenario: a declared membership never reached the directory

  • GIVEN the roster declares a membership
  • AND the directory does not carry it
  • WHEN drift detection runs
  • THEN it is reported as drift

Scenario: a pool drone is not drift

  • GIVEN a provider-provisioned pool principal in the agents group whose deployed slot/name/UUID/grant projection is valid
  • WHEN drift detection runs
  • THEN it is NOT reported as undeclared-persona drift, because the exact deployed pool projection accounts for it; the queen does not create the principal
  • AND nothing acts on it

Requirement: unresolvable drift is alerted, never silently tolerated

Scenario: observed drift whose fix belongs to another component

  • GIVEN Vedanta observes drift it has no authority to resolve
  • WHEN detection completes
  • THEN an alert is raised naming the subject, the observed divergence, and the component that owns the resolution
  • AND Vedanta performs no mutation of its own
  • AND the alert repeats on subsequent runs while the drift persists

Tasks

  • Detect entities in the agents group the roster does not declare, discriminating personas (a+ alias) from pool drones (no alias).
  • Detect declared memberships absent from the directory — the outage-shaped direction.
  • Raise an alert for every observed drift whose resolution is out of Vedanta's scope, naming subject, divergence, and owning component.
  • Prove no detection path mutates a principal: no creation, no deletion, no rotation, no disabling.
  • Consume separately reviewed expansion from #55 when a named identity-plane adapter is ready; it does not block this first detector.
  • Choose a forge subject that has an account so the forge assertion is not vacuous.
  • Run the live case once flake-ops#135 removes freki and geri from the roster; record the before/after directory state on this issue.
  • State on flake-ops#135 that the roster edit leaves entities behind, so the two land in order rather than by luck.

Refs

  • lar.ad/flake-ops#135, #169 — the ruling that drops Freki and Geri
  • lar.ad/flake-ops#306 — the identity spine; the a+ alias becomes a forge join key there
<!-- rationalization-current:2026-09-07 --> ## First required delivery Keep both directions of the existing set comparison together, plus the held-slot alert producer required by [Ting#2 Q2](https://jo.et0.pw/Ting/Ting/issues/2#issuecomment-15474). Prove a real persisted, addressed alert against flake-ops#470; detection can proceed before transport, but alert acceptance requires that real surface. Broader adapters move to [#55](https://jo.et0.pw/Ting/Vedanta/issues/55). The [approved rationalization](https://jo.et0.pw/Ting/Ting/issues/28) corrects the obsolete Jostoph-mutation and queen-mints-principal wording below without reopening the observation mandate. ## Shared vocabulary This record uses Proposal, Design, Tasks, and Specification Delta. ## Proposal ### Why Drift detection has had no concrete case to be accepted against. It has two now, and both arrived by themselves rather than being constructed. **Declared-removed but still present.** `sa-freki` and `sa-geri` are declared entities for personas that have been ruled removed. lar.ad/flake-ops#169 and #135 both state it: *"The Pack roster is Saga, Sköll, Ratatoskr, Völundr, Starkaðr, and Heiðrún. Freki and Geri are dropped; Hati remains a sub-agent."* Their SOULs were always placeholders — `modules/users/fenrisulfr/agents/*/SOUL.md` says *"SLOT, NOT VOICE … standing room"* — but their directory entities are real and reconciled today by `impl/forge-identity/kanidm`. Nothing in the declarative path can remove them, by construction: `nixops-kanidm`'s `provider.nu:27` states it outright — *"NEVER PRUNES, NEVER DESTROYS. Schema v0 has no destroy"* — and the trait is add-and-repair, so *"a persona removed from the roster keeps its entity — removal is a revocation, which is runtime business"*. **Declared but never present.** `sa-odin`'s membership of `agents` was in the roster, evaluated into this impl's `group_membership` member list, and had simply never reached the directory. This is the more expensive direction. A surviving entity is a revocation debt — bad, but inert; the wolves sat for weeks harming nothing. A missing membership is a live outage that presents four layers downstream: it surfaced as `curl: (22) ... 400` in a nushell overlay, and the body kanidm actually sent (`{"error":"access_denied"}`) was being discarded by the caller. Cost: an evening, and every persona token exchange refused throughout. ### Scope **Observation of drift across the identity plane, and an alert wherever Vedanta cannot resolve what it observed.** Not the roster edit itself (lar.ad/flake-ops#135), not entity creation, and not principal disposition — see the ruling below. ## Design **Owner ruling (2026-09-03), Q1 — the observation mandate.** Creation of permanent identities is the **provider's fief**, as previously ruled. The tension that leaves — something must still notice what the declarative path structurally cannot fix — is settled by a standing rule: > **Vedanta is in charge of observing ALL drift on the identity plane, and MUST raise an alert where resolution is out of her scope.** This is broader than the case that produced it. Consequences: - **Observation is unbounded within the identity plane; authority to act is not.** Vedanta observes every divergence between declared and actual identity state, in both directions, regardless of which component owns the fix. - **Detection stays here**, because Vedanta's reconcile projection already holds both sets — the roster as declaration, the directory as observation — and computing the comparison in either order is nearly free where the sets already are. - **Disposition routes out, unchanged.** Entity creation belongs to the provider. Jostoph independently audits and never rotates, disables, or retires a principal; declaration and lifecycle disposition follow the exact canonical provider/private-effector boundary. Vedanta may invalidate only exact resource-credential token IDs through its private validated command boundary. Observing drift confers no power to resolve it. - **The alert is a duty, not a courtesy.** Out-of-scope drift that Vedanta observes and does not raise is a defect in Vedanta, equal in kind to failing to detect it. Silent tolerance of a known divergence is the failure mode this rule exists to forbid. - **Both directions are in scope by construction.** Declared-but-absent is drift on the identity plane, so it is observed here rather than in a sibling record — settling Q2. The two directions are the same set comparison in opposite order; separate scenarios preserve the distinct failure classes. **The forge half is currently vacuous for the wolves, and acceptance must say so.** Neither `freki` nor `geri` has a Forgejo account — `GET /users/freki` and `GET /users/geri` both return nothing, and the only agent accounts on jo.et0.pw are `agent.odin`, `agent.omo`, `agent.teyla` and `codexo`. A test that passes because there was never anything to observe is not evidence that observation works. The forge half needs a subject that actually has an account. **Neither direction catches hand-minting**, worth recording: `sa-odin` was minted *by hand* during the 2026-08-23 incident rather than through the mint path, whose `kanidm-sa` step does *"service account + group membership"* as one act. Hand-minting is what split the account from its membership. Drift detection is the net; that hole is upstream of it. ## Specification Delta ### Requirement: drift is observed in both directions #### Scenario: the wolves leave the roster - GIVEN `sa-freki` and `sa-geri` exist in kanidm, carrying the `agents` group - AND the roster (`flake.users.*.agents`) no longer declares either - WHEN drift detection runs - THEN both are reported as drift, named individually - AND a second run reports them again, because Vedanta cannot resolve them #### Scenario: a declared membership never reached the directory - GIVEN the roster declares a membership - AND the directory does not carry it - WHEN drift detection runs - THEN it is reported as drift #### Scenario: a pool drone is not drift - GIVEN a provider-provisioned pool principal in the `agents` group whose deployed slot/name/UUID/grant projection is valid - WHEN drift detection runs - THEN it is NOT reported as undeclared-persona drift, because the exact deployed pool projection accounts for it; the queen does not create the principal - AND nothing acts on it ### Requirement: unresolvable drift is alerted, never silently tolerated #### Scenario: observed drift whose fix belongs to another component - GIVEN Vedanta observes drift it has no authority to resolve - WHEN detection completes - THEN an alert is raised naming the subject, the observed divergence, and the component that owns the resolution - AND Vedanta performs no mutation of its own - AND the alert repeats on subsequent runs while the drift persists ## Tasks - [ ] Detect entities in the `agents` group the roster does not declare, discriminating personas (`a+` alias) from pool drones (no alias). - [ ] Detect declared memberships absent from the directory — the outage-shaped direction. - [ ] Raise an alert for every observed drift whose resolution is out of Vedanta's scope, naming subject, divergence, and owning component. - [ ] Prove no detection path mutates a principal: no creation, no deletion, no rotation, no disabling. - [ ] Consume separately reviewed expansion from #55 when a named identity-plane adapter is ready; it does not block this first detector. - [ ] Choose a forge subject that has an account so the forge assertion is not vacuous. - [ ] Run the live case once `flake-ops#135` removes freki and geri from the roster; record the before/after directory state on this issue. - [ ] State on `flake-ops#135` that the roster edit leaves entities behind, so the two land in order rather than by luck. ## Refs - lar.ad/flake-ops#135, #169 — the ruling that drops Freki and Geri - lar.ad/flake-ops#306 — the identity spine; the `a+` alias becomes a forge join key there
Author
Owner

Second live case, from the opposite direction — 2026-08-24

This record's design says the drift is one-directional:

"The drift is one-directional and cheap to observe: the roster is the declaration, the directory is the observation, and anything in the second that is not in the first and carries the agents group is a candidate."

That catches declared-removed but still presentsa-freki, sa-geri. A live case arrived today from the mirror image: declared but never present.

sa-odin's membership of agents is in the roster, evaluates into this impl's group_membership member list, and had simply never reached the directory. Under this record's rule it is not a candidate, because the scan walks the directory side and asks what the roster does not declare. Both directions are the same set comparison in opposite order, and the reconciler already holds both sets.

The missing direction is the more expensive one. A surviving entity is a revocation debt — bad, but inert; this record's own case sat for weeks harming nothing. A missing membership is a live outage that presents four layers downstream: today it surfaced as curl: (22) ... 400 in a nushell overlay, and the body kanidm actually sent ({"error":"access_denied"}) was being discarded by the caller. Cost: an evening, and every persona token exchange refused throughout.

This record's acceptance would not catch it either. Both scenarios (the wolves leave the roster, a pool drone is not drift) assert on entities present in the directory. A third is wanted:

GIVEN the roster declares a membership AND the directory does not carry it
WHEN drift detection runs
THEN it is reported as drift

Unlike the forge half noted above, this one is not vacuous — there was a live subject today (now remediated by a heimdallr apply, so a re-run would need a fresh one or a fixture).

What neither direction catches, worth recording: sa-odin was minted by hand during the 2026-08-23 incident rather than through the mint path, whose kanidm-sa step does "service account + group membership" as one act. Hand-minting is what split the account from its membership. Drift detection is the net; the hole is upstream of it.

Whether this rides as a third scenario here or as a sibling issue is an owner call — same mechanism and code path, different failure class and urgency. Filed as a comment rather than a new record so the choice stays open.

(Filed from lar.ad/flake-ops's 2026-08-24 session; cross-forge, no edge created.)

## Second live case, from the opposite direction — 2026-08-24 This record's design says the drift is one-directional: > *"The drift is one-directional and cheap to observe: the roster is the declaration, the directory is the observation, and anything in the second that is not in the first and carries the `agents` group is a candidate."* That catches **declared-removed but still present** — `sa-freki`, `sa-geri`. A live case arrived today from the mirror image: **declared but never present.** `sa-odin`'s membership of `agents` is in the roster, evaluates into this impl's `group_membership` member list, and had simply never reached the directory. Under this record's rule it is not a candidate, because the scan walks the directory side and asks what the roster does not declare. Both directions are the same set comparison in opposite order, and the reconciler already holds both sets. **The missing direction is the more expensive one.** A surviving entity is a revocation debt — bad, but inert; this record's own case sat for weeks harming nothing. A missing membership is a live outage that presents four layers downstream: today it surfaced as `curl: (22) ... 400` in a nushell overlay, and the body kanidm actually sent (`{"error":"access_denied"}`) was being discarded by the caller. Cost: an evening, and every persona token exchange refused throughout. **This record's acceptance would not catch it either.** Both scenarios (`the wolves leave the roster`, `a pool drone is not drift`) assert on entities *present* in the directory. A third is wanted: > GIVEN the roster declares a membership AND the directory does not carry it > WHEN drift detection runs > THEN it is reported as drift Unlike the forge half noted above, this one is **not vacuous** — there was a live subject today (now remediated by a heimdallr apply, so a re-run would need a fresh one or a fixture). **What neither direction catches**, worth recording: `sa-odin` was minted *by hand* during the 2026-08-23 incident rather than through the mint path, whose `kanidm-sa` step does *"service account + group membership"* as one act. Hand-minting is what split the account from its membership. Drift detection is the net; the hole is upstream of it. Whether this rides as a third scenario here or as a sibling issue is an owner call — same mechanism and code path, different failure class and urgency. Filed as a comment rather than a new record so the choice stays open. *(Filed from lar.ad/flake-ops's 2026-08-24 session; cross-forge, no edge created.)*
Author
Owner

Grooming state: Need Grooming — investigation remains.
Evidence: Audit 2026-09-19/20 G48 and S6-F6. Split acceptance into two surfaces: detector acceptance covers both declared-versus-actual drift directions and no out-of-scope mutation; alert acceptance covers a persistent, addressed alert on lar.ad/flake-ops#470.
Gate: The alert half is gated on lar.ad/flake-ops#470, the external persistent alert plane. Keep the live persistent-alert check flagged as a probe until that surface or a capture double exists. The native cross-repo dependency attempt did not complete; retain flake-ops#470 as the explicit cross-repo gate in this comment.
Related: The detector can proceed independently; the existing Q1, Q2, and Q3 rulings remain the governing evidence.

<!-- agent.odin:groom:v1 --> **Grooming state:** Need Grooming — investigation remains. **Evidence:** Audit 2026-09-19/20 G48 and S6-F6. Split acceptance into two surfaces: detector acceptance covers both declared-versus-actual drift directions and no out-of-scope mutation; alert acceptance covers a persistent, addressed alert on lar.ad/flake-ops#470. **Gate:** The alert half is gated on lar.ad/flake-ops#470, the external persistent alert plane. Keep the live persistent-alert check flagged as a probe until that surface or a capture double exists. The native cross-repo dependency attempt did not complete; retain flake-ops#470 as the explicit cross-repo gate in this comment. **Related:** The detector can proceed independently; the existing Q1, Q2, and Q3 rulings remain the governing evidence.
larandar changed title from Drift detection has its first live case: sa-freki and sa-geri outlive the roster that declared them to Observe all drift on the identity plane, and alert what Vedanta cannot resolve 2026-09-03 16:56:40 +00:00
Sign in to join this conversation.
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.

Depends on
You do not have permission to read 1 dependency
Reference
Ting/Vedanta#11
No description provided.