User stores
Every pool’s users, sessions, and tenants live in exactly one user store. New pools use the managed store; user-store moves them where you want. Concepts → User stores covers the model.
user-store use-bucket — the K3 fast path
Point the pool at a K3 bucket you own; users become _appid_* tables you can query like any other data:
dodil appid user-store use-bucket my-app my-users-bucket --create-sa --yes--create-sa provisions the service account AppID uses to reach the bucket (and grants it k3.editor); with an existing account, pass --sa <client-id> instead. --pg-host overrides the default pg-wire host (pg.uk-lon-1.dodil.io:5432).
The pool record stores only the address (k3+sa://sa@host:port/bucket) — never a credential; AppID mints short-lived tokens on demand.
user-store set — explicit DSN
dodil appid user-store set my-app 'k3+sa://[email protected]:5432/my-users-bucket' --yes
dodil appid user-store set my-app 'postgres://user:[email protected]:5432/appdb' --yes
dodil appid user-store reset my-app --yes # back to the managed store
set/resetre-point the pool without copying data — right for an empty pool. For a pool with users, usemigrate.
user-store migrate — move live, with the data
dodil appid user-store migrate my-app bucket:my-users-bucket
dodil appid user-store migrate my-app 'postgres://…' --keep-source
dodil appid user-store status my-app # phase + row counts
dodil appid user-store migrate-abort my-app --yesbucket:<name> is sugar for the full k3+sa:// DSN (combine with --sa / --pg-host as in use-bucket). The command follows progress until done; --no-follow returns immediately.
The migration runs in phases:
warm_copy users copied while the pool stays fully live
locked_delta short write-lock — sign-in and admin writes answer 503
verifying row counts compared source vs target
(cutover) pool re-pointed at the target
purging source data removed — skipped with --keep-source
done | failedA count mismatch aborts and rolls back automatically; the source is only purged after verification passes. While any migration is running, the pool’s state is migrating and mutating operations are refused — pool get and user-store status show the live phase and counts.