# Bento deploy units `bentod` runs as a systemd **user** service on fw13 (the x86_64 build gate). Unlike `sandod` (a hardened system service with a dedicated `sando` user), bentod is a user service under the operator: building the apps needs the operator's SSH keys (to the tailnet build hosts and the mbp ops-agent), the app checkouts under `~/Code/Apps`, and the `_private` layer for signing secrets (`secrets_root`). A dedicated system user can't reach those without copying keys and bind-mounting home — so a user service is the right model, and it lets the operator redeploy bentod with `systemctl --user restart` (no sudo). ## Files | File | Where it goes | Purpose | |------|---------------|---------| | `bentod.service` | `~/.config/systemd/user/` | The user service unit. | | `bento-daemon.toml.example` | `~/.config/bento/bento-daemon.toml` | Daemon-local config (paths + listen). Absolute paths only — no shell expansion. | | `bento.toml.example` | `~/.config/bento/bento.toml` | Build topology (hosts + apps). `repo` paths are tilde-expanded. | ## Stand-up (no root except enable-linger) ```sh # 1. Build + install the binary cd ~/Code/MNW/bento/daemon && cargo build --release install -D -m 0755 target/release/bentod ~/.local/bin/bentod # 2. Config + state dirs mkdir -p ~/.config/bento ~/.local/state/bento/logs ~/Dist install -m 0644 ~/Code/MNW/bento/deploy/bento-daemon.toml.example ~/.config/bento/bento-daemon.toml install -m 0644 ~/Code/MNW/bento/deploy/bento.toml.example ~/.config/bento/bento.toml # (edit the two configs: absolute home paths, real host/app rows) # 3. Service mkdir -p ~/.config/systemd/user install -m 0644 ~/Code/MNW/bento/deploy/bentod.service ~/.config/systemd/user/ loginctl enable-linger "$USER" systemctl --user daemon-reload systemctl --user enable --now bentod # 4. Verify curl -s http://127.0.0.1:8765/state | python3 -m json.tool ``` ## Auth (CF2) On the loopback bind above, build triggers are reachable only from fw13, so no token is required. To operate bentod over the tailnet, bind the tailnet IP in `bento-daemon.toml` and set `BENTO_API_TOKEN` via an `EnvironmentFile` in the unit — bentod refuses to start on a non-loopback bind without it (same posture as Sando's `SANDO_API_TOKEN`). ## Recipes A real `/build` reads `//.rhai` from each app's checkout. The recipes now exist, one per platform per app: | App | macOS | iOS | Linux | Windows | |-----|-------|-----|-------|---------| | goingson | yes | yes | yes | yes (unsigned, unsupported) | | audiofiles | yes | — (egui, no iOS) | yes | yes (unsigned, unsupported) | | balanced_breakfast | pending¹ | pending¹ | yes | yes (unsigned, unsupported) | ¹ BB has no Developer ID signing / notarization infra or iOS project yet, so its macOS/iOS recipes are deferred until that lands. BB ships in a later wave. A `linux.rhai` serves both arches — `build_host()` resolves to the native host the topology assigns the target (`linux/x86_64` → fw13, `linux/aarch64` → astra), so there is no cross-compilation and no hard-coded host name. macOS/iOS recipes run on the mbp `agent`-transport host where the Developer ID key is usable. ### Recipe host-function vocabulary Beyond `step`/`sh`/`sh_ok`/`log`/`collect`/`publish`/`secret`/`env` and the macOS helpers (`codesign`/`notarize`/`staple`/`verify_gatekeeper`/`keychain_*`), recipes can read their own context: | Function | Returns | |----------|---------| | `version()` | the version being built (e.g. `"0.4.2"`) | | `build_host()` | the host this target builds on (e.g. `"fw13"`) | | `repo()` | the app's checkout path (`~`-prefixed; `cd` into it — commands don't auto-cd) | | `target()` / `platform()` / `arch()` | `"linux/x86_64"` / `"linux"` / `"x86_64"` | Non-Tauri apps (audiofiles) set `version_path` in the topology to the crate `Cargo.toml` carrying the version, since they have no `tauri.conf.json`.