From 200c99f2f697ff59c17ec413dde0fc00e97d757e Mon Sep 17 00:00:00 2001 From: Jacob Hagstedt Date: Tue, 20 Oct 2026 19:07:20 +0200 Subject: [PATCH] Pi bundle: drop the informational .env keys KVITT_PI_HOST and KVITT_PI_SSH_USER were read by nothing on the Pi (D-103). They are gone from .env.example; the README's scp, ssh and NPM steps use @ and the Kvitt Pi's LAN IPv4 address in words. install.sh checks only the keys the stack reads, so an older .env that still has the two keys installs unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B --- infra/README.md | 29 ++++++++++++++++------------- infra/pi/.env.example | 9 +-------- infra/pi/compose.yaml | 2 +- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/infra/README.md b/infra/README.md index 6d8296b..a286f03 100644 --- a/infra/README.md +++ b/infra/README.md @@ -164,7 +164,7 @@ Copying the key to the Pi (§10.7 step 7, mode 0440, group 10001) is done by `pi | Gitea Actions (runner label `host`, a Raspberry Pi) | `.gitea/workflows/build-images.yaml`: builds `kvitt-server` and `kvitt-backup` for linux/arm64, pushes them to `gitea.lan/jacobh2/...` tagged `vMAJOR.MINOR.PATCH` and `latest`, pushes the git tag | Gitea, on a push to `server/**`, `infra/backup/**`, `.gitea/workflows/**`, or by hand (Actions, "Run workflow") | | Owner's Mac | `infra/build-images.sh [tag]` (fallback image build), `infra/publish-apk.sh` (release APK into `infra/pi/out/apk/`) | The owner (or an agent, for the APK build only) | | Kvitt Pi, `/opt/kvitt/` | `compose.yaml`, `.env`, `secrets/`, `import/`, `apk/`, and the scripts `deploy.sh`, `bootstrap.sh`, `backup-now.sh`, `install-apk.sh` (copied there by `install.sh`) | The owner, in their own SSH session | -| Home Assistant Pi | Nginx Proxy Manager (NPM): a proxy host for `` with its own Let's Encrypt certificate, TLS on 443, plain HTTP to `:8080` over the LAN (D-120) | The owner, in NPM's web UI (step 6) | +| Home Assistant Pi | Nginx Proxy Manager (NPM): a proxy host for `` with its own Let's Encrypt certificate, TLS on 443, plain HTTP to `:8080` over the LAN (D-120) | The owner, in NPM's web UI (step 6) | | Router (ASUS) | No new rule: the existing external TCP 443 forward to the Home Assistant Pi (NPM, for Home Assistant) serves Kvitt too, NPM picks the proxy host by name; external 80 already reaches NPM for Let's Encrypt; nothing is forwarded to the Kvitt Pi (D-124) | The owner checks it (step 7) | Stack (`pi/compose.yaml`, project name `kvitt`): `server` (publishes `8080:8080` on the Pi's LAN interfaces, the @@ -181,7 +181,7 @@ requires the address because the container cannot resolve an mDNS name such as ` router's forward keeps the internet client's source address, and Docker's published port keeps NPM's (DNAT, no SNAT, for traffic arriving on the LAN interface), so the per-IP limits of D-032, D-115 and D-119 see real addresses. The server does not use `X-Forwarded-Proto` or `Host`: links are built from `KVITT_PUBLIC_URL` (E-12). -Anyone on the home LAN can also reach `http://:8080` directly; such requests count against their own +Anyone on the home LAN can also reach `http://:8080` directly; such requests count against their own address, and only `HA_HOST` may set `X-Forwarded-For`. ### Versions (resolved 2026-10-18) @@ -295,8 +295,8 @@ with their mappings. Then the owner, on the Mac: ```bash cd /Users/jhag/projects/kvitt -scp infra/pi/out/apk/* @:/opt/kvitt/apk/ -ssh @ /opt/kvitt/install-apk.sh +scp infra/pi/out/apk/* @:/opt/kvitt/apk/ +ssh @ /opt/kvitt/install-apk.sh ``` `install-apk.sh` checks the SHA-256 against `latest.json`, copies the APKs into the `data` volume (`/data/apk/`) @@ -309,19 +309,22 @@ Fill these in first (in `infra/pi/.env` on the Mac; the bundle carries the file | Placeholder in `infra/pi/.env` | What | Where to find it | |---|---|---| | `KVITT_PUBLIC_HOST` | Kvitt's own public host name (D-120), for example a DuckDNS name or a name in your own domain, pointing at the home's public address like `icehack.asuscomm.com` does. Not `icehack.asuscomm.com` itself: that name is NPM's proxy host for Home Assistant, and NPM chooses the proxy host by name, not by port | Your choice; tell the orchestrator the name (it goes into the app and `docs/HANDOFF.md`) | -| `KVITT_PI_HOST` | LAN IPv4 address of the Kvitt Pi (give it a static DHCP lease); also NPM's Forward Hostname / IP | ASUS router web UI, LAN, DHCP Server, client list | -| `KVITT_PI_SSH_USER` | Your user on the Kvitt Pi | The user set in Raspberry Pi Imager | | `HA_HOST` | LAN IPv4 address of the Home Assistant Pi, where NPM runs (static DHCP lease); the server trusts `X-Forwarded-For` only from it. An address such as `192.168.1.20`, not `homeassistant.local` | Router client list, or Home Assistant, Settings, System, Network | | `KVITT_TAG` | Image tag to run: the newest `v1.0.N` the Gitea workflow pushed (or the SHA from `build-images.sh`) | Gitea, the repository, Packages, `kvitt-server` | | `KVITT_REGISTRY_USER`, `KVITT_REGISTRY_TOKEN` | Only if the Gitea packages are private: a Gitea user and a token with `read:package` | Gitea, Settings, Applications, Generate New Token | | `KVITT_REGISTRY` | Already `gitea.lan/jacobh2` | | | `RCLONE_REMOTE`, `RCLONE_FOLDER_ID` | Already `gdrive:` and `1jDp1dGE5R3H2DBcXCqIPN1iWzQnPkdlV` | | +Not in `.env` (nothing on the Pi reads them; dropped from `.env` on 2026-10-20): in this README, +`` is the Kvitt Pi's LAN IPv4 address (give it a static DHCP lease; ASUS router web UI, LAN, DHCP +Server, client list), also NPM's Forward Hostname / IP (step 6), and `` is your user on the Kvitt Pi (the user +set in Raspberry Pi Imager). + ### 1. Provision the Kvitt Pi Raspberry Pi Imager on the Mac: Raspberry Pi 3, **Raspberry Pi OS Lite (64-bit)**; in its settings set the host name, your user and password, the network, time zone Europe/Stockholm, and enable SSH with your public key. Boot -it, give it a static DHCP lease in the router, then from the Mac: `ssh @`. +it, give it a static DHCP lease in the router, then from the Mac: `ssh @`. ### 2. Install Docker Engine on the Pi (docs.docker.com, Debian arm64 repository) @@ -370,7 +373,7 @@ D-120: do not copy them (and no SSH add-on or `authorized_keys` entry on the Hom On the Mac: ```bash -scp -r infra/pi @:kvitt-pi +scp -r infra/pi @:kvitt-pi rm -r infra/pi/secrets # the originals stay in infra/secrets/ ``` @@ -414,7 +417,7 @@ Details tab: |---|---| | Domain Names | `` | | Scheme | `http` | -| Forward Hostname / IP | `` (the IPv4 address) | +| Forward Hostname / IP | The Kvitt Pi's LAN IPv4 address (``) | | Forward Port | `8080` | | Cache Assets | off | | Block Common Exploits | on | @@ -497,7 +500,7 @@ the headers again (Operations, "Headers through NPM"). **What NPM logs (SPEC §8.7).** NPM writes one access log line per request to `/data/logs/proxy-host-_access.log` inside NPM, in its `proxy` format: time, cache and upstream status, status, method, scheme, host, `"$request_uri"` (the path **with** its query string), `[Client
]`, length, gzip ratio, -`[Sent-to ]`, user agent and referer. It logs no request headers, so the `Authorization` token is never +`[Sent-to ]`, user agent and referer. It logs no request headers, so the `Authorization` token is never logged. Query strings are logged for every path except `/i/`; the other query parameters of the API are `since` and `limit` (`GET /sync`) and `size` and `v` (pictures), which hold no personal content. `/i/?from=` is not logged at all: the block turns the access log off for `/i/` and sends its error log to `/dev/null`, so neither the @@ -521,7 +524,7 @@ router's forward keeps the source address), so their `X-Real-IP` is ignored. No new rule (D-124): external TCP 443 already reaches the Home Assistant Pi (NPM) for Home Assistant, and NPM picks Kvitt's proxy host by name on the same port. Verify it exists: ASUS router web UI, WAN, Virtual Server / Port Forwarding: the list has a TCP rule with External Port `443` and Internal IP Address `` (yes/no). Nothing is -forwarded to the Kvitt Pi: remove a rule to `` if you made one for the Caddy version, and a rule with +forwarded to the Kvitt Pi: remove a rule to `` if you made one for the Caddy version, and a rule with External Port `8443` if you made one for the earlier NPM version (D-120). Then on the phone with Wi-Fi off, open `https:///health` in Chrome: it shows `"status":"ok"` without a certificate warning (yes/no). @@ -563,7 +566,7 @@ copying the bundle again (step 4, without the secrets) and running `./install.sh * NPM or the Home Assistant Pi down means Kvitt is unreachable from outside, and also from home over `https://` (that path goes through NPM too); Home Assistant is then unreachable from outside as well (same NPM, same port 443). The server on the Kvitt Pi keeps running and nothing there needs to change. The phones keep working on their full local copy and sync when NPM is back. -* On the home Wi-Fi only **debug builds** can reach the server directly, at `http://:8080` (AC-2.8 +* On the home Wi-Fi only **debug builds** can reach the server directly, at `http://:8080` (AC-2.8 allows `http://` to private LAN addresses in debug builds only). **Release builds require HTTPS with a publicly trusted certificate (SPEC §8.5) and cannot use that address: there is no LAN fallback for the release app.** A debug build is a separate app (`se.hagstedt.kvitt.debug`) with its own pairing and data, not a stand-in for the @@ -610,7 +613,7 @@ Backup encryption (security review M-1) is decided: no rclone `crypt`, the `driv * Step 6 check or `deploy.sh` says /health through NPM failed: "Could not resolve host" or "SSL certificate problem": the proxy host has no certificate for `` yet (SSL tab) or a different name; a `502 Bad Gateway`: - NPM cannot reach `:8080` (wrong Forward Hostname / IP or Forward Port, or the server is down: + NPM cannot reach `:8080` (wrong Forward Hostname / IP or Forward Port, or the server is down: `docker compose ps`); the Home Assistant page instead of `{"status":"ok"}`: Domain Names does not match the name you requested (NPM fell back to another host). * "Request a new Certificate" fails in NPM: `` does not point at the home's public address yet, diff --git a/infra/pi/.env.example b/infra/pi/.env.example index 815d883..df12f7a 100644 --- a/infra/pi/.env.example +++ b/infra/pi/.env.example @@ -1,5 +1,5 @@ # Kvitt Pi settings (SPEC §10.2). Copy to .env in this folder, replace every CHANGE_ME, then run ./install.sh, -# which copies it to /opt/kvitt/.env (mode 0600). install.sh refuses a .env that still contains CHANGE_ME. +# which copies it to /opt/kvitt/.env (mode 0600). install.sh refuses a CHANGE_ME left in a key it checks. # No secret files here: those go to /opt/kvitt/secrets/ (infra/README.md "Pi: what the owner does"). # --- Read by compose.yaml ----------------------------------------------------------------------------------------- @@ -30,10 +30,3 @@ RCLONE_FOLDER_ID=1jDp1dGE5R3H2DBcXCqIPN1iWzQnPkdlV KVITT_REGISTRY_USER= # Gitea access token of that user with read:package scope; leave empty for anonymous pull. Never commit a filled .env. KVITT_REGISTRY_TOKEN= - -# --- Informational: used in the owner's commands on the Mac (scp) and in NPM, read by nothing on the Pi --------- - -# LAN address of the Kvitt Pi itself (OQ-3); also NPM's Forward Hostname / IP (infra/README.md, NPM section). -KVITT_PI_HOST=CHANGE_ME_KVITT_PI_HOST -# SSH user on the Kvitt Pi (OQ-3). -KVITT_PI_SSH_USER=CHANGE_ME_KVITT_PI_SSH_USER diff --git a/infra/pi/compose.yaml b/infra/pi/compose.yaml index 96a9898..4ee013a 100644 --- a/infra/pi/compose.yaml +++ b/infra/pi/compose.yaml @@ -24,7 +24,7 @@ services: security_opt: - no-new-privileges:true mem_limit: 160m - # On all interfaces of the Pi: NPM on the Home Assistant Pi connects to :8080 over the LAN. + # On all interfaces of the Pi: NPM on the Home Assistant Pi connects to this Pi's LAN address, port 8080. ports: - "8080:8080" environment: