Commit graph Idunn/docs/deployment-authority.md
Author SHA1 Message Date
c13e175bbf Steward brake: refuse a shared steward directory, name the proven owner, steward-only remedy
Binding validation refuses a steward store whose directory is shared with,
inside or around any other Idunn authority directory, since the steward owns
its directory and could unlink a root-owned store. The wait note names a
steward stop by the binding's steward user, not the record's claimed by. The
Unreadable hint for a steward store says only the owner uid can repair it.
Root-run tests skip with a message. Docs gain a provisioning runbook and the
sealed-binding recovery caveat.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 18:09:41 +00:00
c5a0ce3d23 Document the steward stop in deployment-authority
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 17:47:48 +00:00
b230703a5c Idunn: a stop halts before irreversible steps; fail-closed reader; break-glass clear
- Re-read the stop at the static flip intent and at entry into Fencing for a
  deploy (stop_note, the same path as Sealing). After the intent or fencing
  the transaction runs to its end; documented.
- Absent means NotFound in an existing directory; every other lookup error,
  a zero-length file and a missing parent fail closed. Unreadable names the
  idunn-provision deployment-brake-clear command.
- replace_stop is the operator's break-glass: it replaces v1, foreign,
  ambiguous, zero-length and caller-owned undecodable stores, never a symlink
  or another user's file.
- Mutation-proven tests: foreign-owner Clear, Unreadable admitting at
  Sealing, author from USER.
- docs: stop semantics, v6 downgrade hazard and backup; legacy script no
  longer requires the store and drops the signed-grant wording; two stale
  doc lines.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 17:36:57 +00:00
a20568ef70 Idunn: the deployment brake is a stop, not a per-deploy gate
Delete the signed release path (deployment_brake_release, the operator
enroll/export verbs, the anchor option and flag, the consumed-once scan,
the durable re-verification, DeploymentAuthorization::validate_shape).
src/brake.rs is the one reader and writer of the stop
(idunn.deployment_stop.v1: engaged|clear, by from the process uid, reason,
at, sequence). Absent or clear admits; a foreign, corrupt, symlinked,
other-runtime or foreign-owned store fails closed. advance_sealing stamps
deployment_admitted_at_unix_millis (key 38) for Deploy and Rollback alike,
so static releases and rollbacks use the same stop.

Control record v6: key 38 changes canonical bytes, so v5 gets a legacy
layout, a lift, boot migration and pinned bytes tests. Key 16 stays a
never-written legacy field so old terminal records decode.

idunn-provision: deployment-brake-engage|clear|status take --store,
--runtime-id, --reason only; status exits nonzero unless deployments admit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 17:24:34 +00:00
7ba27c362e Idunn: rollback drift after freezing, cross-binding refusal, and docs
Tests: a previous release that drifts after the rollback was frozen ends
FailedBeforeFencing at the Starting-time verify with current untouched (fails if
that verify is removed); rollback_source refuses a previous release deployed
under another release root or current link. Docs: CommandKind::Rollback needs no
schema change but an Idunn at 9f15cee or earlier cannot decode it (forward-only);
flipped_at ordering and the observed-link check; a rollback cannot be cancelled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 17:03:48 +00:00
01b6016193 Idunn: idunn rollback <target>, a brake-gated transaction flipping to the previous static release
CommandKind::Rollback (command ids rollback-*). The daemon freezes the command
into one transaction built like from_continuity: plan, sealed release and
installed release copied from the transaction that flipped the previous release;
no source, no build, source cleanup SkippedContinuity, no admitted generation.
It reaches the deployment brake and advance_static unchanged. The previous
release is derived from the newest Admitted static flip (control.cc + history.cc)
and the observed link, and refused as a rejected transaction when there is no
completed release, no previous release, current is not the newest flip, the
previous directory is gone, or an artifact digest drifted (new port method
verify_installed, also checked before any flip intent). Rollback counts as a
deploy for the busy and live-per-target checks. CLI: idunn rollback via
parse_up(kind); commit_deployment_command and submit take the kind.

Tests: control_plane::tests::static_rollback_ (7) and parse_rollback (1).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 16:55:43 +00:00
be98521120 Idunn: pin static redeploy and stuck-flip behavior; document both
Release ids are per transaction, so redeploying an unchanged tree with a changed
site file yields a new release (the test asserts distinct ids, the first release
keeping its carry, current carrying the new file); previous_release_id can never
equal release_id and no refusal is needed. A flip whose current link was replaced
by a directory after the intent retries visibly: last_error is shown by idunn
status as 'waiting on', cancel is refused, and the docs name the by-hand fix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 16:48:16 +00:00
d5ea779096 Idunn: static release transaction (flip in Starting) and the v5 control record
A static release is the same DeploymentTransaction in three phases: Sealing,
Starting (carry and flip, behind the deployment brake) and Complete. It has no
Expected, activation, workload, warming, isolation, fencing, leasing, ready or
routing, and creates no AdmittedGeneration.

