daybook/docs/deployment.md
peter 4fa20e05c2 Docs: add README, document /admin/, refresh for v0.2.x
- Add a top-level README (repo had none): overview, local quick start,
  posting via API keys / the skill endpoint, a docs index, and the admin
  split.
- deployment.md: new "Django Admin (/admin/)" section covering how to log in,
  the is_superuser vs is_staff distinction, how to grant /admin/ access, and
  the raw-edit-bypasses-app-logic caution.
- Refresh constraint wording to per-user multiple API keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 11:37:35 +01:00

7.8 KiB

Deployment

Daybook deploys the same way on any Linux host with Docker: a cloud VM, a home server, a Proxmox guest, whatever. It runs as a single Docker container (Gunicorn) with all persistent state — the SQLite database and artifact files — on a bind-mounted ./data directory outside the image.

Target host (cloud VM, home server, ...)
  Docker Engine
    daybook container
      /app     application code baked into the image
      /data    persistent volume: db.sqlite3, artifacts_store/

Files In deploy/

File Purpose
Dockerfile Builds the Daybook image.
docker-compose.yml Runs Daybook with a persistent ./data:/data bind mount.
entrypoint.sh Runs migrations, collects static files, then starts Gunicorn.
env.example Template for deployment environment variables.

Prerequisites On The Target Host

  • Docker Engine + the Docker Compose plugin
  • Git (for the clone-and-pull workflow below)
docker --version
docker compose version

First-Time Setup

Clone the repo onto the host at a stable path:

git clone <REPO_URL> /opt/daybook
cd /opt/daybook

Create the environment file:

cp deploy/env.example deploy/.env

Generate a secret key:

python3 -c "import secrets; print('DJANGO_SECRET_KEY=' + secrets.token_urlsafe(50))"

Edit deploy/.env and set at minimum:

DJANGO_DEBUG=0
DJANGO_SECRET_KEY=<generated-secret>
DJANGO_ALLOWED_HOSTS=<vm-ip-or-dns-name>,localhost,127.0.0.1
DAYBOOK_DATA_DIR=/data
DATABASE_PATH=/data/db.sqlite3
ARTIFACTS_ROOT=/data/artifacts_store

There is no shared API token to configure — Daybook uses per-user API keys created under Settings → API Keys once a user exists (each user can hold several, one per agent, each individually revocable). Do not commit deploy/.env.

Create the persistent data directory and bring the stack up:

mkdir -p deploy/data
docker compose -f deploy/docker-compose.yml up -d --build

Create your first login (superuser) so you have something to log in with and a personal API token to post through:

docker compose -f deploy/docker-compose.yml exec daybook python manage.py createsuperuser

Verify Deployment

docker compose -f deploy/docker-compose.yml ps
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. (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://<vm-ip>:8000/ in a browser and log in with the superuser you just created, then create a personal key under Settings → API Keys and use it for a smoke-test post:

curl -fsS -X POST http://127.0.0.1:8000/api/artifacts \
  -H "Authorization: Bearer <your-personal-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "deploy-smoke",
    "kind": "markdown",
    "title": "Deployment Smoke Test",
    "content": "# Deployment Smoke Test\n\nDaybook accepted an artifact."
  }'

Fast Redeploys

Once the host has a git clone, shipping a new version is two commands:

cd /opt/daybook
git pull
docker compose -f deploy/docker-compose.yml up -d --build

The entrypoint runs Django migrations on every start, so schema changes are picked up automatically. deploy/.env and deploy/data/ are untouched by git pull since they aren't tracked in the repo.

Operational Commands

docker compose -f deploy/docker-compose.yml logs -f daybook
docker compose -f deploy/docker-compose.yml restart daybook
docker compose -f deploy/docker-compose.yml down
docker compose -f deploy/docker-compose.yml up -d
docker compose -f deploy/docker-compose.yml exec daybook python manage.py check
docker compose -f deploy/docker-compose.yml exec daybook sh

Django Admin (/admin/)

Daybook ships Django's built-in admin at /admin/ as an operator break-glass tool — for raw-data fixes: reset a password, inspect or delete any user's artifacts, adjust a quota, audit or revoke API keys. It is separate from the in-app Settings area, which is the everyday product surface (see the in-app guide / docs/ for normal use).

Logging in: open /admin/ and sign in with an account that has is_staff = True. The account you create with createsuperuser has it (plus full superuser rights). Note the two different admin concepts:

  • is_superuser — Daybook's in-app admin (can reach Settings → Users).
  • is_staff — can log into Django's /admin/ site.

The createsuperuser account has both. A user promoted in-app via Settings → Users ("Grant admin access") gets is_superuser only — so they can administer Daybook through its own UI but cannot reach /admin/. Regular users have neither. To grant someone /admin/ access, set staff explicitly:

docker compose -f deploy/docker-compose.yml exec daybook \
  python manage.py shell -c "from django.contrib.auth.models import User; u=User.objects.get(username='NAME'); u.is_staff=True; u.save()"

Caution: the admin edits database rows directly and bypasses app logic — e.g. deleting an artifact there does not clean up its files on disk the way the app's own delete does. Use it for inspection and targeted fixes, not routine management.

Backup And Restore

Back up deploy/data/ — it contains the SQLite database and all artifact files:

tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data

For a consistent snapshot during heavy writes, stop the container first:

docker compose -f deploy/docker-compose.yml down
tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data
docker compose -f deploy/docker-compose.yml up -d

To restore: stop Daybook, replace deploy/data/ with the backup contents, then start it again.

Reverse Proxy Notes

If putting Daybook behind Nginx, Caddy, Traefik, or a cloud load balancer:

  • Proxy to http://<vm-ip>:8000.
  • Add the public hostname to DJANGO_ALLOWED_HOSTS.
  • Terminate TLS at the proxy; keep DJANGO_DEBUG=0.
  • Set DJANGO_CSRF_TRUSTED_ORIGINS to the public HTTPS origin, and configure the proxy to forward X-Forwarded-Proto: https. Without both, login and any other POST fails with "CSRF verification failed" — Django has no way to know the original request was HTTPS once the proxy terminates TLS and forwards plain HTTP to the container.
DJANGO_ALLOWED_HOSTS=daybook.example.com,localhost,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=https://daybook.example.com

Forwarding the proto header, by proxy:

Nginx:   proxy_set_header X-Forwarded-Proto $scheme;
Apache:  RequestHeader set X-Forwarded-Proto "https"   (requires a2enmod headers)
Caddy:   header_up X-Forwarded-Proto https

Deployment Checklist

  • deploy/.env exists with a real (non-placeholder) DJANGO_SECRET_KEY.
  • DJANGO_DEBUG=0.
  • DJANGO_ALLOWED_HOSTS includes the hostname/IP being used.
  • If behind a reverse proxy: DJANGO_CSRF_TRUSTED_ORIGINS is set and the 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.
  • 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 key from Settings → API Keys.

Known MVP Constraints

  • SQLite is used by default — fine for a single-VM self-hosted deployment.
  • Per-user API keys (multiple per user, individually revocable); no shared/global key.
  • Reader/browser access requires login; feeds are private per user.
  • Manual drag/drop upload and content revisions are deferred.