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

Application gRPC has a closed listener profile: development-only cleartext on a literal loopback address, verified direct TLS on TCP, or a protected Unix socket. Hosted MCP supports literal-loopback cleartext or verified direct TLS on TCP. There is no insecure remote mode. See Remote and Local Application Ingress.

Project riffdb.toml

The database-shaped init, push, generate, status, diff, and migrate verbs use a closed project document. They select riffdb.toml in the current directory by default; explicit --config and then RIFFDB_CONFIG take precedence. Other CLI commands retain the existing no-discovery behavior.

[client]
endpoint = "http://127.0.0.1:7443"
database = "default"

[project]
schema = "riffdb.application.json"
generators = ["rust"]

The schema path must be bounded, workspace-relative, and free of . or .. components. generators contains one through four unique values from rust, go, typescript, and python. Unknown keys, duplicate targets, symlinked configuration files, absolute or escaping schema paths, and empty target lists reject without fallback. See the Database-Shaped Project Workflow for command semantics.

riffdbd

Every server field resolves independently in this order:

  1. an explicit CLI value;
  2. its RIFFDB_* environment value;
  3. an explicitly selected TOML document; then
  4. the built-in safe local default.

A present invalid higher-precedence value rejects instead of falling through. The TOML path is selected by --config, then RIFFDB_CONFIG, then absence. There is no implicit configuration-file discovery.

The legacy database, environment, and backup settings below define one database with alias default. A process may instead define 1 through 512 [databases.<alias>] tables. Mixing the legacy and named forms rejects.

SettingCLIEnvironmentTOMLDefaultRestartImpact
Database--databaseRIFFDB_DATABASEserver.databasedata/riffdb.redbYesCorrectness, storage compatibility
Application listener--listen (legacy loopback only)RIFFDB_LISTEN (legacy loopback only)server.grpc_listen or server.application_listener127.0.0.1:7443 loopback cleartextYesAvailability, security
Environment--environmentRIFFDB_ENVIRONMENTserver.environmentlocalYesCapability authentication compatibility
gRPC audience--audienceRIFFDB_AUDIENCEserver.audienceriffdb-grpc-loopbackYesCapability authentication compatibility
Hosted MCP listen--mcp-listenRIFFDB_MCP_LISTENserver.mcp_listenabsentYesAvailability, security
Hosted MCP originsrepeatable --mcp-originRIFFDB_MCP_ORIGINSserver.mcp_originsemptyYesBrowser-origin security
Metrics listen--metrics-listenRIFFDB_METRICS_LISTENserver.metrics_listenabsentYesObservability, loopback security
Telemetry flight recorder--telemetry-flight-recorderRIFFDB_TELEMETRY_FLIGHT_RECORDERserver.telemetry_flight_recorderabsentYesObservability, disk usage
Telemetry flight interval--telemetry-flight-interval-msRIFFDB_TELEMETRY_FLIGHT_INTERVAL_MSserver.telemetry_flight_interval_ms10000YesObservability sampling cadence
Backup root--backup-rootRIFFDB_BACKUP_ROOTmaintenance.backup_root$CWD/backupsYesCorrectness, recovery, security
Capability key path--capability-keysRIFFDB_CAPABILITY_KEYSserver.capability_keysconfig/capability.keysYesAuthentication and key compatibility
Idempotency key path--idempotency-keysRIFFDB_IDEMPOTENCY_KEYSserver.idempotency_keysconfig/idempotency.keysYesSame-key recovery compatibility

The only TOML tables and keys are:

[server]
database = "/var/lib/riffdb/data/riffdb.redb"
grpc_listen = "127.0.0.1:7443"
environment = "local"
audience = "riffdb-grpc-loopback"
capability_keys = "/etc/riffdb/capability.keys"
idempotency_keys = "/etc/riffdb/idempotency.keys"
# mcp_listen = "127.0.0.1:7444"
# mcp_origins = ["http://127.0.0.1:3000"]
# metrics_listen = "127.0.0.1:9464"
# telemetry_flight_recorder = "/var/lib/riffdb/telemetry/flight.ndjson"
# telemetry_flight_interval_ms = 10000

[maintenance]
backup_root = "/var/lib/riffdb/backups"

Loopback metrics and telemetry flight recorder

When --metrics-listen (RIFFDB_METRICS_LISTEN or server.metrics_listen) is configured, riffdbd binds a literal IPv4 or IPv6 loopback address with a non-zero port and serves read-only HTTP/1 GET /metrics in Prometheus text format (text/plain; version=0.0.4; charset=utf-8) and GET /v1/metrics in OpenTelemetry JSON ExportMetricsServiceRequest format (application/json). Non-loopback addresses, zero ports, non-GET methods, and other request paths fail closed. All exported counters, gauges, and stage histograms use closed compile-time label vocabularies and numeric identifiers; raw tenant, entity, request, and secret values are never emitted.

