max / makenotwork
4 files changed,
+172 insertions,
-0 deletions
| @@ -0,0 +1,57 @@ | |||
| 1 | + | # Bento deploy units | |
| 2 | + | ||
| 3 | + | `bentod` runs as a systemd **user** service on fw13 (the x86_64 build gate). | |
| 4 | + | Unlike `sandod` (a hardened system service with a dedicated `sando` user), | |
| 5 | + | bentod is a user service under the operator: building the apps needs the | |
| 6 | + | operator's SSH keys (to the tailnet build hosts and the mbp ops-agent), the app | |
| 7 | + | checkouts under `~/Code/Apps`, and the `_private` layer for signing secrets | |
| 8 | + | (`secrets_root`). A dedicated system user can't reach those without copying keys | |
| 9 | + | and bind-mounting home — so a user service is the right model, and it lets the | |
| 10 | + | operator redeploy bentod with `systemctl --user restart` (no sudo). | |
| 11 | + | ||
| 12 | + | ## Files | |
| 13 | + | ||
| 14 | + | | File | Where it goes | Purpose | | |
| 15 | + | |------|---------------|---------| | |
| 16 | + | | `bentod.service` | `~/.config/systemd/user/` | The user service unit. | | |
| 17 | + | | `bento-daemon.toml.example` | `~/.config/bento/bento-daemon.toml` | Daemon-local config (paths + listen). Absolute paths only — no shell expansion. | | |
| 18 | + | | `bento.toml.example` | `~/.config/bento/bento.toml` | Build topology (hosts + apps). `repo` paths are tilde-expanded. | | |
| 19 | + | ||
| 20 | + | ## Stand-up (no root except enable-linger) | |
| 21 | + | ||
| 22 | + | ```sh | |
| 23 | + | # 1. Build + install the binary | |
| 24 | + | cd ~/Code/MNW/bento/daemon && cargo build --release | |
| 25 | + | install -D -m 0755 target/release/bentod ~/.local/bin/bentod | |
| 26 | + | ||
| 27 | + | # 2. Config + state dirs | |
| 28 | + | mkdir -p ~/.config/bento ~/.local/state/bento/logs ~/Dist | |
| 29 | + | install -m 0644 ~/Code/MNW/bento/deploy/bento-daemon.toml.example ~/.config/bento/bento-daemon.toml | |
| 30 | + | install -m 0644 ~/Code/MNW/bento/deploy/bento.toml.example ~/.config/bento/bento.toml | |
| 31 | + | # (edit the two configs: absolute home paths, real host/app rows) | |
| 32 | + | ||
| 33 | + | # 3. Service | |
| 34 | + | mkdir -p ~/.config/systemd/user | |
| 35 | + | install -m 0644 ~/Code/MNW/bento/deploy/bentod.service ~/.config/systemd/user/ | |
| 36 | + | loginctl enable-linger "$USER" | |
| 37 | + | systemctl --user daemon-reload | |
| 38 | + | systemctl --user enable --now bentod | |
| 39 | + | ||
| 40 | + | # 4. Verify | |
| 41 | + | curl -s http://127.0.0.1:8765/state | python3 -m json.tool | |
| 42 | + | ``` | |
| 43 | + | ||
| 44 | + | ## Auth (CF2) | |
| 45 | + | ||
| 46 | + | On the loopback bind above, build triggers are reachable only from fw13, so no | |
| 47 | + | token is required. To operate bentod over the tailnet, bind the tailnet IP in | |
| 48 | + | `bento-daemon.toml` and set `BENTO_API_TOKEN` via an `EnvironmentFile` in the | |
| 49 | + | unit — bentod refuses to start on a non-loopback bind without it (same posture | |
| 50 | + | as Sando's `SANDO_API_TOKEN`). | |
| 51 | + | ||
| 52 | + | ## Not yet: builds need recipes | |
| 53 | + | ||
| 54 | + | Standing up the service makes bentod reachable (TUI/driver, `/state`), but a | |
| 55 | + | real `/build` reads `<app>/<recipe_dir>/<platform>.rhai` from each app's | |
| 56 | + | checkout. Those recipes do not exist yet — writing them (per-app, per-platform) | |
| 57 | + | is the next piece before bentod can actually ship an app. |
| @@ -0,0 +1,26 @@ | |||
| 1 | + | # Bento daemon-local config (machine paths + listen). Install at | |
| 2 | + | # ~/.config/bento/bento-daemon.toml. The build matrix lives in the separate | |
| 3 | + | # topology file (bento.toml). | |
| 4 | + | # | |
| 5 | + | # These paths are NOT tilde-expanded by the daemon (it runs without a shell) — | |
| 6 | + | # use absolute paths. Replace /home/max with the daemon user's home. | |
| 7 | + | # | |
| 8 | + | # Auth (CF2): with a non-loopback `listen`, bentod REFUSES to start unless | |
| 9 | + | # BENTO_API_TOKEN is set (via the service's environment). On the loopback bind | |
| 10 | + | # below, build triggers are reachable only from this host, so no token is | |
| 11 | + | # required. To operate over the tailnet, bind the tailnet IP and set | |
| 12 | + | # BENTO_API_TOKEN (mirror Sando's sando.env model). | |
| 13 | + | ||
| 14 | + | listen = "127.0.0.1:8765" | |
| 15 | + | db_path = "/home/max/.local/state/bento/bento.db" | |
| 16 | + | topology_path = "/home/max/.config/bento/bento.toml" | |
| 17 | + | ||
| 18 | + | # Root of the Syncthing private layer; the recipe `secret()` host function reads | |
| 19 | + | # credential files (signing keys, notary creds) relative to here. Never logged. | |
| 20 | + | secrets_root = "/home/max/Code/_private" | |
| 21 | + | ||
| 22 | + | # Collected artifacts land at <dist_root>/<app>/<version>/. | |
| 23 | + | dist_root = "/home/max/Dist" | |
| 24 | + | ||
| 25 | + | # Per-step run logs: <logs_root>/<app>/<version>/<target>/<step>.log | |
| 26 | + | logs_root = "/home/max/.local/state/bento/logs" |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | # Bento build topology: the native build hosts and the apps they ship. | |
| 2 | + | # Install at ~/.config/bento/bento.toml (path set by `topology_path` in | |
| 3 | + | # bento-daemon.toml). Paths under `repo` are tilde-expanded by the engine; the | |
| 4 | + | # daemon-local paths in bento-daemon.toml are NOT — use absolute paths there. | |
| 5 | + | # | |
| 6 | + | # House rule: no cross-compilation. A target builds only on a host whose row | |
| 7 | + | # lists it (topology::host_for); validate() rejects an app target no host | |
| 8 | + | # declares, and a host with targets that doesn't grant `build`. | |
| 9 | + | ||
| 10 | + | [[host]] | |
| 11 | + | name = "fw13" | |
| 12 | + | ssh = "fw13" # tailnet alias; the daemon's own host (local) | |
| 13 | + | targets = ["linux/x86_64"] | |
| 14 | + | ||
| 15 | + | [[host]] | |
| 16 | + | name = "astra" | |
| 17 | + | ssh = "astra" | |
| 18 | + | targets = ["linux/aarch64"] | |
| 19 | + | ||
| 20 | + | [[host]] | |
| 21 | + | name = "mbp" | |
| 22 | + | ssh = "mbp" | |
| 23 | + | targets = ["macos/aarch64", "ios/universal"] | |
| 24 | + | transport = "agent" # in-session ops-agent — required to codesign | |
| 25 | + | agent_url = "http://mbp:8765" # tailnet addr of the Mac's ops-agent | |
| 26 | + | actuate = ["build", "sign", "notarize", "staple"] | |
| 27 | + | observe = ["build-log", "gatekeeper"] | |
| 28 | + | ||
| 29 | + | [[host]] | |
| 30 | + | name = "windows-x86" | |
| 31 | + | ssh = "me@windows-x86" | |
| 32 | + | targets = ["windows/x86_64"] | |
| 33 | + | # transport defaults to "ssh"; actuate defaults to ["build", "package"] | |
| 34 | + | ||
| 35 | + | [app.goingson] | |
| 36 | + | repo = "~/Code/Apps/goingson" | |
| 37 | + | branch = "main" | |
| 38 | + | recipe_dir = "dist/recipes" | |
| 39 | + | targets = ["macos/aarch64", "ios/universal", "linux/x86_64", "linux/aarch64", "windows/x86_64"] | |
| 40 | + | ||
| 41 | + | [app.balanced_breakfast] | |
| 42 | + | repo = "~/Code/Apps/balanced_breakfast" | |
| 43 | + | branch = "main" | |
| 44 | + | recipe_dir = "dist/recipes" | |
| 45 | + | targets = ["macos/aarch64", "ios/universal", "linux/x86_64", "linux/aarch64", "windows/x86_64"] | |
| 46 | + | ||
| 47 | + | [app.audiofiles] | |
| 48 | + | repo = "~/Code/Apps/audiofiles" | |
| 49 | + | branch = "main" | |
| 50 | + | recipe_dir = "dist/recipes" | |
| 51 | + | # egui desktop app (no Tauri, no iOS). | |
| 52 | + | targets = ["macos/aarch64", "linux/x86_64", "linux/aarch64", "windows/x86_64"] |
| @@ -0,0 +1,37 @@ | |||
| 1 | + | # Bento app-build controller (bentod) — systemd USER service under the operator. | |
| 2 | + | # | |
| 3 | + | # bentod is a user service (not system) because it builds the apps and so needs | |
| 4 | + | # the operator's environment directly: SSH keys to the tailnet build hosts + the | |
| 5 | + | # mbp ops-agent, the app checkouts under ~/Code/Apps, and the _private layer for | |
| 6 | + | # signing secrets (secrets_root). A hardened system user can't reach those. | |
| 7 | + | # | |
| 8 | + | # Install (one-time, no sudo except enable-linger): | |
| 9 | + | # mkdir -p ~/.config/systemd/user | |
| 10 | + | # install -m 0644 bentod.service ~/.config/systemd/user/ | |
| 11 | + | # loginctl enable-linger "$USER" # keep it running across logout/reboot | |
| 12 | + | # systemctl --user daemon-reload | |
| 13 | + | # systemctl --user enable --now bentod | |
| 14 | + | # | |
| 15 | + | # Watch: journalctl --user -u bentod -f | |
| 16 | + | # Deploy a new bentod: build, copy to ~/.local/bin/bentod, `systemctl --user | |
| 17 | + | # restart bentod` (no sudo — that's the point of a user service). | |
| 18 | + | [Unit] | |
| 19 | + | Description=Bento app build controller | |
| 20 | + | After=network-online.target | |
| 21 | + | Wants=network-online.target | |
| 22 | + | ||
| 23 | + | [Service] | |
| 24 | + | Type=simple | |
| 25 | + | ExecStart=%h/.local/bin/bentod | |
| 26 | + | Restart=on-failure | |
| 27 | + | RestartSec=5 | |
| 28 | + | Environment=BENTO_CONFIG=%h/.config/bento/bento-daemon.toml | |
| 29 | + | # Loopback bind (default) needs no token. For a tailnet bind, set listen to the | |
| 30 | + | # tailnet IP in bento-daemon.toml AND provide BENTO_API_TOKEN here, e.g.: | |
| 31 | + | # EnvironmentFile=-%h/.config/bento/bento.env # contains BENTO_API_TOKEN=... | |
| 32 | + | StandardOutput=journal | |
| 33 | + | StandardError=journal | |
| 34 | + | SyslogIdentifier=bentod | |
| 35 | + | ||
| 36 | + | [Install] | |
| 37 | + | WantedBy=default.target |