Settings reference
Every settings key Vaultbase reads from the vaultbase_settings table.
Runtime configuration lives in the vaultbase_settings table — keyed by
string, valued by string. Edited from the admin Settings page; values are
cache-invalidated on save.
Secret-valued keys — names ending in .password, .pass, .secret,
.client_secret, .api_key, .private_key, .access_key, .token,
.service_account and similar — read back as ******** from
GET /api/v1/admin/settings and the MCP settings tools. Sending ********
back in a PATCH keeps the stored value; any other value (including "")
replaces it.
Rate limiting
Section titled “Rate limiting”| Key | Type | Default | Notes |
|---|---|---|---|
rate_limit.enabled | "1"/"0" | "1" | Master switch. When unset, the VAULTBASE_RATE_ENABLED env var (0 = off) sets the default. |
rate_limit.rules | JSON array | (defaults below) | See shape below |
rate_limit.max / rate_limit.window_ms | int | — | Legacy single rule (*), used only when rate_limit.rules is empty and both are set. |
rate_limit.rules shape:
[ { "label": "*:auth", "max": 10, "windowMs": 3000, "audience": "all" }, { "label": "*:create", "max": 60, "windowMs": 5000, "audience": "all" }, { "label": "/api/*", "max": 300, "windowMs": 10000, "audience": "all" }]label syntax: <path>[:<action>]
path— exact (/api/v1/posts), prefix (/api/*), or wildcard (*)./api/v1/...and/api/...are equivalent.action—auth,create,list,view,update,deleteaudience—all,guest(no token),auth(any user/admin token)
| Key | Type | Default |
|---|---|---|
smtp.enabled | "1"/"0" | "0" |
smtp.host | string | — |
smtp.port | string (int) | "587" |
smtp.secure | "1"/"0" | "0" |
smtp.user | string | — |
smtp.pass | string | — |
smtp.from | string | — — e.g. "Acme" <noreply@acme.com>; must be an address or Name <address> |
Test via Settings → SMTP → Send test. The cache TTL is 30 seconds.
| Key | Type | Default | Notes |
|---|---|---|---|
email.transport | string | "smtp" | Name of a registered email transport; an unknown name is rejected with 422. |
Email templates
Section titled “Email templates”| Key | Type | Default |
|---|---|---|
app.url | string | — — base URL of your frontend; used in {{link}} |
email.verify.subject | string | "Verify your email" |
email.verify.body | string | (multi-line default) |
email.reset.subject | string | "Reset your password" |
email.reset.body | string | (multi-line default) |
email.otp.subject | string | "Your sign-in code" |
email.otp.body | string | (multi-line default) |
email.email_change.subject | string | "Confirm your new email address" |
email.email_change.body | string | (multi-line default) |
email.invite.subject | string | "You're invited" |
email.invite.body | string | (multi-line default) |
Variables in templates: {{email}}, {{token}}, {{code}} (otp only),
{{link}}, {{appUrl}}, {{collection}}. In email_change, {{email}} is
the new address; in invite, the invited address. Empty values fall back to
the defaults. All five are edited at Settings → Email templates.
Auth features
Section titled “Auth features”| Key | Default | Notes |
|---|---|---|
auth.otp.enabled | "0" (off) | Magic link / OTP flow. Requires SMTP. |
auth.mfa.enabled | "1" (on) | TOTP enrollment. Disabling blocks new enrollment only — existing users keep working. |
auth.anonymous.enabled | "0" (off) | POST .../anonymous |
auth.impersonation.enabled | "1" (on) | Admin can impersonate users |
Disabled features return 422 with a clear message.
Token lifetimes
Section titled “Token lifetimes”| Key | Default | Notes |
|---|---|---|
auth.admin.window_seconds | 604800 (7 d) | Admin JWT lifetime. |
auth.user.window_seconds | 604800 (7 d) | User JWT lifetime. |
auth.anonymous.window_seconds | 2592000 (30 d) | Anonymous user JWT lifetime. |
auth.impersonate.window_seconds | 3600 (1 h) | Impersonation token lifetime. |
auth.refresh.window_seconds | 604800 (7 d) | Refresh window. |
auth.file.window_seconds | 3600 (1 h) | File download token lifetime. |
auth.session.max_seconds | 2592000 (30 d) | How long a sign-in can be kept alive through refresh, from the original sign-in. 0 = no cap. |
Window keys accept 60–31536000 seconds (1 minute to 365 days);
auth.session.max_seconds accepts 0 or at least 3600. Other values are
rejected with 422.
HTTP API cross-origin allow-list. Edited from Settings → CORS. Empty origin list blocks all cross-origin requests (defensive default).
| Key | Type | Default | Notes |
|---|---|---|---|
cors.origins | comma-separated | "" | * permits any origin (incompatible with credentials → silently downgraded to echoing matched origin). |
cors.methods | comma-separated | GET,POST,PUT,PATCH,DELETE,OPTIONS | |
cors.headers | comma-separated | Authorization,Content-Type,If-Match,X-VB-Idempotency-Key | |
cors.credentials | "1"/"0" | "0" | Sends Access-Control-Allow-Credentials: true. |
cors.max_age | seconds | "600" | Preflight cache lifetime. A negative value is refused. |
Cache TTL 5s; preflight is short-circuited at onRequest.
Password policy
Section titled “Password policy”Applies whenever a password is set — admin creation and password changes, auth-collection sign-ups, resets and changes — not at login. Defaults are off — only length is enforced (12 characters minimum, floor 8).
| Key | Type | Default | Notes |
|---|---|---|---|
password.min_length | int | "12" | Floor 8 (NIST 800-63B). |
password.require_upper | "1"/"0" | "0" | Reject if no uppercase letter. |
password.require_lower | "1"/"0" | "0" | Reject if no lowercase letter. |
password.require_digit | "1"/"0" | "0" | Reject if no digit. |
password.require_symbol | "1"/"0" | "0" | Reject if no non-alphanumeric character. |
password.hibp_check | "1"/"0" | "0" | k-anonymity check against api.pwnedpasswords.com. Fails open on network error. |
Brute-force lockout
Section titled “Brute-force lockout”Per-account + per-IP failed-login throttle. On by default. The IP limit is
5 × max_attempts. Behind a reverse proxy, set security.trusted_proxies
(or the env var) so the IP is the real client’s, not the proxy’s.
| Key | Type | Default | Notes |
|---|---|---|---|
auth.lockout.max_attempts | int | "10" | "0" = off. Lockout fires when failures within window reach this. |
auth.lockout.duration_seconds | int | "900" | Window seconds. Min 60 (15 min default); a smaller value is refused. |
Trusted proxies
Section titled “Trusted proxies”Setting overrides VAULTBASE_TRUSTED_PROXIES env. Without either, vaultbase
ignores X-Forwarded-For entirely (defensive default — prevents spoofing on
direct-exposed deployments).
| Key | Type | Default | Notes |
|---|---|---|---|
security.trusted_proxies | comma-separated IPs / CIDRs | "" | Affects ratelimit + audit log + brute-force IP keying. An entry that isn’t an IP or CIDR is refused. |
security.allowed_origins | comma-separated origins | "" | Origins allowed to open realtime (WebSocket / SSE) connections. Empty = same-origin only; * allows any. Requests without an Origin header (non-browser clients) are allowed. |
Metrics
Section titled “Metrics”Prometheus exposition at /api/v1/metrics. Off by default. Optional bearer
auth via metrics.token.
| Key | Type | Default | Notes |
|---|---|---|---|
metrics.enabled | "1"/"0" | "0" | Master switch. 404 when off. |
metrics.token | string | "" | Required as Authorization: Bearer <token>. Empty = only an admin token is accepted. |
Always available to admins as JSON at /api/v1/_/metrics.
File storage
Section titled “File storage”Edited at Settings → File storage.
| Key | Type | Default | Notes |
|---|---|---|---|
files.max_upload_size | int (bytes) | "52428800" (50 MB) | Per upload request; 413 above it. Range 1024–134217728 (128 MB, the server’s request body limit); other values are rejected with 422. |
files.orphan_cleanup | "1"/"0" | "1" | Periodic sweep that deletes stored files no record references any more (after a grace period; collections with record history are skipped). "0" turns it off. |
storage.driver | string | "local" | Storage driver name (local, s3, or one an extension registers). An unknown name is rejected with 422. |
s3.endpoint | string | "" | S3-compatible endpoint URL. |
s3.bucket | string | "" | |
s3.region | string | "auto" | |
s3.access_key_id | string | "" | |
s3.secret_access_key | string | "" | Encrypted at rest; reads back masked. |
s3.public_url | string | "" | Optional public base URL for objects. |
Backups
Section titled “Backups”Scheduled database backups. Edited at Settings → Backups.
| Key | Type | Default | Notes |
|---|---|---|---|
backups.enabled | "1"/"0" | "0" | Turns scheduled backups on. |
backups.cron | cron (UTC) | "0 3 * * *" | Schedule. An invalid expression is rejected with 422. |
backups.keep | int | "7" | Backups to keep per location. Range 1–1000 (422 otherwise). |
backups.s3.enabled | "1"/"0" | "0" | Also upload each backup to S3. |
backups.s3.endpoint | string | "" | |
backups.s3.bucket | string | "" | |
backups.s3.region | string | "auto" | |
backups.s3.prefix | string | "vaultbase-backups/" | Key prefix for uploaded backups. |
backups.s3.access_key_id | string | "" | |
backups.s3.secret_access_key | string | "" | Encrypted at rest; reads back masked. |
backups.last_run_at, backups.last_error and backups.next_run are
written by the scheduler (status, not configuration).
API surface
Section titled “API surface”| Key | Type | Default | Notes |
|---|---|---|---|
api.openapi_public | "1"/"0" | "0" | Serve GET /api/v1/openapi.json to non-admins. They don’t see collections whose list, view and create rules are all admin-only. Admins can always read it. |
flags.public_evaluate | "1"/"0" | "1" | Allow POST /api/v1/flags/evaluate without a session. "0" returns 401 to anonymous callers. |
Data retention
Section titled “Data retention”Edited at Settings → Data retention. A cleanup runs once a day.
| Key | Type | Default | Notes |
|---|---|---|---|
maintenance.retention_days | int (days) | "30" | Finished queue jobs, finished webhook deliveries and daily request-log files older than this are deleted. 0 keeps them forever. Range 0–3650 (422 otherwise). Expired token revocations and one-time auth tokens are always cleaned up. |
audit.retention_days | int (days) | "0" | Admin audit-log entries older than this are deleted daily. 0 keeps them forever. Range 0–3650. |
Admin UI accent + surface colors. Edited at Settings → Theme. Empty value
falls back to the built-in default. Hex format only (#RGB, #RRGGBB,
#RRGGBBAA). The login page picks up the operator’s overrides too.
| Key | CSS variable | Default |
|---|---|---|
theme.accent | --accent | #e85a4f |
theme.accent_hover | --accent-hover | #f06f64 |
theme.accent_light | --accent-light | #f5807a |
theme.bg_app | --bg-app | #0e0f12 |
theme.bg_sidebar | --bg-sidebar | #131418 |
theme.bg_panel | --bg-panel | #181a1f |
theme.text_primary | --text-primary | #e6e8ed |
theme.text_secondary | --text-secondary | #9aa0ac |
theme.success | --success | #22c55e |
theme.warning | --warning | #f59e0b |
theme.danger | --danger | #ef4444 |
Public read endpoint: GET /api/v1/admin/theme — admin SPA fetches it before
mounting React.
Update checker
Section titled “Update checker”Polls GitHub for the latest release tag. Boot delay 30 s, then every 6 h. Vaultbase never auto-updates — this is purely a notification.
| Key | Type | Default | Notes |
|---|---|---|---|
update_check.enabled | "1"/"0" | "1" | Master switch. |
update_check.latest_version | string | (cached) | Last tag fetched from GitHub. |
update_check.checked_at | unix-seconds | (cached) | Last successful check. |
update_check.error | string | (cached) | Last fetch error, cleared on success. |
Hook egress (SSRF guard)
Section titled “Hook egress (SSRF guard)”Outbound helpers.http.* calls from hooks/routes/cron pass through a CIDR
filter. See Hooks → Egress filter for the default deny
list and DNS-rebind caveat.
| Key | Type | Default | Notes |
|---|---|---|---|
hooks.http.deny | comma-separated CIDRs | (default-deny RFC1918 + link-local + loopback) | Replaces defaults. off disables filtering. An entry that isn’t a CIDR is refused. |
hooks.http.allow | comma-separated CIDRs | "" | Allow exceptions evaluated after deny. An entry that isn’t a CIDR is refused. |
Cache TTL 5s; PATCH /api/v1/admin/settings invalidates immediately.
OAuth2 providers
Section titled “OAuth2 providers”For each of google, github, gitlab, facebook, microsoft, discord,
twitch, spotify, linkedin, slack, bitbucket, notion, patreon,
twitter, oidc:
| Key | Type |
|---|---|
oauth2.<name>.enabled | "1"/"0" |
oauth2.<name>.client_id | string |
oauth2.<name>.client_secret | string |
A provider counts as “enabled” only when all three are set + enabled is "1".
Provider-specific keys:
| Key | Notes |
|---|---|
oauth2.apple.client_id, oauth2.apple.team_id, oauth2.apple.key_id, oauth2.apple.private_key | Apple signs its client secret with this key instead of using client_secret. private_key is encrypted at rest. |
oauth2.oidc.authorization_url, oauth2.oidc.token_url, oauth2.oidc.userinfo_url | Endpoints of your OIDC provider. |
oauth2.oidc.scopes | Scopes to request. |
oauth2.oidc.display_name | Button label. Default OIDC. |
Redirect allow-list:
| Key | Default | Notes |
|---|---|---|
oauth2.allowed_redirect_uris | "" | Comma-separated redirect URIs a sign-in may return to. A trailing * extends the path; * alone allows any. Empty = only the origin of app.url. |
oauth2.<name>.allowed_redirect_uris | — | Per-provider override of the list above. |
Push notifications
Section titled “Push notifications”See Push notifications for end-to-end setup.
| Key | Type |
|---|---|
notifications.providers.onesignal.enabled | "1"/"0" |
notifications.providers.onesignal.app_id | string (UUID) |
notifications.providers.onesignal.api_key | string (REST API key — encrypted at rest) |
notifications.providers.fcm.enabled | "1"/"0" |
notifications.providers.fcm.project_id | string (optional — defaults to project_id from the service account JSON) |
notifications.providers.fcm.service_account | string (full service-account.json contents — encrypted at rest) |
At-rest encryption of secrets
Section titled “At-rest encryption of secrets”Settings whose key matches a
secret-shaped suffix are AES-GCM-encrypted before being written to
vaultbase_settings and decrypted on read. The wrapper is the same
vbenc:1:<iv>:<ciphertext> envelope used by encrypted record fields
(core/encryption.ts), so backups + snapshots stay symmetric.
Suffixes that trigger encryption (case-insensitive): .password,
.pass, .api_key, .apikey, .secret, .client_secret,
.private_key, .privatekey, .access_key, .accesskey,
.service_account, .token.
The key is VAULTBASE_ENCRYPTION_KEY, or, when that is unset, a key
generated on first start into <dataDir>/.encryption-key. Secret settings
stored in plaintext before a key existed are encrypted at startup. If the key
changes so that it no longer decrypts the stored secrets, Vaultbase refuses to
start and says how to restore the right key (see
Encrypted fields → Key rotation).
Reading & writing
Section titled “Reading & writing”GET /api/v1/admin/settings ← admin authPATCH /api/v1/admin/settings { "<key>": "<value>", ... }PATCH is partial — keys not in the body are left untouched. The whole PATCH is
rejected with 422 if any value fails validation. Settings caches
(rate-limit, SMTP, storage, CORS, hook egress) are invalidated on save. The
MCP vaultbase.update_setting tool applies the same validation and cache
refreshes.
Programmatic access
Section titled “Programmatic access”Settings are not exposed to hooks or routes through ctx.helpers. For
automation, use PATCH /api/v1/admin/settings from an admin session, or the
MCP settings tools (vaultbase.list_settings, get_setting,
update_setting, scope mcp:admin) with an API token.