Files
overseerandClaude Opus 5.5 5019b0cdc2 Remove every automatic timezone-setting mechanism (task 7f685179)
The application timezone is now only ever set by an admin saving it:

- API: drop adopt_reported_timezone; the panel household clock reads the
  stored settings only and ignores any timezone request header.
- Integration: stop reporting Home Assistant's time_zone in an
  X-NestQuest-Timezone header (snapshot, completion and SSE requests).
- Admin app: remove the browser-zone detection and the automatic PATCH
  on load; the timezone field stays a manual control.
- Remove the timezone_configured marker from the core settings, the
  settings store and the admin settings API. A stored document that
  still carries the key loads harmlessly (unknown keys are ignored).

Tests replace the adoption expectations with proofs that a panel
timezone header never changes the stored settings, a fresh install's
empty zone stays empty with no settings write, the admin settings carry
no timezone_configured, and a manual admin timezone PATCH persists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 07:00:06 -04:00
..

NestQuest API

Standalone FastAPI service owning the NestQuest database (Feature 16). Served with uvicorn --factory api.app:create_app; configuration is environment-supplied (api/config.py) and every variable is required.

Runtime dependencies

api/requirements.txt is the API deployable's OWN dependency manifest (fastapi, uvicorn, pyjwt[crypto], httpx) — deliberately kept out of the integration's pyproject.toml dev group, which exists only so the monorepo test venv can run the API tests.

Container deployment (scaffold)

The image is built from the repository ROOT context (the API loads the bundled domain core from custom_components/nestquest/core/ by file location, so both trees must be in the build context):

docker build -t nestquest-api:latest .

Run it with compose — the SQLite file lives on the named volume nestquest-db so recreating the container keeps household data:

cp deploy/.env.example deploy/.env   # then fill in real values
docker compose -f deploy/compose.yaml up -d --build

deploy/.env is git-ignored and supplies the secrets (panel service token, Authentik OIDC settings); NESTQUEST_DB_PATH is fixed to /var/lib/nestquest/nestquest.db by compose.

The published port binds NESTQUEST_BIND_ADDRESS from deploy/.env and defaults to 127.0.0.1 (localhost only). In production TLS is terminated by the shared Traefik reverse proxy, which serves https://nestquest.cubecraftlabs.com (Let's Encrypt, auto-renewed, HTTP redirected to HTTPS) and forwards to the API box on port 8080 — so on that box set NESTQUEST_BIND_ADDRESS to its LAN address (10.60.1.14). Traefik only allows /api/v1/panel from the Home Assistant host. The full deployment, restore and verification procedure is in deploy/RUNBOOK.md.

Continuous backup (Litestream)

Compose also runs a litestream sidecar (litestream/litestream:0.5.17, config deploy/litestream.yml). It mounts the same nestquest-db volume and continuously replicates /var/lib/nestquest/nestquest.db over SFTP to the Synology NAS CubeCraft-SRVR1 (joshua@10.60.1.50), directory /volume1/homes/Joshua/nestquest-backups (/homes/Joshua/... in the NAS's chrooted SFTP view) — so the backup is outside the Docker host and its volume. A snapshot is taken daily and a week of snapshots plus incremental changes is retained; the NAS host key is pinned in the config.

The SFTP login uses a dedicated keypair that lives only on the API box and is git-ignored. Create it once, before up:

ssh-keygen -t ed25519 -N "" -C nestquest-litestream -f deploy/litestream_key

then append restrict + the contents of deploy/litestream_key.pub to joshua@10.60.1.50:~/.ssh/authorized_keys. Compose mounts the private key read-only at /etc/litestream/sftp_key. The sidecar runs as uid/gid 999 (the API's nestquest user), so give it the key:

sudo chown 999:999 deploy/litestream_key && chmod 600 deploy/litestream_key

The sidecar needs WRITE access to the database volume: Litestream adds its own _litestream_seq/_litestream_lock tables to the database, writes the -wal/-shm files and checkpoints the WAL, and fails with "unable to open database file" on a read-only mount. That access is contained rather than removed — the sidecar runs as the non-root volume owner with a read-only root filesystem, no-new-privileges and all Linux capabilities dropped.

Check replication with docker compose -f deploy/compose.yaml logs litestream or docker compose -f deploy/compose.yaml exec litestream litestream ltx /var/lib/nestquest/nestquest.db. To restore (high level): stop the api service, run litestream restore -config /etc/litestream.yml -o <new path> /var/lib/nestquest/nestquest.db from a litestream container with the same mounts, verify the restored file, move it into place on the volume (removing any stale -wal/-shm files), and start api again.