Idunn/deploy/legacy/README.md

78 lines
4.5 KiB
Markdown
Raw Normal View History

Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
# Legacy actuator — the generation currently installed on yggdrasil
**These files are superseded. Do not use them for a new installation.** They are
kept because they are what is *running*, and a migration needs both sides.
`idunn-yggdrasil` is a root shell actuator reached through the narrow sudo grant
in `idunn-yggdrasil.sudoers`. It hardcodes ten targets, their repositories
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
and their admitted refs, then delegates to executable manifests under
`/srv/odin/deploy-manifests/<target>`. It calls `idunn validate-minimum-source-revision`,
a subcommand the current binary does not have.
Checked on yggdrasil 2026-09-30: `/usr/local/libexec/idunn-yggdrasil` is installed
and matches this file byte for byte (before the retirement below), but
`idunn-yggdrasil.sudoers` is not installed. `sudo -l -U idunn` says the `idunn`
user may not run sudo, so the daemon cannot reach this actuator; only an
operator running it as root can.
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
## What replaced it
Everything this script does by convention, the current Idunn does by contract:
| Legacy mechanism | Current owner |
|---|---|
| `deployment_target_policy()` — a `case` over ten target names | one operator binding per target, in `--bindings-dir` |
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
| `upstream_ref=refs/heads/...` in shell | `[repository] admitted_ref` |
| `minimum_source_revision_required` | `[repository] minimum_revision` |
| `requires_bifrost_authority` + `--release-authority-store` (retired with its only target) | `[repository] selection = "signed-release"`, per target |
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
| `/srv/odin/deploy-manifests/<target>` shell scripts | recipe `[[steps]]` in the target's own repository |
| `run_manifest_while_holding_authority_locks` | the deployment transaction and process write-lease |
| `flock` on a brake store | `[brakes] deployment_store` and `lifecycle_store` |
| root shell with `IDUNN_ACTUATOR=1` | `systemd-transient` workload driver under `DynamicUser` |
The structural difference is not tidiness. The legacy path admits a deployment
by *validating arguments to a root shell script*, and everything after that
validation is unverified shell running as root. The current path seals exact
source and artifacts, admits one incarnation against typed operator authority,
and hands execution to systemd — with no place to pass a command string,
enforced by `cli_exposes_only_declarative_commands`.
## The target inventory, which is still useful
This is the real migration scope, read out of `deployment_target_policy()`:
| Target | Repository | Admitted ref | Notes |
|---|---|---|---|
| `odin` | GameCult/Odin | `codex/ygg-idunn-independent-bootstrap` | ref is a branch, not main |
| `codex-connector` | GameCult/CodexConnector | `codex/ghostlight-release-binding` | minimum revision enforced |
| `ghostlight` | GameCult/Ghostlight | `codex/ghostlight-dungeon-mvp` | minimum revision enforced |
| `voidbot` | GameCult/VoidBot | `main` | |
| `heimdall` | GameCult/Heimdall | `main` | restart is `docker compose restart` |
| `epiphany` | GameCult/Epiphany | `codex/epiphany-shakedown-live` | |
| `epiphany-capstone-17` | GameCult/Epiphany | `codex/epiphany-shakedown-live` | second target, same repo |
| ~~`bifrost-persona-feedback`~~ | GameCult/Bifrost | `main` | **retired 2026-09-30**, see below |
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
| `repixelizer` | GameCult/repixelizer | `main` | |
| `streampixels` | GameCult/StreamPixels | `main` | two units, one target |
| `gjallar` | GameCult/Gjallar | `codex/yggdrasil-aggregate-daemon` | |
Seven of the ten live targets are pinned to a non-`main` branch. That is its own problem — the
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
swarm goal is everyone on `main` and current with each other — and migrating a
target is the moment to fix its ref, not to copy it forward.
## Retired targets
- `bifrost-persona-feedback`, 2026-09-30. Its manifest ran the Bifrost compose lane
(`docker compose up persona-feedback`), while the live provider is the native
`bifrost-persona-feedback.service`, so firing it would have started a container
beside the native unit. The operator ruled "delete the Bifrost lane". Its grant,
dispatch case and the Bifrost release-authority check it alone used are gone; a
deploy request that claims a Bifrost authority is refused. It was the only
caller of `idunn validate-release-authority`. Rebinding persona-feedback to a
current Idunn binding is a separate follow-up.
Make the public entry point describe the binary that actually ships The README and unit file I wrote at extraction documented the previous generation's CLI. Running the deployed ExecStart against a build of this repository returns `unknown Idunn command "--swarm-profile"`: commit 330e4cd rebuilt Idunn around typed authority ports and a test now asserts that --deploy-command, --restart-command and --swarm-profile are rejected. Anyone following the README would have installed a unit that crashloops on exec every 5 seconds under Restart=always. Corrected against the binary, not against the old docs: - deploy/idunn-yggdrasil.service now invokes `idunn serve` with the real flags. systemd-analyze verify passes on yggdrasil. /var/lib/gamecult/odin moved from ReadWritePaths to ReadOnlyPaths -- Idunn only reads Odin's topology correlation (pull_all_read_only_snapshot, absent store yields None), and it gates deployment promotion only: continuity never consults it, per stale_odin_provider_receipt_cannot_gate_continuity_restart. - README documents the three declarative commands, and adds a section on the recipe/binding split, which is the security model: capability lives in the target's repo, privilege lives in the host binding, and a binding that omits an affordance the recipe derives fails admit(). - Corrected two inherited claims. The distinct-brake check is per-binding validation ("deployment and lifecycle brakes are identical"), not a startup check. And the "inputs: nothing else" claim was false -- the Odin correlation read is a real, deliberate exception and now says so. The legacy actuator moves to deploy/legacy/ rather than being deleted: it is what is installed and running, so it is the before-side of the migration. Its README maps each shell mechanism to the contract that replaced it, and preserves the eleven-target inventory read out of deployment_target_policy(). Seven of the eleven are pinned to a codex/ branch rather than main. docs/guide.md is marked as the previous generation rather than silently carrying dead commands, and docs/migration.md is new: per-target order, what to fix before carrying a ref forward, and why the authority cuts over last. Also fixes a broken cross-link in authority-map.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:15:46 +02:00
## When these files die
When every target above has a binding and a recipe, and `idunn serve` owns
actuation on yggdrasil. At that point delete the sudoers grant first, then the
script, then `/srv/odin/deploy-manifests/`. The grant is the privileged half; it
should not outlive its consumer.