No description
Find a file
Claude cf299e06dd fix: run PocketBase as root so it can write Coolify's persistent mount
Coolify attaches a root-owned volume/bind at /pb_data; the non-root pb user
couldn't write it, so SQLite failed with "unable to open database file (14)".
Drop the pb user and run as root. Verified against a root-owned bind mount
locally: server starts clean, migrations apply, data.db persists to the mount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 21:08:12 +02:00
pb_migrations feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00
scripts feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00
.dockerignore feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00
.gitignore feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00
docker-compose.yml feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00
Dockerfile fix: run PocketBase as root so it can write Coolify's persistent mount 2026-07-15 21:08:12 +02:00
README.md feat: PocketBase backend + account/profile schema (epic #6, issue #9) 2026-07-15 20:22:45 +02:00

myanime-backend

PocketBase backend for the myanime app — auth, database, realtime, file storage and admin UI in a single Go binary. This is the foundation the account features build on: cloud sync, codeless device sign-in, profiles, child profiles, recommendations, and gated auto-update (epic #6).

  • No E2E. TLS in transit + at-rest on disk. The server is deliberately readable so recommendations, the admin nsfwEnabled flag, and painless password reset all work.
  • Schema as code. The collections live in pb_migrations/ and auto-apply on boot — never hand-click the schema in the admin UI, or the next deploy will drift.
  • Version-pinned. The Dockerfile downloads the official PocketBase v0.39.6 static binary from GitHub releases (no third-party image). Bump PB_VERSION there and in docker-compose.yml to upgrade.

Data model

users ─┬─* profiles ─┬─* watch_state   (resume points; 1 row per profile+item)
 (auth)│             ├─* watchlist      (1 row per profile+item)
       │             └─1 prefs          (playback/audio/subtitle blob)
       └ nsfwEnabled (admin-only bool → gates adult vs clean build, issue #16)
Collection Type Key fields
users auth username (unique when set), email, password, nsfwEnabled (admin-only)
profiles base user→users, name, avatar (file), isChild, pinHash (hidden)
watch_state base profile→profiles, itemId, itemType, position, duration, updatedAt
watchlist base profile→profiles, itemId, itemType, addedAt
prefs base profile→profiles, data (json)

Access rules. A user only ever reads/writes their own users row, their own profiles, and rows whose profile.user is them. nsfwEnabled is never accepted from a client (@request.body.nsfwEnabled:isset = false on create and update) — only a superuser sets it, from the admin UI. Relations cascadeDelete, so deleting a user removes their profiles and all child rows. Unique indexes keep one resume/watchlist row per (profile, itemId) and one prefs row per profile.

Local run / verify

docker compose up --build            # http://localhost:8090
# create the first superuser (one-time):
docker compose exec pocketbase pocketbase superuser upsert you@example.com 'a-strong-pass' --dir=/pb_data
# admin UI: http://localhost:8090/_/

The end-to-end access-rule check (two users can't see each other's data; a user can't set their own nsfwEnabled; a superuser can) lives in scripts/verify.py:

python scripts/verify.py            # expects the server on :8090 + the superuser above

Deploy on Coolify

The schema and image are automated; the steps below need your hands, Coolify access and secrets (which never live in this repo).

  1. Push this repo to a remote Coolify can reach (e.g. GitHub, like myanime-pair-server).
  2. New Resource → Dockerfile application, point it at this repo. It listens on 8090.
  3. Persistent storage: add a volume mounted at /pb_data (DB + uploaded files). Without this you lose data on redeploy.
  4. Domain + TLS: set the domain to pb.petruzalekr.cz; Coolify/Traefik provisions the Let's Encrypt cert and terminates TLS in front of the plain :8090. Force-HTTPS on.
  5. Bootstrap the superuser once, from the container terminal: pocketbase superuser upsert you@example.com 'a-strong-pass' --dir=/pb_data (or open the printed /_/#/pbinstall/... link on first boot). Lock down the admin UI — strong password; the _superusers collection is the only way in.
  6. SMTP (admin UI → Settings → Mail settings): point at your SMTP host so verification / password-reset emails send. Send the test email to confirm.
  7. Automated backups (admin UI → Settings → Backups): enable the schedule and, ideally, S3 off-site upload. Coolify volume backups are a second layer.
  8. App wiring (later issues, not here): point the app's API base at https://pb.petruzalekr.cz.

Verify the deploy

  • Admin UI reachable over HTTPS only; HTTP redirects up.
  • Create a test user + profile + a couple of rows via admin or REST; confirm a second user can't read them (run scripts/verify.py against the live URL by editing BASE).
  • Trigger a password-reset email and confirm delivery.
  • Confirm a backup ran and can be downloaded/restored.

Security

Account/session tokens and the addon plaintext-credential "tokens" are never logged or echoed (see the app's CLAUDE.md). pinHash is a hidden field and never leaves the API. nsfwEnabled is admin-only by rule, not just by UI.

Upgrading PocketBase

Bump PB_VERSION in the Dockerfile (and the compose arg), rebuild, redeploy. Check the PocketBase release notes for migration-API or schema changes first, and test locally with docker compose up --build before deploying.