> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment

> Docker Compose setup, deployment modes, environment variable reference, and migrations

The root Compose files are development examples with local credentials and
`SKIP_AUTH=true`. Recall 0.9.1 adds a separate authenticated installation recipe
under `deploy/compose/`, using qualified Linux AMD64 image digests and persistent
volumes. Use the selected release's published image record; prepared packaging
alone does not establish that images are available from the registry. Configure
TLS, ingress, credentials, backups and rollback before serving remote callers.

## Authenticated Compose installation

From a verified 0.9.1 source archive, select both exact image digests from its
release record. The Recall image runs the HTTP/MCP gateway and managed workers;
the paired server image supplies the required database reference capability.
A stock SurrealDB image is not a substitute for that paired capability.

```bash theme={null}
python3 scripts/release/prepare-compose \
  --output /srv/meerkat-recall \
  --recall-image ghcr.io/lukacf/meerkat-recall@sha256:YOUR_RECALL_DIGEST \
  --paired-image ghcr.io/lukacf/recall-surreal@sha256:YOUR_PAIRED_DIGEST
cd /srv/meerkat-recall
docker compose pull
docker compose up -d
```

The digest placeholders must be replaced; the preparation command validates
canonical repositories and complete immutable digests. It creates a fresh
private directory, random signing/database secrets and a complete runtime TOML;
it refuses an existing destination and never starts Docker. Review the generated
configuration before startup. The init service copies secrets/configuration
into owned volumes, the database becomes healthy, migrations run, and only then
does the Recall service start. The HTTP port binds to loopback by default.

Persistent database data, Recall state and each service's secret volume remain
separate. Preserve them on upgrades; deleting volumes removes their contents.
The Recall service runs without root privileges and uses an explicit config
path. Its health check proves process liveness only. Complete named-space grants,
provider approvals and worker credentials using [service setup](/recall/operations/service-setup),
then verify authenticated readiness with a separate status token. The generated
`semantic.override.example.yaml` shows how to mount a private worker bearer and
provider key and select a granted discovery space; copy it to the active override
only after its files, space approvals and current grants exist. Its adjacent
README gives the complete token and provider setup. Ready gateway transport does
not establish model access or searchable source publication.

Container publication promotes the exact qualified Docker archives without
recompilation. Both source/capability/license identities and anonymous pulls of
the resulting immutable registry digests must pass before a release advertises
ready-to-pull images. Existing versioned tags must not be replaced with different
bytes. Review the paired server's BSL license terms separately from Recall's
MIT/Apache source license.

## Architecture Overview

Meerkat Recall can run against either `surreal-db` (default) or embedded `elephant-db`.
The default Docker deployment starts `meerkat-recall serve` and `surrealdb`; standalone workers are optional:

| Service | Binary | Default Port | Description |
| - | - | - | - |
| `elephant` | `meerkat-recall serve` | 3000 | HTTP API server (Axum), MCP endpoints, and managed workers |
| `elephant-worker` | `meerkat-recall worker` | N/A | Optional standalone background workers |
| `surrealdb` | Paired Recall SurrealDB 2.6.5 | 8000 | Persistent remote backend with the required reference-fence capability |

The API owns the managed runtime by default, including background worker loops. The pipeline can also run as a separate advanced worker deployment.

## Release binaries

