fjwood69

mori

Community fjwood69
Updated

Mori MCP server — A shared memory layer for AI coding agents — one that compounds.

Mori — A shared memory layer for AI coding agents

Mori (森) is a shared memory layer for AI coding agents — one that compounds.Sessions feed a dream pipeline that distils activity into durable knowledge,so every instance starts informed rather than cold. One Mori, many agents —every session benefits from what every other session learned.

Works with any OpenAI-compatible provider. No homelab, no Anthropicaccount, no LLM Gateway required — though those all work too.

Multi-Instance Coherence

One Forest, Many Agents

If you run Claude Code across multiple machines or profiles — one focused on theAPI layer, another on the frontend, a third on infrastructure — you already knowthe problem: each instance is brilliant in isolation, but none of them know whatthe others decided.

Instance B doesn't know that Instance A just changed the auth contract. Instance Cdoesn't know that Instance B's deployment assumptions shifted. They find out thehard way, mid-task, when something breaks.

Mori solves this.

Every CC instance sends its session events — prompts, tool calls, errors, decisions— to the shared Mori server. The dream pipeline distils those events from allinstances into a unified memory store. At the start of any session, /briefsurfaces what the other instances have been doing: the cross-cutting decisions,the architectural tensions, the gotchas one instance hit that another is aboutto repeat.

From turn one, each instance knows what the others know.

Real-time awareness via NATS

The dream pipeline runs on a schedule. For decisions thatcan't wait, NATS provides real-time cross-instance messaging:

# Instance A just changed the auth contract:
/nats pub "Auth contract changed — JWT now includes org_id claim. See memory: api-auth-contract"

# Instance B picks it up immediately:
/nats sub
→ [Instance A] Auth contract changed — JWT now includes org_id claim.

Any instance can publish, any instance can subscribe. Messages replay for 7 daysso instances that were offline don't miss decisions.

What gets shared

The dream pipeline captures and synthesises across instances:

  • Architecture decisions — "Instance A moved to event-driven auth; Instance Bshould update its session handling assumptions"
  • Cross-cutting gotchas — "This provider 429s under load; all instances shoulduse the fallback routing"
  • Deferred decisions — "Instance C flagged a migration risk; nobody has resolved it yet"
  • Conventions — patterns that emerge across sessions become shared standards

What doesn't get shared: one-off bugs, noise, anything recoverable from docs or git.The dream pipeline filters aggressively. You get signal, not a transcript.

Setup for multi-instance use

Point every instance at the same Mori server. That's it.

{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://<your-mori-server>:8968/mcp"
    }
  }
}

Add the lifecycle hooks to each instance's settings.json so events flow in.Each instance gets a ?client=<hostname> tag so the dream pipeline knows whocontributed what. Attribution is preserved — you can always trace a memory backto the session and device that produced it.

Recommended dream cadence for multi-instance setups

Instances Recommended interval
1–2 1 hour
3–5 30 minutes
5+ 30 minutes + manual /dream after significant decisions

The PreCompact hook triggers an immediate dream run before any instance'scontext is compressed — ensuring nothing is lost at the moment it matters most.

Core capabilities

1. Event Logging

Claude Code lifecycle hooks POST session events to POST /api/events/raw.These events feed the dream pipeline. The PreCompact hook posts toPOST /api/precompact and triggers an immediate synchronous dream.

# Minimal hook config — add to settings.json:
curl -sf -X POST 'http://localhost:8968/api/events/raw?client=my-hostname' \
  -H 'Content-Type: application/json' -d @-

Every Claude Code session emits lifecycle events — tool calls, prompts, errors,stop reasons. Mori receives these via HTTP POST and stores them in an append-onlyevent log. This is the raw material everything else builds on.

What it captures:

  • PostToolUse — tool name, input, output, errors
  • PostToolUseFailure — tool call errors (high-value for dream distillation)
  • PreCompact — session snapshot before context compression (triggers synchronous dream)
  • UserPromptSubmit — the prompt text
  • Stop / SessionEnd — stop reason, model used
  • Session ID, client hostname, working directory, transcript path

Components required: Mori server only. No LLM provider needed.

