Execute runtime credentials for declared identity promotion #18

Closed
opened 2026-08-26 10:24:02 +00:00 by agent.odin · 2 comments
Owner

Proposal

Vedanta's optional runtime executor for a promotion that flake-ops has already declared and a NixOps4 provider has already applied: mint the workflow-time credential that reflects the new authority.

Delivery class

agent-unit — a gated runtime path with capture doubles. It reuses src/kanidm.rs's bounded mutations rather than growing new ones.

Design

This record is a duplicate, deliberately, and must never be the only implementation. The provider (nixops4-providers#25) is the required path for declared identity and role state. What Vedanta adds is workflow-time immediacy: a running workflow that needs a credential reflecting a promotion should not wait for the next apply.

Gated twice, and the second gate is the load-bearing one. It acts only on a declared promotion, and only after the provider-observed projection shows it applied. Gating on the declaration alone would make Vedanta decide what the declaration meant — which is the provider's job — and would let a runtime credential exist for an authority the directory has not actually granted.

Bounded to credentials. It invalidates the prior short-lived workflow JWT and mints a fresh one carrying only the declared promoted authority. It creates no directory state, changes no role, and touches no durable principal — those are the provider's, and Vedanta's law forbids it either way.

Not the pool's path. This is a durable stable identity being promoted, not a pool slot being allocated. src/pool.rs and src/checkout.rs are not involved.

Tasks

  • Verify the declaration exists and the provider-observed projection shows it applied; refuse otherwise.
  • Invalidate the prior short-lived workflow JWT by exact token ID.
  • Mint a fresh JWT carrying only the declared promoted authority.
  • Prove no directory state is created, changed, or removed on this path.
  • Prove a declaration without an observed projection is refused, and says why.

Specification Delta

Requirement: a runtime promotion follows the provider, never leads it

Scenario: the declaration exists but the provider has not converged

  • GIVEN a declared promotion whose projection does not yet show it applied
  • WHEN the runtime endpoint is called
  • THEN it refuses
  • AND no credential is minted or invalidated

Requirement: the runtime path touches credentials only

Scenario: a runtime promotion succeeds

  • GIVEN a declared and observed promotion
  • WHEN the runtime endpoint acts
  • THEN the prior workflow JWT is invalidated and a fresh one minted with the declared authority
  • AND no directory entity, role, or membership is altered

OpenSpec

OpenSpec 85

Structural parent

Ting/Ting#86 — Epic: promotion.

## Proposal Vedanta's optional runtime executor for a promotion that flake-ops has already declared and a NixOps4 provider has already applied: mint the workflow-time credential that reflects the new authority. ## Delivery class `agent-unit` — a gated runtime path with capture doubles. It reuses `src/kanidm.rs`'s bounded mutations rather than growing new ones. ## Design **This record is a duplicate, deliberately, and must never be the only implementation.** The provider ([nixops4-providers#25](https://jo.et0.pw/lar.ad/nixops4-providers/issues/25)) is the required path for declared identity and role state. What Vedanta adds is workflow-time immediacy: a running workflow that needs a credential reflecting a promotion should not wait for the next apply. **Gated twice, and the second gate is the load-bearing one.** It acts only on a declared promotion, and only after the **provider-observed projection** shows it applied. Gating on the declaration alone would make Vedanta decide what the declaration meant — which is the provider's job — and would let a runtime credential exist for an authority the directory has not actually granted. **Bounded to credentials.** It invalidates the prior short-lived workflow JWT and mints a fresh one carrying only the declared promoted authority. It creates no directory state, changes no role, and touches no durable principal — those are the provider's, and Vedanta's law forbids it either way. **Not the pool's path.** This is a durable stable identity being promoted, not a pool slot being allocated. `src/pool.rs` and `src/checkout.rs` are not involved. ## Tasks - [ ] Verify the declaration exists and the provider-observed projection shows it applied; refuse otherwise. - [ ] Invalidate the prior short-lived workflow JWT by exact token ID. - [ ] Mint a fresh JWT carrying only the declared promoted authority. - [ ] Prove no directory state is created, changed, or removed on this path. - [ ] Prove a declaration without an observed projection is refused, and says why. ## Specification Delta ### Requirement: a runtime promotion follows the provider, never leads it #### Scenario: the declaration exists but the provider has not converged - **GIVEN** a declared promotion whose projection does not yet show it applied - **WHEN** the runtime endpoint is called - **THEN** it refuses - **AND** no credential is minted or invalidated ### Requirement: the runtime path touches credentials only #### Scenario: a runtime promotion succeeds - **GIVEN** a declared and observed promotion - **WHEN** the runtime endpoint acts - **THEN** the prior workflow JWT is invalidated and a fresh one minted with the declared authority - **AND** no directory entity, role, or membership is altered ## OpenSpec [OpenSpec 85](https://jo.et0.pw/Ting/Ting/wiki/OpenSpec-85-stable-identity-promotion-and-suppression) ## Structural parent [Ting/Ting#86](https://jo.et0.pw/Ting/Ting/issues/86) — Epic: promotion.
Owner

Re-pointed by the 2026-09-04 restructure of the promotion path.

This record is now a Deliverable under Ting/Ting#86, not a Session. The Meta/Session label is removed.

Three freeholder rulings moved it here:

  • Q1 — the Campaign left Ting/Vedanta for the estate tracker (Ting/Ting#85). It spans three repositories and Vedanta holds the optional half.
  • Q2 — one OpenSpec package covers both promotion and suppression: OpenSpec 85. They share one identity spine and two packages would let the copies drift.
  • Q3 — the Session tier is dissolved. Every node here carried two or three tasks and no Specification Delta, so none of them was a coherent bundle of two or more Deliverables. A Session re-forms only where OpenSpec 85's derived leaves show one within a single repository.

What to build against: OpenSpec 85, not this body. This record predates the package and has not yet been refined to project it — that refinement is the next pass, and until it runs the body here is provenance rather than instruction.

**Re-pointed by the 2026-09-04 restructure of the promotion path.** This record is now a **Deliverable** under [Ting/Ting#86](https://jo.et0.pw/Ting/Ting/issues/86), not a Session. The `Meta/Session` label is removed. Three freeholder rulings moved it here: - **Q1** — the Campaign left `Ting/Vedanta` for the estate tracker ([Ting/Ting#85](https://jo.et0.pw/Ting/Ting/issues/85)). It spans three repositories and Vedanta holds the optional half. - **Q2** — one OpenSpec package covers both promotion and suppression: [OpenSpec 85](https://jo.et0.pw/Ting/Ting/wiki/OpenSpec-85-stable-identity-promotion-and-suppression). They share one identity spine and two packages would let the copies drift. - **Q3** — the Session tier is dissolved. Every node here carried two or three tasks and no Specification Delta, so none of them was a coherent bundle of two or more Deliverables. A Session re-forms only where OpenSpec 85's derived leaves show one **within a single repository**. **What to build against:** OpenSpec 85, not this body. This record predates the package and has not yet been refined to project it — that refinement is the next pass, and until it runs the body here is provenance rather than instruction.
Owner

Refinement against OpenSpec 85 — the pass this record was told to wait for

The body predates the package. This projects it, and answers the one question the body leaves open.

Q1 — what "the provider-observed projection" is, concretely

The body gates on it twice and never says how Vedanta reads it. It cannot read nixops4's state: that file lives on whichever operator's machine ran the apply, and Vedanta is a service on another host.

Proposed: the gate is what the DIRECTORY shows, read by Vedanta itself.

This is not a substitute for the provider's projection — it is it. nixops-kanidm publishes outputs read back from kanidm after the write, never the declared values (nixops4-providers#44 states that as its own rule, for exactly this reason: an output that echoed the declaration would report success for a write the directory refused). So "what the provider observed" and "what the directory holds" are the same fact by construction, and only one of the two requires coupling Vedanta to a deployment tool it does not run.

It is also the stronger gate. A state file records what a tool believed at apply time; the directory is the thing authority is actually read from. If they disagree, the directory is right and the state file is drift — which is #11's to notice, not this endpoint's to trust.

Concretely: the promotion is observed when the declared uuid is a member of the declared role group, read from kanidm at call time.

Q2 — what the gate is NOT

Not "a declaration exists". Not "an apply ran". Membership, now, for that uuid. A declaration the provider has not converged leaves the directory unchanged, and this endpoint must refuse against the directory rather than against a record of intent.

Unchanged

Bounded to credentials; no directory entity, role, or membership altered; not the pool's path. Those hold as written.

Delivery

Building with #19 in one PR — Vedanta's law is one PR per session, and the two share the gate. Two PRs would carry two copies of it.

## Refinement against OpenSpec 85 — the pass this record was told to wait for The body predates the package. This projects it, and answers the one question the body leaves open. ### Q1 — what "the provider-observed projection" is, concretely The body gates on it twice and never says how Vedanta reads it. It cannot read nixops4's state: that file lives on whichever operator's machine ran the apply, and Vedanta is a service on another host. **Proposed: the gate is what the DIRECTORY shows, read by Vedanta itself.** This is not a substitute for the provider's projection — it *is* it. `nixops-kanidm` publishes outputs read back from kanidm after the write, never the declared values ([nixops4-providers#44](https://jo.et0.pw/lar.ad/nixops4-providers/pulls/44) states that as its own rule, for exactly this reason: an output that echoed the declaration would report success for a write the directory refused). So "what the provider observed" and "what the directory holds" are the same fact by construction, and only one of the two requires coupling Vedanta to a deployment tool it does not run. It is also the stronger gate. A state file records what a tool believed at apply time; the directory is the thing authority is actually read from. If they disagree, the directory is right and the state file is drift — which is [#11](https://jo.et0.pw/Ting/Vedanta/issues/11)'s to notice, not this endpoint's to trust. **Concretely:** the promotion is observed when the declared uuid is a member of the declared role group, read from kanidm at call time. ### Q2 — what the gate is NOT Not "a declaration exists". Not "an apply ran". Membership, now, for that uuid. A declaration the provider has not converged leaves the directory unchanged, and this endpoint must refuse against the directory rather than against a record of intent. ### Unchanged Bounded to credentials; no directory entity, role, or membership altered; not the pool's path. Those hold as written. ### Delivery Building with [#19](https://jo.et0.pw/Ting/Vedanta/issues/19) in **one PR** — Vedanta's law is one PR per session, and the two share the gate. Two PRs would carry two copies of it.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

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