Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration Reference

A consolidated reference for the server’s knobs.

Server (patinadb-raft)

FlagDefaultNotes
--config <path>offLoad settings from a YAML config file (see below).
--print-config-schemaoffPrint a JSON Schema for the config file to stdout and exit.
--check-configoffValidate the resolved config, print what was understood, and exit without booting — an nginx -t-style dry run (see below).
--id <u64>requiredUnique Raft node id (here or in the config file).
--addr <host:port>requiredHTTP (REST + management + peer RPC).
--db <dir>requiredDatabase root (one subdir per database).
--bootstrapoffSelf-init a single-voter cluster.
--join <member>offAuto-join an existing member as a learner on startup. Excludes --bootstrap.
--bolt-addr <addr>127.0.0.1:7687Bolt listener; "" disables Bolt.
--advertised-addr <a>= --bolt-addrPublic Bolt address for routing behind a proxy.
--auth-user <name>neo4jAuth username.
--auth-password <p>""Shared password. Empty = no auth, fail-closed: the node won’t start without --insecure-disable-auth.
--insecure-disable-authoffExplicitly allow running open (empty password). Trusted networks only.
--tls-cert <path>offPEM cert chain. With --tls-key, serves the HTTP plane (REST + peer RPCs) over HTTPS.
--tls-key <path>offPEM private key (required with --tls-cert).
--tls-ca <path>system rootsPEM CA peers verify each other with (self-signed / private-CA clusters).
--require-metrics-authon (default)Gate GET /metrics behind Basic auth. Default-on since series carry db=<name> labels.
--insecure-open-metricsoffOpt out of metrics auth — serve /metrics open on a private monitoring network.
--query-timeout-secs <n>300Per-request budget for a REST /cypher read; overrun → 503 (deadline, not a hard cancel). 0 = off.
--max-concurrent-requests <n>512Cap on in-flight HTTP requests; excess shed with 503. 0 = unlimited.
--readiness-max-lag <n>50/ready apply-lag tolerance: report not ready (503 lagging) when last_log_index − last_applied exceeds this. See Day-2 operations.
--otel-endpoint <url> (PATINADB_OTEL_ENDPOINT)unsetEnable request/trace correlation (X-Request-Id + inbound W3C traceparent) and force structured JSON logs for OpenTelemetry-collector ingestion. See Day-2 operations.
--allow-csv-dir <dir>deny-allDirectory LOAD CSV FROM 'file://…' may read from. Repeatable. Unset ⇒ every LOAD CSV file read is refused. See Cypher file I/O.
--allow-export-dir <dir>deny-allDirectory the CSV export procs (patinadb.export.*) may write to. Repeatable. Unset ⇒ every export write is refused.
--rbac-closed (PATINADB_RBAC_CLOSED)offClosed-mode RBAC: deny a non-admin any database it holds no explicit grant on. See Authentication & TLS.
--telemetry-degrade-override-until <value> (PATINADB_TELEMETRY_DEGRADE_OVERRIDE)unsetTime-boxed break-glass: keep writes enabled past a missed telemetry grace window until an absolute deadline. See Day-2 operations.
--bookmark-wait-timeout-secs <n> (PATINADB_BOOKMARK_WAIT_TIMEOUT_SECS)10How long a Bolt BEGIN/RUN will block for this node to catch up to an incoming driver-session bookmark before failing with a retryable BookmarkTimeout. 0 disables the wait (bookmarks are parsed but ignored). See Bolt → Causal consistency.

Environment: PATINADB_AUTH_PASSWORD sets the auth password (preferred over a shell flag).

More server flags (RBAC, Bolt/transaction limits, telemetry, ops)

The table above covers the flags you’ll set on day one. These round out the full surface — most have safe defaults and only need touching for a specific requirement (multi-tenant RBAC, transaction-buffer bounds, dead-voter auto-repair, telemetry/licensing). Every one is also settable via PATINADB_* env var and/or the YAML config file, same precedence rules.

