Installation
For application-language package names and the common four-step workflow, see Driver and CLI Packages.
RiffDB’s alpha deployment target is Linux. Protected key and bearer loaders
depend on Linux /proc/self/status and exact owner/mode checks. The convenience
installer retains its loopback development profile; controlled remote
application deployments can opt into verified direct TLS through explicit
server configuration.
RiffDB is available under either the MIT License or the Apache License, Version 2.0, at your option. A verified release bundle includes the ownership notice and both license texts.
Copyright © 2026 Kevin O’Shea and O’Shea & Sons, LLC.
Source Checkout Convenience Install
Run the convenience commands from the repository root so Cargo reads
.cargo/config.toml and its riffdb alias. Rust 1.98.1, Linux, systemd, Git,
jq, a util-linux uuidgen with UUIDv7 support, and the ordinary utilities
checked by the installer are required.
Use a clean, reviewed checkout for an attributable installation. When the
checkout is clean, the installer embeds its exact Git revision. A dirty
checkout is allowed for development, but its binaries are labeled
development-unversioned and are not release evidence or attributable to a
specific revision.
For a service owned by the current user:
cargo riffdb install --user
cargo riffdb bootstrap --user
To register the generic MCP developer capability with Codex during bootstrap:
cargo riffdb bootstrap --user --register-codex
To create several isolated databases under one service, provide every alias on the first install and then bootstrap them separately:
cargo riffdb install --user --database ea --database orders
cargo riffdb bootstrap --user --database ea
cargo riffdb bootstrap --user --database orders
To add a database to an existing installer-owned user or system installation, use the offline migration command:
cargo riffdb install --user --add-database ea
cargo riffdb bootstrap --user --database ea
The command stops an active service, preserves a legacy database as alias
default, moves the legacy backup contents to backups/default, creates the
new sibling root backups/ea, atomically publishes named configuration, and
starts the service again. Its private phase journal makes the operation
resumable after process or machine interruption. --no-start requires the
service to already be inactive and leaves it inactive. The command accepts
only an exact installer-generated configuration; it refuses customized TOML
without changing it.
--database is repeatable on install, accepts the canonical
[a-z][a-z0-9_-]{0,63} alias grammar, and is bounded to 32 unique
installer-managed aliases (riffdbd itself supports up to 512 [databases.<alias>]
tables with online configuration reload and optional idle_hibernate_seconds;
see Multiple Databases).
Repeating installation with explicit aliases requires the exact retained alias
list. Only --add-database changes an existing installer-owned registry. The
installer never edits custom server configuration.
Named bootstrap writes separate private files such as client-ea.toml,
operator-ea.credential, mcp-ea.toml, and mcp-ea.credential. With
--register-codex, it registers riffdb_ea. The unsuffixed filenames and
registration name remain the compatibility form for alias default.
For a machine-wide service, select the same scope in both phases:
cargo riffdb install --system
cargo riffdb bootstrap --system
System scope is an administrator-facing POC path. Repository automation
validates the shared release service assets but does not execute this source
installer’s privileged account, /etc, or /usr/local flow. Perform the first
privileged installation and service-start acceptance on the intended
disposable or staging host, review every sudo prompt, and require
authenticated RiffDB Health to report the exact deployment-required state
before creating the first application.
Always invoke these as the intended unprivileged operator. Never use
sudo cargo riffdb ...; the installer rejects a root caller. The system
installer builds all three binaries as the operator and invokes sudo
internally to inspect protected destination state, publish root-owned files,
create the two service accounts and their directories, verify the unit, and
control the system service.
System bootstrap still runs as the operator and uses the public loopback API.
The helper is mutable source-checkout code and is not suitable for a narrow
sudoers allowlist. System installation assumes the operator already has
ordinary administrative authorization for the displayed sudo operations.
Privileged publication opens only root-owned destination paths; the
unprivileged process opens and streams each checkout or build source.
Installed Paths
| Asset | --user | --system |
|---|---|---|
| Product binaries | $HOME/.local/bin/{riffdbd,riffdb,riffdb-mcp} | /usr/local/bin/{riffdbd,riffdb,riffdb-mcp} |
| Server configuration and digest keys | ${XDG_CONFIG_HOME:-$HOME/.config}/riffdb/ | /etc/riffdb/ |
| Database and backups | ${XDG_DATA_HOME:-$HOME/.local/share}/riffdb/data/riffdb.redb and .../backups/, or <alias>.redb and backups/<alias>/ | /var/lib/riffdb/data/riffdb.redb and /var/lib/riffdb/backups/, or <alias>.redb and backups/<alias>/ |
| Service unit | ${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/riffdbd.service | /etc/systemd/system/riffdbd.service |
| Service state/working directory | ${XDG_STATE_HOME:-$HOME/.local/state}/riffdb/ | /var/lib/riffdb/ |
| Service accounts | Current user | riffdb and riffdb-mcp |
| MCP service roots | Operator XDG files described below | /etc/riffdb-mcp/ and /var/lib/riffdb-mcp/ |
| Account/directory definitions | Not applicable | /usr/local/lib/sysusers.d/riffdb.conf and /usr/local/lib/tmpfiles.d/riffdb.conf |
Bootstrap always writes the current operator’s private client files beneath
${XDG_CONFIG_HOME:-$HOME/.config}/riffdb/ and recovery state beneath
${XDG_STATE_HOME:-$HOME/.local/state}/riffdb/, including for a --system
database. In particular, client.toml, operator.credential, mcp.toml, and
mcp.credential are user-owned mode-0600 files. The same private state root
retains operator-capability.id and mcp-capability.id for deliberate
server-side revocation. System scope does not turn an operator credential into
a root-owned secret.
User binaries follow the XDG-recommended ~/.local/bin convention. Add that
directory to PATH for direct riffdb and riffdb-mcp invocations when the
login environment does not already include it. The cargo riffdb bootstrap
command uses the installed absolute paths and does not depend on that PATH
entry.
Start Control and User Managers
Installation normally verifies, enables, and starts the selected service. To publish and verify files without reloading, enabling, starting, or restarting the service:
cargo riffdb install --user --no-start
cargo riffdb install --system --no-start
Bootstrap requires the selected service to be running and reachable. After a
user --no-start installation, start it deliberately with:
systemctl --user daemon-reload
systemctl --user enable --now riffdbd.service
Every user-scope installation requires a valid existing XDG_RUNTIME_DIR
owned by the current user with mode 0700; systemd-analyze --user verify
uses it even with --no-start. Automatic user-service start or restart also
requires a functioning per-user systemd manager, normally supplied by a PAM
login session. install --user --no-start verifies the unit without contacting
the live user bus. The installer does not call loginctl, enable lingering,
or fabricate a runtime directory or user bus. On a headless or restricted SSH
host, arrange the runtime directory, user manager, and linger policy
explicitly, retain --no-start until the manager exists, or use the system
scope. For system --no-start, an administrator later runs:
sudo systemctl daemon-reload
sudo systemctl enable --now riffdbd.service
Explicit Bootstrap Semantics
Install creates filesystem state and, unless --no-start is selected, starts
an unbootstrapped server. It does not silently create database authority. The
separate bootstrap command:
- submits the one-time first-human bootstrap through loopback gRPC;
- durably retains uncertainty-recovery material before submission;
- writes private operator CLI configuration and credential files;
- leaves the active contract catalog empty;
- creates a separate generic developer capability and config for
riffdb-mcp, limited to contract validation, contract catalog reads, contract deployment, and authenticated health; and - with
--register-codex, runscodex mcp add riffdb -- ...only after the database and MCP credential are ready.
Successful bootstrap checks the exact empty-catalog health state, including a healthy primary replication component with no registered followers. Projection and outbox workers may still report degraded readiness during startup.
These are authoritative database operations, not local file setup. A failure
after owner bootstrap may leave the database successfully bootstrapped even
when later MCP-capability creation or Codex registration failed. Rerun the same
bootstrap command: it preserves retained bootstrap material and pending
capability identity so uncertainty is resolved rather than assigned a new
identity. It refuses to replace changed credential/config files or a
conflicting Codex MCP entry named riffdb (or riffdb_<alias>), while
treating an identical existing registration as an idempotent success. Bootstrap
serializes --register-codex across RiffDB callers with an exclusive lock on
${XDG_CONFIG_HOME:-$HOME/.config}/riffdb/.codex-mcp.lock; avoid concurrent
non-RiffDB edits to that user’s Codex MCP configuration.
The MCP developer can author, validate, and deploy an application contract without installation choosing an application. Because application permissions name exact contract lineage and stable IDs, the pre-deployment capability does not grant wildcard command, entity, index, projection, commit, provenance, or outbox access. After deployment, use the administrator CLI config to bind a compiled application role or issue an exact scoped capability, then configure the application or a separate MCP identity with that credential.
For an exact locked application, the canonical installed workflow is:
riffdb application lock --write
riffdb application generate --locked
riffdb application deploy \
--provision-role EaApplication \
--tenant organization_acme \
--seed
Deployment refuses unlocked or drifted sources. Contract and query deployment
are idempotently resumable, role creation is explicit, and seed files run as
ordinary idempotent commands under the resulting application credential.
Private lock- and database-bound progress, the application credential, and
generated application-only client.toml and mcp.toml live beneath
.riffdb/deployments/<database>/; this state records identities but is never
an authority source. The journal records the exact lock hash, contract bundle
hash, query-module names, versions and hashes, compiled role identity, and
capability ID. A deploy compares those locked identities with every server
response; it never treats an operation name or a locally completed stage as
proof that the active server identity matches.
The deploy command is safe to rerun after interruption at contract deployment,
query-module deployment, role binding, or any individual seed command. Remote
success is reconciled with the retained request and capability identity before
the next stage is published locally. Do not delete
.riffdb/deployments/<database>/ to recover from an uncertain deploy: rerun the
same command with the same exact lock.
The lower-level, caller-identified installation campaign start and observation
surface is documented in
Application Installation Campaigns.
It reports durable partial progress and never treats running as installed.
Python applications
The Python application distribution is riffdb-application and supports
CPython 3.13 and 3.14 with manylinux 2.28 wheels on x86_64 and aarch64. Release bundles carry the
matching limited-API wheel under public/python, allowing the installed CLI to
create a fully locked, offline scaffold:
riffdb new my-app --language python
cd my-app
uv sync --locked
Direct pip and uv wheel installation, source-distribution builds, explicit
application-format migration, sync and async client usage, and the release
artifact commands are documented in Python Application Driver.
Changing the application lock is a successor deployment, not a resume. When a prior application role exists, RiffDB refuses to mutate the server until the operator supplies both the role and the explicit replacement acknowledgement:
riffdb application deploy \
--provision-role EaApplication \
--replace-role-credential \
--seed
That path deploys the exact successor and starts a resumable least-authority credential rotation. RiffDB retains the old capability while it creates and proves the exact successor, atomically publishes the new protected client and MCP configuration, and then explicitly revokes the predecessor. A retry continues from the retained phase; it does not revoke the only proven secret. A module compare-and-swap race fails with the database alias plus the lock, locked-module, expected-active, and actual-active hashes. Never copy a hash from an error into source or bypass the lock; rerun deployment after reconciling the competing deployment.
For a successor, run riffdb application lock --write while the intended
database alias is selected. Unlike deployment, this is an authorized read-only
preview: the server compiles against the active parent and the CLI pins the
canonical result in generated/riffdb.contract.bundle. Deployment later sends
the exact parent version/hash and candidate hash from that artifact. Parent or
candidate drift is rejected before catalog admission and the error reports the
lock, expected parent, actual active, and compiled candidate hashes.
Role check, bind, and provisioning consume that pinned parent-aware bundle;
they do not recompile successor source as a genesis contract. If the symbolic
role widened, use --provision-role <role> --replace-role-credential so the
new role is proven and published before the retained predecessor capability is
explicitly revoked. The legacy --replace-expired-credential spelling remains
an alias.
Do not use contract deploy as a probe. A successful direct deployment changes
the active pointer and the POC has no rollback RPC. Use contract validate or
the successor lock preview to inspect a candidate. Recovery from an unwanted
activation is an offline backup restore (or a destructive disposable-alpha
reset), performed only while the service is stopped.
Bootstrap defaults to http://127.0.0.1:7443. To select a deliberately
reconfigured loopback listener, set an exact endpoint for that invocation:
RIFFDB_ENDPOINT=http://127.0.0.1:7553 \
cargo riffdb bootstrap --user
Treat that value as an authority selection: verify it identifies the intended
unbootstrapped database, and do not leave a stale override in the environment.
User and system installs both default to port 7443, so choose one default
scope. Running both on one host requires distinct server listen ports and
separate operator XDG config/state roots; otherwise their service listeners and
private client filenames collide.
The generated owner capability requests 30 days and the MCP developer
capability requests less than 30 days. The POC does not renew either
in the background; run cargo riffdb bootstrap (--user|--system) [--database ALIAS] --renew
or riffdb capability rotate <CAPABILITY_UUIDV7> --credential-output <PATH> --revoke-old
before the current administrative capability expires.
Rerun and Update Limits
Re-running install validates ownership, modes, non-symlink paths, and existing key documents, then republishes the three binaries. It preserves existing digest keys, server configuration, service-unit files, and system sysusers/tmpfiles definitions byte-for-byte rather than treating them as upgrade templates. During first-time key creation it publishes a private, create-only marker before either key. A rerun creates a missing typed counterpart only when that exact marker is still present, server configuration and the service unit have not been published, and the data, backup, and state directories remain empty. Partial key state without the marker, or any missing key beside later-phase configuration, unit, data, backup, or state evidence, fails closed as possible key loss. It refuses linked or malformed key state, unsafe file types, unexpected ownership, or modes.
Each binary replacement is staged on its destination filesystem and backed up in
.riffdb-binary-rollback-v1 until all three binaries are published and unit
verification succeeds. If installation fails or is interrupted during binary
publication, the installer restores the previous binary set on exit or at the
start of the next cargo riffdb install invocation.
Consequently, the convenience command is not a general configuration
migration, key-rotation, data-deletion, or compatibility-aware upgrade tool
(cargo riffdb uninstall removes only binaries and unit files; see
Upgrade and Removal).
Pre-Alpha Database Reset
A reset is an offline, destructive operator action for disposable pre-alpha data. It is not a schema migration, is never performed by contract deployment, and has no runtime API. Prefer an additive successor whenever the compatibility rules permit it.
Resetting one alias creates a new database identity. Its old operator, MCP, and application credentials no longer authorize the new database. Retained bootstrap material, capability IDs, generated client configuration, and application deployment progress for that alias must be archived with the old database and recreated.
For an installer-owned user database named ea, first close every client that
could restart the service, then stop the one server process that owns all
configured aliases:
systemctl --user stop riffdbd.service
systemctl --user is-active riffdbd.service
Require the second command to report inactive. Set the XDG roots, create a
private timestamped archive, and move the selected database plus its
database-bound local authority and recovery files into it:
config_root="${XDG_CONFIG_HOME:-$HOME/.config}/riffdb"
data_root="${XDG_DATA_HOME:-$HOME/.local/share}/riffdb"
state_root="${XDG_STATE_HOME:-$HOME/.local/state}/riffdb"
reset_archive="$data_root/manual-reset/ea-$(date -u +%Y%m%dT%H%M%SZ)"
install -d -m 0700 "$reset_archive/config" "$reset_archive/state"
mv -- "$data_root/data/ea.redb" "$reset_archive/ea.redb"
for path in \
"$config_root/operator-ea.credential" \
"$config_root/client-ea.toml" \
"$config_root/mcp-ea.credential" \
"$config_root/mcp-ea.toml" \
"$state_root/bootstrap-ea.credential" \
"$state_root/bootstrap-local-owner-ea.json" \
"$state_root/mcp-local-developer-ea.json" \
"$state_root/operator-capability-ea.id" \
"$state_root/mcp-capability-ea.pending-id" \
"$state_root/mcp-capability-ea.id"
do
if [[ -e "$path" || -L "$path" ]]; then
case "$path" in
"$config_root"/*) mv -- "$path" "$reset_archive/config/" ;;
"$state_root"/*) mv -- "$path" "$reset_archive/state/" ;;
esac
fi
done
Before executing this procedure, confirm the selected [databases.ea].path in
the active riffdbd.toml; custom installations may use another path. Backups
under backups/ea are deliberately retained. Do not move another alias, the
shared capability or idempotency digest keys, or the server configuration.
Start the service, bootstrap only the new empty alias, and deploy a complete genesis application:
systemctl --user start riffdbd.service
cargo riffdb bootstrap --user --database ea
riffdb application lock --write
riffdb application generate --locked
riffdb --config "$config_root/client-ea.toml" application deploy \
--provision-role EaApplication \
--tenant organization_acme \
--seed
Archive or remove the application’s old .riffdb/deployments/ea directory
before the final command so the application does not reuse database-bound
deployment identities. If Codex or another MCP host retained the old
riffdb_ea registration, replace it only after the new bootstrap has produced
the new mcp-ea.toml. A reset is complete only after authenticated health,
active-contract inspection, and an application query succeed against alias
ea.
Review Upgrade and Removal and compatibility policy
before changing revisions. Use --no-start when replacing binaries should not
immediately restart the existing service.
--from-binaries DIR is an advanced staging/test input. DIR must contain
exactly named executable regular files riffdbd, riffdb, and riffdb-mcp.
This mode skips the locked Cargo build and performs no revision, checksum,
signature, or provenance verification; use only a directory whose complete
contents were independently verified. Normal source installs should omit it.
After bootstrap succeeds, the convenience flow is complete. The procedures below are advanced alternatives; do not manually reprovision keys or overwrite configuration created by the convenience installer.
Advanced: Install a Verified Release Bundle
Use this procedure for an extracted archive produced by
scripts/release-poc --verify. Keep the archive, its adjacent checksum, and the
extracted directory together until verification completes:
sha256sum -c riffdb-0.1.0-<target>.tar.gz.sha256
tar -xzf riffdb-0.1.0-<target>.tar.gz
cd riffdb-0.1.0-<target>
sha256sum -c SHA256SUMS
The extracted directory is the bundle root. Install exactly its three product binaries and service definitions:
sudo install -o root -g root -m 0755 \
bin/riffdbd bin/riffdb bin/riffdb-mcp /usr/bin/
sudo install -o root -g root -m 0644 sysusers.d/riffdb.conf \
/usr/lib/sysusers.d/riffdb.conf
sudo install -o root -g root -m 0644 tmpfiles.d/riffdb.conf \
/usr/lib/tmpfiles.d/riffdb.conf
sudo systemd-sysusers
sudo systemd-tmpfiles --create /usr/lib/tmpfiles.d/riffdb.conf
sudo install -o root -g riffdb -m 0640 \
release/config/riffdbd.toml.example /etc/riffdb/riffdbd.toml
sudo install -o root -g root -m 0644 systemd/riffdbd.service \
systemd/riffdb-mcp.socket systemd/riffdb-mcp@.service \
/usr/lib/systemd/system/
Install the reviewed operational documentation, examples, and bundle demo without changing their relative asset layout:
sudo install -o root -g root -m 0755 -d \
/usr/share/doc/riffdb/docs \
/usr/share/doc/riffdb/release/config \
/usr/share/doc/riffdb/examples/contracts
sudo install -o root -g root -m 0644 \
README.md COPYRIGHT LICENSE-MIT LICENSE-APACHE /usr/share/doc/riffdb/
sudo install -o root -g root -m 0755 demo /usr/share/doc/riffdb/demo
sudo install -o root -g root -m 0644 docs/*.md \
/usr/share/doc/riffdb/docs/
sudo install -o root -g root -m 0644 \
release/README.md release/RELEASE_NOTES.md \
/usr/share/doc/riffdb/release/
sudo install -o root -g root -m 0644 release/config/* \
/usr/share/doc/riffdb/release/config/
sudo install -o root -g root -m 0644 examples/contracts/budget.riff \
/usr/share/doc/riffdb/examples/contracts/budget.riff
The release verifier installs the staged bundle into a private root, compares every required asset and mode, checks the extracted layout, and runs the demo from the private installed layout before creating the archive.
Advanced: Manual Build and Install From Source
Install rustup, let rust-toolchain.toml select Rust 1.98.1, and build the
three product binaries:
cargo build --locked --release \
-p riffdb-server --bin riffdbd \
-p riffdb-cli --bin riffdb \
-p riffdb-mcp-stdio --bin riffdb-mcp
Install the binaries and service definitions from the source tree:
sudo install -o root -g root -m 0755 \
target/release/riffdbd target/release/riffdb target/release/riffdb-mcp \
/usr/bin/
sudo install -o root -g root -m 0644 release/sysusers.d/riffdb.conf \
/usr/lib/sysusers.d/riffdb.conf
sudo install -o root -g root -m 0644 release/tmpfiles.d/riffdb.conf \
/usr/lib/tmpfiles.d/riffdb.conf
sudo systemd-sysusers
sudo systemd-tmpfiles --create /usr/lib/tmpfiles.d/riffdb.conf
sudo install -o root -g riffdb -m 0640 release/config/riffdbd.toml.example \
/etc/riffdb/riffdbd.toml
sudo install -o root -g root -m 0644 \
release/systemd/riffdbd.service \
release/systemd/riffdb-mcp.socket \
release/systemd/riffdb-mcp@.service \
/usr/lib/systemd/system/
Install the documentation assets using the same installed layout as the release bundle:
sudo install -o root -g root -m 0755 -d \
/usr/share/doc/riffdb/docs \
/usr/share/doc/riffdb/release/config \
/usr/share/doc/riffdb/examples/contracts
sudo install -o root -g root -m 0644 \
README.md COPYRIGHT LICENSE-MIT LICENSE-APACHE /usr/share/doc/riffdb/
sudo install -o root -g root -m 0755 release/demo \
/usr/share/doc/riffdb/demo
sudo install -o root -g root -m 0644 docs/*.md \
/usr/share/doc/riffdb/docs/
sudo install -o root -g root -m 0644 \
release/README.md release/RELEASE_NOTES.md \
/usr/share/doc/riffdb/release/
sudo install -o root -g root -m 0644 release/config/* \
/usr/share/doc/riffdb/release/config/
sudo install -o root -g root -m 0644 contracts/examples/budget.riff \
/usr/share/doc/riffdb/examples/contracts/budget.riff
The remaining examples use the installed asset root:
asset_root=/usr/share/doc/riffdb
You may instead set asset_root to an extracted release-bundle root.
Provision Digest Keys
The service requires two distinct protected key documents. Each file must be a
regular file owned by the riffdb effective user with no group or other mode
bits. The first entry is the current write key; up to seven later entries are
readable retired keys.
The following uses OpenSSL only as an operator-side random-byte source:
sudo -u riffdb sh -eu -c '
umask 077
capability=$(openssl rand -hex 32)
idempotency=$(openssl rand -hex 32)
test "$capability" != "$idempotency"
printf "riffdb-capability-digest-keys-v1\n1:%s\n" "$capability" \
> /var/lib/riffdb/capability.keys.new
printf "riffdb-idempotency-digest-keys-v1\n1:%s\n" "$idempotency" \
> /var/lib/riffdb/idempotency.keys.new
'
sudo install -o riffdb -g riffdb -m 0600 \
/var/lib/riffdb/capability.keys.new /etc/riffdb/capability.keys
sudo install -o riffdb -g riffdb -m 0600 \
/var/lib/riffdb/idempotency.keys.new /etc/riffdb/idempotency.keys
sudo rm /var/lib/riffdb/capability.keys.new \
/var/lib/riffdb/idempotency.keys.new
Do not reuse material between the documents. Back them up through a separate
secret-management procedure. /etc/riffdb is administrator-owned, while the
key files remain owned by the riffdb effective user because the protected
loader requires exact owner matching. The database backup root deliberately
does not contain them.
Start riffdbd
riffdbd.service selects the strict /etc/riffdb/riffdbd.toml configuration.
Review its state, backup, key, loopback, audience, and optional hosted-MCP
settings before enabling it. Unknown or malformed configuration fails startup.
The RiffDB service account must be the only writer to
/var/lib/riffdb/data. Never start two riffdbd processes against the same
database and never grant a backup tool, CLI user, or bridge direct write access
to the data directory. The packaged unit and tmpfiles rules enforce this
ownership boundary for the standard layout.
Validate the unit syntax, reload systemd, and start the service:
systemd-analyze verify /usr/lib/systemd/system/riffdbd.service
sudo systemctl daemon-reload
sudo systemctl enable --now riffdbd.service
sudo journalctl -u riffdbd.service -n 50 --no-pager
Type=exec means systemd’s active state is a process state, not RiffDB
readiness. The server writes a bounded line beginning with
riffdbd-ready-v1<TAB> after its startup proof. Once bootstrap exists, use the
authenticated riffdb server health operation for database readiness.
The unit closes stdin. riffdbd ignores clean stdin EOF and stops on SIGTERM
through its bounded graceful drain. TimeoutStopSec=80s exceeds the server’s
35-second transport drain plus runtime shutdown allowance.
Bootstrap the First Operator
Create private operator directories and a request from the installed example:
umask 077
mkdir -p "$HOME/.config/riffdb" "$HOME/.local/state/riffdb"
install -m 0600 \
"$asset_root/release/config/bootstrap-operator.json.example" \
"$HOME/.config/riffdb/bootstrap-operator.json"
Generate and durably retain both the one-time bootstrap document and its bearer presentation while submitting the bootstrap request:
riffdb --endpoint http://127.0.0.1:7443 --output json \
capability bootstrap \
--request "$HOME/.config/riffdb/bootstrap-operator.json" \
--generate "$HOME/.local/state/riffdb/bootstrap.credential" \
--bearer-output "$HOME/.config/riffdb/operator.credential"
If the response is uncertain, retry with --bootstrap-file and the retained
bootstrap document. Do not generate a second identity:
riffdb --endpoint http://127.0.0.1:7443 --output json \
capability bootstrap \
--request "$HOME/.config/riffdb/bootstrap-operator.json" \
--bootstrap-file "$HOME/.local/state/riffdb/bootstrap.credential" \
--bearer-output "$HOME/.config/riffdb/operator-recovered.credential"
Bearer output paths are create-only. Choose a new absent output path for an uncertain retry. After a terminal bootstrap result and successful authenticated health call, retain the exact owner capability ID before securely retiring the bootstrap document:
mapfile -t bootstrap_lines \
< "$HOME/.local/state/riffdb/bootstrap.credential"
[[ "${#bootstrap_lines[@]}" -eq 3 \
&& "${bootstrap_lines[0]}" == "riffdb-bootstrap-credential-v1" \
&& "${bootstrap_lines[1]}" =~ ^capability-id:([0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$ ]] \
|| exit 1
operator_capability_id="${BASH_REMATCH[1]}"
operator_id_file="$HOME/.local/state/riffdb/operator-capability.id"
[[ ! -e "$operator_id_file" && ! -L "$operator_id_file" ]] || exit 1
(set -o noclobber; printf '%s\n' "$operator_capability_id" > "$operator_id_file") \
|| exit 1
chmod 0600 "$operator_id_file"
Retain every later capability ID through the same private operational
inventory; operators can also inspect issued and revoked capability metadata
offline with riffdb capability list and riffdb capability show. Retain the normal
operator credential at mode 0600.
Install client configuration:
install -m 0600 "$asset_root/release/config/client.toml.example" \
"$HOME/.config/riffdb/client.toml"
sed -i "s|/home/operator|$HOME|" "$HOME/.config/riffdb/client.toml"
riffdb --config "$HOME/.config/riffdb/client.toml" server health
The CLI performs no implicit configuration discovery. Every later example
therefore passes --config explicitly.
Deploy the example contract through the public application service:
contract_path="$asset_root/examples/contracts/budget.riff"
riffdb --config "$HOME/.config/riffdb/client.toml" \
contract validate "$contract_path"
riffdb --config "$HOME/.config/riffdb/client.toml" \
contract deploy "$contract_path"
Run the Installed-Binary Demo
The packaged demo starts a disposable server with closed stdin, bootstraps it,
deploys the bundled contract, commits CreateBudget and AllocateBudget,
queries the resulting commit record, and stops the server with SIGTERM. It uses
only the three installed binaries and packaged assets; it does not invoke
Cargo, repository tests, or Git:
"$asset_root/demo" --assert
This is an installation smoke, not the complete POC-exit evidence run. The
revision-specific acceptance demo remains ./scripts/demo --assert in the
matching full source checkout.
Optional MCP Bridge
The normal deployment model is for an MCP client to spawn:
riffdb-mcp --config /path/to/mcp.toml
This gives the client a direct stdio child and is the most widely supported form. The bridge is still an ordinary public gRPC client and has no storage access.
For clients that can speak MCP stdio framing over a local Unix stream, the release includes an optional socket-activated systemd pair. Install the MCP configuration and a normal restricted capability:
sudo install -o riffdb-mcp -g riffdb-mcp -m 0600 \
/secure/staging/riffdb-mcp.credential \
/var/lib/riffdb-mcp/credential
sudo install -o root -g riffdb-mcp -m 0640 \
"$asset_root/release/config/mcp.toml.example" \
/etc/riffdb-mcp/config.toml
sudo systemctl enable --now riffdb-mcp.socket
The socket is /run/riffdb-mcp.sock, mode 0660, group riffdb-mcp. Adding a
user to that group gives the user the effective authority of the bridge’s
shared capability. Use a narrowly scoped agent capability, review group
membership, and prefer one spawned bridge per client when principals must
remain separate. Never give the bridge access to /var/lib/riffdb.