Skip to Content
We are live but in Staging 🎉
CLI GuideUser Stores

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/reset re-point the pool without copying data — right for an empty pool. For a pool with users, use migrate.

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 --yes

bucket:<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 | failed

A 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.