FlagDefaultNotes
--cluster-secret <s> (PATINADB_CLUSTER_SECRET)= --auth-passwordDedicated peer /raft/* secret, separate from the root-admin credential so the two rotate independently.
--rbac-closed (PATINADB_RBAC_CLOSED)offDeny a non-admin any database it holds no explicit grant on (tenant isolation). See Authentication & TLS.
--rbac-rel-grants (PATINADB_RBAC_REL_GRANTS)offStrict relationship-type RBAC: a label-scoped user must also hold a per-rel-type grant for every edge type traversed. See Authentication & TLS.
--rbac-rel-property-grants (PATINADB_RBAC_REL_PROPERTY_GRANTS)offStrict relationship-property RBAC: a label-scoped user must also hold a per-(relType, prop) grant. See Authentication & TLS.
--auth-max-attempts-per-min <n> (PATINADB_AUTH_MAX_ATTEMPTS_PER_MIN)0 (disabled)Brute-force auth throttle, per source IP and per username. Blocks a failed-login flood before it reaches the argon2 verify.
--max-reads-per-user <n> (PATINADB_MAX_READS_PER_USER)0 (unlimited)Cap concurrent reads per authenticated principal — one user’s burst of expensive read procs can’t exhaust the blocking pool.
--blocking-threads <n> (PATINADB_BLOCKING_THREADS)tokio default (512)Absolute ceiling on the tokio blocking-thread pool.
--max-bolt-connections <n>1024Cap on concurrent authenticated Bolt connections; 0 = unlimited. See the note below (issue #440) — an unauthenticated connection never spends this budget.
--bolt-handshake-timeout-secs <n> (PATINADB_BOLT_HANDSHAKE_TIMEOUT_SECS)10Close a Bolt socket that sends nothing (or stalls mid-handshake) before completing the version-negotiation handshake. 0 disables.
--bolt-idle-timeout-secs <n> (PATINADB_BOLT_IDLE_TIMEOUT_SECS)300Close a Bolt connection that sits with no open transaction and no started result stream and sends no message for this long (covers a connection that finishes HELLO/LOGON then goes silent). 0 disables.
--idle-tx-timeout-secs <n> (PATINADB_IDLE_TX_TIMEOUT_SECS)300Roll back + close an explicit Bolt transaction left idle this long (“idle in transaction” guard). 0 disables.
--max-tx-ops <n> (PATINADB_MAX_TX_OPS)5,000,000Max buffered DeltaOps an explicit Bolt transaction may accumulate before it’s aborted. 0 = unlimited.
--stream-pull-timeout-secs <n> (PATINADB_STREAM_PULL_TIMEOUT_SECS)300Cancel a streaming Bolt RUN result the client never PULLs within this window. 0 disables.
--audit-max-entries <n> (PATINADB_AUDIT_MAX_ENTRIES)100,000Retention for the durable node-local security-audit log. 0 = unlimited.
--auto-evict-after-secs <n> (PATINADB_AUTO_EVICT_AFTER_SECS)0 (disabled)Auto-evict a voter that stays dead (unreachable + not replicating) past this many seconds, using the same quorum-preserving guard as POST /mgmt/evict-voter. See Day-2 operations.
--license <path|token> (PATINADB_LICENSE)none (community)Signed license file or inline token. See Licensing & Telemetry.
--install-name <text> (PATINADB_INSTALL_NAME)unset (anonymous)Opt-in, operator-chosen telemetry label.
--telemetry-interval-secs <n>21600 (6h)Heartbeat interval.
--telemetry-grace-secs <n>259200 (72h)Community-mode grace window before write-degrade.
--disable-telemetryoffTurn off telemetry entirely — requires a valid license (fail-closed otherwise).

Also note PATINADB_MAX_CONCURRENT_RAFT_RPCS — a separate, env-var-only backpressure cap (default 1024, 0 = unlimited) on inbound peer /raft/* RPCs, distinct from --max-concurrent-requests (which governs client traffic) so a peer-RPC flood and a client-request flood can’t shed each other’s capacity.

YAML config file (--config)

Instead of (or alongside) flags, point the server at a YAML file mirroring the settings above:

# node.yaml
id: 1
addr: "127.0.0.1:21001"
db: "/var/lib/patinadb"
bootstrap: true
bolt_addr: "0.0.0.0:7687"
auth_user: "neo4j"
# auth_password: prefer the PATINADB_AUTH_PASSWORD env var over the file
query_timeout_secs: 30
max_concurrent_requests: 256
patinadb-raft --config node.yaml

Every field is optional; an absent key falls back to the CLI flag, then to the built-in default. The YAML key names match the long flag names with - replaced by _ (e.g. --bolt-addrbolt_addr). Unknown keys are rejected so a typo fails loudly.

Precedence (highest wins): an explicitly-passed CLI flag (or its bound env var, e.g. PATINADB_AUTH_PASSWORD) > the config file > the built-in default. A flag left at its clap default does not override a value set in the file — only flags the operator actually typed do. The required settings (id, addr, db) may come from either the file or flags; if neither supplies one, startup fails with a clear error.

Config JSON Schema (--print-config-schema)

patinadb-raft --print-config-schema prints a JSON Schema (Draft 7) for the config file — every property carries its description (lifted from the Rust doc-comments) so editors can offer autocompletion and validation. It works without any other arguments:

patinadb-raft --print-config-schema > patinadb-config.schema.json

Config dry run (--check-config)

patinadb-raft --check-config is an nginx -t for patinaDB: it resolves the effective configuration exactly the way a real boot does — CLI/env > <VAR>_FILE/_COMMAND > --config file > default — prints what it understood, validates it, and exits without starting the node. Exit code 0 means valid, 1 means a setting is invalid or a runtime env knob is set to a value the engine would silently ignore. Drop it into a deploy script or CI gate:

patinadb-raft --id 1 --addr 127.0.0.1:21001 --db /var/lib/patinadb \
  --bootstrap --auth-password "$PW" --cache-limit 40% --check-config || exit 1

It prints the resolved node identity, cluster mode, security posture (flagging auth-on-without-TLS), every limit with its effective value, the resolved cache budget in bytes (so you can see the caches are actually enabled and with how much), the telemetry/licensing state, and the filesystem sandbox.

Its most valuable job is the runtime env-knob scan. Several engine knobs (PATINADB_MAX_AGG_ROWS, PATINADB_CACHE_LIMIT, …) are read lazily deep in the engine and fall back to their default if the value doesn’t parse — silently. So PATINADB_MAX_AGG_ROWS=5M looks like five million but is ignored (the runtime wants a plain integer, 5000000), and — the case that motivated this feature — a PATINADB_CACHE_LIMIT typo could silently leave the cache off. --check-config resolves each set knob with the same parser the runtime uses and fails the check on any set-but-unparseable value, naming it:

Runtime env knobs (curated — set values only)
  PATINADB_CACHE_LIMIT               512MiB → 536870912 bytes
  PATINADB_MAX_AGG_ROWS              5M ✗ expected a plain integer, got '5M'

configuration INVALID — 1 problem(s):
  ✗ PATINADB_MAX_AGG_ROWS is set to '5M' but expected a plain integer … SILENTLY IGNORED

Secrets management

The three sensitive server settings — PATINADB_AUTH_PASSWORD (root-admin credential), PATINADB_CLUSTER_SECRET (peer /raft/* shared secret), and PATINADB_LICENSE — can be sourced without ever putting the plaintext value in a -e env var or a config file. patinaDB embeds no secrets-manager SDK; two conventions integrate one while keeping its client out of the binary (the server only ever sees the resolved value at startup):

  • <VAR>_FILE — names a file whose contents are the real value (mirrors POSTGRES_PASSWORD_FILE). Point it at a Docker Compose secrets: file, a Kubernetes Secret volume, or a tmpfs file a Vault Agent / AWS or GCP Secrets Store CSI-driver sidecar keeps refreshed — the DB reads the file, the sidecar owns the fetch + rotation. This is the pattern to prefer when you need auto-rotation. A trailing newline is trimmed.

  • <VAR>_COMMAND — names a shell command whose stdout (trailing newline trimmed) is the value, for a direct dynamic fetch with no sidecar:

    PATINADB_AUTH_PASSWORD_COMMAND="vault kv get -field=pw secret/patinadb"
    # AWS: "aws secretsmanager get-secret-value --secret-id patinadb/pw \
    #        --query SecretString --output text"
    # GCP: "gcloud secrets versions access latest --secret=patinadb-pw"
    

    The command runs via sh -c "<cmd>" once at startup (the fetch CLI must be present + authenticated). Fail-closed: a command that can’t spawn, exits non-zero, or produces empty output aborts startup with a loud error — it never silently falls back to “no secret” (which would look like --insecure-disable-auth). The fetched value is never logged.

Precedence (most specific wins): direct flag/env > <VAR>_FILE > <VAR>_COMMAND > config file > built-in default. Both _FILE and _COMMAND are only consulted when the direct flag/env is unset. See deploy/README.md for the sidecar + command examples.

Resource limits & quotas

Guards that bound the cost of a single query so one statement can’t exhaust memory or run unbounded. The engine budgets are environment variables; the two server request limits are flags (see the patinadb-raft table above).

LimitWhereDefaultPurpose
PATINADB_MAX_HOPSenv1000Depth cap for an unbounded variable-length hop ([*], or [*..n] with n unset). An explicit [*a..b] in the query always wins. Prevents runaway traversal on a cyclic graph.
PATINADB_CARTESIAN_CAPenv10000Max rows a disjoint (cross-product) multi-MATCH may produce before the query errors. Stops an accidental N×M blow-up.
PATINADB_MAX_AGG_ROWSenv5000000Cap on the O(input)/O(result) row buffers behind GROUP BY/aggregate, a full (non-top-K) ORDER BY, UNION dedup, and a hash-join build side. A clear error over the cap instead of a silent OOM. See Query Planning.
PATINADB_MAX_CAPTURE_OPSenv5000000Cap on the number of resolved ops a single write statement (the Raft leader’s resolve step) may buffer before recording one engram/Raft entry. A whole-graph SET/bulk CREATE over the cap fails with a clear error pointing at CALL {…} IN TRANSACTIONS instead of risking an OOM or a giant single Raft entry.
PATINADB_MAX_ALGO_WORKenv1e9Static work-budget backstop for O(V·E) read procedures (betweenness/closeness): refuses a call whose estimated n·(n+e) exceeds this, so a Reader can’t trigger unbounded compute even without --query-timeout-secs. Admits realistic analytic graphs (10k nodes / 50k edges ≈ 6e8); raise it for a genuinely large centrality run. See Day-2 operations.
PATINADB_MAX_SNAPSHOTSenvunlimited (0/unset)Prunes on-disk periodic time-travel snapshot files down to the N most recent (+ every pinned/tagged one) after each snapshot-taking commit. A long-lived, write-heavy database otherwise grows snapshot files unbounded; pruning only slows reconstruction of an old, out-of-window engram — every AS OF result stays byte-identical.

Write pipelining

KnobDefaultPurpose
PATINADB_PIPELINED_WRITESonLets concurrent autocommit writes to one database resolve in parallel instead of serializing behind each other’s whole Raft round-trip, admitting each under a short optimistic conflict check. =0 is the rollback valve — writes then serialize per database, as before.

Pipelining is what makes the optimistic concurrency model in Production Deployment the one you actually get: with it on, a write–write conflict between concurrent writers is detected and reported as the driver-retryable Neo.TransientError.Transaction.LockClientStopped rather than being avoided by serializing everyone. There are no lost updates either way — a client (or, better, a driver’s managed transaction) simply retries the transient.

The trade-off is isolation: pipelined autocommit writes run at snapshot isolation, the same level Bolt’s explicit transactions already use, so write skew is possible — two writers each reading a value and writing disjoint rows based on it leave no write–write overlap to detect. That matters only for a multi-row invariant that genuinely needs serializability; the sentinel-node pattern in Concurrency & retries turns such a case back into a detectable conflict. Turning the knob off restores serialized execution per database at a real throughput cost (measured ~4–5× on a disjoint-write workload).

Time-travel performance knobs

These change only how an AS OF read is served, never what it returns — a historical result is byte-identical whichever path runs. Both are read-path accelerations with a fallback that is always correct, so they are safe to flip either way; see Time Travel for the full picture.

KnobDefaultPurpose
PATINADB_PARTIAL_TIMETRAVELonReconstructs only the subgraph an AS OF read actually touches, when the query’s labels/relationship types are statically bounded (an unbounded shape falls back to a full reconstruct). Read-only, so a write-only workload pays nothing. =0 is the rollback valve — set it if you ever suspect the partial path; the full reconstruct then serves every read, slower but identical.
PATINADB_INDEXED_SNAPSHOTSoffBuilds a point-queryable .snapidx sidecar per snapshot so a cold single-vertex AS OF lookup is a point read instead of a full-graph rebuild. Opt-in because it costs disk. The build is lazy (triggered by a read that would benefit, never by a commit) and asynchronous (it never blocks the read that triggers it — that read is served by the scan path), so enabling it can’t stall a writer or the server’s apply loop.
PATINADB_MAX_SNAPSHOT_INDEXES16Caps how many .snapidx sidecars may exist at once (0 = unlimited). Bounds sidecar count, not bytes — worst-case disk is roughly N × the size of a snapshot file. Only relevant when PATINADB_INDEXED_SNAPSHOTS is on.

Why PATINADB_INDEXED_SNAPSHOTS is opt-in. It earns its keep on repeated historical point reads: the read that triggers a build is served by the scan path, so the sidecar only helps later reads. If your backend time-travels rarely, or reads a different point each time, leave it off — you’d pay the disk without the win. Turn it on when a known set of historical points is read repeatedly. | --query-timeout-secs | server flag | 300 | Per-request wall-clock budget for a REST read; overrun → 503. 0 = off. | | --max-concurrent-requests | server flag | 512 | Max in-flight HTTP requests; excess shed with 503. 0 = unlimited. |

Set an env budget to 0 (or unset) to fall back to the built-in default. Tighten them as DoS guards on a shared node, or raise PATINADB_MAX_HOPS for a genuinely deep graph. A RETURN … LIMIT k is the normal way to bound result size — there is no implicit result cap (an unlimited query streams every row).

For observability, set PATINADB_SLOW_QUERY_MS (server env, off by default) to log a WARN for any REST query slower than that many milliseconds — carrying the query’s normalized shape (literals + $params folded to ?), not raw values. Per-shape latency stats are also served at GET /mgmt/queries.

Commercial entitlements

The server resolves a set of commercial caps (cluster HA size, combined node+edge scale, database count, history-retention window, and two feature gates) either from the hard-coded Community ceiling or from a signed license token’s entitlement claims. This is configured entirely by which license you install (--license / PATINADB_LICENSE / <db-root>/license.key — see Licensing & Telemetry), not by a server flag. See Editions & Limits for the full Community/Pro/Enterprise table, what each limit does when you hit it, and how to read a running node’s resolved tier + live usage via GET /version (also scraped onto patinadb_entitlement_usage_ratio{axis} / patinadb_entitlement_limit{axis} Prometheus gauges).

Cache memory budget

patinaDB can keep a governed, RAM-budgeted cache of decoded objects / adjacency / query results above the OS page cache. The server defaults it ON, at PATINADB_CACHE_LIMIT=40% of the resolved memory limit, when you leave the flag/env var/config key unset — so a fresh node ships with caching already engaged. Set PATINADB_CACHE_LIMIT=0 (or the equivalent flag/config key) explicitly and the cache is entirely off — only the OS page cache and the existing plan/stats caches are used, at zero residual cost.

The budget derives from a cgroup-aware total (the real ceiling the kernel OOM-kills at in a container, not the host’s RAM): cgroup v2 memory.max → v1 memory.limit_in_bytes → host MemTotal, taking min(cgroup, MemTotal). From that total, PATINADB_MEMORY_LIMIT is patinaDB’s own-heap ceiling (not including the OS page cache), and four regions are carved from it. Each knob is an absolute size (8GiB), a fraction of its parent (40%), or auto; precedence is explicit-absolute > fraction > default, and fractions compose against the resolved parent.

Env varRegionDefaultParent
PATINADB_MEMORY_LIMITown-heap ceiling (excl. page cache)auto = TOTAL − min_freediscovered TOTAL
PATINADB_CACHE_LIMITcache pool (L1/L2/L3) — unset/0 disables caching40%MEMORY_LIMIT
PATINADB_WORK_MEM_LIMITaction reserve (concurrent query working memory)45%MEMORY_LIMIT
PATINADB_MEM_HEADROOMtransient-spike / allocator-slop / OOM safety15%MEMORY_LIMIT
PATINADB_CACHE_MIN_FREEpage-cache floor (system free RAM kept resident for the storage engine’s page cache)max(1GiB, 10%)discovered TOTAL

Every knob is also a patinadb-raft flag (--memory-limit, --cache-limit, --work-mem-limit, --mem-headroom, --cache-min-free) and a YAML config key (memory_limit, cache_limit, work_mem_limit, mem_headroom, cache_min_free), with the same explicit-flag/env > file > default precedence as every other setting.

The budget is validated at startup and fails loud (the node refuses to boot) when it over-commits — CACHE_LIMIT + WORK_MEM_LIMIT + HEADROOM ≤ MEMORY_LIMIT and MEMORY_LIMIT + CACHE_MIN_FREE ≤ TOTAL — with an error naming the offending knobs and the resolved bytes. When enabled, the fully-resolved budget (bytes per region) is logged at startup so it is never a mystery.

Example — a container with memory.max=16GiB, PATINADB_CACHE_LIMIT=40% and otherwise defaults: TOTAL=16GiB, min_free≈1.6GiB, MEMORY_LIMIT≈14.4GiB (TOTAL − min_free), then Cache ≈5.8GiB · Actions ≈6.5GiB · Headroom ≈2.2GiB. Set PATINADB_MEMORY_LIMIT=10GiB to leave more RAM to the page cache on a read-heavy deployment.

Internal defaults (informational)

These are not user-configurable flags today, but are useful to know:

SettingValue
Snapshot intervalevery 50 commits
Raft election timeout750–1500 ms
Raft heartbeat250 ms
BM25 parametersk1 = 1.2, b = 0.75
Full-text prefix/fuzzy expansion cap256 terms
Bolt streaming channel256 records (bounded)
Default database namedefault

On-disk layout

The --db directory contains the B-tree tables for the graph, the property/compound indexes, the engram log and snapshots, the full-text catalog and index data, and the persistent Raft log and state-machine metadata. Back up the whole directory as a unit. Each database is a subdirectory of --db.