Settings object
GET /api/settings (superuser-only) returns the whole object below,
minus write-only secrets, stripped by Settings::to_public_json
(crates/core/src/settings.rs): smtp.password, s3.secret,
backups.s3.secret, llm.apiKey, push.vapid.privateKey,
push.fcm.serviceAccountJson, and push.apns.key are all accepted on
write and stored, but never echoed back on read. PATCH /api/settings
deep-merges a partial payload onto the current settings, so omitting a
secret on an update keeps its stored value rather than blanking it.
| Key | Default |
|---|---|
appName |
"Acme" |
appURL |
"http://localhost:8090" |
senderName |
"Support" |
senderAddress |
"support@example.com" |
hideControls |
false |
accentColor |
"#1055c9" |
logoUrl |
"" |
brandColor |
"" |
logoUrl/brandColor style the shared email base layout — see
Email → Templates: a non-empty logoUrl is
shown at the top of every layout-wrapped email, and brandColor
replaces the layout’s default #16161a button/accent color.
| Key | Default | Notes |
|---|---|---|
enabled |
false |
false routes mail through the zero-config Log backend instead. |
host |
"smtp.example.com" |
|
port |
587 |
|
username |
"" |
|
password |
(write-only) | Stripped from every read. |
authMethod |
"" |
|
tls |
false |
|
localName |
"" |
HELO/EHLO hostname. |
smtp.enabled: false — the default, and every fresh local install —
doesn’t mean mail goes nowhere: the Log backend keeps the last 100
emails it “sends” in memory, browsable superuser-only at
GET /api/dev/mails and in the dashboard’s Dev inbox tab (next to
Delivery, both under Email), including the raw HTML and any
verification / password-reset / OTP token link. It stops the moment
smtp.enabled is true — /api/dev/mails then 404s outright — so real
outgoing mail is never captured there.
s3 (file storage)
Section titled “s3 (file storage)”Configured from the dashboard’s Application → Storage tab, not
Email — file storage isn’t an email concern, even though it shares a
settings object with smtp at the API level.
| Key | Default |
|---|---|
enabled |
false (local disk) |
bucket |
"" |
region |
"" |
endpoint |
"" |
accessKey |
"" |
secret |
(write-only) |
forcePathStyle |
false |
storage
Section titled “storage”Image transforms and the per-user storage quota — both apply to files on local disk too, not just
S3, which is why they live in their own section instead of under s3.
| Key | Default |
|---|---|
imageTransformsEnabled |
true |
maxTransformDimension |
4000 (0 = no limit) |
userQuotaBytes |
0 (disabled) |
imageTransformsEnabled gates ?w=/?h=/?fit=/?format=/?q= on the files route (?thumb= is
unaffected either way). maxTransformDimension clamps w/h for a non-superuser request rather
than rejecting it, capping the set of distinct cached variants a request can generate. See
Files → Image transforms.
userQuotaBytes caps, per auth record, the sum of size across every file field of every record
whose collection has an ownerField set (a per-collection schema setting, not here) to that auth
record. 0 disables enforcement entirely — no collection is ever gated, regardless of
ownerField. See Files → Storage quota.
backups
Section titled “backups”| Key | Default |
|---|---|
cron |
"" (disabled) |
cronMaxKeep |
3 |
s3 |
a nested s3-shaped block for uploading backups somewhere other than local disk |
Setting backups.cron to a non-empty cron expression is what makes the
__pbAutoBackup__ scheduled job appear alongside the always-present
__pbDBOptimize__/__pbMFACleanup__/__pbOTPCleanup__/__pbLogsCleanup__
jobs — see Crons.
rateLimits
Section titled “rateLimits”| Key | Default |
|---|---|
enabled |
true |
excludedIPs |
[] |
rules |
eight built-in rules, see below |
Each rule is { label, audience, duration, maxRequests } — label is a
path prefix (/api/), an exact path, or a <collection-or-*>:<tag> pair
(*:auth, *:create, posts:list); audience is "" (everyone),
@guest, or @auth; duration is the window in seconds. The shipped
defaults:
label |
audience |
duration |
maxRequests |
|---|---|---|---|
*:auth |
"" |
3s | 2 |
*:create |
"" |
5s | 20 |
/api/batch |
"" |
1s | 3 |
mails:send |
"" |
60s | 20 |
mails:send:user |
@auth |
60s | 10 |
realtime:publish |
"" |
10s | 60 |
realtime:presence |
"" |
10s | 120 |
/api/ |
"" |
10s | 300 |
mails:send:user only ever applies to a non-superuser/non-API-key caller
sending through a template’s sendRule
— it’s checked before the general mails:send rule for that caller
(more specific wins), so it’s the effective ceiling for that caller
shape even though mails:send is technically an exact-tag match too.
More specific labels win over less specific ones for the same request — see Rate limiting for the matching order and how per-collection tags are derived from the URL.
trustedProxy
Section titled “trustedProxy”| Key | Default |
|---|---|
headers |
[] |
useLeftmostIP |
false |
See Reverse proxy & TLS for why the client IP used for rate limiting depends on this being configured correctly behind a proxy.
| Key | Default |
|---|---|
enabled |
false |
maxRequests |
50 |
timeout |
3s |
maxBodySize |
0 (unlimited) |
| Key | Default |
|---|---|
maxDays |
5 |
minLevel |
0 |
logIP |
true |
logAuthId |
false |
maxDataSize |
0 (unlimited) |
mailLogMaxDays |
30 |
mailLogMaxDays prunes _mailLog rows (see
Email → Mail log) on the same 0 */6 * * *
cadence as maxDays prunes _logs; <= 0 disables cleanup.
| Key | Default | Notes |
|---|---|---|
enabled |
false |
false routes /api/llm/chat and auto-embedding through a deterministic, network-free echo provider. |
provider |
"openai" |
Any OpenAI-compatible /chat/completions API (including a local Ollama) works; anything else falls back to echo. |
baseUrl |
"https://api.openai.com/v1" |
|
apiKey |
(write-only) | |
model |
"gpt-4o-mini" |
No single provider choice — a _push_subscriptions row’s own platform
(web/android/ios) selects one of three independently-configured
backends:
| Key | Default | Notes |
|---|---|---|
vapid.enabled |
false |
Web Push (RFC 8292). |
vapid.publicKey / vapid.privateKey |
"" |
Raw unpadded-base64url P-256 key pair, as produced by web-push generate-vapid-keys — not PEM. privateKey is write-only. |
vapid.subject |
"" |
A mailto:/https: contact URI, required by every VAPID JWT. |
fcm.enabled |
false |
Firebase Cloud Messaging HTTP v1, for Android. |
fcm.serviceAccountJson |
(write-only) | Raw contents of the Firebase service-account key file; project id, client email, and signing key are parsed out of it. |
apns.enabled |
false |
Apple Push Notification service, HTTP/2 provider API. |
apns.key |
(write-only) | Raw .p8 token-auth key contents. |
apns.keyId / apns.teamId / apns.bundleId |
"" |
From the Apple Developer portal. |
apns.production |
false |
Picks api.push.apple.com over the sandbox host. |
triggers |
[] |
Record-event → push rules: { enabled, collection, events, title, body, targetField }. events is a comma-separated subset of create/update/delete; title/body are {{field}}-templated against the triggering record; an empty targetField broadcasts to every enabled subscription, a non-empty one scopes delivery to subscriptions whose recordRef matches that field’s value. |
notifications
Section titled “notifications”| Key | Default |
|---|---|
retentionDays |
90 |
Prunes read _notifications rows
older than retentionDays on the same 0 */6 * * * cadence as
logs.mailLogMaxDays; an unread notification is kept regardless of age.
<= 0 disables cleanup.
superuserIPs
Section titled “superuserIPs”A top-level string[], default [] (unrestricted) — an IP allow-list
for every _superusers auth endpoint when non-empty. Entries are either
an exact IP (10.0.0.5) or a CIDR range (10.0.0.0/8, 2001:db8::/32).
See Reference → REST API → Settings
for the endpoint shape itself, including the testS3/testMail/
testApple diagnostic actions.