The operator ruled "delete the Bifrost lane". This route was that lane's live trigger: /srv/odin/deploy-manifests/bifrost-persona-feedback runs `docker compose up persona-feedback`, and the provider actually running on Yggdrasil is the native bifrost-persona-feedback.service. Firing the route would start a container next to it. Following deploy/legacy's own order, the grant goes first, then the dispatch: - sudoers: drop `deploy bifrost-persona-feedback *`. - idunn-yggdrasil: drop the target's policy case and its dispatch token, plus the Bifrost release-authority check that only it used (requires_bifrost_authority and the `idunn validate-release-authority` call). A deploy request that claims a Bifrost authority is still refused. The manifest environment keeps IDUNN_REQUIRES_BIFROST_AUTHORITY=false, because the installed Epiphany manifest checks for it. - README: record the retirement and the counts. Also record that on 2026-09-30 the installed actuator matched this file, but the sudoers grant was not installed: the idunn user cannot sudo. Rebinding persona-feedback to a current Idunn binding is a separate follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78 lines
4.5 KiB
Markdown
78 lines
4.5 KiB
Markdown
# 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
|
|
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.
|
|
|
|
## 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` |
|
|
| `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 |
|
|
| `/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 |
|
|
| `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
|
|
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.
|
|
|
|
## 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.
|