For archive layout, checksum verification, installation, and model-backed demo
prerequisites, see [binary installation](/recall/installation). Download released
artifacts from [lukacf/meerkat-recall](https://github.com/lukacf/meerkat-recall/releases).
Use the selected release record for actual platform acceptance. The build
workflow below prepares future candidates; it does not rebuild published assets.

The **Build release candidates** workflow is started manually for a selected
commit or ref. It validates the source, then builds native Linux x86-64 and
macOS ARM64 binaries with the `rocksdb` feature and default Meerkat extraction
support. Builds use Rust 1.94.0, the repository Cargo wrapper, `--locked`, and the
`deploy` profile. Linux uses Ubuntu 22.04 for compatibility with Debian 12.

Each candidate archive contains `meerkat-recall`, its `elephant` alias, both license texts, and
`provenance.json` with the source commit/tree, build command, workflow run, and
binary SHA256. Its adjacent checksum file names the archive without directory
prefixes. The workflow attests its archives and uploads candidates; it does not
publish a GitHub release. Creating a version tag does not start another build.

To promote a release:

1. Select the reviewed source commit and confirm its CI checks passed. Verify
   that the proposed `vX.Y.Z` tag matches the workspace package version.
2. Download successful build artifacts, verify archive checksums and inspect
   their bundled provenance. Smoke test `meerkat-recall --help` on each target host.
   Keep the binary checksum, build source, and release source in the release
   record. If promoting an existing build from another commit, verify identical
   source trees or document and review the exact build-excluded differences.
3. Create the version tag at the reviewed release commit. Create a draft GitHub
   release with the verified archives, a flat `checksums.sha256` manifest, and
   release notes listing the actual supported platforms. Verify the uploaded
   bytes before publishing the draft. A locally prepared provenance file is
   descriptive evidence and is not a GitHub build attestation.
4. Before production activation, rehearse migrations on a consistent database
   backup and retain the previous executable and matching data/configuration
   for rollback. Verify health and representative application requests after
   activation.

For builds promoted outside the candidate workflow, record their actual build
commands and validation rather than claiming workflow attestations. Publishing
release binaries does not publish Rust crates or the local-helper npm package.

## Independent native and container recipes

The source includes `scripts/release/build-native` and
`scripts/release/build-container`. Both prepare candidate artifacts from an exact
clean source identity without publishing or deploying. Native packaging uses
`meerkat-recall-native/v1`; the image contains
`/usr/share/meerkat-recall/provenance.json` in
`meerkat-recall-container/v1` format. Artifact creation is separate from target
qualification. Darwin ARM64 must be built and checked on a native ARM64 host;
Linux acceptance does not establish Darwin behavior.

`deploy/native/` contains launchd and systemd templates plus an environment
example. Install the complete verified archive, create private service-owned
state directories, and substitute absolute paths before service registration.
Configure the same backend, namespace and database for explicit `migrate` and
`serve`. In 0.9.1, use `--config /absolute/runtime.toml` on both commands or set
`ELEPHANT_CONFIG` to that path; invalid explicit configuration fails without
fallback. Loaded-file precedence is described in [service setup](/recall/operations/service-setup). The server verifies schema; it does not silently migrate a store.

`deploy/kubernetes/recall.yaml` contains a single-replica StatefulSet with separate
RWOP data, recovery-journal and backup claims. It uses an explicit schema init
container, authenticated readiness and a rotating scoped probe token. Render
its image placeholder to the exact qualified registry digest with
`scripts/release/render-container`. The recipe needs a CSI driver that supports
ReadWriteOncePod and a `meerkat-recall-auth` Secret; its adjacent README describes
bootstrap and grant creation. No cluster resources are created by the renderer.

Exclusive store ownership is enforced when opening local persistent storage,
through a stable sibling OS lock held by the shared store lifetime. Kubernetes
volume exclusivity and `OnDelete` updates provide additional protection. Do not
delete the lock inode or force a second writer while an old pod may still run.
Remote SurrealDB and embedded ElephantDB remain available; these templates
select embedded RocksDB for a concrete independent deployment.

## Noninteractive authentication and rotation

Create a signing key once in a private directory:

```bash theme={null}
meerkat-recall service-token bootstrap --secret-file /private/recall/signing-key
```

For first-space/grant creation, deliberately issue a short-lived bootstrap
administrator with only management scopes:

```bash theme={null}
meerkat-recall service-token issue --secret-file /private/recall/signing-key \
  --global-admin --subject bootstrap-admin --expires-in 900 \
  --scope manage:grant --scope read:space_config --scope write:space_config \
  --scope read:status --output /private/recall/bootstrap.token
```

Global administration is a distinct opt-in: it conflicts with `--space`, refuses
ordinary data/wildcard scopes and is limited to one hour. The complete permitted
scope list is `manage:grant`, `manage:provider`, `read:space_config`, `write:space_config`,
`read:status`, `read:system`, and `write:system`. Snapshot operators use the last
two as needed. This token does not create grants or acquire ordinary data access.

Set `JWT_SECRET_FILE` for the service, or provide `JWT_SECRET` from a secret
manager. Supplying both is an error. Keep issuer/audience identical for issuance
and validation. Bootstrap refuses to overwrite an existing key and prints only
the output path. Issuance requires explicit scopes and space grants:

```bash theme={null}
meerkat-recall service-token issue --secret-file /private/recall/signing-key \
  --subject report-reader --space business-a --space business-b \
  --scope read:entity --scope read:doc --read-only --expires-in 3600 \
  --output /private/recall/report-reader.token
```

`--space` is repeatable. Multi-space tokens do not select a default or first
space: callers choose an authorized path space or `X-Space-Id`. Signed claims
are an upper bound; current service grants remain authoritative. Administrative
creation/revocation of those grants is separate from local token issuance.
`--clearances-file` and `--subject-allowlist-file` accept explicit JSON arrays.
An empty subject array stays restrictive. Read-only issuance refuses write or
wildcard scopes. Token lifetime is bounded to thirty-one days.

Use `--output` to write a new private bearer file, or `--json` for a deliberate
secret-bearing stdout response. Neither overwrites existing files. Rotation
issues a second token for the same subject, verifies it, then atomically replaces
the client's file. Existing tokens remain valid until expiry unless current
grants are revoked. A signing-key change invalidates prior tokens; retain the
key separately in a secret manager or plan explicit reissuance after recovery.
Do not include signing keys or token bytes in artifact/qualification records.

## Liveness, readiness and shutdown

`GET /v1/health` reports process liveness without probing storage or providers.
`GET /v1/readiness` requires `read:status` (or applicable system administration)
and returns `ready: true` with HTTP 200, or HTTP 503 while unready. Restore replay,
failed recovery, missing required capabilities and drain keep readiness false.
The CLI preserves bounded diagnostic JSON even when readiness fails:

```bash theme={null}
meerkat-recall --api-url http://127.0.0.1:3000 \
  --token-file /private/recall/probe.token \
  status --json --require-ready --space business-a
```

| Exit | Meaning |
| - | - |
| 0 | Ready, or a successful non-strict diagnostic command |
| 1 | Operational failure outside the readiness categories |
| 2 | Invalid command arguments |
| 3 | Reachable but not ready |
| 4 | Unavailable, malformed or inconsistent readiness response |
| 5 | Readiness authorization refused |

Status requests run concurrently, each with a ten-second timeout and a 1 MiB
response limit. In 0.9.1, `--space` adds `space_readiness` to the JSON result and
`--require-ready` requires both global and named-space readiness. Readiness is a service capability check, not proof that a
requested source revision is current. Consumers retain their own optional
startup and authorized fallback behavior.

SIGTERM and SIGINT withdraw readiness and reject new mutations before draining
workers and requests. The default total shutdown deadline is sixty seconds;
service-manager templates allow seventy-five seconds before forced kill. A
worker-drain or snapshot-flush failure exits unsuccessfully. Managed embedded
storage with a persistent destination synchronizes acknowledged mutations to its
write-ahead log and replays it before admission; shutdown autosave is separate
from that commit durability boundary. Explicitly ephemeral library/test stores
remain volatile. See [managed embedded commit durability](https://github.com/lukacf/meerkat-recall/blob/d71722482183a802fcaeaaf41cb04ef7d16f4b85/docs/embedded-durable-commits.md).

## Named backup, verification and restore

Set `ELEPHANT_SNAPSHOT_DIR` to the permitted backup root and use a global
operator token with `read:system` for export and `write:system` for restore.
Export/restore paths are on the server; offline verify/prune paths are local to
the command. These commands never prompt:

```bash theme={null}
meerkat-recall --token-file /private/recall/backup-operator.token \
  snapshot export --output /backups/nightly-2026-10-06 --name nightly \
  --retention-days 7 --quiesce-timeout-seconds 30 --json
meerkat-recall snapshot verify --input /backups/nightly-2026-10-06 --json
meerkat-recall --token-file /private/recall/backup-operator.token \
  snapshot restore --input /backups/nightly-2026-10-06 --json
```

Export pauses admissions/scheduling and drains active work for a bounded
quiesce window (default thirty seconds, maximum sixty). The storage barrier
refuses a capture with Running work in any space. Failure reports refusal;
it does not label an inconsistent file as a successful backup. A successful
immutable directory contains `manifest.json` and `data.snapshot`, with format
`recall-snapshot-v1`, version, backend, UUID, name, creation/expiry, byte length
and SHA256. Existing output directories are refused. The CLI management request
has a 120-second deadline; a timeout is an uncertain result requiring status
inspection before retry, not permission to overwrite a previous backup.

Verification checks format, expiry, length, digest and backend payload before
any store replacement. Restore uses the independently retained erasure journal
and leaves a durable barrier on interruption or failure. Readiness stays false
until replay, restored attempt fencing and final persistence complete. SurrealDB restore replaces the selected database under the same exclusive
barrier; it is not restricted to an empty destination. Embedded restore replaces
the embedded store. Both require the original independent journal and a matching
backend. The `snapshot import` spelling remains an alias for restore.

Retention defaults to seven days and is bounded to thirty days. The durability
registry tracks owned generations, incomplete exports and prior live snapshots;
cleanup runs at startup and every sixty seconds. For an existing store-specific
journal, an independent maintenance timer can invoke:

```bash theme={null}
meerkat-recall snapshot prune --journal-directory /recovery/STORE_ID --json
```

Keep the recovery journal outside the replaced data store, including on a
separate persistent volume in the container recipe. Never restore an older
journal with a data backup. Ensure retention maintenance continues while the
service is stopped, and give external backup copies/cloud disk snapshots their
own finite deletion policy. Processor-side retention follows the approved
provider's account policy; Recall's live deletion cannot erase remote processor
copies. Backup keys, runtime configuration and provider credentials are managed
separately from source payloads and must have an explicit recovery/reissuance
procedure. A successful restore rehearsal and digest receipt qualify a backup;
a file's existence does not.

## Paired remote database image

Both retained development Compose files require `RECALL_SURREAL_IMAGE` to name
an already built and loaded paired image. The source recipe is
`packaging/recall-surreal/README.md`: it pins server 2.6.5 and copies the reviewed
Recall core, SDK and repaired SurrealKV sources. Preserve its source/build
receipts and image identity. No qualified image download is implied by this
example. Vanilla SurrealDB cannot pass Recall's reference-fence readiness check.

```bash theme={null}
export RECALL_SURREAL_IMAGE=recall-surreal:REVIEWED_SOURCE
```

Substitute the actual local tag or digest. Compose refuses an unset image and
uses `pull_policy: never`. Database health only establishes that the paired
process responds; Recall verifies capability, schema and recovery separately.
The paired image owns its data directory as UID/GID 65532; the Recall image
owns its state directories as UID/GID 10001. Fresh named volumes inherit these
image directory permissions. Existing volumes retain their ownership and must
be reviewed before reuse; this example does not reset or migrate old volumes.

## Deployment Modes

<Tabs>
  <Tab title="Managed workers (default)">
    After explicit migration, `docker compose up -d elephant` runs `meerkat-recall serve` with managed workers in the same container. Storage is the paired remote SurrealDB backend.

    ```bash theme={null}
    docker compose up -d --wait surrealdb
    docker compose run --build --rm elephant meerkat-recall migrate
    docker compose up -d elephant
    ```
  </Tab>

  <Tab title="Separate workers">
    Custom deployments can run `meerkat-recall serve --server-only` and
    `meerkat-recall worker` as separate services against the same SurrealDB namespace
    and database. Configure the worker's extractor and maintenance MCP URLs to
    reach the API service, and supply an authenticated MCP token when needed.

    The supplied Compose file uses managed workers and does not include a
    separate-worker profile. Embedded `elephant-db` cannot share its in-memory
    state between processes.
  </Tab>

  <Tab title="Manual (Development)">
    For local development without Docker:

    ```bash theme={null}
    # 1. Start SurrealDB
    docker compose up -d --wait surrealdb

    # 2. Use one explicit database configuration for migrations and runtime
    export SURREALDB_URL=ws://localhost:8000
    export SURREALDB_NAMESPACE=elephant
    export SURREALDB_DATABASE=main
    export SURREALDB_USER=root
    export SURREALDB_PASS=root
    export SKIP_AUTH=true  # Local development only
    export ELEPHANT_HOST=127.0.0.1
    export ELEPHANT_DURABILITY_PATH="$PWD/.elephant/manual-recovery"
    export ELEPHANT_SNAPSHOT_DIR="$PWD/.elephant/manual-backups"
    ./scripts/repo-cargo run -p meerkat-recall-bin -- migrate

    # 3. Start API server
    ./scripts/repo-cargo run -p meerkat-recall-bin -- serve --server-only

    # 4. (Optional) Start pipeline worker separately
    ./scripts/repo-cargo run -p meerkat-recall-bin -- worker

    # 5. (Optional) Run fully embedded mode instead of SurrealDB
    ELEPHANT_DB=elephant-db ./scripts/repo-cargo run -p meerkat-recall-bin -- serve
    ```
  </Tab>
</Tabs>

## Setup

<Steps>
  <Step title="Prerequisites">
    Install Docker and Docker Compose. SurrealDB v2.4.1 is pulled automatically.
  </Step>

  <Step title="Configure environment">
    Copy `.env.example` to `.env` and set required variables:

    ```bash theme={null}
    cp .env.example .env
    ```

    The supplied Compose file selects OpenAI embeddings and an Anthropic
    extractor. Credentials and explicit approved per-space processing bindings
    are required before those roles can dispatch; environment defaults do not
    constitute provider approval. A directly launched binary uses the provider selected
    by its own model and embedding settings. For a credential-free starting
    point, use the [local demo](/recall/quickstart).
  </Step>

  <Step title="Start the database and apply schema">
    ```bash theme={null}
    docker compose up -d --wait surrealdb
    docker compose run --build --rm elephant meerkat-recall migrate
    ```

    This applies schema using the same service configuration as the server.
    For existing data, first rehearse against a consistent backup. Migrations
    are forward-only; rollback requires the matching database backup.
  </Step>

  <Step title="Start Meerkat Recall">
    ```bash theme={null}
    docker compose up -d elephant
    ```
  </Step>

  <Step title="Verify health">
    ```bash theme={null}
    # API health
    curl http://localhost:3000/v1/health

    # SurrealDB health
    curl http://localhost:8000/health
    ```

    Then inspect `GET /v1/runtime/status`: semantic ingestion needs storage,
    managed workers, and a real extractor to be ready. Submit and follow one
    representative document through [the ingest guide](/recall/guides/ingesting-data).
    HTTP health alone does not establish that the pipeline can process data.
  </Step>
</Steps>

## Environment Variable Reference

The tables describe defaults in the current source checkout unless a Compose
override is stated. The GPT-6.1 Sol and Claude Sonnet 5.5 defaults, and the
summary output and thinking policy, require the newer source; published
0.9.1 binaries retain their original defaults and summary behavior.
Compose reads its `.env` file for substitution; running the binary directly
does not automatically source that file. Set variables in the environment
actually inherited by the process or use the managed runtime configuration.

### Core (API Server)

| Variable | Default | Description |
| - | - | - |
| `ELEPHANT_HOST` | `0.0.0.0` | Server bind address |
| `ELEPHANT_PORT` | `3000` | Server bind port |
| `ELEPHANT_DB` | `surreal-db` | Storage backend: `surreal-db` or `elephant-db` |
| `ELEPHANT_DB_SNAPSHOT_LOAD` | *(none)* | Snapshot file to load at startup in embedded mode |
| `RUST_LOG` | `info` | Log level filter (see [Observability](/recall/operations/observability)) |

### Authentication

| Variable | Default | Description |
| - | - | - |
| `JWT_SECRET` | *(required with authentication)* | Random secret with at least 32 bytes for HS256 token validation |
| `JWT_ALGORITHM` | `HS256` | Only HS256 is supported by the current shared-secret decoder |
| `JWT_ISSUER` | *(none)* | Required token issuer (optional) |
| `JWT_AUDIENCE` | *(none)* | Required token audience (optional) |
| `SKIP_AUTH` | `false` | Skip authentication entirely |

<Warning>
  Use `SKIP_AUTH=true` only for isolated local development. With authentication
  enabled, startup rejects a missing/development/short secret or an unsupported
  algorithm. Generate a random secret, pass it through your secret manager,
  and do not commit it.
</Warning>

### SurrealDB Connection

| Variable | Default | Description |
| - | - | - |
| `SURREALDB_URL` | `ws://localhost:8000` | Database WebSocket URL |
| `SURREALDB_NAMESPACE` | `elephant` | Database namespace |
| `SURREALDB_DATABASE` | `test` for API/worker; `main` for migration CLI | Set explicitly and identically for all processes; `.env.example` uses `main` |
| `SURREALDB_USER` | `root` | Database username |
| `SURREALDB_PASS` | `root` | Local development database password; configure your own credentials for deployment |
| `SURREALDB_QUERY_TIMEOUT_SECS` | `30` | Embedded SurrealDB engine query deadline, positive integer seconds (1-86400) |
| `SURREALDB_TRANSACTION_TIMEOUT_SECS` | `30` | Embedded transaction limit; the shorter of this and query timeout bounds the query batch |
| `SURREAL_QUERY_TIMEOUT` | `30s` in Compose | Remote SurrealDB server query deadline; not a Meerkat Recall client-side limit |

<Note>
  `SURREALDB_*` variables only apply when `ELEPHANT_DB=surreal-db`. In Docker Compose, the SurrealDB URL is `ws://surrealdb:8000`; for local development, use `ws://localhost:8000`.
</Note>

Embedded SurrealDB deadline changes require a process restart. SurrealDB 2.6.5
does not enforce its transaction option independently, so Meerkat Recall applies the
shorter configured limit as its engine query-batch deadline. Remote connections
do not apply these client options to the server. See the
[capacity and retention guide](https://github.com/lukacf/meerkat-recall/blob/d71722482183a802fcaeaaf41cb04ef7d16f4b85/docs/work-queue-capacity.md)
for cancellation and archival details.

### MCP transport

| Variable | Default | Description |
| - | - | - |
| `MCP_ALLOWED_HOSTS` | loopback hosts | Comma-separated remote hostnames or `host:port` values; loopback remains allowed |
| `MCP_ALLOWED_ORIGINS` | unrestricted by this optional origin filter | Comma-separated exact browser origins; scheme, host, and effective port must match |
| `MCP_BEARER_TOKEN` | *(none)* | Private worker bearer with current named-space data grants and clearances; extraction requires `read:predicate`. See [extractor MCP setup](/recall/operations/service-setup#configure-extractor-mcp-access). |

Requests without an `Origin` header remain accepted. The opaque `null` origin
requires an explicit entry. Host/origin checks do not replace JWT or ABAC.
For example, use `MCP_ALLOWED_HOSTS=elephant.example.com` behind a reverse proxy
and forward the corresponding host header.

### Managed snapshots

CLI and HTTP snapshot export/restore support both `elephant-db` and retained
`surreal-db`. MCP snapshot tool advertisement remains embedded-only and globally
administrative; this does not remove the remote backend's CLI/HTTP operations.

| Variable | Default | Description |
| - | - | - |
| `ELEPHANT_SNAPSHOT_DIR` | Server working directory's `.elephant-snapshots` | Required containment root for snapshot directories |

Use the running server and a global operator token with `read:system` for export
or `write:system` for restore, without a named-space restriction:

```bash theme={null}
meerkat-recall --api-url http://127.0.0.1:3000 --token-file /private/backup-operator.token \
  snapshot export --output /backups/nightly-UNIQUE --name nightly \
  --retention-days 7 --quiesce-timeout-seconds 30 --json
meerkat-recall snapshot verify --input /local/nightly-copy --json
meerkat-recall --api-url http://127.0.0.1:3000 --token-file /private/restore-operator.token \
  snapshot restore --input /backups/nightly-UNIQUE --json
```

Export/restore paths are directories on the server host within its configured
root. Verification is local to the CLI. Preserve both `manifest.json` and
`data.snapshot` in each `recall-snapshot-v1` bundle; this is not the old single
JSON-file format. Positional `snapshot export PATH` and `snapshot import PATH`
spellings remain accepted, with the directory format and same guarded runtime
semantics. Whole-store restore replaces every space, requires a matching backend
and the original independent erasure journal, and stays unready through replay.
See the [snapshot reference](/recall/api/system#managed-snapshots).

### Pipeline: Embed Worker

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_EMBED_ENABLED` | `true` | Enable the embedding worker |
| `PIPELINE_EMBED_POLL_INTERVAL` | `5` | Poll interval in seconds |
| `PIPELINE_EMBED_BATCH_SIZE` | `100` | Batch size for embedding requests |
| `PIPELINE_EMBED_PROVIDER` | `fake` | Provider: `fake`, `openai`, or `fastembed` |
| `PIPELINE_EMBED_MODEL` | `text-embedding-3-large` | Embedding model name |
| `PIPELINE_EMBED_DIMS` | `3072` | Embedding vector dimensions |

<Accordion title="Embedding provider details">
  * **`fake`**: Returns deterministic synthetic vectors derived from the input bytes. Use for tests, not meaningful semantic similarity.
  * **`openai`**: Uses the OpenAI embeddings API. Requires `OPENAI_API_KEY`.
  * **`fastembed`**: Local ONNX-based embeddings via FastEmbed. No API key needed but requires the `fastembed` feature flag at compile time.
</Accordion>

### Pipeline: Retention Worker

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_RETENTION_ENABLED` | `true` | Enable the retention worker |
| `PIPELINE_RETENTION_POLL_INTERVAL` | `3600` | Poll interval in seconds (default: 1 hour) |
| `PIPELINE_RETENTION_DAYS` | `30` | Age cutoff for legacy cleanup and terminal work archival |
| `PIPELINE_WORK_HISTORY_POLL_INTERVAL` | `60` | Independent work-history archival cadence in seconds (1-86400) |
| `PIPELINE_WORK_HISTORY_BATCH_SIZE` | `1000` | Maximum jobs and completed attempts archived per pass (1-1000) |

### Pipeline Work Queue

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_WORK_QUEUE_ENABLED` | `true` | Enable the work queue processor |
| `PIPELINE_WORK_QUEUE_POLL_INTERVAL` | `5` | Poll interval in seconds |
| `PIPELINE_WORK_QUEUE_LEASE_SECS` | `300` | Lease duration for claimed work items (seconds) |
| `PIPELINE_WORK_QUEUE_BATCH_SIZE` | `100` | Max items fetched per poll |
| `PIPELINE_WORK_QUEUE_WORKER_ID` | *(auto)* | Worker identifier override |
| `PIPELINE_MAX_CONCURRENT` | `8` | Max concurrent work items processed in parallel |
| `PIPELINE_MAX_EXTRACT_CONCURRENT` | `4` | Max concurrent assertion extraction work items |
| `PIPELINE_WORK_ITEM_TIMEOUT_SECS` | `420` | Claimed work-item lifecycle timeout |

### Pipeline: Transient Retry

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_WORK_TRANSIENT_RETRY_MAX_ATTEMPTS` | `10` | Bounded work-queue transient retry attempts; explicit values remain authoritative |
| `PIPELINE_WORK_TRANSIENT_RETRY_BASE_DELAY_SECS` | `10` | Base delay for exponential backoff (seconds) |
| `PIPELINE_WORK_TRANSIENT_RETRY_MAX_DELAY_SECS` | `120` | Maximum backoff delay cap (seconds) |

### Pipeline: Resolution / Disambiguation

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_RESOLUTION_DISAMBIGUATION_ENABLED` | `true` | Enable LLM disambiguation for multiple entity candidates |
| `PIPELINE_RESOLUTION_DISAMBIGUATION_MODEL` | `claude-sonnet-5-5` | Compatibility-path model default; configure a model available to your provider |
| `PIPELINE_RESOLUTION_DISAMBIGUATION_MAX_TOKENS` | `800` | Max tokens per disambiguation call |
| `PIPELINE_RESOLUTION_DISAMBIGUATION_TIMEOUT_SECS` | `15` | Wall time limit for disambiguation (seconds) |
| `PIPELINE_PROMOTION_DISAMBIGUATION_MODEL` | `claude-sonnet-5-5` | LLM model for entity-reuse disambiguation at promotion (active when `ANTHROPIC_API_KEY` is set; without a key, type-incompatible matches create a new entity instead of merging) |

### Pipeline: Summarization

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_SUMMARIZER_MODEL` | `claude-sonnet-5-5` | Default option for doc-tree section summaries; governed dispatch uses the space's approved summary profile |

The approved profile also selects the [summary output and thinking policy](/recall/pipeline/extraction#summary-budgets). The model environment variable does not override that binding. Empty output or known typed incomplete completions use a bounded source excerpt with logged counters; missing or revoked provider authority remains an error.

### Pipeline: Truth Maintenance

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_TRUTH_MAINTENANCE_ENABLED` | `true` | Enable truth maintenance worker |
| `PIPELINE_TRUTH_MAINTENANCE_POLL_INTERVAL` | `5` | Poll interval in seconds |
| `PIPELINE_TRUTH_MAINTENANCE_BATCH_SIZE` | `100` | Max events per batch |
| `PIPELINE_TM_MODEL` | `claude-sonnet-5-5` | LLM model for conflict adjudication. Adjudication is active only when `ANTHROPIC_API_KEY` is set; without a key, TM falls back to deterministic conflict policies and never supersedes without configured semantics |

### Pipeline: Maintenance (Identity Resolution)

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_MAINTENANCE_ENABLED` | `true` | Enable maintenance worker |
| `PIPELINE_MAINTENANCE_POLL_INTERVAL` | `300` | Poll interval in seconds (default: 5 minutes) |
| `PIPELINE_MAINTENANCE_MCP_URL` | `http://127.0.0.1:3000/mcp/maintenance` | MCP URL for maintenance agent |
| `PIPELINE_MAINTENANCE_MODEL` | `gpt-6.1-sol` | LLM model for maintenance decisions |
| `PIPELINE_MAINTENANCE_AUTO_MERGE_THRESHOLD` | `0.9` | Confidence threshold for auto-merging entities |
| `PIPELINE_MAINTENANCE_MAX_CANDIDATES` | `50` | Max candidate pairs per sweep |

### Pipeline: Agentic Extraction

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_AGENTIC_MCP_URL` | `http://127.0.0.1:3000/mcp/agent` | MCP URL for the agentic extractor |
| `PIPELINE_AGENTIC_MODEL` | `gpt-6.1-sol` | Binary and Compose default; explicit model settings and approved per-space profiles take precedence |
| `PIPELINE_SKILLS_DIR` | `./skills` | Extraction skill directory; Compose mounts and selects `/skills` |

The extractor and maintenance URL defaults do not follow `ELEPHANT_PORT`.
Match each URL to the reachable listener and its profile path (`/mcp/agent` or
`/mcp/maintenance`). For config-backed bootstrap, set `extractor_mcp_url` through
`initialize_runtime` or `configure_runtime` and check `get_runtime_config`.
Setting `PIPELINE_AGENTIC_MCP_URL` before launch selects configured runtime mode.
See [extractor MCP setup](/recall/operations/service-setup#configure-extractor-mcp-access)
for token, grant and per-space tool checks.

### Living Wiki Editorial Models

| Variable | Default | Description |
| - | - | - |
| `ELEPHANT_WIKI_WRITER_MODEL` | `PIPELINE_AGENTIC_MODEL`, then `gpt-6.1-sol` | Meerkat model used to write structured, citation-bearing wiki articles |
| `ELEPHANT_WIKI_VERIFIER_MODEL` | writer model | Meerkat model used in a separate call to verify every article statement for evidence entailment and subject relevance |
| `ELEPHANT_WIKI_MAX_OUTPUT_TOKENS` | `32000` | Maximum output tokens for one wiki model call, clamped to 4000-64000 so evidence-rich core articles can complete their structured response |
| `ELEPHANT_WIKI_STRUCTURED_OUTPUT_RETRIES` | `5` | In-session schema-repair retries, clamped to 1-8, before a malformed structured response is returned to the outer editorial loop |
| `ELEPHANT_WIKI_MAX_CONTEXT_CHARS` | `400000` | Maximum serialized evidence catalog supplied to one article writer/verifier, clamped to 50000-1000000 so evidence-rich core pages do not fail at the earlier 200000-character ceiling |
| `ELEPHANT_WIKI_MAX_EDITORIAL_ATTEMPTS` | `6` | Bounded writer-verifier attempts per article, clamped to 1-12; structured-output failures receive an explicit format-recovery prompt before retry |
| `ELEPHANT_WIKI_PLANNER_CONCURRENCY` | `4` | Concurrent bounded full-graph planning shards, clamped to 1-8 |
| `ELEPHANT_WIKI_ARTICLE_CONCURRENCY` | `4` | Concurrent article writer and verifier jobs inside one serialized reconciliation, clamped to 1-32 |
| `ELEPHANT_WIKI_CHANGE_DEBOUNCE_SECS` | `300` | Delay used to coalesce canonical graph changes before managed wiki reconciliation, clamped to 30-3600 seconds |
| `ELEPHANT_KNOWLEDGE_VIEW_EVENT_POLL_SECS` | `5` | Independent outbox polling interval for projection invalidation and Living Wiki change scheduling, clamped to 1-300 seconds |

### Pipeline Budget Control

| Variable | Default | Description |
| - | - | - |
| `PIPELINE_BUDGET_DAILY_EUROS_GRAPH` | `50` | Graph accounting-unit limit per space and UTC day |
| `PIPELINE_BUDGET_DAILY_EUROS_RETRIEVAL` | `10` | Retrieval accounting-unit limit per space and UTC day |
| `PIPELINE_BUDGET_DAILY_ITEMS_GRAPH` | `10000` | Graph work items per space and UTC day |
| `PIPELINE_BUDGET_DAILY_ITEMS_RETRIEVAL` | `1000` | Retrieval work items per space and UTC day |
| `PIPELINE_BUDGET_PER_ITEM_TOKENS` | `100000` | Per-item token cap |
| `PIPELINE_BUDGET_PER_ITEM_WALL_TIME_SECS` | `300` | Per-item time cap in seconds |
| `PIPELINE_BUDGET_DOWNGRADE_POLICY` | `degrade_mode_with_queue` | `strict`, `degrade_mode`, `degrade_mode_with_queue`, or `alert_only` |

These are environment-only worker defaults. For managed TOML configuration,
set the corresponding lower-case fields under `[budget]`, for example:

```toml theme={null}
[budget]
daily_euros_graph = 50.0
daily_euros_retrieval = 10.0
daily_items_graph = 10000
daily_items_retrieval = 1000
per_item_tokens = 100000
per_item_wall_time_secs = 300
downgrade_policy = "degrade_mode_with_queue"
```

Include both daily unit limits when adding a `[budget]` table; omitted numeric
fields in that table deserialize as zero. Omitting the entire table uses the
default budget. Omitted optional item/token/time fields become worker defaults,
not unlimited values. The historic `euros` names are abstract token-accounting
units (one per million measured tokens), not a provider bill; estimated
reservations also consume headroom. Limits apply independently per space.
`alert_only` records overruns instead of refusing admission; other policies
retain their documented downgrade/queue behavior. Before bulk ingestion,
[inspect effective limits and remaining headroom](/recall/operations/service-setup#inspect-budget-before-an-import)
with the authorized `GET /api/v1/budget` endpoint. Check separate extractor and
worker lifecycle limits too; raising a daily budget does not remove them.

For long documents, use an approved new `text-full-v2` embedding profile and
complete any required reindex activation. See the
[profile migration sequence](/recall/operations/service-setup#long-documents-and-embedding-profiles).

### API Keys

| Variable | Default | Description |
| - | - | - |
| `OPENAI_API_KEY` | *(none)* | OpenAI API key for the default extractor and `openai` embed provider; each requires its own approved binding |
| `ANTHROPIC_API_KEY` | *(none)* | Required for an explicitly selected Anthropic extractor; also enables truth-maintenance adjudication, promotion disambiguation, and doc-tree summarization |

## Migrations

Meerkat Recall uses SurrealQL migrations managed by `meerkat-recall migrate`:

```bash theme={null}
# Use the same database environment as the running server.
# With Compose, inherit it from the elephant service:
docker compose run --build --rm elephant meerkat-recall migrate
```

<Warning>
  Migrations are forward-only. There is no automatic rollback. To revert, restore from a database backup and re-run migrations against the restored database.
</Warning>

## Docker Compose Reference

The `docker-compose.yml` defines two services:

* **`surrealdb`** -- Explicit paired image with file persistence in `surrealdb_data`. Health check via `/usr/local/bin/recall-surreal is-ready`.
* **`elephant`** -- API server with managed workers. The `recall_state` volume retains its original journal and managed snapshots under `/var/lib/meerkat-recall`; explicit migration uses that same volume. `GET /v1/health` is liveness only.

The local ports bind to loopback. The skills directory is mounted read-only at
`/skills` in the Meerkat Recall container. Configure a separate production deployment
with database credentials, TLS, JWT authentication, and restricted network
access; publishing the development ports is not a production configuration.

## Stopping and Cleanup

```bash theme={null}
# Stop services (preserves data)
docker compose down
```

The database and its original Recall recovery journal must remain paired.
Stop the Recall process before running the one-shot migration; its journal lock
prevents a second live owner. Normal Compose shutdown retains both volumes.
Deleting the recovery volume while retaining a database or backup is not a
schema repair and prevents safe replay of erased sources. Backup retention is
managed by Recall's snapshot coordinator, not by deleting that original journal.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.