Wisp configuration lives in a few clearly separated layers. This guide maps every knob to where you set it.
The layers
- Compile-time —
config/config.exs(all envs),config/dev.exs,config/prod.exs. - Runtime —
config/runtime.exs(the:prodblock reads env vars; required for secrets). - Database — the
Settingssingleton (id=1), user-editable via the admin UI and audited. - Environment —
fly.toml[env](static), merged with Fly secrets (fly secrets set, runtime-injected, overriding[env]).
Deployment knobs: env vars + Fly secrets
Required in production:
DATABASE_PATH— SQLite file on the persistent volume (boot raises if missing)SECRET_KEY_BASE— cookie/session signing key (mix phx.gen.secret)PHX_HOST— public hostname (also derives WebAuthnrp_id/origin and the mailer sender)RESEND_API_KEY— transactional email key (optional; falls back toMailer.Local)
Other deployment knobs: PORT (default 4000; 8080 on Fly, must match
http_service.internal_port), POOL_SIZE (default 5), PHX_SERVER, DNS_CLUSTER_QUERY.
Set only as Fly secrets: LITESTREAM_REPLICA_URL, LITESTREAM_ACCESS_KEY_ID,
LITESTREAM_SECRET_ACCESS_KEY, and (optionally) LITESTREAM_ENDPOINT.
Compile-time adapters (in config/config.exs): billing_adapter (the stub),
media_adapter (Local), theme_adapter (HEEx), mailer_adapter, plus the Ghost
content_api_token and the WebAuthn Wisp.Staff settings.
The admin Settings page (/admin/settings)
All install-wide content config is the Settings singleton, editable by any staff member
(manager-only forms noted):
- Basic identity: site title (required, 1–120 chars), author, contact email, sections, active theme
- SEO defaults: default meta description, default og:image URL,
robots.txtbody - Reading: posts per page (1–100), date format (strftime), time zone (IANA; unknown zones safely fall back to UTC at render)
- Discussion: default new-post visibility (public/members/paid), comments-on-by-default,
comment moderation (
auto_approve/hold), comment blocklist (one term per line, case-insensitive substring), and email-on-new-comment - Maintenance (manager-only, lockout-safe): toggle + message. The toggle uses a dedicated mutator that bypasses title validation, so an admin can always turn it back off.
The Appearance page (/admin/appearance, manager-only)
Separate from Settings purely for authorization (it writes the same Settings fields):
- Accent colour — strict hex (
#RGBor#RRGGBB), default#6366F1 - Heading font — a server-side allowlist (
Wisp.Appearance.heading_fonts()), defaultInter - Custom CSS — free text, stored verbatim and sanitized at render time before injection
The first-run setup wizard (/admin/setup)
Reachable without a staff session only while setup_complete=false; once finished it
redirects to the editor.
- Self-check — validates
DATABASE_PATHand that the request host matches the WebAuthnrp_id. - Name your site — title, author, contact email.
- Owner email — creates the owner staff user (idempotent), issues an enrollment token.
- Create a passkey — the WebAuthn enrollment ceremony.
- Don't lock yourself out (HARD GATE) — satisfy ONE recovery path: connect email (a real verified test-send) or add a second passkey. Finish stays disabled until then.
- Finish — flips
setup_complete=true, seeds a welcome draft, opens the editor.
Knob → location table
| Knob | Where to configure | Type |
|---|---|---|
| Site title / author / contact email | Settings page (or wizard step 1) | DB Settings |
| Sections, active theme | Settings page | DB Settings |
| SEO description / image / robots.txt | Settings page | DB Settings |
| Posts per page, date format, time zone | Settings page | DB Settings |
| Default visibility, comments, moderation, blocklist | Settings page | DB Settings |
| Maintenance mode + message | Settings page (manager-only) | DB Settings |
| Accent color, heading font, custom CSS | Appearance page (manager-only) | DB Settings |
| HTTP port, database path, pool size | fly.toml [env] / env var |
Config (runtime.exs) |
WebAuthn rp_id / origin |
runtime.exs (prod) / config.exs (dev) | Config |
SECRET_KEY_BASE, PHX_HOST, RESEND_API_KEY |
Fly secrets / env var | Config (runtime.exs) |
| Litestream URL + credentials | Fly secrets | Config (runtime.exs) |
| Billing / media / theme adapter | config/config.exs |
Config (compile-time) |
Things worth knowing
- Settings is a singleton (id=1).
Wisp.Settings.get()lazily creates it with defaults. - Audit logging never stores values — only action metadata and changed keys (never
secrets,
custom_css,robots_txt, or field contents). - Maintenance mode is lockout-safe: anonymous visitors get a 503 + message, but staff,
the
/admintree,/authroutes, and static assets are always exempt.