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

Licensing & Telemetry

patinaDB’s server (patinadb-raft) runs in one of two modes. The distributed Docker image ships without a license, so by default a node runs in community mode and sends an anonymous usage heartbeat to a telemetry server. Installing a license file switches the node to licensed mode for on-prem / offline / air-gapped operation, where telemetry is best-effort and can be turned off entirely.

This chapter explains both modes, documents exactly what the telemetry heartbeat contains (and, just as importantly, what it never contains), and shows how to obtain and install a license.

The two modes

Community mode (default — no valid license)

A background heartbeat is mandatory. On startup the node sends an initial heartbeat, then repeats it on an interval. It tolerates transient network outages (retries with backoff, plus a long grace window), so a brief blip is harmless. But it must not run indefinitely offline:

  • If no heartbeat succeeds within the grace window (default 72 hours), the node degrades. It refuses client writes with a clear 503 error and logs the reason loudly.
  • Reads keep working during and after the grace window — degradation only blocks writes.
  • Control / admin commands (creating databases, users, indexes) are not blocked, so an operator can still recover the node.
  • As soon as a heartbeat succeeds again, the block is lifted automatically and writes resume.

The refusal is surfaced as an HTTP 503 (REST) or a Bolt failure, with a message explaining that the node could not reach the telemetry server and is running unlicensed.

Licensed mode (valid license file)

A valid license unlocks on-prem operation:

  • Telemetry is best-effort: it is still sent by default (so the maintainer can see version adoption), but a failure to send never blocks anything and the node never degrades.
  • Telemetry can be turned off completely with --disable-telemetry.
  • --disable-telemetry is honored only with a valid license. An unlicensed node started with --disable-telemetry refuses to start (fail-closed) with a clear message — a community node must send telemetry.

What the telemetry heartbeat sends

The heartbeat is a coarse, anonymous JSON POST. The payload is a strict allowlist of counters and environment facts. Nothing outside the table below is ever sent. This is not a promise on paper alone: an automated guard test asserts the serialized payload’s key set equals this allowlist, and a second doc↔code drift test parses this very table and asserts it lists exactly the fields the code sends — so if a field is ever added to the wire without being documented here (or vice versa), the build fails. The table cannot silently drift from reality.

Every field, exhaustively:

FieldTypeMeaning
install_idUUIDA random id generated once and persisted at <db-root>/.patinadb_install_id. Stable and anonymous — not derived from anything identifying.
nonceUUIDA fresh random value generated per heartbeat. It exists only so the server’s signed response can be bound to this exact request (anti-tamper — see Response signing); it carries no information about you.
install_namestring | nullOpt-in, operator-chosen label (--install-name / PATINADB_INSTALL_NAME). Unset ⇒ null (anonymous), the default. This is the only free-text field and it is consent-based: it is whatever you type, and is never auto-derived from the environment (no hostname, no username).
versionstringThe node build version.
protocol_versionintegerThe Raft protocol version.
osstringTarget OS (e.g. linux).
archstringTarget CPU architecture (e.g. x86_64).
coresintegerLogical CPU core count (≥ 1). A coarse hardware-sizing signal.
memory_bytesintegerTotal host/cgroup RAM in bytes. A coarse hardware-sizing signal — not a live memory-usage figure.
uptime_secsintegerSeconds since this process started.
node_countintegerCluster membership size (voters + learners).
is_leaderbooleanWhether this node is currently the Raft leader.
database_countintegerNumber of databases in the registry.
total_verticesintegerAggregate vertex count across all databases (a coarse volume signal only).
total_edgesintegerAggregate edge count across all databases (a coarse volume signal only).
requests_per_minnumberCoarse recent request rate: total recorded query executions ÷ uptime minutes. An aggregate count only — carries no query text.
avg_query_msnumberMean query latency in milliseconds, aggregated (count-weighted) across all query shapes. An aggregate timing only — carries no query text.
license_statusstring"community" or "licensed".
license_customerstringLicensed mode only. The licensee’s own customer id from the license (the buyer of the license — this is not an end-user). Omitted in community mode.

What we NEVER send

The telemetry payload will never contain any of the following. When in doubt, it is left out:

  • No database content — no rows, no graph data of any kind.
  • No names — no property keys or values, no label or relationship-type names, and no database names.
  • No queries — no query text or any fragment of one (the requests_per_min and avg_query_ms figures are pure aggregate counters/timings).
  • No node or edge UUIDs.
  • No hostname, IP address, username, or location. (install_name is the one label you may choose to send — it is never read from the host.)
  • No RBAC user names, engram messages, or authors.
  • No personally-identifiable information of any kind.

Only the coarse counts and environment metadata in the table above leave the node.

How and when it sends; the endpoint

On startup the node sends one heartbeat, then repeats it on the interval. It is a single coarse anonymous POST — see what it sends above. The stable, anonymous install_id lets the maintainer count distinct installs across restarts without knowing anything about who you are; the optional install_name is a label you may choose to attach so your own installs are recognizable in your reports — it defaults to anonymous and is never taken from the host.

