diff --git a/deploy/legacy/README.md b/deploy/legacy/README.md index dd91a25..54bb919 100644 --- a/deploy/legacy/README.md +++ b/deploy/legacy/README.md @@ -4,11 +4,16 @@ 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 eleven targets, their repositories +in `idunn-yggdrasil.sudoers`. It hardcodes ten targets, their repositories and their admitted refs, then delegates to executable manifests under -`/srv/odin/deploy-manifests/`. It calls `idunn validate-release-authority` -and `idunn validate-minimum-source-revision` — subcommands the current binary -does not have. +`/srv/odin/deploy-manifests/`. 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 @@ -16,10 +21,10 @@ Everything this script does by convention, the current Idunn does by contract: | Legacy mechanism | Current owner | |---|---| -| `deployment_target_policy()` — a `case` over eleven target names | one operator binding per target, in `--bindings-dir` | +| `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` | `[repository] selection = "signed-release"`, per target | +| `requires_bifrost_authority` + `--release-authority-store` (retired with its only target) | `[repository] selection = "signed-release"`, per target | | `/srv/odin/deploy-manifests/` 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` | @@ -45,15 +50,26 @@ This is the real migration scope, read out of `deployment_target_policy()`: | `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` | **only** target requiring Bifrost release authority | +| ~~`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 eleven are pinned to a non-`main` branch. That is its own problem — the +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 diff --git a/deploy/legacy/idunn-yggdrasil b/deploy/legacy/idunn-yggdrasil index 1d83b52..26970c0 100644 --- a/deploy/legacy/idunn-yggdrasil +++ b/deploy/legacy/idunn-yggdrasil @@ -96,14 +96,12 @@ deployment_target_policy() { daemon_id=yggdrasil-odin repository_full_name=GameCult/Odin upstream_ref=refs/heads/codex/ygg-idunn-independent-bootstrap - requires_bifrost_authority=false manifest_takes_action=true ;; codex-connector) daemon_id=yggdrasil-codex-connector repository_full_name=GameCult/CodexConnector upstream_ref=refs/heads/codex/ghostlight-release-binding - requires_bifrost_authority=false minimum_source_revision_required=true manifest_takes_action=true ;; @@ -111,7 +109,6 @@ deployment_target_policy() { daemon_id=yggdrasil-ghostlight repository_full_name=GameCult/Ghostlight upstream_ref=refs/heads/codex/ghostlight-dungeon-mvp - requires_bifrost_authority=false minimum_source_revision_required=true manifest_takes_action=true ;; @@ -119,52 +116,39 @@ deployment_target_policy() { daemon_id=yggdrasil-voidbot repository_full_name=GameCult/VoidBot upstream_ref=refs/heads/main - requires_bifrost_authority=false manifest_takes_action=true ;; heimdall) daemon_id=yggdrasil-heimdall repository_full_name=GameCult/Heimdall upstream_ref=refs/heads/main - requires_bifrost_authority=false ;; epiphany-capstone-17) daemon_id=yggdrasil-epiphany-capstone-17 repository_full_name=GameCult/Epiphany upstream_ref=refs/heads/codex/epiphany-shakedown-live - requires_bifrost_authority=false manifest_takes_action=true ;; epiphany) daemon_id=yggdrasil-epiphany repository_full_name=GameCult/Epiphany upstream_ref=refs/heads/codex/epiphany-shakedown-live - requires_bifrost_authority=false manifest_takes_action=true ;; - bifrost-persona-feedback) - daemon_id=yggdrasil-bifrost-persona-feedback - repository_full_name=GameCult/Bifrost - upstream_ref=refs/heads/main - requires_bifrost_authority=true - ;; repixelizer) daemon_id=yggdrasil-repixelizer repository_full_name=GameCult/repixelizer upstream_ref=refs/heads/main - requires_bifrost_authority=false ;; streampixels) daemon_id=yggdrasil-streampixels repository_full_name=GameCult/StreamPixels upstream_ref=refs/heads/main - requires_bifrost_authority=false ;; gjallar) daemon_id=yggdrasil-gjallar repository_full_name=GameCult/Gjallar upstream_ref=refs/heads/codex/yggdrasil-aggregate-daemon - requires_bifrost_authority=false ;; *) echo "deployment target has no root actuator policy: $target" >&2; exit 1 ;; esac @@ -190,19 +174,10 @@ require_signed_deployment_grant() { *) echo "signed deployment request is not for root target $target" >&2; exit 1 ;; esac - case "$requires_bifrost_authority" in - true) - [ -n "$release_authority_id" ] || { echo "missing Bifrost release authority id" >&2; exit 1; } - case "$release_authority_sha256" in - *[!0-9a-f]*|'') echo "invalid or missing Bifrost release authority digest" >&2; exit 1 ;; - esac - [ "${#release_authority_sha256}" -eq 64 ] || { echo "Bifrost release authority digest must be 64 lowercase hex characters" >&2; exit 1; } - ;; - false) - [ -z "$release_authority_id" ] || { echo "source-observed release cannot claim Bifrost authority id" >&2; exit 1; } - [ -z "$release_authority_sha256" ] || { echo "source-observed release cannot claim Bifrost authority digest" >&2; exit 1; } - ;; - esac + # No target carries Bifrost release authority: its only one, bifrost-persona-feedback, + # was retired on 2026-09-30. A request that claims one is refused. + [ -z "$release_authority_id" ] || { echo "source-observed release cannot claim Bifrost authority id" >&2; exit 1; } + [ -z "$release_authority_sha256" ] || { echo "source-observed release cannot claim Bifrost authority digest" >&2; exit 1; } require_root_owned_resolved_executable "$idunn_provision" require_root_owned_regular_path "$brake_store" @@ -234,18 +209,6 @@ require_signed_deployment_grant() { --daemon "$daemon_id" \ --source-revision "$source_commit" fi - - if [ "$requires_bifrost_authority" = true ]; then - require_root_owned_resolved_executable /usr/local/bin/idunn - /usr/local/bin/idunn validate-release-authority \ - --store /var/lib/gamecult/idunn/yggdrasil.keepalive.cc \ - --release-authority-store /srv/bifrost/state/repository-release-authority.cc \ - --repository-full-name "$repository_full_name" \ - --upstream-ref "$upstream_ref" \ - --source-revision "$source_commit" \ - --release-authority-id "$release_authority_id" \ - --release-authority-sha256 "$release_authority_sha256" - fi } manifest_group_exists() { @@ -355,7 +318,7 @@ case "$action:$target" in run_manifest_while_holding_authority_locks "$manifest" restart exit $? ;; - deploy:voidbot|deploy:epiphany|deploy:epiphany-capstone-17|deploy:odin|deploy:codex-connector|deploy:ghostlight|deploy:heimdall|deploy:repixelizer|deploy:streampixels|deploy:bifrost-persona-feedback|deploy:gjallar) + deploy:voidbot|deploy:epiphany|deploy:epiphany-capstone-17|deploy:odin|deploy:codex-connector|deploy:ghostlight|deploy:heimdall|deploy:repixelizer|deploy:streampixels|deploy:gjallar) manifest="/srv/odin/deploy-manifests/$target" require_root_owned_regular_path "$manifest" [ -x "$manifest" ] || { echo "root actuator manifest is not executable: $manifest" >&2; exit 1; } @@ -368,7 +331,8 @@ case "$action:$target" in export BIFROST_RELEASE_AUTHORITY_ID="$release_authority_id" export BIFROST_RELEASE_AUTHORITY_SHA256="$release_authority_sha256" export IDUNN_DEPLOYMENT_REQUEST_ID="$deployment_request_id" - export IDUNN_REQUIRES_BIFROST_AUTHORITY="$requires_bifrost_authority" + # Installed manifests (Epiphany's) still require this to read false. + export IDUNN_REQUIRES_BIFROST_AUTHORITY=false if [ "$manifest_takes_action" = true ]; then run_manifest_while_holding_authority_locks "$manifest" "$action" exit $? diff --git a/deploy/legacy/idunn-yggdrasil.sudoers b/deploy/legacy/idunn-yggdrasil.sudoers index 8872c25..a855527 100644 --- a/deploy/legacy/idunn-yggdrasil.sudoers +++ b/deploy/legacy/idunn-yggdrasil.sudoers @@ -1,7 +1,7 @@ # Narrow root actuator grant for the Idunn Yggdrasil deployment/continuity path. -# One line per live target. codex-connector and gjallar were retired 2026-09-06; -# their grants are removed here first, because the privileged half of a retired -# target must not outlive its consumer. +# One line per live target. codex-connector and gjallar were retired 2026-09-06, +# and bifrost-persona-feedback on 2026-09-30; their grants are removed here first, +# because the privileged half of a retired target must not outlive its consumer. Cmnd_Alias IDUNN_YGGDRASIL_DEPLOY = \ /usr/local/libexec/idunn-yggdrasil deploy odin *, \ /usr/local/libexec/idunn-yggdrasil deploy ghostlight *, \ @@ -9,7 +9,6 @@ Cmnd_Alias IDUNN_YGGDRASIL_DEPLOY = \ /usr/local/libexec/idunn-yggdrasil deploy heimdall *, \ /usr/local/libexec/idunn-yggdrasil deploy epiphany-capstone-17 *, \ /usr/local/libexec/idunn-yggdrasil deploy epiphany *, \ - /usr/local/libexec/idunn-yggdrasil deploy bifrost-persona-feedback *, \ /usr/local/libexec/idunn-yggdrasil deploy repixelizer *, \ /usr/local/libexec/idunn-yggdrasil deploy streampixels * Cmnd_Alias IDUNN_YGGDRASIL_RESTART = \