loomemDocs
Project

Deployment Guide

Local development

Prerequisites

  • Rust toolchain (stable)
  • clang + libclang-dev (for RocksDB)
  • Node.js (builds the dashboard, which the server embeds at compile time)
  • OpenAI API key for the LLM steps (extraction, consolidation, dream); without one, start with LOOMEM_ALLOW_KEYLESS=1

Build and run

git clone <repository-url>
cd loomem

# Build (dashboard first: the server embeds it at compile time)
cp entities.toml.example entities.toml
(cd loomem-dashboard && npm ci && npm run build)
cargo build --release -p loomem-server

# Local embedding model (required with the default embedding_provider = "local")
./scripts/fetch-embedding-model.sh

# Set environment
export OPENAI_API_KEY="sk-..."       # or: export LOOMEM_ALLOW_KEYLESS=1
export LOOMEM_AUTH_TOKEN="your-secret-token"

# Run
./target/release/loomem-server

# Verify
curl http://localhost:3030/health

First memory

curl -X POST http://localhost:3030/v1/store \
  -H "Authorization: Bearer your-secret-token" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello, this is my first memory"}'

Local embeddings (no OpenAI dependency)

Set embedding_provider = "local" in config.toml under [llm] — this is the default on a fresh install. Uses the tract ONNX runtime — pure Rust, no native dependencies, and nothing leaves the machine. The model is a separate download (./scripts/fetch-embedding-model.sh); without it the server refuses to start (refusing cloud fallback) instead of falling back to OpenAI.

Note: consolidation, extraction, and dream use OpenAI completions (gpt-4.1-mini). Without an OPENAI_API_KEY the default config refuses to start unless LOOMEM_ALLOW_KEYLESS=1 is set, and then those steps fail (memory_ingest returns a missing-key error); embeddings and search stay fully local either way.


Docker

Build

docker build -t loomem .

Run

docker run -d \
  --name loomem \
  -p 3030:3030 \
  -v loomem-data:/data \
  -e OPENAI_API_KEY="sk-..." \
  -e LOOMEM_EMBEDDING_PROVIDER=openai \
  -e LOOMEM_EMBEDDING_DIM=1536 \
  -e LOOMEM_AUTH_TOKEN="your-secret-token" \
  -e LOOMEM_AT_REST_MASTER_KEY="base64-encoded-32-bytes" \
  loomem

The Dockerfile automatically: - Binds to 0.0.0.0 (accessible from outside container) - Sets data directory to /data (mount a volume here) - Requires LOOMEM_AUTH_TOKEN and LOOMEM_AT_REST_MASTER_KEY at startup — the server refuses to start without them (deliberate opt-outs: LOOMEM_ALLOW_UNAUTH=1, LOOMEM_AT_REST_EXPECT_ENABLED=0) - Enables per-stream rate limiting ([rate_limit]; over-limit requests get HTTP 429 with Retry-After) - Ships without the local embedding model — set LOOMEM_EMBEDDING_PROVIDER=openai and LOOMEM_EMBEDDING_DIM=1536 (as above), or the server refuses to start. Stored text is then sent to the OpenAI embeddings API. A database already built with 384-dim local vectors needs loomem-server --reembed after the switch.

Docker Compose

version: '3.8'
services:
  loomem:
    build: .
    ports:
      - "3030:3030"
    volumes:
      - loomem-data:/data
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - LOOMEM_EMBEDDING_PROVIDER=openai
      - LOOMEM_EMBEDDING_DIM=1536
      - LOOMEM_AUTH_TOKEN=${LOOMEM_AUTH_TOKEN}
      - LOOMEM_AT_REST_MASTER_KEY=${LOOMEM_AT_REST_MASTER_KEY}
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3030/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  loomem-data:

Railway

One-click deploy

  1. Fork the repository
  2. Connect it to Railway
  3. Create new project → Deploy from GitHub repo
  4. Add environment variables: - OPENAI_API_KEY — your OpenAI API key - LOOMEM_EMBEDDING_PROVIDER=openai and LOOMEM_EMBEDDING_DIM=1536 — the image has no local embedding model - LOOMEM_AUTH_TOKEN — admin token (generate a random string) - LOOMEM_AT_REST_MASTER_KEY — a base64-encoded 32-byte master key, required by the Docker image; keep a separate secure copy for recovery
  5. Add a volume mounted at /data for persistent storage
  6. Deploy

Configuration

If you deploy to Railway, add a railway.toml at the repo root (not shipped by default):

[build]
dockerfilePath = "Dockerfile"

