# defaults.env — the COMMITTED default configuration for the serving stack. # # Compose LAYERS this file, then .env (overrides), for both container env AND # ${...} interpolation. Enable the layering once per shell (later file wins): # # export COMPOSE_ENV_FILES=defaults.env,.env # # or per command: docker compose --env-file defaults.env --env-file .env # # or just use the Makefile: `make up` / `make logs` (sets it for you) # # Then put ONLY per-environment overrides + secrets in .env (gitignored) — at # least BOOT_IFACE / SERVER_IP / DHCP range / IMAGE_VERSION for your LAN. Changing # a default here reaches every run without anyone re-copying it. (.env must exist # when COMPOSE_ENV_FILES lists it; create it with at least those overrides.) # Release being served. Must match the Packer build (fleet-.efi). IMAGE_VERSION=0.0.0-2eefd97 # OCI release-artifact image (built by scripts/build-oci.sh). Mounted directly # as a Docker image volume — the default artifact source. Pin by digest in # production, e.g. OCI_IMAGE=git.bitasylum.org/audiobuzz/fleet-boot@sha256:… OCI_IMAGE=git.bitasylum.org/audiobuzz/fleet-boot # Fallback only (Docker < 28 / offline): local Packer output dir, used by the # commented bind-mounts in docker-compose.yml. Ignored when image volumes work. ARTIFACT_SRC=../artifacts/0.0.0-2eefd97 # SPDK NVMe/TCP target image — prebuilt + pushed by CI # (.gitea/workflows/spdk-image.yaml); the serving host just pulls it. SPDK_IMAGE=git.bitasylum.org/audiobuzz/spdk-nvmf SPDK_VERSION=v24.09 # dnsmasq image — prebuilt + pushed by CI (.gitea/workflows/dnsmasq-image.yaml); # GENERIC (no Secure Boot keys / no iPXE binary). Pulled, not built at deploy. DNSMASQ_IMAGE=git.bitasylum.org/audiobuzz/fleet-dnsmasq DNSMASQ_VERSION=latest # fleet-ipxe image — the SIGNED iPXE bootloader (snponly.efi) for PXE-only hosts, # built + signed (fleet db key) + pushed by CI (.gitea/workflows/ipxe-image.yaml). # Mounted into dnsmasq at /ipxe. Per-environment (signature is db-key-specific); # pin by digest in production, e.g. fleet-ipxe@sha256:… IPXE_IMAGE=git.bitasylum.org/audiobuzz/fleet-ipxe IPXE_VERSION=latest # kiosk SPA image — the spaceship-terminal web app, now an INDEPENDENT project # (the kiosk-app git submodule) with its own CI that builds + pushes this image. # The 'kiosk' compose service pulls it; the main nginx reverse-proxies /kiosk/ to # it. For local dev, uncomment `build: ./kiosk-app` on that service instead. KIOSK_IMAGE=git.bitasylum.org/audiobuzz/fleet-kiosk KIOSK_VERSION=latest # --- This serving host on the boot LAN ------------------------------------- BOOT_IFACE=ens19 # interface facing the booting client / test bridge SERVER_IP=192.168.50.10 # this host's address on BOOT_IFACE (DHCP/DNS/HTTP/NVMe) GATEWAY_IP=192.168.50.1 UPSTREAM_DNS=10.42.42.254 # --- DHCP scope ------------------------------------------------------------ DHCP_RANGE_START=192.168.50.100 DHCP_RANGE_END=192.168.50.200 DHCP_LEASE=12h # --- Kiosk SPA ------------------------------------------------------------- # Loopback port the kiosk-app container publishes; the host-network main nginx # reverse-proxies /kiosk/ to http://127.0.0.1:${KIOSK_APP_PORT}. Change only if # 8088 clashes on the serving host (also update http/default.conf proxy_pass). KIOSK_APP_PORT=8088 # Local testing secrets for the kiosk BFF. This file is GITIGNORED — safe to # paste a real token here. Template/docs: .env.example. HASS_URL=http://cortana.bitasylum.org:8123 # 👇 paste your Home Assistant long-lived access token here, then restart the BFF # (npm run bff). Test it with: curl -s localhost:8787/api/health | jq HASS_TOKEN= # --- DNS names ------------------------------------------------------------- BOOT_DOMAIN=fleet.lab BOOT_HOST=boot.fleet.lab # HTTP boot server name (UKI) # NVMe/TCP discovery name. Put THIS value in the UKI cmdline as NVMET_TRADDR # (extract-artifacts.sh) so rd.nvmf.discover resolves here per environment. NVME_HOST=nvme.fleet.lab # Remote audit collector name. Host-generic — bake into the image's rsyslog # forward rule so every host offloads auditd here; resolved to this serving host. AUDIT_HOST=audit.fleet.lab # Fleet-manager AGENT plane name. Baked into the image (files/agent/agent.env # FLEET_AGENT_MANAGER) so every enrolled host dials the control plane here; # resolved per-environment to the manager's fleet-LAN address (this serving host # now; a routable mgmt address once the manager moves to k8s). Just another # *.fleet.lab like the boot/nvme/audit names. Must match the agent server cert SAN # (server-pki.sh AGENT_SANS). MGMT_HOST=manager.fleet.lab # --- NVMe/TCP target ------------------------------------------------------- # Per-version subsystem NQN base. The long-lived target serves each rootfs # version as its OWN subsystem "-" (so a new build is added live # without disturbing connected hosts). Per release the exact NQN is read from # that version's verity.json (.subnqn, written by extract-artifacts.sh); this is # only the fallback/default base and MUST match SUBNQN_BASE in that script. SUBNQN_BASE=nqn.2026-06.org.example.fleet:rootfs # Legacy single-version NQN (only the deprecated serving/spdk-setup.sh path). SUBNQN=nqn.2026-06.org.example.fleet:rootfs TARGET_PORT=4420 SPDK_CPUMASK=0x1 # one polling core is plenty for a read-only boot fs # --- TPM enrolment broker (TEMPORARY in this stack — see docs/TODO.md #11) --- # The broker is the one unauthenticated pre-auth endpoint (nginx proxies /enrol/ # to it). Defaults run the zero-dependency LAB path: a `mock` signer (ephemeral # CA; certs NOT trusted by RADIUS/NVMe) + plaintext loopback admin — enough to # exercise the request/approve/poll flow while the host side is built. # # For REAL enrolment: set ENROL_SIGNER=step, point ENROL_STEP_CA_URL at a # reachable step-ca (e.g. the one in serving/dot1x-bench/), drop its root + # provisioner password into ./enrol-pki/, generate the admin mTLS PKI # (./enrol-broker/admin-pki.sh ./enrol-pki/broker ), and set # ENROL_ADMIN_INSECURE=false. Then it belongs in its own CA-co-located stack. ENROL_SIGNER=mock # mock (lab, no CA) | step (real, needs a CA) ENROL_ADMIN_INSECURE=true # true = plaintext loopback admin (LAB ONLY); false = mTLS ENROL_PENDING_TTL=10m # time-box on unapproved requests ENROL_WORDS=8 # fingerprint word count ENROL_HOSTNQN_BASE=nqn.2026-06.org.example.fleet:host- # step signer (only when ENROL_SIGNER=step) — paths are INSIDE the container # (./enrol-pki is mounted at /pki): ENROL_STEP_CA_URL= # e.g. https://ca.fleet.lab:8443 ENROL_STEP_CA_ROOT=/pki/root_ca.crt ENROL_STEP_PROVISIONER=enrol ENROL_STEP_PASSWORD_FILE=/pki/password # Issued-cert lifetime. NOTE: the step-ca provisioner must ALLOW this — a stock # step-ca caps certs at 24h, so 720h is rejected ("requested duration ... more # than the authorized maximum certificate duration of 24h"). The bench # step-ca/bootstrap.sh now raises authority.claims.maxTLSCertDuration; a # different CA needs the same. Verified end-to-end against a real step-ca. ENROL_CERT_NOT_AFTER=720h # admin mTLS (only when ENROL_ADMIN_INSECURE=false): ENROL_ADMIN_CERT=/pki/broker/server.crt ENROL_ADMIN_KEY=/pki/broker/server.key ENROL_ADMIN_CLIENT_CA=/pki/broker/admin-ca.crt # --- SPDK version loader / reaper (declarative rolling updates) ------------- # The spdk-loader service stages + adds ${IMAGE_VERSION} to the long-lived target # on `compose up`, then auto-removes OLD versions once they drain. Migration = # bump IMAGE_VERSION above + `docker compose up -d`. REAP_OLD=true # false = keep old versions until you remove them manually REAP_POLL_SECONDS=30 # how often to poll connection counts # Sustained-zero window before an old version is removed. Keep it comfortably # ABOVE the host's nvme ctrl_loss_tmo so a transient reconnect never gets reaped. REAP_GRACE_SECONDS=300