The endpoint is a compile-time constant baked into the binary — for the distributed community build, the maintainer’s server. In a release build it is NOT overridable: the --telemetry-endpoint flag and the PATINADB_TELEMETRY_ENDPOINT environment variable are compiled out, so a community node always phones the real host (redirecting it to /dev/null would defeat the point of community telemetry). Only debug/test builds accept an override, so the test suite can point at a mock server.

Honest note on the endpoint lock. Locking the endpoint is a soft deterrent, not a hard control. Telemetry is best-effort, so a determined operator can still firewall-block the host or patch the binary — we don’t pretend otherwise. The lock also means a licensed organization cannot point the heartbeat at its own telemetry server; the supported path for a licensed org that doesn’t want to phone home is --disable-telemetry (turn it off), not redirection.

SettingFlagEnv varConfig keyDefault
Heartbeat interval--telemetry-interval-secs <n>telemetry_interval_secs21600 (6h)
Grace window--telemetry-grace-secs <n>telemetry_grace_secs259200 (72h)
Opt-in install label--install-name <text>PATINADB_INSTALL_NAMEinstall_nameunset (anonymous)
Disable telemetry (licensed only)--disable-telemetrydisable_telemetryfalse

The endpoint itself has no run-time flag in release (see above). See the Configuration Reference for how flags, environment variables, and the config file combine.

Response signing (anti-tamper server authentication)

Locking the endpoint stops a community node from redirecting its phone-home, but a determined operator could still point telemetry.patinadb.org at a fake server (via /etc/hosts or DNS) that just returns 200 OK — dodging the degrade-after-grace without ever reaching the real host. To close that, the real telemetry server cryptographically signs its ping response and the node verifies the signature.

How it works:

  1. The node sends a fresh random nonce with each heartbeat (see the field table above), alongside its install_id.
  2. The server signs its response. The genuine server holds an Ed25519 private key and returns { server_time, signature }, where the signature covers nonce ‖ install_id ‖ server_time. Only a server holding that private key can produce a signature the node accepts.
  3. The node verifies the signature against a second Ed25519 public key embedded in the binary (TELEMETRY_RESPONSE_PUBLIC_KEY, separate from the license key), checks the nonce matches the one it sent, and checks server_time is fresh (within ±5 minutes, to bound replay).
  4. The verdict feeds the grace clock. A valid signature is a successful heartbeat (it resets the grace clock). A missing, malformed, forged, or stale signature is a failed heartbeat — it does not reset the clock, so a community node still degrades after the grace window.

A fake server has no private key, so it cannot produce a valid signature and therefore cannot stop the node from degrading. Because the signing key lives entirely outside the TLS / system-trust chain, this defeats even an attacker who has managed to insert a rogue CA into the host’s trust store (a plain /etc/hosts redirect is already stopped by strict TLS certificate validation — the node’s HTTPS client never disables cert/hostname verification).

Honest ceiling. This is an anti-forgery control, not an anti-patch control. A determined operator can still edit the embedded public key (or delete the verification) out of their own rebuilt binary, or simply firewall-block the host. Signing raises the bar from “edit one line in /etc/hosts” to “patch and recompile the binary” — a soft deterrent consistent with the community-telemetry model. The supported opt-out for a licensed org remains --disable-telemetry, not evasion.

Opting out

There is exactly one supported way to stop telemetry, and it requires a license:

  • --disable-telemetry, honored only with a valid license. It turns the heartbeat off completely — nothing is sent. An unlicensed node started with this flag refuses to start (fail-closed): a community node must send telemetry.

If you run community mode and simply stop reaching the endpoint (network block, air-gap), the node does not silently continue forever — after the grace window (default 72h) it degrades: client writes are refused with a 503, while reads keep working, until a heartbeat succeeds again. This “degrade after grace” behavior is the honest community-mode contract; the way to run offline without degrading is to install a license.

Installing a license (on-prem / offline / air-gapped)

A license is a compact, Ed25519-signed token. Because the node verifies it against a public key embedded in the binary, a license can be validated with zero network access — ideal for air-gapped deployments.

A node looks for a license in this order:

  1. The --license <value> flag or the PATINADB_LICENSE environment variable. The value may be either a path to a license file or an inline token.
  2. A license.key file in the database root (<db-root>/license.key).

If no valid license is found, the node logs why and falls back to community mode. An invalid signature or an expired license is likewise logged and treated as community mode — the node still boots, just unlicensed.

To operate a node fully offline:

  1. Obtain a license token (a license.key file) from patinaDB, your vendor, or your account representative — see Editions & Limits for what each tier includes.
  2. Save it as license.key in the node’s database root (or point --license at it, or set PATINADB_LICENSE).
  3. Start the node. It logs licensed mode (on-prem) on success.
  4. Optionally add --disable-telemetry to stop all outbound heartbeats.

Privacy stance

patinaDB’s telemetry is designed to be anonymous by construction: the payload is a fixed allowlist of coarse counters and environment facts, guarded by an automated test that fails the build if any field outside the allowlist is added. No database content, no names, and nothing personally identifiable ever leaves the node. Licensed / air-gapped deployments can disable telemetry entirely.

Telemetry and licensing are node-local — each node phones home independently and they are never routed through the Raft log, so they add no cross-node coordination. For authentication and transport security of the data plane, see Authentication & TLS.