diff --git a/.gitignore b/.gitignore index 69aaed4..6b58209 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CoreSystemConfig.json.template b/CoreSystemConfig.json.template index e4165d9..9c3c638 100644 --- a/CoreSystemConfig.json.template +++ b/CoreSystemConfig.json.template @@ -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" } } diff --git a/README.md b/README.md index b948c21..aee614d 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/tools/README.md b/tools/README.md index 67429ef..5163893 100644 --- a/tools/README.md +++ b/tools/README.md @@ -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--.iso`. That naming +own artifact in `iso-out/`, named `smarthome--.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 diff --git a/tools/config-export.py b/tools/config-export.py index c9a08c9..f5b2f71 100755 --- a/tools/config-export.py +++ b/tools/config-export.py @@ -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: diff --git a/tools/lib/coreconfig.sh b/tools/lib/coreconfig.sh index a8e875f..6776311 100644 --- a/tools/lib/coreconfig.sh +++ b/tools/lib/coreconfig.sh @@ -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