diff --git a/deploy/cloud_agent_deploy.md b/deploy/cloud_agent_deploy.md index 82d8003..d90f5db 100644 --- a/deploy/cloud_agent_deploy.md +++ b/deploy/cloud_agent_deploy.md @@ -1,25 +1,33 @@ # Cloud Deployment Runbook For A Dedicated Deploy Agent -This document is written for an autonomous AI agent responsible for deploying -and operating Daybook on a cloud VM, without a human walking through each -step. Follow it in order. Where a decision depends on information you don't -have (VM IP, domain, whether TLS is required), ask the operator once, then -proceed autonomously for routine redeploys. +This is written for a capable (Opus-class) agent deploying and operating +Daybook on a cloud VM. Treat it as a guide and a source of Daybook-specific +facts, **not a rigid script** — you're expected to use judgment, adapt to +the actual VM, and read the authoritative sources rather than follow steps +blindly. The steps below are a sensible default path; deviate where the host +warrants it. -For background on the app itself and the general (human-facing) version of -this workflow, see `docs/deployment.md`. This file is the operational SOP; -that one is the reference. +Ground truth, in order of authority (prefer these over this doc if they ever +disagree): -## Source Of Truth +- **The repo** — `https://git.cygnus.systems/peter/daybook.git` (private + Forgejo). Everything ships from here; deploy from a clone so `git pull` + works. Note: this Forgejo's git-SSH is on port **2222**, not 22. +- **`deploy/`** — `Dockerfile`, `docker-compose.yml`, `entrypoint.sh`, + `env.example`. These *are* the deployment: the image installs all Python + deps from `requirements.txt` (Django, DRF, Markdown, Pygments, nh3, + openpyxl, gunicorn, whitenoise), and `entrypoint.sh` runs `migrate` + + `collectstatic` on every start, so schema and static assets are always + current. Read them; don't assume. +- **`docs/deployment.md`** — the general (host-agnostic) deployment + reference and rationale. -The canonical source is a private Forgejo repository: - -```text -https://git.cygnus.systems/peter/daybook.git -``` - -Do not deploy from a copied/rsynced tree. Always deploy from a git clone of -this repository so `git pull` is available for fast redeploys. +Where a decision needs info you don't have (VM IP, domain, whether it's +internet-facing, whether a reverse proxy/secondary volume exists), ask the +operator once, then proceed autonomously for routine redeploys. The rest of +this file is the recommended path plus the non-obvious, Daybook-specific +gotchas (SSH port, CSRF-behind-proxy, bind address, data volume, healthcheck) +that are easy to get wrong. ## 0. Get Repo Access @@ -166,11 +174,14 @@ every start. ```bash docker compose -f deploy/docker-compose.yml ps -curl -fsS http://127.0.0.1:8000/api/capabilities +curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/api/capabilities # expect 403 ``` A `403` with `"Login or a valid API token is required"` is the **expected** -response when unauthenticated — it confirms the app is up. This `curl` runs +response when unauthenticated — it confirms the app is up. (Don't use +`curl -f` here: `--fail` treats the 403 as an error and hides it. The +container's own healthcheck hits the login page instead, which is 200 +without auth.) This `curl` runs against `127.0.0.1` from on the VM itself, so it works regardless of whether `DAYBOOK_BIND_ADDR` is set. Log in from a browser at whichever address is actually externally reachable at this point in the runbook — `http://:8000/` diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 2c9380f..cbb276c 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -22,7 +22,11 @@ services: volumes: - ${DAYBOOK_HOST_DATA_DIR:-./data}:/data healthcheck: - test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/api/capabilities"] + # Hit the login page, not /api/capabilities: capabilities returns 403 + # when unauthenticated, and `curl --fail` treats 403 as an error, which + # would mark a perfectly healthy container unhealthy. The login page + # serves 200 without auth and still proves Django is up and responding. + test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/accounts/login/"] interval: 30s timeout: 5s retries: 5 diff --git a/docs/deployment.md b/docs/deployment.md index 5f69b0f..aafbfad 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -86,11 +86,13 @@ docker compose -f deploy/docker-compose.yml exec daybook python manage.py create ```bash docker compose -f deploy/docker-compose.yml ps -curl -fsS http://127.0.0.1:8000/api/capabilities +curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/api/capabilities # expect 403 ``` An unauthenticated capabilities check returns 403 (login/token required) — -that's expected; it confirms the app is up and enforcing auth. Open +that's expected; it confirms the app is up and enforcing auth. (Don't add +`curl -f` — it treats the 403 as an error. The container healthcheck uses +the login page, which is 200 without auth.) Open `http://:8000/` in a browser and log in with the superuser you just created, then grab a personal token from Settings → Account and use it for a smoke-test post: @@ -187,8 +189,8 @@ Caddy: header_up X-Forwarded-Proto https proxy forwards `X-Forwarded-Proto: https` (login will 403 otherwise). - `deploy/data/` exists and persists across `git pull` + rebuild. - `docker compose -f deploy/docker-compose.yml ps` shows `daybook` healthy. -- `curl -fsS http://127.0.0.1:8000/api/capabilities` returns 403 without auth, - 200 with a valid token or session. +- `GET /api/capabilities` returns 403 without auth, 200 with a valid token or + session (check the status code — don't use `curl -f`, which errors on 403). - At least one superuser exists (`manage.py createsuperuser`) with a token from Settings → Account.