Security
SoliDB includes robust built-in security features to protect your data and infrastructure from common attack vectors.
Authentication & Authorization
| Feature | Description |
|---|---|
| JWT Authentication | All API endpoints require Bearer token authentication signed with HMAC-SHA256. |
| Random Admin Password | Default admin password is randomly generated on first startup and displayed in logs. |
| Secure Password Hashing | Passwords are hashed using Argon2id, a memory-hard algorithm resistant to GPU cracking. |
| API Keys | Long-lived keys for programmatic access, alternative to ephemeral JWT tokens. |
| Login Rate Limiting | Failed login attempts are limited to 20 per 60 seconds per (IP address, username) pair — successful logins are never counted. Over the limit, the server responds 429 Too Many Requests with a Retry-After header. Tune via SOLIDB_MAX_LOGIN_ATTEMPTS and SOLIDB_LOGIN_RATE_WINDOW_SECS. |
| API Rate Limiting | Every endpoint — not just login — is throttled per client IP, defaulting to 600 requests per 60 seconds (a sustained ~10 req/s) and answering 429 with Retry-After before any handler runs. Internal cluster traffic and CORS preflights are exempt. Tune via SOLIDB_API_RATE_LIMIT (0 disables) and SOLIDB_API_RATE_WINDOW_SECS. |
| Native TLS | --tls-cert / --tls-key terminate TLS 1.2/1.3 in the server itself (rustls, no OpenSSL), for deployments that cannot put a reverse proxy in front. Both flags are required together. |
User & Role Management
SoliDB uses a granular Role-Based Access Control (RBAC) system. Users are assigned one or more roles, and roles define specific permissions to access databases and resources.
Users
Users are identified by a unique username. Each user can have multiple roles assigned to them.
- Managed via
/_api/auth/users - Password protected (Argon2id)
- Can be assigned roles per-database
Roles
Roles are collections of permissions. SoliDB comes with built-in roles, but you can create custom roles with tailored permissions.
- Managed via
/_api/auth/roles - Granular permissions (Admin, Write, Read)
- Scoped globally or to specific databases
Permission Structure
Permissions are defined by an action and a scope.
{
"action": "write", // admin, write, read
"scope": "database", // global, database
"database": "products" // Required if scope is "database"
}
Built-in Roles
| Role | Permissions | Description |
|---|---|---|
| admin | All Permissions | Full access to all databases and system configuration (including user/role management). |
| editor | Read + Write (Global) | Can read and write to all databases, but cannot manage users or system settings. |
| viewer | Read (Global) | Read-only access to all databases. |
Production Configuration
For production deployments, configure these environment variables to verify security and persistence:
# REQUIRED: Set a secure JWT secret (32+ characters)
# If not set, a random secret is generated on startup (invalidating tokens on restart)
export JWT_SECRET="your-secure-random-secret-here"
# OPTIONAL: Set admin password (otherwise randomly generated)
# Useful for automated deployments where you can't read logs
export SOLIDB_ADMIN_PASSWORD="your-secure-password"
# RECOMMENDED: Restrict browser origins allowed by CORS and WebSocket upgrades.
# Comma-separated list of scheme://host[:port]. Default is deny-all when unset.
# Use "*" only in development; the wildcard disables credentialed CORS.
export SOLIDB_CORS_ALLOWED_ORIGINS="https://app.example.com,https://admin.example.com"
# OPTIONAL: Allowlist for solidb.redirect() destinations from Lua scripts.
# Comma-separated list of scheme://host[:port]. When unset, all redirects are allowed.
export SOLIDB_ALLOWED_REDIRECT_ORIGINS="https://app.example.com"
# Per-client API rate limit: requests per window, and window length.
# 0 disables the limiter entirely.
export SOLIDB_API_RATE_LIMIT=600
export SOLIDB_API_RATE_WINDOW_SECS=60
# Behind a proxy that overwrites X-Forwarded-For: key rate limits on the
# forwarded address instead of the socket peer. Only set this when the
# proxy is trusted — otherwise clients can spoof their own identity.
export SOLIDB_TRUST_PROXY_HEADERS=1
Important Warning
If you do not set JWT_SECRET, all user sessions and API tokens will be invalidated every time the server restarts.
Transport Security (TLS)
SoliDB speaks plain HTTP by default and expects a reverse proxy to terminate TLS. When that is not an option — a single-binary deployment, an appliance, an edge node — the server can terminate TLS itself. The implementation uses rustls, so the build stays free of OpenSSL.
# PEM certificate chain and private key. Both flags are required together;
# passing one without the other refuses to start rather than silently
# listening in plaintext.
solidb --port 6745 --data-dir ./data \
--tls-cert /etc/solidb/fullchain.pem \
--tls-key /etc/solidb/privkey.pem
What runs inside the tunnel
It depends on which port mode you run. In dual-port mode (--port and --replication-port differ), the API port serves HTTPS and replication stays on its own port, configured separately.
In multiplexed mode (one port for everything), the listener sniffs the first byte of each connection and only performs a handshake when the client actually offers a TLS ClientHello. An HTTPS client gets the tunnel — and because the multiplexer then sniffs the decrypted stream exactly as it does plaintext, both HTTP and the binary driver protocol work inside it. A plaintext peer is passed through untouched.
Why plaintext is still accepted
None of the shipped client SDKs speak TLS over the native driver protocol yet, and neither do the inter-node sync and cluster transports. Handshaking unconditionally on the multiplexed port would cut off every driver client and every peer connection. Mixed mode is what keeps --tls-cert usable on a clustered node.
Refusing plaintext
If nothing on the port needs plaintext — a single node with no native-protocol clients and no peers — you can close it off:
# Drop any connection on the multiplexed port that does not offer TLS.
export SOLIDB_TLS_REQUIRE=1
Handshakes are bounded at 10 seconds in both port modes, so a client that connects and stalls cannot hold a file descriptor open indefinitely.
Cluster Security
For multi-node clusters, SoliDB uses a sheared secret keyfile to authenticate nodes.
# 1. Generate a secure keyfile (do this once)
openssl rand -base64 756 > solidb-keyfile
chmod 600 solidb-keyfile
# 2. Copy the keyfile to all nodes
# 3. Start with keyfile authentication
solidb --keyfile solidb-keyfile --peer node2:6746 --peer node3:6746
# 4. Enforce keyfile in production: refuse to start if missing.
export SOLIDB_REQUIRE_KEYFILE=true
The cluster communication protocol uses HMAC-SHA256 to sign all handshake messages, ensuring that only nodes possessing the shared keyfile can join the cluster. Set SOLIDB_REQUIRE_KEYFILE=true so that nodes started without a keyfile fail closed instead of silently accepting unauthenticated peers.
Built-in Protections
Request Body Limits
Global limit of 10MB for standard requests. Specialized endpoints (imports, blob uploads) allow up to 500MB. Attempts to send larger payloads are rejected with HTTP 413.
Query Timeout
SDBQL queries have a strict 30-second timeout on both the HTTP API and the native driver protocol. Long-running queries (e.g., infinite loops) are terminated to free up server resources; a mutation that overruns the cap still commits, and its cached results are invalidated when it lands.
Per-Client Request Throttle
The whole router is rate limited per client IP (600 requests / 60s by default), rejecting a flood with 429 before it reaches a handler. A caller whose address cannot be determined is not throttled rather than sharing a bucket with unrelated callers. Set SOLIDB_API_RATE_LIMIT=0 to disable — useful in test environments that drive thousands of requests from one address.
Header Injection Prevention
User-provided filenames in Content-Disposition headers are strictly sanitized to prevent HTTP header injection attacks.
Timing Attack Protection
API key validation uses constant-time string comparison algorithms to prevent side-channel timing attacks that could reveal key contents.
RBAC on Privileged Endpoints
DELETE /_api/database/{name}, GET /_api/auth/api-keys, and the cluster remove-node / rebalance endpoints require the caller to hold the admin role. Viewer and editor tokens receive HTTP 403.
Anonymous Script Audit Log
Anonymous calls into permissive script routes (/api/{db}/{service}/...) emit a WARN-level audit event with method, path, and peer fields under the audit tracing target.
Fuzzed Input Surfaces
The parsers that accept untrusted bytes have cargo-fuzz harnesses in the repository: the SDBQL lexer and parser, MessagePack command decoding on the driver port, and the JSONL parsing solidb-restore performs. Run them with cargo +nightly fuzz run <target>.
Range & Pagination Bounds
SDBQL a..b ranges are capped at 10M elements and overflow-safe. LIMIT offset + count is computed with checked arithmetic so 64-bit overflow yields an empty result instead of a panic.
Recommendations
-
1
Use HTTPS in production. Either terminate TLS at a reverse proxy (Nginx, Caddy, Apache) or let SoliDB do it natively with
--tls-cert cert.pem --tls-key key.pem— see Transport Security for what runs inside the tunnel on a multiplexed port. - 2 Firewall internal ports. Restrict access to the replication port (default 6746) to trusted cluster nodes only.
- 3 Change the admin password immediately after your first login via the dashboard or API.
-
4
Tune rate limiting if needed. The API is throttled per client IP (default 600 requests / 60s, answered with
429 Retry-After); internal cluster traffic and CORS preflights are exempt. Raise it for heavy legitimate traffic withSOLIDB_API_RATE_LIMIT, or set it to0to disable; window length isSOLIDB_API_RATE_WINDOW_SECS. -
5
Monitor logs for repeated login failures and
429s, which may indicate a brute force or flood attempt.