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:

setup.sh
# 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. 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. 2 Firewall internal ports. Restrict access to the replication port (default 6746) to trusted cluster nodes only.
  3. 3 Change the admin password immediately after your first login via the dashboard or API.
  4. 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 with SOLIDB_API_RATE_LIMIT, or set it to 0 to disable; window length is SOLIDB_API_RATE_WINDOW_SECS.
  5. 5 Monitor logs for repeated login failures and 429s, which may indicate a brute force or flood attempt.