VeltrixDB Docs
v1.1.0 GitHub FAQ Quickstart

Security

VeltrixDB provides encryption at rest, TLS for both client and inter-node traffic, role-based access control, admin API access control, and an append-only audit log. This page covers how to enable each control and what it does and does not guarantee.

Encryption at rest

VeltrixDB encrypts stored values with AES-256-GCM. Encryption is off by default and is enabled per-node with a server flag:

veltrixdb -addr :9000 -data-dirs /mnt/nvme0,/mnt/nvme1 \
  -encrypt-at-rest

The encryption key is not passed on the command line. It is read from the VELTRIXDB_ENCRYPTION_KEY environment variable at startup:

export VELTRIXDB_ENCRYPTION_KEY=$(openssl rand 32 | base64)
veltrixdb -encrypt-at-rest -data-dirs /mnt/nvme0,/mnt/nvme1
Key rotation is offline. VeltrixDB does not support online re-keying. Rotating the encryption key requires stopping every node, generating a new key, and forcing a full rewrite of stored data at the new key (veltrix migrate). See the Disaster Recovery runbook for the exact sequence.

On Kubernetes, supply the key through a Secret rather than a plain environment value baked into a manifest:

kubectl create secret generic veltrixdb-enc \
  --from-literal=key=BASE64_32B

If the encryption key is missing at startup, nodes fail to open the VLog with an error resembling encryption: no key in VELTRIXDB_ENCRYPTION_KEY. For HSM/KMS-backed key sourcing, mount the secret through a CSI driver (for example the AWS Secrets Manager CSI driver) so the raw key material is never stored in cluster state at rest.

TLS for client connections

The TCP data server can require TLS for client connections using a server certificate and key:

veltrixdb -addr :9000 \
  -tls-cert /etc/veltrixdb/tls/server.crt \
  -tls-key  /etc/veltrixdb/tls/server.key

VeltrixDB negotiates TLS 1.3 and falls back to a restricted TLS 1.2 cipher suite list (TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) — older ciphers are rejected. Without -tls-cert/-tls-key, the listener serves plaintext.

Inter-node TLS and mutual TLS

Client-facing TLS and inter-node (Raft / replication) TLS are configured independently, because they usually terminate at different network boundaries. Use the -cluster-tls-* flags to secure Raft, replication, and gossip traffic between nodes:

veltrixdb -mode raft -node-id n1 -addr 127.0.0.1:9000 \
  -peers n1@127.0.0.1:9000,n2@127.0.0.1:9100,n3@127.0.0.1:9200 \
  -cluster-tls-cert /etc/veltrixdb/tls/cluster.crt \
  -cluster-tls-key  /etc/veltrixdb/tls/cluster.key \
  -cluster-tls-ca   /etc/veltrixdb/tls/cluster-ca.crt \
  -cluster-mtls

Setting -cluster-mtls switches the inter-node listener into mutual-TLS mode: every peer connection must present a certificate signed by -cluster-tls-ca, and connections without a valid client certificate are rejected outright. Without -cluster-mtls, -cluster-tls-ca still enables server-side TLS, but peer identity is not verified.

FlagApplies toEffect
-tls-cert / -tls-keyClient-facing TCP data portServer-side TLS for application connections
-cluster-tls-cert / -cluster-tls-key / -cluster-tls-caRaft / replication / gossip listenersTLS between nodes; CA enables client-cert verification
-cluster-mtlsRaft / replication / gossip listenersRequires and verifies peer certificates (mutual TLS)

Role-based access control

Authentication and authorization are driven by a JSON config file passed via -auth-config. Each entry maps a username to a password hash and a role:

{
  "users": [
    { "username": "alice", "password_hash": "pbkdf2$sha256$210000$<salt-b64>$<dk-b64>", "role": "admin" }
  ]
}

Hashes are generated with veltrix-admin hash-password — see Password hashing below for the hash format and the legacy-hash migration path.

Three built-in roles are supported:

RolePermissions
adminread + write + delete + admin (full access)
readwriteread + write + delete
readonlyread only (GET, PING, INFO)

Clients authenticate over the text protocol with:

AUTH alice secretpassword
OK

The binary protocol carries the same operation as command 0x09 with a length-prefixed username and password. Password hashes are compared with a constant-time comparison, and lookups against unknown usernames run a dummy constant-time comparison as well, to reduce user-enumeration via timing. When -auth-config is not supplied, authentication is disabled and every connection is granted full permissions — do not expose an unauthenticated data port to an untrusted network.

Loading an auth config with zero users is rejected. VeltrixDB refuses to enable auth enforcement from an empty user list, to avoid silently locking out (or silently permitting) every connection.

Password hashing

Password hashes in the auth config use PBKDF2-HMAC-SHA256 with 210,000 iterations, a random per-user 16-byte salt, and a 32-byte derived key. The stored value is self-describing:

