Skip to main content

max / makenotwork

6.1 KB · 130 lines History Blame Raw
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 | (none, hand-written 0600) | `~/.config/bento/bento.env` | `BENTO_API_TOKEN=...` for a tailnet bind. Read by the unit's `EnvironmentFile=-`, so a loopback install can leave it absent. No example file ships: it holds a secret. |
20
21 ## Stand-up (no root except enable-linger)
22
23 ```sh
24 # 1. Build + install the binary
25 cd ~/Code/MNW/bento/daemon && cargo build --release
26 install -D -m 0755 target/release/bentod ~/.local/bin/bentod
27
28 # 2. Config + state dirs
29 mkdir -p ~/.config/bento ~/.local/state/bento/logs ~/Dist
30 install -m 0644 ~/Code/MNW/bento/deploy/bento-daemon.toml.example ~/.config/bento/bento-daemon.toml
31 install -m 0644 ~/Code/MNW/bento/deploy/bento.toml.example ~/.config/bento/bento.toml
32 # (edit the two configs: absolute home paths, real host/app rows)
33
34 # 3. Service
35 mkdir -p ~/.config/systemd/user
36 install -m 0644 ~/Code/MNW/bento/deploy/bentod.service ~/.config/systemd/user/
37 loginctl enable-linger "$USER"
38 systemctl --user daemon-reload
39 systemctl --user enable --now bentod
40
41 # 4. Verify (address must match `listen` in bento-daemon.toml, see Auth below)
42 curl -s http://127.0.0.1:8765/state | python3 -m json.tool
43 ```
44
45 ## Auth (CF2)
46
47 On a loopback bind, build triggers are reachable only from the daemon's own
48 host, so no token is required. To operate bentod over the tailnet, bind the
49 tailnet IP in `bento-daemon.toml` and set `BENTO_API_TOKEN` via an
50 `EnvironmentFile` in the unit. bentod refuses to start on a non-loopback bind
51 without it (same posture as Sando's `SANDO_API_TOKEN`).
52
53 **fw13 runs the second form**, not the loopback default the example config
54 ships. `listen` is the tailnet address and `/build` and `/retry` require the
55 bearer token, so a `127.0.0.1` curl gets no answer at all rather than an auth
56 error. Read the address out of the config and the token out of its own file
57 rather than passing either on a command line:
58
59 ```sh
60 set -a; . ~/.config/bento/bento.env; set +a
61 curl -s -X POST http://$(grep -oP '(?<=^listen = ")[^"]+' ~/.config/bento/bento-daemon.toml)/build \
62 -H "authorization: Bearer $BENTO_API_TOKEN" -H 'content-type: application/json' \
63 -d '{"app":"makeover-webview","version":"0.5.1"}'
64 ```
65
66 `/state`, `/status.json` and the log endpoints are read-only and unauthed, so
67 watching a run needs the address but not the token.
68
69 ## Recipes
70
71 A real `/build` reads `<app>/<recipe_dir>/<platform>.rhai` from each app's
72 checkout. The recipes now exist, one per platform per app:
73
74 | App | macOS | iOS | Linux | Windows |
75 |-----|-------|-----|-------|---------|
76 | goingson | yes | yes | yes | yes (unsigned, unsupported) |
77 | audiofiles | yes | — (egui, no iOS) | yes | yes (unsigned, unsupported) |
78 | balanced_breakfast | pending¹ | pending¹ | yes | yes (unsigned, unsupported) |
79
80 ¹ BB has no Developer ID signing / notarization infra or iOS project yet, so its
81 macOS/iOS recipes are deferred until that lands. BB ships in a later wave.
82
83 A library crate (`kind = "library"` in its own `bento.toml`) has no per-platform
84 artifact, so the runner reads a single `publish.rhai` instead of the table above.
85 Every library configured in the topology now carries one: `makeover`,
86 `makeover-geometry`, `makeover-build`, `makeover-webview`, `makeover-layout`,
87 `pter`, `everycycle`, `supernote-push`, `alloy_tui`. That recipe asserts HEAD is
88 exactly a tag before it uploads, so a release wants `git tag -a v<version>`
89 first; a crate published by hand before it was wired up has no such tag, and its
90 next release is the one that gets one.
91
92 A `linux.rhai` serves both arches — `build_host()` resolves to the native host
93 the topology assigns the target (`linux/x86_64` → fw13, `linux/aarch64` → astra),
94 so there is no cross-compilation and no hard-coded host name. macOS/iOS recipes
95 run on the mbp `agent`-transport host where the Developer ID key is usable.
96
97 ### The prebuild gate
98
99 Every recipe runs a `prebuild` step between checkout and build:
100
101 ```
102 cargo clippy --workspace --all-targets <features> -- -D warnings
103 cargo test --workspace <features>
104 ```
105
106 `sh_ok` aborts the recipe on a non-zero exit, and a failed step lands in the
107 step ledger, so a red gate bars publish as well as build. It runs on the target's
108 own build host rather than once centrally: a break confined to one platform
109 (cfg-gated code, a Windows path) is then caught on the machine that would have
110 shipped it. The cost is that the suite runs once per target.
111
112 Features come from `feature_flags()`, so the gate compiles the same
113 configuration the release build does.
114
115 ### Recipe host-function vocabulary
116
117 Beyond `step`/`sh`/`sh_ok`/`log`/`collect`/`publish`/`secret`/`env` and the
118 macOS helpers (`codesign`/`notarize`/`staple`/`verify_gatekeeper`/`keychain_*`),
119 recipes can read their own context:
120
121 | Function | Returns |
122 |----------|---------|
123 | `version()` | the version being built (e.g. `"0.4.2"`) |
124 | `build_host()` | the host this target builds on (e.g. `"fw13"`) |
125 | `repo()` | the app's checkout path (`~`-prefixed; `cd` into it — commands don't auto-cd) |
126 | `target()` / `platform()` / `arch()` | `"linux/x86_64"` / `"linux"` / `"x86_64"` |
127
128 Non-Tauri apps (audiofiles) set `version_path` in the topology to the crate
129 `Cargo.toml` carrying the version, since they have no `tauri.conf.json`.
130