Output images to iso-out/, and note why arm64 audio is one image for all rooms

Renames the image output directory from build-output/ to iso-out/ (config
default, .gitignore, docs).

Also documents the asymmetry that showed up when building a household with
several endpoints: kiosks and amd64 audio endpoints get one image per array
entry, because their hostname is baked in at build time; the arm64 audio image
is generic and built once no matter how many arm64 rooms are listed, because Pi
Imager sets hostname and Wi-Fi per unit at flash time. That is project-plan
Phase 15.5's existing decision, now reflected in what build-all actually does
rather than only in prose.

Verified with a stubbed full run of a six-endpoint household: one build-all
invocation produced nine distinctly-named images with no overwrites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
digest-per-person-and-agendas
Amir Alexander Abdelbaki 2026-07-31 13:28:08 +02:00
parent bbeabde97e
commit 919ce94d52
6 changed files with 10 additions and 10 deletions

2
.gitignore vendored
View File

@ -101,7 +101,7 @@ CoreSystemConfig.*.json
# Built images. These have every secret above burnt into them by design, so they are
# credential-bearing artifacts, not just large ones.
build-output/
iso-out/
*.iso
*.img
*.img.xz

View File

@ -183,7 +183,7 @@
],
"build": {
"_comment": "output_dir is where finished ISOs land. It is gitignored — see .gitignore.",
"output_dir": "build-output"
"_comment": "Where finished images land, relative to the repo root. Gitignored — see .gitignore — because these carry every secret in this file.",
"output_dir": "iso-out"
}
}

View File

@ -102,7 +102,7 @@ chores/ Presence/calendar-driven household chore nudging +
- [ ] `chores` (Phase 20) — presence/calendar-driven household chore nudging: "I don't care who does it, as long as it gets done" — prefers whoever's been assigned a chore in `identity`'s admin panel but falls through to whoever's actually home rather than waiting (`CHORE_ASSIGNMENT_STRICT` flips that), redirects to someone else if a chore goes neglected, keeps a passive fairness tally that never feeds back into who gets nudged, and camera-checks trash bins/dishes/litter via Frigate + an Ollama vision model. **Litter remains the exception to everything** — it ignores both chore-exemption and assignment, because cleaning up what you left out was never a task anyone could be assigned. Built and wired into `setup-container-host.sh` (`ENABLE_CHORES`, off by default, every-2-hours systemd timer), **no Tapo camera hardware chosen and nothing run against real hardware**, see `chores/README.md`
- [ ] Music Assistant (optional, additive multi-room audio) — wired into `setup-container-host.sh` (`ENABLE_MUSIC_ASSISTANT`, off by default), **its default port is an unverified guess that collides with `PANTRY_VISION_PORT`** if both are enabled together, see `docs/project-plan.md` open decision #31
- [ ] `docs/network-integration.md` (OPNsense VLAN segmentation, the WireGuard split tunnel that carries arrival notifications, and why nothing here — ntfy included — gets port-forwarded to the WAN) — written, not run against a real OPNsense instance
- [ ] `tools/` + `CoreSystemConfig.json` — every build and setup script in one place, reading one config. The container host and LLM host build as a **twinned pair**: you set two last octets and the container host's `OLLAMA_HOST` is *derived* from the LLM host's, so the two ISOs cannot be built disagreeing about where the other one is; every kiosk's service URLs derive from the container host's address the same way. `build-all.sh` builds the set, `validate-config.py` refuses a build on duplicate ports (the `music_assistant`/`pantry_vision` 8095 clash, open decision #31), placeholder or padded tokens, duplicate hostnames, or a kiosk pointed at a disabled service. All secrets are burnt into the images so installs are unattended — **which makes every ISO a credential**; the filled-in config and `build-output/` are gitignored. **No ISO has ever been built with this** (`lb build` needs live-build, root and a long fetch) — what is tested is config validation/derivation and every generated artifact, with `lb` stubbed. See `tools/README.md`
- [ ] `tools/` + `CoreSystemConfig.json` — every build and setup script in one place, reading one config. The container host and LLM host build as a **twinned pair**: you set two last octets and the container host's `OLLAMA_HOST` is *derived* from the LLM host's, so the two ISOs cannot be built disagreeing about where the other one is; every kiosk's service URLs derive from the container host's address the same way. `build-all.sh` builds the set, `validate-config.py` refuses a build on duplicate ports (the `music_assistant`/`pantry_vision` 8095 clash, open decision #31), placeholder or padded tokens, duplicate hostnames, or a kiosk pointed at a disabled service. All secrets are burnt into the images so installs are unattended — **which makes every ISO a credential**; the filled-in config and `iso-out/` are gitignored. **No ISO has ever been built with this** (`lb build` needs live-build, root and a long fetch) — what is tested is config validation/derivation and every generated artifact, with `lb` stubbed. See `tools/README.md`
## Quick start
@ -124,7 +124,7 @@ kiosk's service URLs derive from the container host's address the same way. See
Built ISOs contain every secret in the config, by design (nothing to configure
post-install) — which makes each one a credential. `.gitignore` covers both the
filled-in config and `build-output/`.
filled-in config and `iso-out/`.
To set up a host by hand instead of from an ISO:

View File

@ -97,7 +97,7 @@ doesn't abandon the rest.
### One entry, one image
Kiosks and audio endpoints come from **arrays** in the config, and every entry gets its
own artifact in `build-output/`, named `smarthome-<hostname>-<pairid>.iso`. That naming
own artifact in `iso-out/`, named `smarthome-<hostname>-<pairid>.iso`. That naming
is load-bearing rather than cosmetic: live-build always writes the same filename into
the same per-host tree, so two thin clients would otherwise have the second silently
overwrite the first — leaving one ISO carrying the second room's hostname and nothing
@ -119,7 +119,7 @@ no env files to edit on a freshly-booted host. It also means **every ISO is a
credential**: Wi-Fi PSK, service tokens, MQTT and HA credentials, all readable by
anyone holding the stick.
`.gitignore` covers `CoreSystemConfig.json` and `build-output/`, so neither can be
`.gitignore` covers `CoreSystemConfig.json` and `iso-out/`, so neither can be
committed by accident. Wiping old USB sticks is on you.
## Two things can't be burnt in

View File

@ -126,7 +126,7 @@ def main(argv: list[str]) -> int:
emit(f"CORE_ENABLE_{flag.upper()}", value)
emit("CORE_VOICE_WAKE_WORD", cfg.get("voice", {}).get("wake_word", "ok_nabu"))
emit("CORE_BUILD_OUTPUT_DIR", cfg.get("build", {}).get("output_dir", "build-output"))
emit("CORE_BUILD_OUTPUT_DIR", cfg.get("build", {}).get("output_dir", "iso-out"))
# --- The selected kiosk, if one was asked for ---
if kiosk_hostname:

View File

@ -173,10 +173,10 @@ SMARTHOME_LLM_HOST=${CORE_LLM_HOST_IP}
EOF
}
# Move a finished image out of its live-build tree into build-output/, named for the
# Move a finished image out of its live-build tree into iso-out/, named for the
# thing it actually is.
#
# THIS IS LOad-BEARING FOR MULTI-INSTANCE BUILDS, not cosmetic. live-build always
# THIS IS LOAD-BEARING FOR MULTI-INSTANCE BUILDS, not cosmetic. live-build always
# writes the same filename (live-image-amd64.hybrid.iso) into the same per-host tree,
# so building two thin clients — a living-room one and a bedroom one — would have the
# second silently overwrite the first, leaving one ISO carrying the second room's