CLI: explicit 'paskia migrate' subcommand for legacy conversion

Serve never converts databases: with no paskia.kantadb it points at
'paskia init', or at 'paskia migrate' when legacy *.paskiadb candidates
exist. migrate converts a lone candidate, or the one named by --rp-id
when several exist; the rest stay in place. devserver fails fast with
the same hint. gitignore covers paskia.kantadb and *.converted-bak.
This commit is contained in:
2026-09-06 22:11:17 +00:00
parent 9b10663c10
commit fefd54f02a
7 changed files with 148 additions and 56 deletions
+23 -19
View File
@@ -188,15 +188,19 @@ instance:
- `--listen`: stored into `Config.listen` (process-global).
- Seeds the admin user + registration reset link (link URL from the
default realm) and prints the link. Refuses to run if
`paskia.kantadb` already exists, or if an un-adopted legacy
`*.paskiadb` is present (serve adopts it first).
`paskia.kantadb` already exists, or if an unconverted legacy
`*.paskiadb` is present (`paskia migrate` converts it first).
- **`paskia migrate`** — converts a legacy `<rp-id>.paskiadb` database
(§10) to `paskia.kantadb`. With several legacy candidates, `--rp-id`
selects `<rp-id>.paskiadb` by name; the others are left in place.
- **`paskia`** — serve. Takes **no realm options**; only `--listen`
(per-run override of stored `Config.listen`, never persisted). Startup:
legacy-adoption pre-flight → open `paskia.kantadb` → validate the
stored realm set cross-realm (rp-ids distinct; auth hosts distinct from
each other and from every rp-id; related origins capped and
collision-free) → build the realm registry → serve. Missing database →
startup error pointing at `paskia init`.
open `paskia.kantadb` → validate the stored realm set cross-realm
(rp-ids distinct; auth hosts distinct from each other and from every
rp-id; related origins capped and collision-free) → build the realm
registry → serve. The serve command never converts databases: with no
`paskia.kantadb`, the startup error points at `paskia init`, or at
`paskia migrate` when legacy `*.paskiadb` candidates are present.
Nested rp-ids are allowed (longest-suffix dispatch determinism). Adding a
child rp-id moves **no data** — users are global; only new ceremonies
@@ -397,17 +401,17 @@ effective_auth_host(realm) = realm.auth_host or first_configured_auth_host or No
deployment.
- **User files** (avatars) live in the fixed sibling directory
**`paskia.data/users/`**.
- **Legacy adoption**: if `paskia.kantadb` is absent and exactly one
`*.paskiadb` candidate exists in CWD — a directory containing
`main.db`, or a legacy single-file database — it is adopted: `main.db`
(or the single file) becomes `paskia.kantadb`, `users/` becomes
`paskia.data/users/`, and the old directory is renamed aside to
`<name>.converted-bak`. Multiple candidates → startup error listing
them, asking the operator to remove or rename strays (e.g. a
`*.bak.paskiadb` backup); empty directories are ignored. Adoption runs
as an explicit pre-flight step in the serve command, before the
read-only startup open — read-only opens never trigger adoption or
writes.
- **Legacy conversion**: `paskia migrate` converts a legacy
`*.paskiadb` database — a directory containing `main.db`, or a legacy
single-file database — into `paskia.kantadb`: `main.db` (or the single
file) becomes `paskia.kantadb`, `users/` becomes `paskia.data/users/`,
and the old directory is renamed aside to `<name>.converted-bak`. A
lone candidate converts without options; with several candidates
`--rp-id <rp-id>` selects `<rp-id>.paskiadb` by name and the rest are
left in place (e.g. a `*.bak.paskiadb` backup does not block
conversion). Empty directories are ignored. Conversion is an explicit
operator action, never a serve side effect — read-only opens never
trigger conversion or writes.
- The legacy database's structs live in a separate module
(`paskia/db/legacy.py`). There is no multi-database merging.
- The startup box prints per-realm lines.
@@ -417,7 +421,7 @@ effective_auth_host(realm) = realm.auth_host or first_configured_auth_host or No
- One `Kanta` for `paskia.kantadb`, opened once in the lifespan; one
background cleanup task (DB is global).
- The kanta bootstrap hook only ever fires for a database created by
`paskia init`; the serve command never bootstraps.
`paskia init` or `paskia migrate`; the serve command never bootstraps.
- The registry is built from the stored `Config` after open; per-realm
`Passkey` instances constructed (each realm's origins validated at
startup — fail-fast, including related-origin cap checks).