When --telemetry-flight-recorder <PATH> (RIFFDB_TELEMETRY_FLIGHT_RECORDER or server.telemetry_flight_recorder) is configured, riffdbd appends one bounded riffdb.telemetry.flight-record.v1 NDJSON line at startup, every --telemetry-flight-interval-ms (100..=3600000, default 10000), and after graceful drain on shutdown. Operators can aggregate a recorded flight file offline with riffdb telemetry summarize <PATH> (in human or --output json mode) to inspect throughput deltas, stage latency p50/p95/p99 high-water marks, writer busy saturation, health transitions, online retention state/fence and pruned-sequence progress, and storage/journal byte growth across 24-hour and 72-hour soak runs.

Named archive directories

An operator may bind up to 16 archive names per database in TOML. The legacy single-database form uses [[maintenance.archives]]; the named form uses [[databases.<alias>.archives]]. Each entry requires name, path, and encryption:

[[maintenance.archives]]
name = "daily"
path = "/var/lib/riffdb/archives/daily"
encryption = "operator_managed"

Names are checked identifiers and must be unique within their database. Paths must be bounded absolute directories and disjoint from every database, backup, projection, archive and other reserved process path. Invalid or overlapping entries reject configuration. encryption is either unencrypted or operator_managed; the latter declares external operator-provided encryption, and does not ask RiffDB to encrypt files. Archive paths never come from restore request input.

Add backup = "baseline" to the entry to collect published changelog frames beside the verified full backup named baseline under this database’s backup_root. Without backup, the entry is available for restore only. The backup must already exist when the database starts. Each collector verifies its backup and existing archive before appending; it never rebinds an archive to a different backup or history incarnation. Use a new directory for a new base.

Collection runs independently of command acknowledgement and the write gate. Each collector buffers at most one uncertain frame and retries those exact bytes up to three attempts. A missing or invalid backup, unavailable startup sink, pruned history gap, changed lineage, or exhausted append retries stops that collector with a redacted diagnostic. It does not stop primary writes. Correct the cause and restart the database to revalidate and resume. Shutdown joins all collectors before closing storage. The POC supports local filesystem sinks; operator_managed encryption must be supplied outside RiffDB. See Backup and Restore.

Durable local outbox destination

An optional [maintenance.outbox] section, or [databases.<alias>.outbox] for named databases, configures one durable_local destination with an absolute protected directory and positive quota_encoded_bytes. Configuration is restart-applied and refused on followers. See Durable Local Outbox Archive for custody, delivery, accounting, and offline inspection.

Online retention

riffdbd can compact commit history while it serves. The legacy single-database form uses [maintenance.retention]; the named form uses [databases.<alias>.retention]:

[maintenance.retention]
retained_sequences = 1000000
sequences_per_epoch = 256

Set exactly one of retained_sequences, a positive count of commit sequences to keep below the durable application head, or hold = true. sequences_per_epoch bounds each step, from 1 through 256 (default 256). An absent section is a hold, so a configuration that never names retention prunes nothing online. Targets are sequence counts, never ages. Any other key, both forms together, or an out-of-range value rejects configuration.

With a target, one background lane per database advances the retention watermark in steps. Each step is one administration transition through the database’s single ordered writer. It removes whole command segments, appends the chained tombstone, and advances the watermark in one atomic commit. A journal-backed command waits until its extent is sealed. A sealed extent may span several logical prune steps; each step still covers at most 256 sequences. Logical pruning keeps the extent’s bytes intact. The lane reclaims a whole file only after every command in it has been pruned and the existing source-history protocol has released every receipt reference. The retained source resume anchor, older pinned snapshots, and an active background scrub keep the file alive. The lane never creates a source acknowledgement or discards a receipt to free space. One retired file can supply the next preallocated journal spare; excess eligible files are deleted. File work runs outside the ordered writer gate. Repeated requests for a missing spare share the unfinished preparation job and its completion signal. They do not queue additional waiting sealer threads or create new extent-publication obligations. Each actual extent publication keeps its own completion tracking. A completed failed preparation permits a fresh attempt; it does not certify a failed extent publication. The lane holds no exclusive lease and opens no second writer. A writer waits behind at most the one step in progress. After each attempt that took the writer gate, the lane rests nineteen times the time it held the gate, so it holds the writer at most about five percent of the time and a large backlog compacts gradually rather than at full speed. It plans each step without the writer gate and takes the gate only when it has history to remove. With the Standard durability profile, it prepares retained views and the tombstone digest from a pinned composite snapshot outside the gate. The writer revalidates every precondition and current fence before submitting one V4 prune frame through the existing group-sync journal lane. Durability completes after releasing the gate; watermark publication precedes the corresponding deletion view. It does not drain unrelated command frames just to prune. Checkpoint materialization remains background work. Hardened profile steps retain their direct engine transaction. Once the lane reaches its target or a fence it rechecks about once a second. A single command segment larger than the budget is removed alone, never more than 256 sequences. A crash at any point in a step reopens with the watermark either before or after that step, and the restarted lane resumes.