DeploymentTransaction gains key 37 `flip` (FlipEvidence: release, previous
release, carry source, carried sha256, time). Adding a key changes the bytes of
every record, and the control store refuses a record that does not re-encode to
what is stored, so this is schema idunn.deployment_transaction.v5. The v4
layout is kept as LegacyDeploymentTransactionV4 and lifted by the existing
mechanism (read_transaction_record, history decode, and the boot migration,
which rewrites v4 records in control.cc to v5). Migration is forward-only: an
Idunn that predates v5 cannot read a migrated control.cc.

Also: is_static() and brake_subject() (the brake names the binding's runtime id
and the sealed release, read by deployment_authorization, durable-authority
validation and status); validate_static; advance_sealing skips readiness_class
and expected_projection for static; advance_starting delegates to
advance_static (intent with the previous release read from current_link,
carry_and_flip then record, observe link == release, then Complete with
Admitted{generation-<tx>} and post_commit_cleanup(SkippedNoIncumbent, source
Pending)); a failure in Starting takes the existing pre-fencing abort;
Engine.static_release Arc<dyn StaticReleasePort>. Docs: a Static release
section in deployment-authority.md and a scope line in
deploy-provenance-target.md.

Tests: control_plane::tests::static_release_ (3), a pinned v4 record lifts and
re-encodes as the pinned v5, a v4 control store copy in a temp dir migrates to
v5 and an already-v5 store is untouched byte for byte, and the pinned-bytes
test now pins v5 with the v4 bytes kept as a legacy fixture. The test runner is
a stand-in over the real seal_artifact behind a cfg(test) Engine seam, because
DockerRunnerDriver workspace ownership requires root.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 16:32:06 +00:00
Meta Vens
6e2a31cf31 Body map after reincarnation cut 2: two slots, take_target, forward retry, operator-required
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:40:55 +02:00
Meta Vens
9a0a6a2ce9 Body map after reincarnation cut 1; cut 1 installed on Yggdrasil
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:36:21 +02:00
Meta Vens
9e5b7f84fd Close the B1 transition: lift, precondition, boot reconciliation, closed history
Soul found B1's rule sound and its transition unsafe. This makes the rule's
old inputs impossible to reintroduce a stuck state from.

- The control store's legacy lift owns the transition. A pre-B1 continuity
  abort that issued an activation lifts with topology_reconciliation Pending,
  so the one resolution demotes its own activation. A record already terminal
  under the old rule is reopened at Starting, since a terminal record cannot
  owe work. History lifts records as written. Before this, one resident
  record of that shape failed validation and ControlSnapshot::read refused the
  whole store, so Idunn could not boot.
- Supervision owns the Expected-only precondition. When its pre-restart
  demotion fails it mints no continuity and writes next_restart_at_unix_millis
  on the generation's ContinuityBackoff (attempts and window untouched), so it
  neither restarts over an undemotable projection nor retries every tick.
- Boot reconciles once: a projected activation that exactly matches the
  activation a failed continuity in history (or a resident terminal record)
  issued is demoted. Nothing is adopted; an activation no admitted generation,
  live transaction or failed issuer accounts for is reported and left alone.
  New read-only CultCacheTopologyDriver::projected_activation.
- Unreadable history.cc fails closed. The continuity restart ceiling and the
  consumed-command check no longer read an empty history; they stop, and the
  fault is reported once while it lasts (ReportOnce). The undecodable-record
  report is a pure function, pinned.
- docs/deployment-authority.md no longer says the projection never drifts.

Tests: legacy fixtures written by the 9001b58 encoder for the three abort
shapes (a second throwaway generator, see the fixtures README), driven through
the Engine; the seeded-transaction helper now takes a command kind and an
incumbent, and a Deploy carries a frozen-source receipt and a signed brake
release, so Deploy commit, Deploy pre- and post-fence aborts and a continuity
abort over an incumbent run through the real phase machine.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 06:11:29 +02:00
Meta Vens
44277698c3 A continuity abort resolves its own projection (route continuity B1)
A continuity candidate shares the admitted Expected key. Both abort intents
marked its topology reconciliation Skipped, so a failed continuity left its
candidate activation projected under the admitted key while the admitted
receipt named the older one, and a later deploy's post-fence demotion refused
as substituted.

DeploymentTransaction::abort_topology_reconciliation is now the one rule for
both intents and pre-fence validation: a deployment owes the projection when it
published its Expected, a continuity owes it when it issued an activation.
reconcile_failed_candidate_projection is the one resolution for both abort
paths: a deployment withdraws its own key, a continuity demotes the shared key
to Expected-only with its own exact activation and never withdraws the
Expected. The post-fence incumbent demotion stays deployment-only and exact.