pbkdf2$sha256$<iterations>$<salt-base64>$<derived-key-base64>

Because the iteration count and salt are embedded in each hash, entries generated at different work factors verify correctly side by side. Generate a hash — together with a ready-to-paste auth config snippet — using the admin CLI:

veltrix-admin hash-password --user alice --password secret

Legacy hashes still work. The previous scheme — a bare 64-character hex digest of SHA-256(password + username) — is still verified for backward compatibility, so existing config files keep working. However, the server logs a deprecation warning for every legacy entry when the auth config is loaded. Re-hash those users with veltrix-admin hash-password and update the config file. Both schemes are compared in constant time.

Emitting the deprecated format. veltrix-admin hash-password accepts a --legacy-sha256 flag that emits the old SHA-256(password + username) hash instead of PBKDF2. Use it only when a config must be consumed by a pre-upgrade server; new deployments should always use the default PBKDF2 output.

Admin API access control

As of v1.1.0 the /admin/* endpoints on the admin/metrics HTTP port (-metrics-addr, default :2112) are guarded. With no token configured, they accept loopback connections only — a request from any non-loopback address receives 403. To allow remote admin access, start the server with a bearer token:

veltrixdb -admin-token "$(openssl rand -hex 32)"
# or via the environment:
export VELTRIX_ADMIN_TOKEN=...

Once a token is set, every /admin/* request — including from loopback — must present it in either header, or it receives 401:

curl -H "Authorization: Bearer $VELTRIX_ADMIN_TOKEN" http://localhost:2112/admin/stats
curl -H "X-Admin-Token: $VELTRIX_ADMIN_TOKEN"        http://localhost:2112/admin/stats

Token comparison is constant-time. /metrics, /healthz, and /readyz are deliberately not guarded, so Prometheus scrapes and Kubernetes probes work with no configuration. The repl-ship tool authenticates against a guarded source with --src-token (or VELTRIX_ADMIN_TOKEN) — see Replication & CDC.

The token is a second layer, not a substitute for isolation. On untrusted networks, keep -metrics-addr behind a network policy / firewall as before — the bearer token protects the admin surface, but the port also serves unauthenticated metrics and probe endpoints.

Audit log

VeltrixDB can record every authenticated mutating operation to an append-only, newline-delimited JSON (JSONL) file:

veltrixdb -audit-log /var/log/veltrixdb/audit.jsonl

Each line is a single JSON object:

{"ts":"2026-05-10T11:35:55Z","op":"PUT","actor":"user-bob","key":"orders/4521","ns":"default","status":"ok"}

Fields: ts (RFC 3339 nanosecond UTC), op (PUT, DEL, CAS, INCR, …), actor (authenticated username, empty when auth is disabled), key, ns (namespace, when distinct), status, and err when the status is an error.

Audit writes are asynchronous: records are enqueued to a buffered channel and a background goroutine appends them to disk with periodic fsync. The audit path is intentionally never allowed to block the data plane — if the channel fills up, records are dropped and the drop count is tracked internally rather than backpressuring writes.

The audit log is not tamper-evident. Entries are appended locally in plain JSONL — they are not cryptographically chained. Value payloads are deliberately excluded from records (only operation, key, namespace, actor, and outcome are logged). If your compliance framework requires hash-chained or immutable audit trails, ship the log to an external, write-once backend (for example S3 Object Lock or a WORM-mounted volume).

Compliance posture

VeltrixDB ships a control mapping document (docs/SOC2_CONTROLS.md) that maps its built-in capabilities — encryption at rest, RBAC, mTLS, the audit log, Prometheus-based monitoring, and backup/restore — to SOC 2 Trust Service Criteria (CC6.x, CC7.x, CC8.1, A1.x, C1.x, PI1.x).

This is a control mapping, not a certification. VeltrixDB's own documentation is explicit that a SOC 2 attestation is a property of your operating environment, not of the database alone. The project provides the technical capabilities an auditor will look for; it does not claim SOC 2 certification for VeltrixDB itself, and there is no attestation report to hand to an auditor out of the box.

The control-mapping document also lists gaps to disclose to an auditor rather than paper over: the audit log is not hash-chained, at-rest encryption applies to whole values (no per-field/column-level encryption), the master key is sourced from an environment variable rather than a built-in KMS/HSM integration, and there is no built-in PII detection or redaction scanner. Each gap has a documented mitigation (external immutable log storage, application-layer field encryption, CSI-based KMS secret injection, and external DLP scanning, respectively).

Operators preparing for an audit should be able to produce, at minimum: proof the encryption secret exists, the current auth-config user/role list with sample login traces, a TLS/mTLS handshake trace, recent backup manifests or volume snapshots, a sample of the shipped audit log, and Grafana/alert history. See Monitoring & Observability for the metrics and alerting side of that evidence set.