The lane never passes a fence: projection frontiers, durable event-consumer checkpoints, undelivered outbox events, a staged contract migration, operator holds, and registered followers’ acknowledged frontiers. An unreadable fencing source stops the lane with an internal incident until the next restart; it never prunes past it. No gRPC health member, control RPC, or MCP tool drives the lane, and application principals have no retention operation. While riffdbd serves, /metrics, /v1/metrics, and the periodic NDJSON flight recorder export the closed online retention state (riffdb_online_retention_state / online_retention_state), active binding fence (riffdb_online_retention_fence / online_retention_fence), completed step count (riffdb_online_retention_steps_total), pruned sequence count (riffdb_online_retention_sequences_total), and latest watermark sequence (riffdb_online_retention_watermark_sequence). The shutdown release census line also records its state after graph teardown has joined the lane, the name of the fence that last held it, and its final step and sequence counts. An enabled lane normally ends as cancelled; a refused lane remains refused, and a configured hold that starts no worker remains holding. The graph and process release receipts share that frozen observation. The separate riffdb-retention-extents-v1 line records files deleted or recycled, passes held by references or readers, and off-writer reclamation microseconds. Logical sequence progress does not imply physical space reclamation.

The retention lane also owns source-receipt reclamation across observations. It validates immutable history in pages of at most 256 rows, then yields if a writer is queued, a checkpoint is in flight, or the journal is not drained. It also yields until off-gate extent sealing finishes, before capturing a validation pin. A changed publication discards the candidate. At a later independently durable checkpoint it rechecks consumer and derived-reader holds and removes at most 256 receipts. Source holds retain their separate 4,096-registration bound. Its own reclamation and prefix-refresh receipts cannot create an idle reclamation loop. The separate riffdb-retention-receipts-v1 shutdown line records removed receipts and gate_us; that time participates in the lane’s existing writer-occupancy pacing. Receipt removal does not prove that an extent was deleted or reused.

Eligibility planning and checkpoint licensing resolve receipt points on the same immutable source view that passed native validation, including its acknowledged journal suffix. A newer installed checkpoint does not extend an older captured source’s readable range. Missing coverage beyond that range yields; a missing receipt inside claimed coverage still refuses the lane.

Follower acknowledgement preparation shares this source-proof owner and its maintenance rest deadline. Initial validation runs in bounded pages outside the writer; the closed acknowledgement enters the existing commit coordinator only after preparation. A completed candidate retains custody until admission, yield or cancellation. Ordinary queued writers keep their FIFO order. The 32-frame report cadence and existing request deadlines are unchanged. The internal riffdb-follower-ack-work-v1 diagnostic reports numeric phase (1 preparation, 2 admission), result (0 ready/completed, 1 yielded, 2 refused), off_gate_us, writer_wait_us, gate_held_us, initial_rows and continued_rows. Writer wait and gate residence come from the native FIFO census; off-gate elapsed work excludes both. Durations are elapsed observations, not CPU measurements or a latency promise; the maximum unsigned value means unknown. These observations contain no hold identity or payload and grant no authority to advance a hold.

Held bootstrap manifest/page reads share that same source certificate through a closed native read protocol. The first complete proof advances in bounded pages outside the writer; subsequent checks use the qualified source pin or checked native successors. Each release rechecks current exact custody after bounded artifact I/O. Incomplete validation releases no bytes, and a reader cannot displace a prepared follower acknowledgement. These remain foreground reads under existing source-job capacity and deadlines; they create no control write, reclamation license or extra maintenance budget. Receiver startup and publication continue to use their complete validators. The internal riffdb-bootstrap-custody-work-v1 diagnostic reports numeric result (0 ready, 1 yielded, 2 refused), elapsed_us, initial_rows and continued_rows for actual native proof work. It contains no hold identity, payload or endpoint. Elapsed observations are not CPU measurements; the maximum unsigned value means unknown.

The runtime binds the shared source scheduler even when retention is held. Acknowledgement custody uses the same rest policy. Rebinding cannot reset already charged rest or clock-overflow refusal.

