Pools & configuration
pool
# create — --with-local enables email+password sign-in in the same call
dodil appid pool create my-app --display-name "My App" --with-local
dodil appid pool list
dodil appid pool get my-app # includes live migration status when one is running
# partial update: only the documents you pass change; "{}" clears one
dodil appid pool update my-app --display-name "My App (EU)" \
--policies '{"cors_allowed_origins":["https://app.example.com"]}'
dodil appid pool rotate-keys my-app # new signing key; old key stays in JWKS for cached verifiers
dodil appid pool delete my-app --yes # keeps bucket data if the store is a K3 bucket
dodil appid pool delete my-app --yes --wipe-bucket-datapool update takes any of --display-name, --policies, --settings, --branding, --role-catalog (inline JSON, @file, or -). The dedicated settings / branding / roles commands below are ergonomic wrappers over the same documents.
pool test-flow — the headless smoke test
Drives the full authorization-code + PKCE flow — authorize, credential submit, code exchange — without a browser, and prints the resulting tokens:
dodil appid pool test-flow my-app [email protected] 'S3curePass!' \
--redirect-uri https://app.example.com/callbackUse it after every configuration change; it exercises exactly what your app will.
settings
The pool’s behavioral knobs:
dodil appid settings get my-app
dodil appid settings set my-app \
--allow-signup=true \
--require-email-verification=true \
--password-min-length 10 \
--access-ttl-secs 900 \
--refresh-ttl-secs 2592000 \
--refresh-reuse-grace-secs 10 \
--redirect-uris "https://app.example.com/callback,http://localhost:5173/callback"| Flag | Default | Meaning |
|---|---|---|
--allow-signup | true | Self-service signup on the hosted page and /signup endpoint |
--require-email-verification | false | Block sign-in until the email link is clicked |
--password-min-length | 8 | 8–128; the only password rule |
--access-ttl-secs | 900 | Access-token lifetime |
--refresh-ttl-secs | 30 days | Refresh-family lifetime |
--refresh-reuse-grace-secs | 10 | Double-submit forgiveness window (0 = strict, max 60) |
--redirect-uris | — | The /authorize allowlist — required for the code flow |
branding
One JSON document that themes the hosted login/reset/verify pages and the transactional emails:
dodil appid branding get my-app
dodil appid branding set my-app @branding.json{
"logo_url": "https://cdn.example.com/logo.svg",
"colors": { "primary": "#4f46e5", "background": "#ffffff" },
"dark": { "colors": { "background": "#0b0b12" } },
"layout": "card",
"copy": { "title": "Sign in to My App" },
"links": { "terms": "https://example.com/terms", "privacy": "https://example.com/privacy" },
"email": { "from_name": "My App", "accent": "#4f46e5" }
}All keys are optional; colors accept #hex / rgb() / hsl(), image URLs must be https. The hosted page picks branding up immediately (it’s served pre-auth from {issuer}/branding). Full schema in the hosted login recipe.
roles
The role → permission catalog, expanded into every token’s permissions[] claim:
dodil appid roles get my-app
dodil appid roles set my-app admin=users.write,billing.read editor=posts.write viewer=posts.read
dodil appid roles set my-app --file roles.jsonRenaming or re-scoping a role here re-shapes future tokens without touching any user.
connection
How users sign in. local (email + password) is the implemented kind:
dodil appid connection add my-app local --enabled
dodil appid connection list my-app
dodil appid connection remove my-app local --yesconnection add accepts a --config JSON for kinds that take configuration; other kinds (oauth:google, oidc, saml, anonymous) are reserved but not implemented yet — adding them today does not enable a new sign-in method.