Configuration¶
Faridoon loads a YAML config file, then applies a small set of environment overrides for database connectivity (and a few bootstrap values). Runtime feature toggles live in the database as configuration variables (cvars), edited under Account → Settings.
Config file location¶
Search order:
--configdir <dir>/config.yaml(if the flag is set)./config.yaml./config/config.yaml$FARIDOON_CONFIG_FILE(if set and the path exists)/config/config.yaml
Docker images typically set FARIDOON_CONFIG_FILE=/config/config.yaml.
Database¶
These keys may be set in YAML or overridden by environment variables:
database.host/DB_HOSTdatabase.port(default3306; YAML only)database.user/DB_USERorDB_USERNAMEdatabase.password/DB_PASSorDB_PASSWORDdatabase.name/DB_NAMEorDB_DATABASE
sql-migrate (on container start) expects DB_HOST, DB_USER, DB_PASS, and DB_NAME. The entrypoint maps Laravel-style names to these when needed. DB_DRIVER selects the migration tree under database/ (default mysql; Faridoon ships MySQL only).
Application (YAML / env)¶
siteTitle/SITE_TITLE: initial site title used to seed thesite_titlecvar on first startup. After that, the live header and document title come from Settings.listen: HTTP listen address (default:8080)PORT: if set, overrideslisten(bare port like8080or a full address)
The expected database migration id is a compile-time constant (config.RequiredMigration), not a config setting.
Feature flags are not environment variables and are not set under a YAML features: block.
Settings (configuration variables)¶
Admins manage cvars at Account → Settings (/admin/settings). Missing defaults are inserted on startup if they do not already exist. Title, description, category, and ordinal metadata for known cvars are refreshed from application defaults on every startup (including upgrades of older installs). The settings editor groups by category and orders by ordinal.
| Key | Type | Default | Effect |
|---|---|---|---|
site_title | string | from siteTitle / SITE_TITLE | Header and document (<title>) site title |
quotes_per_page | int | 5 | Quotes shown per page on listing (1–127) |
theme_mode | string | auto | Site-wide light/dark appearance (auto, light, or dark) for all users |
custom_theme | string | (empty) | Optional PicoCrank supplemental drop-in theme for all users; empty uses Femtocrank only |
enable_voting | bool | off | Show vote controls; allow VoteQuote |
enable_registration | bool | on | Allow /register and Register |
enable_guest_add | bool | on | Allow logged-out users to submit quotes |
guest_add_require_approval | bool | on | Guest quotes wait for approval; do not disable on public/untrusted networks |
enable_syntax_highlighting | bool | off | Show syntax highlighting field when adding/editing quotes |
enable_markdown | bool | off | Allow per-quote Markdown formatting when adding/editing quotes |
enable_pwa_prompt | bool | off | Show the PWA install banner when the browser supports it |
Changing Settings reloads Init so the UI picks up new values without a process restart.
Privileges¶
Default groups: Admins (id 1) have SUPERUSER; Users (id 2) have no privileges until granted.
SUPERUSER— admin access (settings, users, webhooks, header links, diagnostics, audit logs, delete quotes). Implies all other privileges.APPROVE_QUOTES— approvals queue, approve/reject pending quotes, edit quotes.BYPASS_APPROVAL— new quotes are published without approval.
The first registered user joins Admins. Later registrations join Users. Rejecting a pending quote permanently deletes it. The last remaining admin cannot be demoted, deleted, or stripped of SUPERUSER.
Webhooks¶
Admins configure outbound HTTP callbacks at Account → Webhooks (/admin/webhooks). Each target has a URL, signing secret, enabled flag, and zero or more subscribed events from a code-defined catalog. Secrets are write-only (never returned by the API). Disabled targets and targets without a matching event subscription are skipped at delivery.
Supported events¶
| Event | When |
|---|---|
approval.requested | A quote is created pending approval |
Payload (approval.requested)¶
{
"event": "approval.requested",
"timestamp": "2026-08-05T13:52:00Z",
"quote": {
"id": 42,
"content": "...",
"created": "2026-08-05 13:51:00",
"approval": 0
}
}
Headers and signature¶
Content-Type: application/jsonX-Faridoon-Event: <event name>X-Faridoon-Signature: sha256=<hex>— HMAC-SHA256 of the raw JSON body using the target secret
Consumers verify by recomputing HMAC-SHA256 over the request body with the shared secret and comparing to the hex after sha256=. Delivery is fire-and-forget (short HTTP timeout); failures do not roll back the user action.
Auth (auth)¶
httpauthshim session settings. Sample keys:
localSessionCookieName(e.g.faridoon-sid)baseDir(session storage directory)
User accounts and passwords live in the MySQL users table. Legacy sha1/bcrypt hashes are accepted and upgraded to argon2id on login. Authorization always uses privileges loaded from the database for the current request.