After retention invalidates optional validated-prefix startup evidence, the maintenance lane can rebuild it from a complete structural, catalog-semantic, and retained-history scrub on one pinned snapshot. Each validation page contains at most 64 rows and observes shutdown cancellation. Population validation and checkpoint planning run outside the writer gate. Publication requires the exact unchanged history binding and a drained, sealed checkpoint; otherwise the candidate is discarded. The lane never reuses deleted evidence or forces a journal drain to publish it. Pin capture and publication participate in the existing maintenance pacing. Sustained-load recovery remains subject to the milestone’s first-successful-write rehearsal gate.

Pruned history stays auditable. A pruned command keeps its outcome and provenance, so its idempotent retry answers the original result. Reads of its events or commit, and commit scans that start at or below the watermark, return the typed HistoryPruned error.

Read the retention limits in Known limitations before enabling it. In particular, unless [maintenance.outbox] (or [databases.<alias>.outbox]) is configured so the local outbox worker archives and acknowledges emitted events, a contract that emits events compacts only the history before its first undelivered event. The offline riffdb retention operations remain available on a stopped database; see Backup and Restore.

The legacy server.grpc_listen form always means literal-loopback cleartext. For an explicit closed listener profile, omit grpc_listen and select exactly one tagged table:

[server.application_listener]
mode = "loopback_cleartext"
listen = "127.0.0.1:7443"
[server.application_listener]
mode = "direct_tls"
listen = "0.0.0.0:7443"
public_endpoint = "https://riffdb.internal.example:7443"
certificate_chain = "/etc/riffdb/tls/server-chain.pem"
private_key = "/etc/riffdb/tls/server-key.pem"

[server.application_listener.bounds]
max_connections = 1024
max_streams_per_connection = 128
handshake_timeout_seconds = 10
idle_timeout_seconds = 300
keepalive_interval_seconds = 30
drain_timeout_seconds = 30
[server.application_listener]
mode = "local_socket"
path = "/run/riffdb/application.sock"
access = "owner_only" # or "owner_and_group"

The explicit listener table cannot be mixed with grpc_listen, --listen, or RIFFDB_LISTEN. TLS has no cleartext fallback, trust-all mode, mutual-TLS selector, cipher-suite selector, or provider selector. The certificate must contain the DNS/IP identity in public_endpoint; validation happens before bind. Certificate/key replacements are adopted atomically for new handshakes only after the complete pair validates.

The equivalent multi-database form is:

[server]
grpc_listen = "127.0.0.1:7443"
audience = "riffdb-grpc-loopback"
capability_keys = "/etc/riffdb/capability.keys"
idempotency_keys = "/etc/riffdb/idempotency.keys"

[databases.ea]
path = "/var/lib/riffdb/data/ea.redb"
backup_root = "/var/lib/riffdb/backups/ea"
environment = "local"

[databases.budget_demo]
path = "/var/lib/riffdb/data/budget-demo.redb"
backup_root = "/var/lib/riffdb/backups/budget-demo"
environment = "local"

Aliases match [a-z][a-z0-9_-]{0,63}, sort by canonical bytes, and are unique. All database, backup, projection, source/receiver artifact, and key paths across the process must be pairwise lexically disjoint. The process opens every database and builds every graph before publishing readiness. One structural startup failure stops the process.

When riffdbd runs in primary mode with [databases.<alias>] loaded from a configuration file (--config or RIFFDB_CONFIG), updating that file on disk reconciles added and removed database aliases online without interrupting peer databases:

  • Newly added [databases.<alias>] sections are validated, opened, and published onto the gRPC and Hosted MCP routing tables only after startup and disjoint-path checks succeed.
  • Removed [databases.<alias>] sections (when at least one database remains configured) are atomically unpublished from the routing tables, drained, closed with a clean-close checkpoint, and released from their redb file locks.
  • Any reload that fails TOML validation, introduces overlapping paths, mutates process-global listener/audience/key settings, or changes a retained database’s immutable storage paths or environment fails closed, logs a riffdbd config reload rejected diagnostic, and retains the active inventory unchanged.

Backup roots must be siblings. This is valid:

[databases.default]
path = "/var/lib/riffdb/data/riffdb.redb"
backup_root = "/var/lib/riffdb/backups/default"
environment = "local"

[databases.ea]
path = "/var/lib/riffdb/data/ea.redb"
backup_root = "/var/lib/riffdb/backups/ea"
environment = "local"

This is invalid because one configured root contains another:

[databases.default]
path = "/var/lib/riffdb/data/riffdb.redb"
backup_root = "/var/lib/riffdb/backups"
environment = "local"