Tests read the persisted topology store after the production
publish/demote sequence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 01:30:19 +02:00
Meta Vens
87fa57dd8c Revert "Recover fenced rollback from current activation"
This reverts commit a148802cb6.

Rollback after fencing adopted whatever activation was projected under the
shared Expected key, so a continuity abort's leftover activation could be
demoted by a later deploy. The drift is closed at its source by the next
commit: a continuity abort demotes its own exact activation. Rollback demotes
the admitted generation's exact activation again.

The doc paragraph is dropped; the 9f00e7a paragraph above it stays until B4.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 01:30:09 +02:00
Meta Vens
865190611f Accept dynamic primary group in workload audit 2026-09-28 15:19:52 +02:00
Meta Vens
9f00e7ab84 Replay admitted provider receipts at admission time 2026-09-28 14:47:55 +02:00
Meta Vens
7a61c77420 Revert "Continue commit from refreshed topology receipt"
This reverts commit 55e5d559d6.
2026-09-28 14:20:26 +02:00
Meta Vens
55e5d559d6 Continue commit from refreshed topology receipt 2026-09-28 14:18:22 +02:00
Meta Vens
a148802cb6 Recover fenced rollback from current activation 2026-09-28 13:56:05 +02:00
Meta Vens
2eb25b6a85 Cut 0: kill the stale docs, verify transaction campaign
Deletes the two stale "Current implementation boundary" sections in
docs/deployment-authority.md and docs/authority-map.md; both described a
pre-drivers.rs era and were wrong (drivers.rs has driven Git, runners,
systemd, nginx, brakes, CultCache and CultMesh for weeks). The still-true
second paragraph of the deployment-authority.md section (private plan state
is never published as Odin topology) moves to the end of "Typed deployment
state", reworded present tense.

Fixes README.md's stale claims: the CLI has five commands (cancel and
validate were missing), and docs/guide.md plus deploy/legacy/ describe a
generation that no longer runs anywhere, not "still what runs on yggdrasil".

Documents present truth in "Candidate promotion": advance_sealing resolves,
freezes, runs every recipe step and installs before the deployment brake is
consulted at all. The brake only gates the move into Starting, the first
Verse-visible change.

Docs only, no build required.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 15:21:11 +02:00
Meta Vens
5ae006ca8b Doc: the route driver owns the stable endpoint's firewall allow
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 00:23:41 +02:00
Meta Vens
903e1c8e63 Key the projection by incarnation; publish Expected after the brake; consume commands once; add cancel
The projection kept one Expected/activation/lease slot per target. A
deployment's Sealing overwrote that slot with the candidate's Expected, the
incumbent failed activation-expected-projection on its next heartbeat and
exited, the deployment was aborted for having killed its incumbent, and the
command -- left resident after its transaction was retired to history --
read as queued and was frozen again. Odin cycled on that every few minutes.

- Topology driver records are keyed {target}@{expected sha}; the incumbent and
  the sealed candidate are projected side by side. One CAS mutation replaces
  the six bespoke loops. withdraw_incarnation removes only one incarnation;
  demote_to_expected_only replaces restore_admitted_expected_only and is used
  only for an incarnation whose process is gone. Legacy target-keyed records
  are retired on the first publish for that target.
- receive() takes the incarnation; the read-as-absence compensator for a
  correlation about another incarnation is gone.
- Candidate Expected is published as Starting's first step, after the brake
  admitted the transaction, not in Sealing before it.
- A pre-fencing abort withdraws only the candidate; a post-fencing abort also
  demotes the stopped incumbent.
- A consumed command is archived with its last transaction; status still
  answers for it from history.
- An engaged lifecycle brake stops continuity being minted instead of parking
  a transaction on the target.
- idunn cancel <command-id> retires a queued command as an operator refusal.

Verified with cargo test --lib in rust:1.95-bookworm on yggdrasil: 114 passed,
2 ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 22:22:46 +02:00
Meta Vens
e79ad7bc99 Make Idunn independently usable: unit files, docs, and a public entry point
Brings across everything an operator needs to run Idunn without reference to
Odin. deploy/ carries the worked systemd installation -- unit, operator
wrapper, and the narrow sudoers grant. docs/ carries the four documents that
were scattered across Odin's tree.

The README is new and is the public entry point the repo lacked: what Idunn
decides, how to build and run it, the authority model in the form doctrine
asks for, how to install it as a service, and where the deeper documents are.

Two corrections made while moving:

Requires=docker.service becomes Wants=. Deployment runners use Docker;
continuity actuation does not. A hard requirement lets a Docker outage stop
Idunn itself and take daemon survival down with it, which is precisely the
invariant Idunn exists to hold. This is live on yggdrasil today and should be
changed there too.

The guide still told operators to run from E:\Projects\Odin -- a drive that no
longer exists -- via npm wrappers that belonged to Odin. Paths generalised,
commands moved to direct cargo invocations, and a note added that the document
predates the extraction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:15:40 +02:00