# Deploying pom pom ships through **Bento**, not Sando, and not by hand. Sando cannot do this job. `sando-daemon.toml` sets `build_host = "fw13"` and `build::run` refuses to compile anywhere else, which is the never-build-on-prod invariant and also makes Sando single-architecture. pom runs on astra (aarch64) and on the Hetzner box (x86_64), so Sando could never build half of this release without breaking its own rule. Bento already fans out native builds across both hosts, so pom is a Bento recipe. `deploy.sh` is gone. It cross-compiled with `cargo zigbuild` (against the native-per-architecture rule, with a cargo-zigbuild that is not installed on fw13), reached Hetzner on port 2200 which refuses this machine's key, and copied config and the unit file over the live ones with no diff and no backup. It did not work as written when 0.4.0 went out; that deploy was done by hand. ## Running a deploy Bump the version in `Cargo.toml`, commit, tag `v`, push, then trigger the build through Bento for both targets. The `/deploy` skill has the procedure. What the recipe does, per target (`dist/recipes/linux.rhai`): 1. Pin the checkout to the release tag, and check both hosts report one commit. 2. `cargo clippy -D warnings` and `cargo test`, on that target's own build host. 3. `cargo build --release`. 4. Compare the binary's highest `GLIBC_` symbol against the service host's `ldd --version`, and assert `pom --version` matches the tag. 5. Stage the binary on the service host and call the privileged installer. 6. Poll `/api/health` until the restarted instance answers, then assert the running binary reports the version that was just installed. The two instances go one at a time, and `require_all_targets` keeps the release from counting as done until both are green. pom watches its own deploy, so the restart takes one watcher down for a moment; the other stays up and keeps watching. Never both at once. ## What a deploy does not touch **Config.** `pom-astra.toml` and `pom-hetzner.toml` differ per instance, and the live config on prod once carried a `[targets.mnw.tests]` block this repo did not have. `deploy.sh` would have silently deleted it. Config is a separate, deliberate act; the installer refuses to go near it. **The unit file.** Same reasoning. A hardened unit that has drifted from the repo is a question for a human, not something a binary deploy overwrites. ## One-time host setup Each host that runs pom needs the installer and its scoped sudo grant: ``` sudo install -d /usr/local/lib/bento sudo install -m 0755 install-service.sh /usr/local/lib/bento/install-service.sh sudo install -m 0440 bento-deploy.sudoers /etc/sudoers.d/bento-deploy sudo visudo -cf /etc/sudoers.d/bento-deploy ``` Edit the sudoers file first so the user matches how Bento reaches that host: `max` on astra, `root` over Tailscale SSH on the Hetzner box. The grant covers one script and nothing else; the script bounds its own arguments (source under `/var/tmp/bento-deploy`, destination under `/usr/local/bin`, a bare `*.service` unit), so it is a script-guarded grant rather than a broad `install` + `systemctl` one. ## Rollback The installer keeps the previous binary as `.prev`. Rolling back is putting it back and restarting: ``` sudo install -m 0755 /usr/local/bin/pom.prev /usr/local/bin/pom sudo systemctl restart pom.service ```