Deployment

Start with one.
Add nodes when you need them

All the documentationEvery part of the site, and the deep reference

The same binary serves both shapes. There is no separate cluster build and no coordinator process to deploy alongside it.

Single-node

Embedded storage, everything in one process. Suits edge deployments, development, and the large majority of production knowledge bases.

Multi-node cluster

Consistent hashing places data, a Kademlia DHT discovers peers, and queries scatter-gather across the mesh. Nodes join without an election.

ROUTING 01

Decision logic

The receiving node decides locally whether it owns the key or forwards it.

ROUTING 02

Read workflow

Searches fan out in parallel; partial results merge on the way back.

ROUTING 03

Bulk write

Writes group by owning node so each batch commits once.

Containers

A distroless image
multi-platform

The published image carries the server, the CLI and the MCP server, the same as the binary. It is distroless and runs as a non-root user, so there is no shell inside it to attack. 0.3.5 is the current release and the one to run: it is where the hardening and stability work landed.

docker
# the image runs as uid 65532, so the data directory has to be its own
mkdir -p $(pwd)/data/cameodb
chown -R 65532:65532 $(pwd)/data/cameodb

docker run -d --name cameodb-server \
  -p 9480:9480 -p 9580:9580 \
  -v $(pwd)/data/cameodb:/data/cameodb \
  goranc/cameodb:0.3.5

Two ports

9480 carries the HTTP API and the MCP endpoint. 9580 is how nodes find each other, and a single node never needs it published.

Config and data, nothing else

Mount a TOML file at /etc/cameodb/cameodb.toml read-only and a data directory at /data/cameodb. No certificates and no keys belong in the image.

Digests, never keys

A node authenticates against SHA-256 digests of API keys, so CAMEODB_API_KEY_HASH goes in the environment and the key itself never does.

The client rides along

The same image is the CLI. Run it with --network host and client --interactive to get a REPL against a running node. :latest tracks the same build if you would rather not pin.

A three-node cluster is a compose file away: each node takes CAMEODB_SEED_NODES and CAMEODB_CLUSTER_NODES and finds the rest of the mesh itself. /_cluster/health stays reachable without a credential even when authentication is on, so a health probe needs no secret of its own.

Security posture

The profile is a claim
the config must meet

A profile is not a preset that rewrites your settings. It states who can reach this node, and a node whose configuration contradicts its own claim refuses to start rather than coming up quietly weaker than you thought.

Requirementlocalinternalexternal
Reachable fromthis machinetrusted networkanywhere
Bind addressloopback onlyanyany
TLSoptionalwarned if offrequired
CORS *warnedrejectedrejected
/_admin/*allowedallowedmust be off
Cluster PSKwarnedrequiredrequired
Authenticationoptionalwarned if offrequired

Choose by who can reach the bind address, not by what the environment is for: a shared test box is internal, not local. Omitting the profile is valid only for a loopback bind, which infers local; a node other hosts can reach has to state its posture.

Keys and limits

Who, what
and how often

Authentication answers who is calling. allowed_indexes answers what they may reach. Neither says anything about how often, and the caller that matters there is not an attacker.

Four capabilities

read, write, index-admin and node-admin, bundled as reader, writer and admin. A key may also be pinned to named indexes, and is refused on any other.

Digests, never keys

A node stores SHA-256 digests of the keys it will accept. Mint one with cameodb keygen, give the node the digest, and rotation never involves handing a secret to a config file.

An audit trail

[security.audit] records what was called and by which key, readable back from /_admin/audit rather than only from disk.

Rate limiting exists for the legitimate caller: a reader key held by an agent that decides to call search_across_indexes in a loop. Every one of those calls is authorized and every one fans out across every shard, so tool_calls_per_minute, max_search_limit and max_federated_indexes bound the cost of being useful.

Under load

Bulk writes do not
stall your reads

The architectural choice that matters most in production is the strict separation of async and blocking work.

The spawn_blocking firewall

Storage I/O against Redb and Tantivy is confined to blocking thread pools. The Tokio and Axum runtime stays purely async, so heavy ingestion cannot stall HTTP request handling.

Supervised smart commits

Tantivy commits against a memory budget rather than on a fixed timer, bounding the window in which recent writes are not yet searchable.

Actor isolation

A Kameo actor model contains faults: a failing component does not take the process down with it.

Tiered cache sizing

A store opens on a boosted cache and reopens on the smaller steady-state one, so a restart warms fast without holding the larger budget.

A writer thread per shard

Each shard owns its writer and is pinned to its own core, so parallel ingest never contends for one. The core budget follows a container’s CPU quota rather than the host’s core count. Two further affinity modes exist and stay off by default: measured, they cost 13–20% of write throughput.

Shutdown in four phases

A stop closes MCP sessions, drains HTTP connections, then commits pending Tantivy writes and flushes the Redb WAL before the coordinator goes down. The next boot replays no WAL. A second interrupt overrides it and exits immediately.