[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 3

The PORT environment variable is automatically set by Railway and overrides server.port in config.

Custom domain

After deployment, you can add a custom domain in Railway settings. Update SERVER_ORIGIN env var to match (required for OAuth).


Container entrypoint and startup migrations

The container image starts via scripts/docker-entrypoint.sh (works on any container platform). It supports an optional one-time data migration before the server starts:

Env var Purpose
LOOMEM_MIGRATE_GRAPH_STREAMS_ON_START Set to 1 to run loomem-migrate --migrate-graph-entity-streams before server start (re-stamps graph entities/edges whose stream disagrees with the chunks they reference). Unset = skip.
LOOMEM_MIGRATE_GRAPH_STREAMS_COMMIT Set to 1 to actually write changes. Default is a dry run.
LOOMEM_MIGRATE_GRAPH_STREAMS_MANIFEST_DIR Where migration manifests are written. Defaults to /data/graph-migration-plans.

The migration is idempotent (already-correct entities are skipped) and creates a backup checkpoint before the first write when committing. After a successful migration, unset the ON_START flag and redeploy so subsequent restarts take the no-op fast path.

Other loomem-migrate subcommands (run manually, not via the entrypoint): --validate-graph-entity-streams and --sample-embeddings.


Production checklist

Security

  • [ ] Set strong LOOMEM_AUTH_TOKEN (64+ random hex chars) — without it the server accepts every request (local passthrough mode)
  • [ ] Move API keys to environment variables (not in config.toml)
  • [ ] Review PII settings — ensure pii.enabled = true
  • [ ] Consider enabling at-rest encryption (LOOMEM_AT_REST_MASTER_KEY) — see SECURITY.md
  • [ ] Set up HTTPS (managed platforms provide this; Docker needs a reverse proxy)
  • [ ] Behind a reverse proxy, set LOOMEM_TRUST_FORWARDED_FOR=1 so the /oauth/register rate limit keys on the client address your proxy appends to X-Forwarded-For, instead of lumping every client into the proxy's own bucket — leave it unset when Loomem is exposed directly (the header is caller-controlled there)

If the authentication token leaks, replace the env var value, restart the service, and verify that a request using the old token is rejected. Master-key rotation is not currently supported: changing the at-rest master key does not re-wrap existing data keys and makes encrypted data unreadable. See Security overview.

Performance

  • [ ] Mount /data on fast storage (SSD)
  • [ ] Set storage.rocksdb.write_buffer_size based on available RAM
  • [ ] Review storage.tantivy.heap_size_mb — more RAM = faster indexing
  • [ ] Consider enabling search.cache for high-traffic deployments

Reliability

  • [ ] Enable storage.intent_log.enabled = true for crash recovery
  • [ ] Set storage.intent_log.sync_on_write = true for durability
  • [ ] Configure backup: worker.backup.enabled = true
  • [ ] Set appropriate resource_guards for your environment
  • [ ] Monitor with GET /v1/status endpoint

Cost control

  • [ ] Set cost.daily_cap_usd to prevent runaway LLM costs
  • [ ] Set cost.alert_threshold_usd for early warnings
  • [ ] Review dream.cost_cap_usd_per_run per dream session
  • [ ] Monitor daily costs via RocksDB cost column family

Connecting MCP clients

Claude.ai (web)

  1. Settings → Customize → Connectors
  2. Add new connector
  3. Enter URL: https://your-loomem.app/mcp
  4. Complete OAuth — enter API key when prompted

Claude Desktop

Edit claude_desktop_config.json:

{
  "mcpServers": {
    "loomem": {
      "url": "https://your-loomem.app/mcp",
      "headers": {
        "Authorization": "Bearer loom_your_api_key"
      }
    }
  }
}

Restart Claude Desktop.

The url form above is for a remote instance reachable over HTTPS. If Loomem runs locally over plain HTTP (http://127.0.0.1:<port>), the desktop app can't use a bare http://localhost URL — bridge it to stdio with "command": "npx", "args": ["-y", "mcp-remote", "http://127.0.0.1:3030/mcp", "--allow-http"]. See the User Guide.

Claude Code (CLI / VS Code)

Add to .mcp.json in your project or global config:

{
  "mcpServers": {
    "loomem": {
      "url": "https://your-loomem.app/mcp",
      "headers": {
        "Authorization": "Bearer loom_your_api_key"
      }
    }
  }
}

Logging

Text logs (default)

RUST_LOG=debug ./target/release/loomem-server

JSON logs (for Grafana / ELK)

LOOMEM_LOG_FORMAT=json ./target/release/loomem-server

Backups

Automatic

Enable in config:

[worker.backup]
enabled = true
interval_secs = 43200    # every 12 hours
max_copies = 2            # keep last 2 backups

Checkpoints are written to {data_dir}/backups/checkpoint-<timestamp>/. For an out-of-band backup, copy that directory (or snapshot the volume) while the server is running — checkpoints are consistent point-in-time copies. See Backup and Restore for details, including the extra requirements for encrypted instances.

Restore

Before taking any restore actions, read and follow the full restore procedure, including the separate intent log, the master key for encrypted backups, index rebuilding, and verification.


Upgrading

  1. Pull latest code: git pull
  2. Build: cargo build --release -p loomem-server
  3. Stop old instance
  4. Start new binary

Loomem handles schema migrations automatically: - RocksDB: backward-compatible (new fields get defaults) - Tantivy: auto-rebuild on schema version mismatch - Config: new keys are required — check config.toml after upgrade


CI

GitHub Actions runs on every push/PR to main: - cargo check --workspace - cargo test --workspace --lib - cargo clippy -- -D warnings - cargo fmt --check


Security notes

  • Never hardcode API keys in config.toml — use OPENAI_API_KEY env var instead. The api_key_env field specifies which env var to read.
  • config_snapshot.toml files (created by eval runs) are gitignored to prevent accidental secret leaks.
  • All dependencies use permissive licenses (MIT, Apache-2.0, BSD, ISC).
Loomem · Apache-2.0Edit on GitHub