Config: MORI_ADVISOR_API_KEY for auth (empty = no auth, only reachable viaTailscale LAN or localhost).

2. Persistent Memory

Memories live in a single SQLite database (memories.db) with:

  • Versioning — every change creates a new version. View history, diff versions, rollback.
  • Attribution — each memory tracks which session(s) and client(s) contributed.
  • Protection — trusted dreamers write directly; others queue for approval.
  • Tagging — memories are taggable (security, architecture, decision) for filtering.
  • Search — keyword search across name, title, description, and body.

The Forest Remembers

Components required: Mori server only. Memories persist in SQLite — noexternal dependencies.

3. Dream Phase

Session events are captured via Claude Code lifecycle hooks (PostToolUse,PostToolUseFailure, UserPromptSubmit, Stop, PreCompact). The dream pipelinereads events since the last watermark, sends them to a configurable LLM, andwrites extracted memories back to the store.

Hook fires  →  POST /api/events/raw  →  SQLite events table
                                             ↓
PreCompact  →  POST /api/precompact  →  dream_run() reads since watermark
                                             ↓
                                      LLM distills events → structured memories
                                             ↓
                                      memories written to store (with attribution)
                                             ↓
                                      watermark advanced

Dream Pipeline

Run it: /dream or mori-dream_run. Check state: /dream --status.

Stale knowledge & eviction

Dream produces three tiers of memory, each with a different lifecycle:

Tier Scope Eviction
Ephemeral Auto-generated session summaries Auto-expire at session end unless explicitly saved
Working Patterns, decisions, project context Flagged for review after 30 days of no retrievals. Not deleted — surfaced via pensieve --since 30d for weekly triage
Canonical Explicitly promoted by a trusted dreamer Indefinite, but freshness-checked before injection via /brief

The freshness check runs during /brief (session bootstrap). For eachcanonical memory about dependencies, infrastructure, or tooling, a lightweightvalidation prompt asks: "Based on current project state, is this still accurate?Answer YES, NO, or STALE." STALE responses suppress injection and queuethe memory for review.

Orphan scandream_run also tracks retrieval recency. Memories notretrieved in 30 days are flagged but preserved. Human reviews the queue,not a batch delete.

This avoids the classic "persistent memory" failure mode where a patchedcluster's stale workaround poisons sessions for months.

Components required: Mori server + LLM provider (for the distillation model).Config: MORI_DREAM_MODEL (defaults to MORI_MODEL, then deepseek/deepseek-v4-flash).

4. Session Context (/brief)

Mori uses session grounding rather than per-query RAG. /brief loadsshared memories, team standards, and dream pipeline state into context atsession start. From turn one, the model knows your security baseline,coding conventions, and current project state — no retrieval needed.

Unresolved /req items also surface via /brief — a sticky note, not a project board.No sync, no drift from JIRA or GH Projects.

# Starting a refactor — add a checklist:
/req add "Extract auth middleware" --project bifrost --pri high
/req add "Add rate limiting" --project bifrost --pri medium
/req add "Write migration guide" --project bifrost --pri low

# Check progress mid-session:
/req --project bifrost
→ 3 requirements, 1 in-progress, 2 pending

# Mark done as you go:
/req done req-bifrost-extract-auth-middleware

# Next session, /brief shows what's still open

When the standards corpus grows beyond one context window, run separateMori instances per namespace rather than adding a vector store:

# Retail team
docker run ... -e MORI_STANDARDS_DIR=/standards/retail -p 8970:8968
# Energy team
docker run ... -e MORI_STANDARDS_DIR=/standards/energy -p 8971:8968
Standards ingestion

Set MORI_STANDARDS_DIR to a directory of .md files:

/path/to/standards/
  ethos/
    values-and-ethical-principles.md
  security/
    security-baseline.md
    pii-handling.md
  coding/
    python-style-guide.md

On startup, every .md file is imported as a protected memory with type: standardand tags from its subdirectory. Standards are read-only to non-trusted dreamers.

Update without restarting: mori-standards_reload (trusted dreamers only).

Components required: Mori server + /brief skill. Config: MORI_STANDARDS_DIR.

5. Strategic Code Review (/consult)