[databases.ea]
path = "/var/lib/riffdb/data/ea.redb"
backup_root = "/var/lib/riffdb/backups/ea"
environment = "local"

Startup reports the two conflicting logical roles and aliases, but never echoes configured path values.

The complete document is UTF-8 and at most 524,288 bytes. Unknown tables or keys, duplicates, wrong types, duplicate scalar flags, missing flag values, and empty selected values reject. Paths are nonempty, at most 4,096 platform bytes, and lexically normalized. The database and key paths may be relative to the server working directory; the backup root must be absolute.

The database path, backup root, projection root, source and receiver artifact roots, capability key path, and idempotency key path must be lexically disjoint. A file is not accepted when another configured path is equal to it, contains it, or is contained by it.

Each database reserves a private replication artifact root by appending .riffreplication to its path (for example, data/main.redb.riffreplication). The derived path also has the 4,096-byte ceiling. This directory holds at most four source bootstrap artifacts and an exclusion lock; it cannot overlap any other configured database or root. Source composition creates it with private permissions and refuses symlink or identity substitution.

Each database also reserves .riffreceiver beside its database path, with the same path-length and disjointness checks. Its fixed receiver inventory is separate from source artifacts so promotion cannot mix the two layouts. The managed receiver holds an exclusive inventory lock and creates private transfer and candidate directories. Initial creation uses temporary names until the first durable progress checkpoint exists; crash recovery discards only those known unpublished files and resumes published progress. After authenticated attachment succeeds, the receiver removes candidate scratch links and then transfer evidence, while retaining the live follower’s engine lock. Interrupted cleanup resumes against the validated live follower. See follower mode for daemon configuration and lifecycle behavior.

The packaged systemd unit fixes the authoritative state root at /var/lib/riffdb/data, the backup root at /var/lib/riffdb/backups, and the configuration root at /etc/riffdb. The latter contains the root-owned riffdbd.toml path-only configuration and the two protected digest-key documents. It contains no raw digest key in TOML.

Per-database resource limits

An optional [maintenance.limits] table (or [databases.<alias>.limits] in the multi-database form) bounds per-database storage, request rates, blocking pool concurrency, commit coordinator capacity, journal extent sizing, and idle hibernation:

[maintenance.limits]
max_storage_bytes = 536870912
max_write_rps = 50
write_burst = 100
max_read_rps = 250
read_burst = 500
max_blocking_operations = 4
blocking_worker_threads = 2
max_coordinator_capacity = 32
extent_profile = "compact"
idle_hibernate_seconds = 300
  • max_storage_bytes (65_536..): refuses new state-growing commands, contract deployments, migrations, and capability creations with typed Overloaded when the authoritative database file plus journal footprint reaches or exceeds the ceiling. Reads, ResolveCommandOutcome, RevokeCapability, and application exports (StartApplicationExport, GetApplicationExportPage, GetApplicationExport, CancelApplicationExport) remain permitted at the storage ceiling so data and outcomes are never stranded.
  • max_write_rps (1..=1_000_000) and optional write_burst (1..=1_000_000, default max_write_rps): monotonic token-bucket governor for mutating commands and administration writes.
  • max_read_rps (1..=1_000_000) and optional read_burst (1..=1_000_000, default max_read_rps): monotonic token-bucket governor for reads, queries, and export scans (GetHealth and GetStatistics probes are exempt).
  • max_blocking_operations (1..=256), blocking_worker_threads (1..=8), and max_coordinator_capacity (1..=4_096): override the per-database blocking pool and commit coordinator queue bounds.
  • extent_profile ("standard" or "compact", default "standard"): selects the per-database journal extent profile. "standard" preallocates 320 MiB active and spare extents; "compact" preallocates an 8 MiB active extent (4 MiB checkpoint rotation threshold) and defers .riffjournal.spare allocation until 2 MiB (25%) active utilization or on-demand checkpoint rotation. Switching profiles across restarts normalizes empty active extents in place and seals extents that contain materialized history stubs before opening the next generation at the newly configured capacity.
  • idle_hibernate_seconds (1..=86_400, default absent): enables scale-to-zero idle hibernation for a database in multi-database hosting. When no gRPC or hosted MCP request has touched the database for idle_hibernate_seconds, and the database has no active service jobs, no active commit notification subscribers, and zero pending outbox deliveries, the supervisor cleanly drains the database’s runtime graph, commits a clean-close certificate, and releases its exclusive redb file lock and worker threads while keeping the process and alias routes alive. The next gRPC or hosted MCP request targeting the hibernated database transparently wakes it via single-flight initialization before dispatching the request.

Optional hosted MCP settings:

