Introduces a `documents` collection and full-stack UI for tracking pilot certificates, aircraft registrations, insurance, airspace authorisations, contracts, and other paperwork. - Migration 1720300800_add_documents.js (also provisioned live on remote PB): doc_type/owner/expiry/status/access_tier + file blob, a self-referential `replaces` version chain, audit fields, and a partial index on expiry_date. - API Server (documents.go): role-scoped CRUD, server-computed expiry assessment, ?expiring=N query, versioning (replaces -> version+1, old row auto-archived), and streamed blob download. admin.go gains multipart upload, file tokens, and protected-file streaming. - Web App: BFF passthrough (multipart create + streamed download), api.js client fns, and Documents.vue wired into the Documents nav slot. Blobs live in PocketBase file storage for now; only that backend swaps when S3-compatible object storage lands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PocketBase — PilotVault schema
PilotVault adds an organizations collection and three fields to the users
auth collection:
preferences(JSON) — each user's settings blob. Written with the user's own token, so PocketBase's default owner-only update rule (@request.auth.id = id) is all the authorization needed.role(select:superadmin|admin|user) — the user-rights level. A superadmin spans every organization; an admin is scoped to their own organization (may manage its users and admins, but not superadmins); a user has no management rights. Missing/empty is treated asuser.organization(relation →organizations, maxSelect 1, optional) — which org the user belongs to. Nullable: a user may belong to no organization.
The organizations collection is a plain base collection with a unique
name. It is reached only through the API Server's superuser service account
(its API rules stay locked to superusers), the same way user management works.
User + org management requires a service account
Listing/creating/deleting users and organizations is done by the API Server
using a superuser service account (POCKETBASE_ADMIN_EMAIL /
POCKETBASE_ADMIN_PASSWORD), but only after verifying the caller's own token
resolves to a manager role (admin for user management, superadmin for org
management). This is the single place the server uses elevated PocketBase
credentials; without the env vars, the /api/users and /api/orgs endpoints
return 503 and the rest is unaffected.
Preferences never need the service account — they use the caller's own token.
Add the schema
Pick one of the following.
Option A — migration (recommended)
Copy the migration files into your PocketBase deployment's pb_migrations/
directory and restart PocketBase (migrations run automatically on boot; they
target the PocketBase v0.22+/v0.23 JS migration API). They are idempotent, so
they are safe even if the schema was already provisioned live:
pb_migrations/1720300000_add_users_preferences.jspb_migrations/1720300100_add_users_role.jspb_migrations/1720300200_add_organizations.jspb_migrations/1720300300_add_users_organization.jspb_migrations/1720300400_extend_users_role_superadmin.jspb_migrations/1720300500_seed_orgs_and_users.js— seeds the PilotVault org + baseline accounts
Option B — Admin UI (any version)
- Open the PocketBase Admin UI → Collections → New collection
organizations(base); add a text fieldname(required) with a unique index. - Collections →
users→ New field. Add JSON fieldpreferences, not required, max size ~5 MB. - Add Select field
role, valuessuperadmin,admin,user, max select 1. - Add Relation field
organization→organizations, max select 1, not required, cascade delete off. - Save.
Verify
With a normal user token you should be able to round-trip the field:
# 1) log in (PocketBase directly, or via the API Server /api/auth/login)
TOKEN=... # the "token" from the auth response
# 2) save
curl -X PATCH "$PB_URL/api/collections/users/records/$USER_ID" \
-H "Authorization: $TOKEN" -H "Content-Type: application/json" \
-d '{"preferences":{"fontSize":"lg","themeMode":"dark"}}'
# 3) read back
curl "$PB_URL/api/collections/users/auth-refresh" -X POST -H "Authorization: $TOKEN"
In the app the round-trip is: browser → GET/PUT /bff/preferences → API Server
GET/PUT /api/preferences → PocketBase user record.