A configurable LLM receives your question plus optional file context andreturns strategic guidance. Supports focus areas (general, architecture,security, performance, style) and depth levels (quick, balanced, deep).

When a specific focus is given (--focus security), relevant team standardsare auto-injected from memory — the advisor checks against your own baseline,not generic advice.

Chain tool output into the advisor:

/consult "review this auth handler" --focus security --file src/auth.py --file snyk-report.json

Components required: Mori server + LLM provider. Config: MORI_MODEL(default moonshotai/kimi-k2.6).

6. Agent Delegation + NATS (/nats, /update)

Cross-device messaging (NATS)

Optional NATS JetStream integration for cross-device state-of-play messages.Each device publishes session summaries; any device can replay the last 7 days.Useful for awareness across a team or fleet of Claude Code instances.

Skill deployment (/update)

The mori-update tool generates install commands for skills and slash commandsacross devices. It knows each device's profile layout and produces the rightshell commands — no manual copy-paste across machines:

Device Profiles
Linux .claude, .claude-sr, .claude-sub, .claude-api
Windows Same paths via $env:USERPROFILE
NUC .claude, .claude-jr, .claude-sub, .claude-api

Command output is base64-encoded to avoid shell quoting issues:

/update --twiggy --nats
→ compact PowerShell block that deploys to all 4 profiles
→ ask approval then execute — no copy-paste needed

This means pushing an updated skill to every Claude Code instance is a single/update command away.

Components required: Mori server. NATS server for cross-device messaging(optional).

7. Governance — Memory Quality & Validity

Memories accumulate over time. Without safeguards, they drift, conflict, oraccumulate noise. Mori has several mechanisms to maintain quality:

Trusted Dreamers

Certain client hostnames are designated as trusted dreamers. Only they candirectly modify protected memories. Writes from other instances are queued aspending writes.

Configured via MORI_TRUSTED_DREAMERS env var (comma-separated hostnames)or in the dreamer_config table.

Protection

Any memory can be toggled protected via mori-memory_protect. When protected:

  • Trusted dreamers write directly (no change in behaviour)
  • Other instances' writes go to pending_writes for review
  • mori-memory_pending_list, mori-memory_approve, mori-memory_reject manage the queue
Versioning & Rollback

Every write snapshots the previous state. You can:

  • View history: mori-memory_history(name)
  • Compare versions: mori-memory_diff(name, from, to)
  • Roll back: mori-memory_rollback(name, version_id) — rollbacks are themselvesversioned, so they can be reversed
Attribution

Every memory tracks its origin:

  • origin_session_ids — which sessions contributed
  • origin_clients — which hostnames contributed
  • mori-memory_session_summary(session_id) — audit what a session produced

This means you can trace any memory back to the session and device that created it.

Export / Import

Memories can be exported to standard .md files and imported elsewhere. Thisserves as both backup and review — you can inspect the full corpus as flatfiles, edit them, and re-import.

Quickstart

1. Pick your platform

Platform Section Recommended path Complexity
Linux 1a Docker Compose or Podman Compose Low
macOS 1b Docker Desktop Low
macOS (dev) 1c Native Python Low
Windows 1d Docker Desktop Low
Windows (advanced) 1e WSL2 + Podman Compose Medium
Cloud (any) 1f GCP Terraform Medium

1a. Linux — Docker Compose

Works with Podman Compose (podman compose) or Docker Compose (docker compose).

git clone https://github.com/fjwood69/mori.git
cd mori
cp deploy/homelab/.env.example deploy/homelab/.env
# Edit deploy/homelab/.env with your provider API key and model
docker compose -f deploy/homelab/docker-compose.yml up -d
curl http://localhost:8968/health

The compose file brings up Mori with a dream-cron sidecar that runs the dreampipeline on a schedule. Configure MORI_DREAM_INTERVAL in .env (default: 60 minutes).

1b. macOS — Docker Desktop

Install Docker Desktop then:

git clone https://github.com/fjwood69/mori.git
cd mori
cp deploy/homelab/.env.example deploy/homelab/.env
# Edit deploy/homelab/.env with your provider API key and model
docker compose -f deploy/homelab/docker-compose.yml up -d
curl http://localhost:8968/health