OptionValueRestartImpact
--mcp-listenLiteral loopback address with nonzero portYesEnables loopback-cleartext Streamable HTTP MCP
--mcp-originRepeatable checked originYesBrowser-origin authorization

In TOML, [server.mcp_listener] may also select an explicit loopback_cleartext or direct_tls profile (mutually exclusive with mcp_listen / mcp_origins / --mcp-listen / --mcp-origin):

[server.mcp_listener]
mode = "direct_tls"
listen = "0.0.0.0:7444"
public_endpoint = "https://riffdb.internal.example:7444"
certificate_chain = "/etc/riffdb/tls/server-chain.pem"
private_key = "/etc/riffdb/tls/server-key.pem"
origins = ["https://app.internal.example:8443"]

--mcp-origin without --mcp-listen rejects. With no allowed origins, only requests without an Origin header are accepted. The MCP protected-resource audience is derived as http://<listen-authority>/mcp for loopback_cleartext and <public_endpoint>/mcp for direct_tls; it cannot be configured and must differ from the gRPC audience. Direct-TLS origins must use https://.

RIFFDB_MCP_ORIGINS is one nonempty comma-separated list with no trimming or normalization. TOML uses a string array and CLI uses one flag per origin. Whichever source wins supplies the whole list. At most 16 unique origins and 8,192 aggregate bytes are accepted; each origin is at most 512 visible ASCII bytes and must pass the hosted transport’s origin checks.

Other RIFFDB_* variables are not server settings. There is no storage-engine selector, group-commit tuning, transaction callback, or nondeterministic scheduler setting in the POC configuration surface.

Digest-Key Documents

Capability keys:

riffdb-capability-digest-keys-v1
1:<exactly 64 lowercase hexadecimal characters>

Idempotency keys:

riffdb-idempotency-digest-keys-v1
1:<exactly 64 lowercase hexadecimal characters>

Documents are at most 1,024 bytes, end with LF, contain one through eight entries, use unique nonzero canonical decimal IDs, and contain no duplicate material. The first listed entry is the write key; later entries remain readable for rotation. The two namespaces must never reuse material.

On Linux, each secret source must be a regular non-symlink final component owned by the process effective user with no group or other mode bits. Metadata is checked before and after a no-follow open.

riffdb Client

Configuration is resolved independently for each field, highest precedence first:

FieldFlagEnvironmentTOMLDefault
Endpoint--endpointRIFFDB_ENDPOINTclient.endpointhttp://127.0.0.1:7443
Database--databaseRIFFDB_DATABASEclient.databasedefault
Output--outputRIFFDB_OUTPUTclient.outputhuman
Attempts--max-attemptsRIFFDB_MAX_ATTEMPTSclient.max_attempts3
Credential file--credential-fileRIFFDB_CREDENTIAL_FILEclient.credential_fileabsent
TLS trust root--tls-trust-rootRIFFDB_TLS_TRUST_ROOTclient.tls_trust_rootsystem CA bundle on https://
TLS server name--tls-server-nameRIFFDB_TLS_SERVER_NAMEclient.tls_server_nameendpoint host on https://

The TOML path is selected only by --config, then RIFFDB_CONFIG, then absence. There is no directory discovery. A present invalid higher-precedence value rejects instead of falling through.

The endpoint is either exact lowercase http://<literal-loopback-IP>:<port-1..65535> or canonical https://<DNS-or-IP>:<port-1..65535>. For HTTPS, omitting tls_trust_root selects the platform system CA bundle, and omitting tls_server_name derives the expected peer identity from the endpoint authority host; when supplied, tls_trust_root must be an absolute, normalized path and tls_server_name must match the endpoint authority. Supplying either TLS field for cleartext or requesting a trust-all/downgrade mode rejects. Generated application client configuration retains any explicit non-secret TLS selectors. Unknown, duplicate, or wrong-type input rejects. Output is human or json. Attempts is canonical decimal 1..=10.

Exactly one normal credential source may resolve:

  • the protected credential file selected above; or
  • RIFFDB_CAPABILITY_TOKEN, an exact 43-byte bearer presentation.

Supplying both rejects. Prefer the protected file. Machine output is one compact JSON object plus LF and no other stdout bytes.

riffdb-mcp Stdio Bridge

The bridge accepts only:

--config PATH
--endpoint LOOPBACK_HTTP_ENDPOINT
--database DATABASE
doctor --config PATH

Configuration path precedence is --config, RIFFDB_MCP_CONFIG, then absent. Endpoint precedence is --endpoint, RIFFDB_MCP_ENDPOINT, [mcp].endpoint, then http://127.0.0.1:7443. Database precedence is --database, RIFFDB_MCP_DATABASE, [mcp].database, then default.

Its TOML contains only:

