Security hardening
Vault secrets, admin 2FA, the pinned vendor key, and limiting exposure.
Updated
A production Railbase has a handful of things worth getting right. None are exotic; they're the difference between a demo and a deployment you can trust.
Protect the vault secrets
Your data is only as safe as two things:
- The vault password — supply it via
RAILBASE_VAULT_PASSWORD_FILE(a file mode0600, or a secrets-manager mount) rather than a plain env var or a shell history. In production, an empty password is refused.Caution
A dev or scaffolded install ships a
<data-dir>/.vault_passwordfile containing the well-known development key — Railbase auto-discovers it (password source #4) and will unlock with it even underRAILBASE_PROD. Delete or replace it before going live, or the dev key still opens your vault. (Also removeruntime.dev: truefromrailbase.yamlif present.) - The master key (
pb_data/.secret) — a 32-byte key created on a non-production first run (or byrailbase init). Guard it and back it up separately from the vault file. In production a missing.secretis a hard error, not a silent regeneration — losing it means field-encrypted data becomes undecryptable, and leaking it undermines encryption at rest.
export RAILBASE_PROD=true
export RAILBASE_VAULT_PASSWORD_FILE=/run/secrets/railbase-vault
export RAILBASE_ENCRYPT_STORAGE=true
The Vault file is encrypted; uploaded file blobs use the filesystem storage
driver and are encrypted at rest only when RAILBASE_ENCRYPT_STORAGE=true is
enabled. Turn it on before production writes so new uploads are encrypted from
the start.
Admin accounts
- The first administrator is seeded explicitly with
railbase admin bootstrap <email>(a first-run-only command that refuses once any admin exists) — there's no default account and no default password. Add admins routinely through the invite flow (admin UI → Users & access → Admins & roles → Invite, orPOST /api/_admin/admins/invite), not the CLI. - Enroll two-factor authentication from the admin's account settings (it's self-service, not forced). Keep the recovery codes you're shown somewhere safe — once enrolled, sign-in requires the 6-digit code.
- Reset a password from the CLI if needed:
railbase admin reset-password <email>.
Limit what's exposed
Admin and lifecycle surfaces are powerful and should not be open to the world:
/_/— the admin console, including Marketplace at/_/marketplace./api/_admin/*— admin-console APIs./_pm/*— plugin-manager and marketplace proxy endpoints (on by default; setRAILBASE_PLUGIN_MANAGER=0to disable). Destructive lifecycle actions require an admin session, but the surface is still worth restricting.
Options, in rough order of preference:
- Put the admin, admin API, and plugin-manager endpoints behind your network boundary (VPN / private network), or restrict them at the reverse proxy (IP allow-list, mTLS, or proxy auth).
- Use
RAILBASE_ALLOW_IPS/RAILBASE_DENY_IPS(CIDR lists) to filter at the app, and setRAILBASE_TRUSTED_PROXIESso those filters see real client IPs. Note the IP filter is a global middleware — it applies to every request (the public site and/api/*included), not just the admin surfaces, so an allow-list locks down the whole instance. The same knobs exist as persisted settings (security.allow_ips, …), which take precedence over the env vars. - Rate-limit with
RAILBASE_RATE_LIMIT_PER_IP/_PER_USER/_PER_TENANT, and constrain browser origins withRAILBASE_CORS_ALLOWED_ORIGINS.
Caution
Don't toggle RAILBASE_PLUGIN_MANAGER=0 "during normal operation." Disabling
the plugin manager also stops the in-core license-enforcement heartbeat for
already-installed plugins — leave it on wherever licensed plugins run, and
gate the /_pm/* surface at the network/proxy instead.
Tip
The admin ships a Security-audit panel (backed by GET /api/_admin/security-audit) that checks your boot/config posture — vault
password source, prod mode, storage encryption, exposed surfaces — and flags
what's risky. Review it after any config change.
The plugin trust anchor
Plugins only run if their signature verifies against the pinned vendor public key (see How plugins work). That pin is what makes marketplace-only acquisition safe — plugins ship as signed, encrypted JS+schema bundles, and there's no path to run an unsigned or unverified bundle. Don't disable verification or repoint the pin at an untrusted key.
Transport and operations
- Always terminate TLS at your reverse proxy; never serve the admin over plain HTTP. See Deployment.
- Ship logs as JSON (
RAILBASE_LOG_FORMAT=json) to your log pipeline, and keep an eye on the audit log — Railbase maintains a tamper-evident, hash-chained audit trail. - Snapshot before risky changes and keep a copy off-box — Backups & restore.