Docker Desktop handles the Linux container layer transparently — no extra setup needed.

1c. macOS — native Python

git clone https://github.com/fjwood69/mori.git
cd mori
pip install -r requirements.txt
cp deploy/homelab/.env.example deploy/homelab/.env
# Edit deploy/homelab/.env with your provider API key
set -a; source deploy/homelab/.env; set +a
python -m mori_advisor.main &

# Dream cron: add to crontab (runs every hour — adjust to match MORI_DREAM_INTERVAL)
# 0 * * * * cd /path/to/mori && python -m mori_advisor.dream_job

SQLite WAL mode works natively on macOS. No container needed.

1d. Windows — Docker Desktop

Install Docker Desktop (handles WSL2 backend automatically) then in PowerShell:

git clone https://github.com/fjwood69/mori.git
cd mori
copy deploy\homelab\.env.example deploy\homelab\.env
# Edit deploy\homelab\.env with your provider API key and model (Notepad works fine)
docker compose -f deploy\homelab\docker-compose.yml up -d
curl http://localhost:8968/health

No WSL knowledge required. Docker Desktop runs the Linux container transparently.Dream cron is handled inside the container — no Windows Task Scheduler needed.

1e. Windows — WSL2 + Podman

Follow the 1a Linux path inside WSL2 Ubuntu. DockerDesktop (1d) is the easier path for most users.

1f. Cloud — GCP Terraform

See deploy/gcp/ for Terraform configs. Creates a GCE e2-small VMwith Podman rootless, persistent disk, Tailscale, and GCP Secret Manager.

cd deploy/gcp
terraform init
terraform plan
terraform apply

2. Verify it's running

curl http://localhost:8968/health
# {"status":"ok","service":"mori-advisor"}

curl http://localhost:8968/api/events/health
# {"status":"ok","total_events":0}

curl http://localhost:8968/metrics
# Prometheus-formatted metrics

3. Connect Claude Code

Option A: .mcp.json (project root, most reliable)

Create a .mcp.json file in your project root:

{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://localhost:8968/mcp"
    }
  }
}

Claude Code picks this up automatically when working in that project directory— no global config needed. For a remote Mori server (e.g. on another machineon the same Tailscale tailnet), use the Tailscale IP:

{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://100.84.128.79:8968/mcp"
    }
  }
}

Option B: settings.json (global)

Add to ~/.claude/settings.json under mcpServers:

{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://localhost:8968/mcp"
    }
  }
}

For user-global scope (works in VS Code extension):

claude mcp add mori --scope user --type http http://localhost:8968/mcp

4. Install slash commands

Copy the skill files from the skills/ directory:

# One-shot for all profiles:
SKILLS_DIRS=(".claude" ".claude-sr" ".claude-sub" ".claude-api")
for d in "${SKILLS_DIRS[@]}"; do
  cp -r skills/* ~/$d/skills/
done

Each skill becomes a /command: /brief, /consult, /dream, /pensieve, /update, /nats, /req.

5. Enable event capture (required for dreams)

Add the hooks from examples/settings.json to your ~/.claude/settings.json.See Claude Code CLI Setup below for a complete walkthrough.

Claude Code CLI — Mori Setup

This section covers the Claude Code CLI-specific configuration that enablesthe full Mori experience: event capture, dream pipeline, and session grounding.

1. Connect Mori as an MCP server

Project-level (recommended — per repo):

// .mcp.json in your project root
{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://localhost:8968/mcp"
    }
  }
}

User-global (available in all CC sessions):

claude mcp add mori --scope user --type http http://localhost:8968/mcp

For a remote Mori server on the same Tailscale tailnet:

{
  "mcpServers": {
    "mori": {
      "type": "http",
      "url": "http://mori.yourteam.ts.net:8968/mcp"
    }
  }
}

2. Enable event capture (required for dreams)

Mori's dream pipeline is fed by Claude Code lifecycle hooks. Add these to~/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -sf -X POST 'http://localhost:8968/api/events/raw?client=$(hostname)' -H 'Content-Type: application/json' -H 'X-Api-Key: your-api-key' -d @- >/dev/null 2>&1; exit 0"
          }
        ]
      }
    ],
    "PostToolUseFailure": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -sf -X POST 'http://localhost:8968/api/events/raw?client=$(hostname)' -H 'Content-Type: application/json' -H 'X-Api-Key: your-api-key' -d @- >/dev/null 2>&1; exit 0"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -sf -X POST 'http://localhost:8968/api/events/raw?client=$(hostname)' -H 'Content-Type: application/json' -H 'X-Api-Key: your-api-key' -d @- >/dev/null 2>&1; exit 0"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -sf -X POST 'http://localhost:8968/api/events/raw?client=$(hostname)' -H 'Content-Type: application/json' -H 'X-Api-Key: your-api-key' -d @- >/dev/null 2>&1; exit 0"
          }
        ]
      }
    ],
    "PreCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -sf -X POST 'http://localhost:8968/api/precompact?client=$(hostname)' -H 'Content-Type: application/json' -H 'X-Api-Key: your-api-key' -d @- >/dev/null 2>&1; exit 0"
          }
        ]
      }
    ]
  }
}

Notes:

  • Replace your-api-key with the value of MORI_ADVISOR_API_KEY
  • Replace localhost:8968 with your Mori server address if running remotely
  • $(hostname) is evaluated at hook fire time — override with a fixed nameif your hostname is long or changes (e.g. ?client=twiggy)
  • PreCompact posts to /api/precompact — this triggers an immediatesynchronous dream run before context compression. Expect 10–30s delay.This is intentional — it preserves session knowledge before it is lost.
  • A full example settings.json is in examples/settings.json

3. Install slash commands

# One-shot for all profiles:
SKILLS_DIRS=(".claude" ".claude-sr" ".claude-sub" ".claude-api")
for d in "${SKILLS_DIRS[@]}"; do
  mkdir -p ~/$d/skills
  cp -r skills/* ~/$d/skills/
done

Each skill becomes a /command in Claude Code:

Command What it does
/brief Load shared memories + standards + dream state into context
/dream Distil undreamed events into memories
/dream --status Show dream pipeline state
/dream --dry-run Preview without writing
/consult "question" Strategic guidance from the advisor model
/pensieve <query> Search memories by keyword
/pensieve read <name> Read a specific memory
/req Requirements dashboard
/nats ping Check cross-device messaging
/wrap Session close — summary, dream flush, NATS publish

4. Session grounding with CLAUDE.md

Add a line to ~/.claude/CLAUDE.md to ensure every session starts withshared context:

At the start of every session, run /brief to load shared memories and
dream pipeline state before responding to the user.

This ensures the agent bootstraps from the Mori memory store automatically,without requiring a manual /brief invocation each time.

5. Trusted dreamer setup

The device that runs the dream pipeline and writes directly to protectedmemories is the trusted dreamer. Set it in your Mori server config:

MORI_TRUSTED_DREAMERS=your-hostname

To find your hostname:

hostname

On Windows:

$env:COMPUTERNAME

Multiple trusted dreamers (comma-separated):

MORI_TRUSTED_DREAMERS=macbook-pro,twiggy,nuc15pro

Writes from non-trusted devices are queued as pending writes for reviewvia mori-memory_pending_list.

6. Verify the full setup

# MCP tools available
claude mcp list

# Health check
curl http://localhost:8968/health
# → {"status":"ok","service":"mori-advisor"}

# Send a test event
curl -X POST 'http://localhost:8968/api/events/raw?client=test' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: your-api-key' \
  -d '{"event_name":"UserPromptSubmit","prompt":"test"}'

# Check dream state
curl http://localhost:8968/metrics

Then start a Claude Code session and run /brief — if shared memoriesload into context, everything is working.

Dream cadence

Configure how often the dream pipeline runs in .env:

MORI_DREAM_INTERVAL=60  # minutes
Team size Recommended
Solo 240 min (4 hours)
1–4 people 60 min (1 hour)
5–10 people 30 min

The PreCompact hook fires an immediate dream regardless of cadence —so no session knowledge is lost at context compression time.

What you get

Tool What it does
mori-memory_search/write/read/list/delete Full CRUD on shared memories
mori-memory_export/import/export_all Portability between instances
mori-memory_history/diff/rollback Versioning — track changes over time
mori-memory_session_summary Attribution — see what a session produced
mori-memory_pending_list/approve/reject/protect Governance — trusted dreamer workflow
mori-consult_advisor Strategic guidance mid-task (configurable model + focus)
mori-dream_run / dream_status Batch distills session events → memories
mori-standards_reload Re-import team standards from disk
mori-brief Session bootstrap — loads memories + standards + dream state
mori-pensieve Search/browse the shared memory store
mori-update Deploy slash command skills to devices
mori-nats_pub/sub/ping Cross-device message bus (NATS optional)
mori-memory_req Requirements tracking dashboard with status workflow
mori-event_log HTTP event capture endpoint for dream pipeline

Slash commands: /brief, /wrap, /consult, /dream, /pensieve, /update, /nats, /req

Quick Reference

Command Usage What it does
/brief /brief Load shared memories + standards + dream state into context
/wrap /wrap Session wrap — writes summary to cc-share, publishes to NATS, runs dream
/consult /consult "question" [--focus security] [--depth quick] [--file path] Get strategic guidance from the advisor model
/dream /dream Distill undreamed events into memories
/dream --status Show dream pipeline state (watermark, event counts)
/dream --dry-run Preview what would be produced without writing
/pensieve /pensieve <query> Search memories by keyword
/pensieve read <name> Read a specific memory by its kebab-case name
/pensieve --type decision --since 30d Filter by type and recency
/pensieve --tag security Filter by tag
/req /req Show requirements dashboard grouped by project
/req --project bifrost Filter by project
/req --project bifrost --status pending Filter by project and status
/req add "Title" --project bifrost --pri high Create a new requirement
/req done req-bifrost-<name> Mark a requirement complete
/nats /nats ping Check NATS connection status
/nats sub Show recent cross-device messages
/nats pub "message" Publish a message to other devices
/update /update --device twiggy --skill nats Generate install commands for a skill on a device

Architecture

Mori Architecture

Configuration

Environment variables

Variable Default Description
MORI_PROVIDER_MODE bifrost direct or bifrost. New users without a custom gateway should set direct.
MORI_API_KEY Provider key (required in direct mode)
MORI_BASE_URL depends OpenAI-compatible base URL
MORI_MODEL moonshotai/kimi-k2.6 Advisor model
MORI_DREAM_MODEL falls back Dream pipeline model
MORI_MCP_SERVER_NAME mori MCP tool prefix
MORI_ADVISOR_DATA /data/mori-advisor SQLite DB location
MORI_ADVISOR_API_KEY Event capture auth (empty = no auth)
MORI_TRUSTED_DREAMERS Comma-separated hostnames for write approval bypass
MORI_STANDARDS_DIR Path to team standards .md directory
MORI_SKILLS_DIR Path to slash command skill files (for /update)
MORI_DREAM_INTERVAL 60 Dream pipeline interval in minutes
MORI_BIFROST_TIMEOUT 300 API timeout in seconds

Dream interval

How often to run the dream phase depends on session density. The PreCompacthook fires on context compression regardless of schedule, so the cron is justa safety net for sessions that never compact.

Set via MORI_DREAM_INTERVAL in your .env file (used by the Docker Composedream-cron sidecar). For Podman/systemd deployments, set via the dream timer.

Team size Suggested interval Rationale
Solo 240 (4 hours) Few events per session, low risk of losing context
1–4 people 60 (1 hour) More events, catches cold restarts and short sessions
5–10 people 30 minutes High event density, any session could be the last before the server goes down

Ports

Port Service
8968 MCP server (streamable HTTP) + event capture API

Deployment

Deployment matrix

Platform Recommended path Complexity
Linux Docker Compose or Podman Compose Low
macOS Docker Desktop or native Python Low
Windows Docker Desktop Low
Windows (advanced) WSL2 + Podman Compose Medium
Cloud (any) GCP Terraform (deploy/gcp/) Medium

Docker Compose (all platforms — recommended)

The compose file in deploy/homelab/docker-compose.yml brings up Mori with adream-cron sidecar. Works with Docker Desktop (macOS/Windows), Podman Compose(Linux), and docker compose.

git clone https://github.com/fjwood69/mori.git
cd mori
cp deploy/homelab/.env.example deploy/homelab/.env
# Edit deploy/homelab/.env with your provider API key and model
docker compose -f deploy/homelab/docker-compose.yml up -d
curl http://localhost:8968/health

Homelab (Podman raw, Linux advanced)

Systemd user services for the dream timer and backup timer are in deploy/homelab/:

git clone https://github.com/fjwood69/mori.git
cd mori
podman build -t localhost/mori-advisor:latest .
podman run -d --name mori --restart=unless-stopped --network=host \
  -v /data/mori-advisor:/data/mori-advisor:Z \
  --env-file deploy/homelab/.env \
  localhost/mori-advisor:latest

# Install systemd timers (user-level)
cp deploy/homelab/mori-dream.*   ~/.config/systemd/user/
cp deploy/homelab/mori-backup.*  ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now mori-dream.timer
systemctl --user enable --now mori-backup.timer

GCP (GCE VM)

See deploy/gcp/ for Terraform configs. Creates:

  • GCE e2-small VM (2 vCPU, 2GB RAM, 20GB persistent disk) — ~$12/month
  • Ubuntu 24.04 LTS with Podman rootless
  • GCS bucket for SQLite backups (daily backup, 90-day archive lifecycle)
  • GCP Secret Manager for all secrets
  • Tailscale join for access (no public ports)
  • Systemd timers for dream and backup
cd deploy/gcp
terraform init
terraform apply
# If migrating from an existing homelab NUC:
#   bash scripts/migrate-secrets.sh
# (Assumes you're on the NUC with ~/.claude/.secrets available.
#  For a fresh GCP deployment without a NUC, create secrets manually
#  in GCP Secret Manager and reference them in your Terraform variables.)
# SSH in and verify:
gcloud compute ssh mori-advisor
curl http://localhost:8968/health

Dual deployment (migration period)

During migration, both homelab and GCP instances can run in parallel pointingat separate databases. Claude Code points at either one via .mcp.json.

To copy memories from an existing instance:

  1. On the old instance: mori-memory_export_all → flat .md files
  2. On the new instance: mori-memory_import → loads into new DB
  3. Verify with mori-memory_list

No downtime — both instances serve during the cutover.

Observability endpoints

Endpoint Purpose Response
/health Liveness probe 200 if process is alive
/ready Readiness probe (HTTP endpoint, not the /ready slash command) 200 if DB accessible, 503 otherwise
/metrics Prometheus exposition format Counts for memories, events, pending writes, eviction queue
/api/events/health Legacy event endpoint Event count

Provider Policy

Mori routes all LLM inference through US and EU sovereign endpoints only. WhileMori can use open-weight models created in the PRC (e.g. DeepSeek, Kimi, GLM,Qwen), inference runs entirely outside the PRC via US-based provider infrastructure:

Model Origin Provider Route
Kimi K2.6 Moonshot AI DeepInfra / Novita / Parasail (US)
DeepSeek V4 DeepSeek DeepInfra / Novita (US)
GLM-5 Zhipu AI DeepInfra / Novita / Parasail / Vertex (US)
Qwen Alibaba Nebius / DeepInfra (US/EU)
Gemma 4 31B it Google Vertex AI (NAM)
Gemini 3 Flash Preview Google Vertex AI (NAM)

This is explicitly documented in the README because the model names alone couldmislead colleagues into thinking direct Moonshot/DeepSeek API usage is involved.It is not. All inference goes through US-based providers that happen to hostopen-weight models.

For teams

Each team member runs their own Claude Code connected to the same Mori.Memories are shared. Trusted dreamers approve writes.

  1. Run Mori on a shared server or as a cloud container
  2. Each member points mcpServers at the shared URL
  3. Each member installs the skills and hooks
  4. Run /dream periodically on one instance to consolidate

Building

git clone https://github.com/fjwood69/mori.git
cd mori
podman build -t localhost/mori-advisor:latest .
# Or with Docker: docker build -t mori-advisor:latest .

License

MIT

Support me on Ko-fi

MCP Server · Populars

MCP Server · New