[mcp]
endpoint = "http://127.0.0.1:7443"
database = "default"
credential_file = "/var/lib/riffdb-mcp/credential"
expected_audience = "riffdb-grpc-loopback"

Credential selection is exactly one of RIFFDB_MCP_CAPABILITY_TOKEN, RIFFDB_MCP_CREDENTIAL_FILE, or [mcp].credential_file. The environment file path takes precedence over the TOML path, while simultaneous raw token and file selection rejects. The credential is loaded once and moved into the ordinary public gRPC client. expected_audience, when present, is bounded target-identity evidence written by application provisioning; it grants no authority and has no flag or environment override.

The bridge accepts a database alias, not a path. It does not accept a storage option, environment, audience authority override, policy, or server-side authority.

Follower mode

--mode follower (or RIFFDB_MODE=follower, or [server] mode = "follower") selects the separate follower startup path before primary initialization or maintenance. The default remains primary. Each configured database must have one complete replication_source block; primary mode rejects such blocks. The mode follows the usual CLI, environment, then file precedence. Source identity and trust settings are file-only and cannot be overridden individually.

For the legacy single-database form, use [server.replication_source]. For a named database, use [databases.<alias>.replication_source]. Required fields are:

FieldMeaning
endpointCanonical HTTPS primary endpoint
trust_rootAbsolute path to the primary’s CA certificate file
server_nameExact verified server identity matching the endpoint
credential_fileAbsolute path to a protected replication capability token file
databaseDatabase alias at the source
database_idExpected UUIDv7 database identity, with hyphens
history_incarnationExpected nonzero history incarnation
leadership_epochExpected nonzero leadership epoch
hold_idStable nonzero 16-byte source hold ID, as 32 lowercase hex digits

Credential files use the existing protected-file loader. TLS always verifies the configured CA and server name. Trust and credential paths cannot overlap database, backup, projection, replication scratch or digest-key paths. All source peers are checked before local construction starts. Receiver scratch is the reserved <database-path>.riffreceiver sibling directory.

Startup bootstraps an absent follower or fully validates and reopens an existing one. A retained complete transfer permits the existing candidate-publication recovery path to check its exact fence and destination before replacement. Tail reception uses the supervised worker’s bounded retry and shutdown rules. A committed promotion is reconciled from local authoritative control and external audit before loading former-source credentials. Once completely validated, it starts as a source even with its existing follower configuration. Unresolved pre-cutover attempts enter restricted promotion-retry hosting after full local validation, without connecting to the former source at boot. Ordinary readiness stays fenced. A freshly authorized exact retry requires that source’s authenticated fence proof before cutover. See follower administration.

After validation, the daemon activates a writer-free application service for each configured alias, binds configured hosted MCP routes, and emits the ordinary application-ready receipt. Standard input accepts shutdown; process signals also stop the daemon. Shutdown closes transport admission, stops replication and drains accepted service reads before releasing the remaining snapshot pins.

Authentication and current policy observe the latest completed follower prefix. A capability or contract installed later on the source becomes visible through replication; followers expose no local capability-bootstrap convenience path. Commands, administration, migration, maintenance and export return the typed follower-mode refusal. The experimental promotion operation has its own external audit owner. Policy-required durable read audit still refuses.

The daemon process tests compare every authoritative namespace under the app-baseline workload and resume across repeated source and follower crashes. The follower replication and projection freshness checks pass their full CI gates, including follower freshness, provider equality and sequence-lag reporting.

Follower named queries can use the existing exact-text, exact-predicate, tokenized-text and long-pattern providers. Their local checkpoints live under the configured projections_root and remain rebuildable. Background workers read only completed follower snapshots; the applied application frontier is their local head for minimum-commit and AdmissionHead checks. A provider that has not caught up returns the existing typed freshness or availability refusal. Configured scalar and vector columnar sources stay cold until demand. One owned worker builds independently validated disposable V2 views under projections_root/follower-columnar; no source artifact is required and no local authoritative control is written. Cold or activating sources return the existing typed Building/unavailable outcome and keep columnar health degraded. Failed materialization remains closed until owned recovery or restart; a request cannot clear failure. Restart rebuilds these disposable views from the completed prefix.

Projected reads retain Causal, Bounded and Available semantics using the applied local head. Replication source-head observations only report lag. Foreign database/history tokens refuse before waiting, and opaque cursors are local to one live process. See sequence-lag health and statistics for both-node reporting.

Exact-provider checkpoint custody

