Security
The Settings → Security tab consolidates the runtime security controls
that don’t fit cleanly under another tab. Every read and every mutation goes
through verifyAuthToken (centralized after the v0.1.3 audit).
Active admin sessions
Section titled “Active admin sessions”Every successful admin login records a row in vaultbase_admin_sessions:
CREATE TABLE vaultbase_admin_sessions ( jti TEXT PRIMARY KEY, admin_id TEXT NOT NULL, admin_email TEXT NOT NULL, issued_at INTEGER NOT NULL, expires_at INTEGER NOT NULL, ip TEXT, -- only set when trusted_proxies is configured user_agent TEXT);The Security tab lists currently-active sessions (jti not yet expired and
not revoked). Each row exposes a Revoke button that inserts the jti
into vaultbase_token_revocations; the next request carrying that token
gets a uniform 401.
Force-logout-all
Section titled “Force-logout-all”The Force logout all button bumps password_reset_at = unixepoch() on
every row in vaultbase_admin. verifyAuthToken rejects any token whose
iat precedes the principal’s password_reset_at, so this single SQL UPDATE
kills every existing admin JWT in one shot — no per-jti DB write.
The button signs you out too (your token is now older than the reset timestamp). Sign back in with your password.
Brute-force lockout
Section titled “Brute-force lockout”Off by default. Two settings drive it:
auth.lockout.max_attempts (default 0 = off)auth.lockout.duration_seconds (default 900, min 60)When a login fails, vaultbase writes one row per principal:
email:<lowercased-email>ip:<client-ip>(only ifsecurity.trusted_proxiesis configured)
Either key reaching max_attempts within the window locks the principal
out — the next login attempt returns 429 Too many failed attempts. Try again later. The lockout gate runs before the password verify, so a
successful attempt by a different account from the same IP doesn’t leak
signal.
A successful login clears both keys. Old failure rows are pruned on a periodic tick.
Trusted proxies
Section titled “Trusted proxies”A safety lever for X-Forwarded-For parsing — both reverse-proxy and
defensive-default behaviors live here:
- Empty + no
VAULTBASE_TRUSTED_PROXIESenv → vaultbase ignoresX-Forwarded-Forentirely. - Setting filled → that CIDR list is used for real-IP extraction.
- Env-only fallback works if the setting is left blank.
Affects: rate limiting, audit log IP column, brute-force IP keying.
Secrets fingerprints
Section titled “Secrets fingerprints”Read-only SHA-256 first 8 bytes of the JWT signing secret and the AES encryption key, rendered in the Security tab. Used to:
- Confirm a fleet of vaultbase instances shares the same secrets (otherwise JWTs from one host won’t verify on another, encrypted fields written on one are unreadable on another).
- Spot accidental key rotation between deploys.
VAULTBASE_ENCRYPTION_KEY missing surfaces an explicit warning:
No
VAULTBASE_ENCRYPTION_KEYset — fields markedencryptedstore plaintext.
The fingerprint is crypto.subtle.digest("SHA-256", utf8(secret)) then the
first 16 hex characters (8 bytes). Collision-resistant for fingerprinting
purposes; doesn’t reveal the secret.
Security headers preview
Section titled “Security headers preview”The Security tab renders the headers vaultbase sends per response, split between API responses and admin UI / non-API responses (CSP applies to the latter only — JSON responses don’t need it).
These come from core/sec.ts → securityHeaders({ isApi }). Common ones:
Strict-Transport-SecurityContent-Security-PolicyX-Frame-Options: DENYReferrer-Policy: no-referrerX-Content-Type-Options: nosniff
The preview is read-only. To customize: edit core/sec.ts and rebuild.
Most operators won’t need to.
CSP allowances
Section titled “CSP allowances”The default Content-Security-Policy is restrictive:
default-src 'self';script-src 'self' 'wasm-unsafe-eval';img-src 'self' data: blob: https://*.tile.openstreetmap.org;style-src 'self' 'unsafe-inline';connect-src 'self' ws: wss:;frame-ancestors 'none';base-uri 'self';form-action 'self'The single externally-allowed source is https://*.tile.openstreetmap.org,
needed by the admin’s Leaflet preview when a collection has a geoPoint
field. All other admin assets (marker icons, fonts, scripts) ship inline or
from 'self'.
If you front vaultbase with a stricter outer CSP layer (Cloudflare Page
Rules, an organization-wide WAF policy, etc.), mirror the same img-src
allowance there or geoPoint maps will render as blank tiles. Disable map
tiles entirely by removing the entry from core/sec.ts and rebuilding —
markers and the admin chrome continue to work, only the OSM raster
background drops out.
REST API
Section titled “REST API”Programmatic access for the same data:
GET /api/v1/admin/security/sessions[?activeOnly=0]DELETE /api/v1/admin/security/sessions/:jtiPOST /api/v1/admin/security/force-logout-allGET /api/v1/admin/security/fingerprintsGET /api/v1/admin/security/headers-previewAll require an admin JWT. Mutations are picked up by the audit log
(security.* action labels).