Co-authored-by: dev_agent <17+dev_agent@code.cubecraftcreations.com> Co-committed-by: dev_agent <17+dev_agent@code.cubecraftcreations.com>
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.