On Linux, exact-text, predicate, nullable-predicate, tokenized-text and long-pattern checkpoint directories are created privately (0700), with private regular checkpoint files (0600) owned by the daemon user. Checkpoint access rejects symlinked path components, foreign ownership, broadened permissions, extra hard links and replaced directory or file identities. Paths are bounded by 4096 encoded bytes and 32 components; relative configured paths remain supported. Unsupported platforms refuse this checkpoint custody.

Publication synchronizes complete pending bytes, publishes without replacing an occupied name, and synchronizes the pinned directory. Restart completes only a verified two-name publication or rebuilds absent derived state. Partial work cannot become authoritative state. Checkpoint envelope formats remain unchanged. An installation with older permissive derived directories or files must stop the daemon and make these owned directories and files private before restarting; RiffDB refuses unsafe custody instead of changing permissions on existing objects. Ownership records retain live directory and file descriptors through cleanup, so a deleted object’s inode cannot be reused while its ownership claim remains. Retirement removes only the recorded selected and pending derived objects after all captures release. A foreign replacement is preserved; the retiring registration continues to consume capacity until its custody can be proved.

Exact-provider retention clock

Primary and follower servers use the same process-local monotonic clock for cursor expiry and exact-provider minimum retention. A failed clock sample, backward time, or an unrepresentable retention deadline returns the existing provider Unavailable error. There is no application or operator override. Each exact-provider family retains its 256-registration limit. Under registration or aggregate admission pressure, the server retires the least-recently-used eligible owner, with canonical family/key tie breaking. Eligibility requires the existing 300-second minimum retention to expire and all query, cursor, worker and publication pins to release. Successful access extends retention; failed capture does not. When every owner is protected, new demand returns typed unavailable. Later access to an evicted registration starts ordinary Building/rebuild admission with a fresh generation. It cannot resume a cursor using a substitute epoch.

One pre-admitted 512-KiB cleanup lane remains reserved for the runtime’s lifetime, allowing cleanup at budget saturation. Cleanup runs outside the discovery lock; contention refuses new admission without changing an owner’s phase. Bounded, admitted worker inventories hold keys only and capture one live owner at a time.

A freshly authorized higher revision of the same capability retires its older registrations across all five families. Existing captures remain charged until released; retirement cannot shorten the 300-second retention obligation. Obsolete workers stop before source observation and at bounded replay, scan, materialization and publication points. Each worker pass attempts at most four eligible retired-owner cleanups, including when no capacity pressure exists; this cleanup does not evict idle active registrations.

New exact-provider registrations reserve owned registration data and shared compiled metadata against one compiled 1-GiB process budget. Shared metadata is charged once and retains its charge through its final data reference, including catalog references. Budget pressure or incomplete ownership evidence returns the existing typed Unavailable result. Servers sharing a process share this pool; there is no budget configuration or application cache control. Queries and workers also reserve their explicit capture pins against this pool; capture pressure returns the same typed unavailable result. Encoded checkpoint read buffers reserve capacity before reading and retain their charge through the last buffer reference. Budget pressure leaves rebuild work eligible for a later retry. Provider source decoding and derived-row normalization reserve construction capacity before allocation. Unchanged rows share their admitted original; materialized fields retain their charge through the final semantic row clone. Private provider row and delta conversion buffers also reserve native capacities before copying, and retain their reservations through destruction. The exact-provider worker emits bounded operator diagnostics on stderr at startup, at most once per 60 seconds while its service clock is healthy, and at worker stop. Lines use trigger=exact_provider_lifecycle and fixed numeric fields: the process limit, fixed owner bytes, owned/reserved bytes, outstanding reservations (reserved_work), live/released accounting allocations, active/retiring owners, unique pinned owners, distinct pin guards, and finally released owners. Cloned references to one pin guard count once. Retiring or pinned state retains its bytes; removing discovery entries cannot report an owner as released. Clock or accounting failure emits available=false without fabricated counters. These lines contain no keys, tenants, capabilities, paths or payloads. Multiple runtimes in a process report the same aggregate pool.

Provider catalog capture borrows an exactly matching catalog already published by the ordinary catalog adapter or completed follower prefix. It reserves shared ownership before capture and refuses when that publication is absent, stale or contended; providers cannot cold-decode a private catalog. Retained physical source descriptors retain their metadata charge through their final capture.

Provider receipt replay and source-pin validation reserve native construction work before decoding, including archived history stubs. Retained cursors and receipts carry their charges through destruction; pressure refuses without advancing a receipt cursor. Provider head checks read the immutable snapshot frontier without decoding a command record.

Provider policy reads, observation copies and evaluation work now reserve capacity on the same captured source; resulting proofs retain their charge through final destruction.

A strictly newer contract can retire eligible provider state for the same named operation and exact partition/authority identity after pins and minimum retention clear. Historical explicit queries can rebuild with a fresh owner.