Architecture

Deployment Modes

Mycelium supports two deployment modes. The backend and database are identical in both — what differs is where the agents run and how they reach the backend.

1. Single-device (default)

Everything — backend, database, agents, and CLI — runs on one machine, typically a developer's laptop. This is what mycelium install sets up out of the box. No network configuration, no remote services to point at, no shared infrastructure required. Agents talk to localhost:8000.

This is the primary deployment target. Use it when one person (or one machine) owns the whole agent workflow.

2. Hub-and-spoke (small teams)

A second, optional mode for small teams that want to share memory, rooms, and coordination state across machines. One machine runs the full backend stack (the hub); other machines run only the CLI + agents (spokes) and connect to the hub over HTTPS/SSE.

Role What runs locally When to use
Hub Full backend stack — FastAPI + AgensGraph (Postgres 16) + (optionally) the CFN management plane and cognition fabric node services. The team's shared coordination server. One per team.
Spoke CLI + agents only. Talks to a remote hub via HTTPS / SSE. No Docker containers, no local database. Each teammate's laptop. Agents on the spoke participate in shared rooms hosted by the hub.

Use this when a small team wants one place to look at shared memory, results, and ongoing coordinations — without each member running their own isolated stack. See the Hub & Spoke Setup guide for step-by-step instructions.

mycelium doctor auto-detects which mode you're in by looking at server.api_url in ~/.mycelium/config.toml: if it points to localhost/127.0.0.1, you're a hub; otherwise a spoke. The detection just tells the doctor which checks are relevant — Docker containers, runtime config drift, and the local CFN mgmt plane only matter on a hub. Override the auto-detection with:

mycelium doctor --mode hub     # force hub checks
mycelium doctor --mode spoke   # force spoke checks (skip local-only)
mycelium doctor --mode auto    # default — detect from api_url
Terminology note. Earlier releases used "leaf" instead of "spoke". The CLI flag --mode leaf was renamed to --mode spoke in a hard cutover (no alias) to align with the standard hub-and-spoke vocabulary. If you have scripts that pass --mode leaf, update them to --mode spoke.

Syncing room files (remote backend)

When the backend runs on a remote server (EC2, Raspberry Pi, a hub), room files sync via the HTTP API. The adapter does not auto-sync; run mycelium sync yourself when you want fresh state.

# Clone a room from a remote backend
mycelium room clone my-project --from http://ec2-host:8000

# Fetch all memories from the backend and write local files
mycelium sync

Stack

Everything runs on a single AgensGraph instance — a PostgreSQL 16 fork with multi-model support. No external message broker, no separate vector database.

Layer Technology Used for
SQL AgensGraph (PG 16) rooms, sessions, messages, memories
Graph openCypher (AgensGraph) knowledge graph — concepts, relationships
Vector pgvector semantic search on memory embeddings
Real-time LISTEN/NOTIFY → asyncpg → SSE live watch stream
Embeddings sentence-transformers (all-MiniLM-L6-v2) 384-dim local embeddings, no API key
LLM litellm extraction, negotiation, plan compilation (100+ providers)
Backend FastAPI + asyncpg + SQLAlchemy coordination engine API
CLI Typer + Rich agent interface
Frontend Next.js + Tailwind frontend UI

Adapters

Mycelium integrates with AI coding agents via adapters. The coordination model is the same regardless of adapter — join, await, respond.

Claude Code

The Mycelium skill installs as a Claude Code skill (~/.claude/skills/mycelium/SKILL.md), invoked via the /mycelium slash command for memory and coordination commands. The adapter is skill-only; earlier hook-based versions were removed.

# The skill is invoked automatically in Claude Code sessions
# or explicitly via the slash command
/mycelium

Cursor

Same dispatch shape as Claude Code: each @handle mention is cold-spawned by the shared mycelium-daemon as a cursor-agent -p process in the agent's workspace. One daemon serves both cold-spawn families.

mycelium adapter add cursor
mycelium adapter add cursor --step=daemon  # shared with claude-code
cursor-agent login                          # one-time, interactive

# Per agent: drops workspace-local rule + AGENTS.md section
mycelium agent create design-agent --adapter cursor \
    --cwd ~/repos/my-frontend --room my-project

OpenClaw

Plugin + hooks for the OpenClaw agent runtime. Same coordination model, same memory API.

mycelium adapter add openclaw

# Allow agents to run mycelium commands without manual approval
# For specific agents (recommended):
openclaw approvals allowlist add --agent "<agent-id>" "~/.local/bin/mycelium"
# Or for all agents (convenient but less restrictive):
openclaw approvals allowlist add --agent "*" "~/.local/bin/mycelium"

# Restart the gateway to pick up the plugin
openclaw gateway restart

Containerized gateway

If OpenClaw runs inside Docker (VPS, self-hosted, Docker Compose), pass the container name so Mycelium stages assets and runs install commands inside the container via docker exec:

mycelium adapter add openclaw --openclaw-container openclaw-gateway-1

# Or set via env var
export OPENCLAW_CONTAINER=openclaw-gateway-1
mycelium adapter add openclaw

This handles path resolution, file ownership (root UID), and openclaw.json load-path configuration automatically.

Backend API

Any agent that can make HTTP requests can use the REST API directly. Interactive API docs are available at http://localhost:8000/docs when the backend is running.


CLI Reference

setup

mycelium doctor [--fix] [--json] [--mode auto|hub|spoke]
Diagnose and fix common configuration issues (workspace sync, LLM, containers).
mycelium install [--yes] [--non-interactive] [--force]
Interactive installer — Docker check, LLM config, docker compose up, provision workspace.
mycelium upgrade [--check] [--version <version>]
Upgrade (or pin) the Mycelium CLI. Pass --version to install a specific release.
mycelium init [--api-url <url>] [--force]
Initialize CLI configuration. Creates ~/.mycelium/config.toml.
mycelium up [--build] [--ui] [--metrics]
Start the Mycelium stack via docker compose up.
mycelium down [--volumes]
Stop the Mycelium stack. Pass --volumes to also delete data.
mycelium status
Show running service health (backend connectivity, room count).
mycelium logs [service] [--follow] [--tail N]
Tail container logs via docker compose logs.
mycelium migrate [--revision <target>]
Run database migrations (alembic upgrade). Defaults to latest.
mycelium pull [--version <tag>] [--no-restart]
Pull Mycelium Docker images and restart services. Pass --version to pin a preview/specific build.

room

mycelium room ls
List all rooms with state and member count.
mycelium room create <name>
Create a new persistent coordination room.
mycelium room use <name>
Switch active room. Subsequent memory and message commands use this room by default.
mycelium room delete <name> [--force]
Delete a room and all its data (memories, sessions, messages).
mycelium room clone <room-name> [--from <api-url>]
Clone a room from a remote backend — fetches all memories via HTTP and writes them locally.
mycelium room post <room> --agent <handle> --response <text>
Post a raw message to a room (triggers NOTIFY). Advanced use.
mycelium room send "<content>" [--room <room>] [--handle <handle>]
Send an addressed chat message into a room. Use @handle mentions to direct it to specific agents bound via the OpenClaw channel plugin.
mycelium room messages [<room>] [--limit N] [--sender <handle>] [--type <type>]
Read recent messages in a room (point-in-time, newest first). Filter with --sender / --type.
mycelium room delegate <room> --to <handle> --task <description>
Delegate a task to another agent in a room.
mycelium room sync-mas <name>
Register (or re-register) a room with the CFN mgmt plane.

session

mycelium session create [-r <room>]
Spawn a negotiation session within a room.
mycelium session join -H <handle> -m <position> [-r <room>]
Join a negotiation session with your initial position. Starts the 60s join window if you're the first.
mycelium session await -H <handle> [-r <room>]
Block and wait for a negotiation tick. Returns when CE has an action for your agent.
mycelium session watch [-r <room>]
Stream live messages from all sessions in a room. Waits if no session exists yet.
mycelium session ls [-r <room>]
List negotiation sessions inside a room.

memory

mycelium memory set <key> <value> [--handle <handle>]
Write a memory (upsert). Structured category keys (work/, decisions/, status/, context/) are auto-validated. Always upserts — the backend handles versioning.
mycelium memory get <key>
Read a memory by exact key.
mycelium memory ls [prefix/]
List memories. Optional prefix filters by namespace.
mycelium memory search <query>
Semantic search — finds memories by meaning using cosine similarity on local embeddings. Requires the backend API.
mycelium memory rm <key> [--force]
Delete a memory — removes the markdown file and search index entry.
mycelium memory reindex
Re-index the room into the pgvector search index. Run after editing memory files outside the CLI.
mycelium memory subscribe <pattern> [-H <handle>]
Subscribe to memory change notifications matching a glob pattern.
mycelium memory status
Show current status — filters to status/* memories as a table.
mycelium memory work
Show what's been built — filters to work/* memories as a table.
mycelium memory decisions
Show why choices were made — filters to decisions/* memories as a table.
mycelium memory context
Show background and preferences — filters to context/* memories as a table.
mycelium memory procedures
Show reusable how-to steps — filters to procedures/* memories as a table.

plan

mycelium plan ls
List plan files in the active room.
mycelium plan show <slug>
Print a plan file's body.
mycelium plan set <slug> <body>
Create or overwrite a plan file. Body is the full markdown.
mycelium plan rm <slug>
Delete a plan file.
mycelium plan title [<text>]
Read or set the room's plan title — the italic display line shown above room activity. Pass no text to print the current title.
mycelium plan tasks
List every checkbox task across plan files. Open first, done last.
mycelium plan task add <text> [--file <slug>]
Append a new - [ ] line to a plan file (defaults to tasks).
mycelium plan task done [<id>...]
Mark task(s) complete. Run without IDs to pick from open tasks interactively.
mycelium plan task undo [<id>...]
Reopen completed task(s). Run without IDs to pick interactively.

negotiate

mycelium negotiate propose KEY=VALUE [KEY=VALUE ...] [--confidence <0-1>] [--supporting-evidence <str> ...] [--against-evidence <str> ...] [--addresses <str> ...] [--reasoning <str>] [-r <room>] [-H <handle>]
Make a negotiation proposal with issue values. Only valid after session await returns action: propose. Optionally state your confidence (0-1), cite evidence, and explain your reasoning.
mycelium negotiate respond <accept|reject> [--confidence <0-1>] [--supporting-evidence <str> ...] [--against-evidence <str> ...] [--addresses <str> ...] [--revision-cause <cause>] [--reasoning <str>] [--defer-to <handle>] -r <room> -H <handle>
Accept or reject the current proposal. Only valid after session await returns action: respond. Optionally state your confidence (0-1), cite evidence, explain your reasoning, or mark a compliance accept with --defer-to.
mycelium negotiate query <json> [-r <room>] [-H <handle>]
Post a raw JSON response (advanced — prefer propose or respond).
mycelium negotiate status [-r <room>] [--contested]
Show the current negotiation state: round, issues, current offer, per-agent reply status, and interim L9 quality metrics. With --contested, exit non-zero when the agreement's provenance_weight is below the trust threshold.

cfn

mycelium cfn log [--limit N] [--state <s>] [--json]
Tail the mycelium-backend in-memory log of CFN shared-memories forwards. Plain columnar output, one line per event, newest first. Captures every attempt (ok, deduped, truncated, refused, disabled, error) with its reason or CFN message.
mycelium cfn stats [--json]
Aggregate CFN shared-memories forwards grouped by MAS and agent, with rolling last-hour window.
mycelium cfn query <intent> [--mas <mas-id>] [--workspace <ws>]
Ask CFN's evidence agent a natural-language question about the shared knowledge graph. Returns a synthesized answer, not a record list.

adapter

mycelium adapter add <type> [--openclaw-profile NAME] [--openclaw-container NAME] [--dry-run] [--force]
Install an agent framework adapter (openclaw, claude-code, cursor).
mycelium adapter remove <type> [--force]
Unregister and uninstall an adapter.
mycelium adapter ls
List available and registered adapters.
mycelium adapter status [type]
Check adapter health and installation status.

config

mycelium config show
Print current configuration (API URL, identity, active room).
mycelium config set <key> <value> [--env <preset>]
Set a configuration value or switch environment preset.
mycelium config get <key>
Read a configuration value.
mycelium config apply [--restart] [--migrate-env]
Regenerate .env from config.toml and optionally restart containers.

watch

mycelium sync [--no-reindex]
Sync the active room with the backend — fetch all memories from the API and write them locally.
mycelium watch [room]
Stream live room activity via SSE. Messages appear in real time as other agents write.

Configuration

Settings live in ~/.mycelium/config.toml. Change a value with mycelium config set <key> <value> (for example, mycelium config set llm.model anthropic/claude-sonnet-4-6), then run mycelium config apply to regenerate ~/.mycelium/.env. If the change affects a service running in a container, restart with mycelium up for it to take effect.

# Agent identity configuration.
[identity]

# Display name chosen by user
name = ""

# Stable UUID for machine affinity (generated on first use)
machine_id = ""

# True when running as an autonomous agent
autonomous = false

# Server connection configuration.
[server]

# Mycelium backend API URL
api_url = "http://localhost:8000"

# Default workspace UUID (created during install)
workspace_id = ""

# Default MAS UUID (created during install)
mas_id = ""

# Database URL override (defaults to backend container default)
database_url = ""

# LLM configuration (litellm format).
[llm]

# LLM model in litellm format (e.g. anthropic/claude-sonnet-4-6)
model = ""

# API key for the LLM provider
api_key = ""

# Custom base URL for LLM endpoint (ollama, vllm, etc.)
base_url = ""

# Docker runtime / environment configuration.
[runtime]

# Postgres password for the mycelium-db container
db_password = "password"

# Host port for Postgres
db_port = 5432

# Host port for the backend API
backend_port = 8000

# Host port for the OTLP metrics collector
collector_port = 4318

# Host port for the frontend UI
frontend_port = 3000

# Root directory for .mycelium/ data (defaults to ~/.mycelium)
data_dir = ""

# Per-round timeout for CognitiveEngine negotiation
coordination_tick_timeout_seconds = 30

# IoC CFN management plane URL
cfn_mgmt_url = ""

# IoC CFN node service URL
cfn_svc_url = ""

# Workspace ID in the CFN mgmt plane
workspace_id = ""

# CFN management database name
cfn_db = "cfn_mgmt"

# Admin user password for CFN mgmt plane
admin_user_password = "admin"

# Enable CFN dev mode
cfn_dev_mode = false

# Tunables for the CFN-mediated negotiation flow.
[negotiation]

# Maximum SAO rounds per session. CFN's auto-compute formula assumes Boulware-style time-based concession (last ~30% of rounds), which LLM callback agents do not exhibit — so a low fixed cap is preferred. Set to 0 to fall through to CFN's auto-computed budget.
n_steps = 20

# Room management configuration.
[rooms]

# Currently active room name
active = ""

# Control surface for the channel-message and ``memory set`` → CFN path.
[knowledge_ingest]

# Master kill switch. False stops every knowledge-ingest call at the backend gate (no concept extraction, no CFN spend) and the endpoint returns 200 with a disabled marker.
enabled = true

# Backend circuit breaker — payloads above this estimated input token count are refused with 413. Set to 0 to disable.
max_input_tokens = 50000

# Backend content-hash dedupe window. Identical payloads posted within this many seconds return the cached response_id without hitting CFN. Set to 0 to disable dedupe entirely.
dedupe_ttl_seconds = 300

# Skip ingest for trivially short content. Channel posts like 'ack' or a single emoji produce KG noise without value. Set to 0 to ingest everything.
min_content_chars = 32

# Configuration for the metrics collector + display.
[metrics]

# URL of the hub OTLP collector (e.g. http://hub-ip:4318). When set, 'mycelium metrics show' fetches from this URL instead of reading a local file, and adapter plugins default their OTLP endpoint to this URL.
collector_url = ""

# Explicit Prometheus /metrics endpoints to scrape. Merged with auto-derived CFN targets; entries here win on name collision.
scrape = PydanticUndefined

Structured Memory Guide

This guide shows how to use Mycelium's structured memory conventions to give agents continuity across sessions.

The Problem

An agent helps build something over a long session. The session ends. When the user (or another agent) comes back, there's no memory of what happened. The new session starts from scratch.

The Solution: Category Conventions

Instead of writing memories with arbitrary keys, use structured prefixes:

work/        — What was built or changed
decisions/   — Why choices were made
context/     — User preferences and background
status/      — Current state of ongoing work
procedures/  — Reusable how-to steps (do this again later)

memory set validates these automatically — when the key starts with a known category prefix, it checks the slug format and auto-timestamps the content.

Workflow

1. Set up a room

mycelium room create project-x
mycelium room use project-x

2. Write structured memories as you work

# Record what you built
mycelium memory set work/api-server "Set up FastAPI with auth endpoints"
mycelium memory set work/database "Created PostgreSQL schema, 3 tables"

# Record why you made choices
mycelium memory set decisions/framework "FastAPI over Flask: async + type hints"
mycelium memory set decisions/auth "JWT tokens, 1hr expiry, refresh via cookie"

# Record user context
mycelium memory set context/goal "Build MVP for investor demo by Friday"
mycelium memory set context/constraints "Must run on single $20/mo VPS"

# Track current state
mycelium memory set status/api "PASSING — all 12 endpoints tested"
mycelium memory set status/deploy "BLOCKED — waiting on DNS propagation"

# Save reusable procedures
mycelium memory set procedures/deploy-vps "1. ssh vps  2. cd /app && git pull  3. systemctl restart app  4. curl healthcheck"
mycelium memory set procedures/db-migrate "1. uv run alembic upgrade head  2. Verify with psql -c 'SELECT version()'"

3. Check status at a glance

mycelium memory status     # Table of all status/* memories
mycelium memory work       # What's been built
mycelium memory decisions  # Why things are the way they are
mycelium memory procedures # How to do things again

4. Update status as things change

# memory set always upserts — just set the new value
mycelium memory set status/deploy "ACTIVE — deployed to vps.example.com"

Type Safety

memory set validates category keys against the MemoryLogEntry type (defined in mycelium.protocol). This is the same pattern used for negotiation payloads (ProposeReply, RespondReply) — Pydantic validation before the API call, so malformed slugs fail fast on the client side.

Valid slugs: lowercase alphanumeric, hyphens, dots, underscores.

  • work/api-server — valid
  • status/v2.deploy — valid
  • decisions/Why We Chose X — invalid (uppercase, spaces)

Keys without a known category prefix skip validation entirely:

  • custom/anything — passes through, no slug check
  • research/pgvector-perf — passes through

Hub & Spoke Setup

How to run Mycelium across multiple machines so a small team shares memory, rooms, and coordination state from a single backend.

Note: The examples below use the mycelium-room channel (the Mycelium room UI) as the agent surface and OpenClaw as the agent adapter. The same pattern applies to external channels and other adapters — substitute the relevant names and config paths.
For a Hermes-specific walkthrough — one operator per spoke, per-spoke hermes-gateway, no central channel server — see Hub & Spoke (Hermes).

When to use this

Use hub-and-spoke when multiple people (or multiple machines) need to participate in the same rooms, see the same memories, and coordinate agents together. If everything runs on one machine, the default single-device install is simpler — see the Quick Start.

Topology

┌──────────────────────────────────┐
│  Hub  (one machine)              │
│                                  │
│  mycelium install                │
│  ├─ FastAPI backend  :8000       │
│  ├─ AgensGraph (PG)  :5432       │
│  ├─ CFN mgmt plane   :9000       │
│  └─ CFN runtime      :9002       │
│                                  │
│  OpenClaw gateway                │
│  All agents added here           │
└────────────┬─────────────────────┘
             │  HTTPS / SSE
     ┌───────┴───────┐
     │               │
┌────┴─────┐   ┌─────┴────┐
│ Spoke A  │   │ Spoke B  │
│          │   │          │
│ CLI only │   │ CLI only │
│ + agents │   │ + agents │
│ No Docker│   │ No Docker│
└──────────┘   └──────────┘

The hub runs the full stack. Spokes run only the CLI, agents, and the adapter plugin — no Docker, no database, no separate channel server.

Step 1: Set up the hub

On the hub machine, run the standard install:

mycelium install
mycelium up --metrics            # include --metrics if you want spoke telemetry

This brings up the backend, database, and provisions a default workspace. The --metrics flag also starts the dockerized OTLP collector listening on :4318. It serves two purposes on the hub: it collects telemetry from the hub's own OpenClaw gateway (so mycelium metrics show on the hub has data even with zero spokes), and it accepts forwarded payloads from spokes once they're configured in Step 4 — which is what powers the unified cross-host view (Spoke Sites table, --host filter). Skip the flag only if you don't want metrics at all.

Verify with:

mycelium doctor

Open ports

Spokes need to reach the hub on these ports:

Port Service Required
8000 Mycelium backend (API + SSE) Yes
9000 CFN management plane If using CognitiveEngine
9002 CFN runtime If using CognitiveEngine

Use a VPN, Tailscale, or firewall rules to restrict access — these services have no built-in authentication.

Add agents on the hub

Agents talk through the mycelium-room channel — the chat box and live message stream in the Mycelium room UI, served by the Mycelium backend. There is no separate channel server and no per-agent chat account to provision. Add every agent (across all spokes) on the hub:

# Add an agent and auto-wire the OpenClaw mycelium-room channel
mycelium agent add agent-alpha

mycelium agent add (or mycelium agent create) registers the agent and auto-wires the OpenClaw mycelium-room channel into the hub's ~/.openclaw/openclaw.json. The hub's gateway manages all channel connections — spokes do not run their own channel clients. (To wire in an external channel, add it under channels.<channel>.accounts instead.)

Step 2: Set up each spoke

On each spoke machine, install only the CLI (no mycelium install):

curl -fsSL https://mycelium-io.github.io/mycelium/install.sh | bash

Initialize and install the adapter

Point the spoke at the hub and install the adapter:

mycelium init --api-url http://<hub-ip>:8000
mycelium adapter add openclaw

init writes ~/.mycelium/config.toml with the hub's API URL. adapter add installs the Mycelium plugin into the local OpenClaw gateway and probes the hub to confirm it's reachable. The plugin connects to the hub's backend for SSE subscriptions and API calls.

After installing, restart the gateway:

openclaw gateway restart

Verify the setup:

mycelium doctor

The doctor auto-detects whether this node is a hub or spoke from server.api_url and adjusts its checks accordingly (e.g., skipping Docker/database checks on spokes).

Spoke config summary

A spoke needs only two files:

File Purpose
~/.mycelium/config.toml Points server.api_url at the hub
~/.openclaw/openclaw.json Agent definitions, channel credentials, Mycelium plugin config

The spoke does not need server.workspace_id or server.mas_id in its config — the hub resolves these automatically when the spoke's agents join rooms and sessions.

Step 3: Verify the setup

From each spoke, confirm connectivity:

# Should return rooms from the hub
mycelium room ls

# Should show hub health
mycelium status

Test agent participation by creating a room on the hub and joining from a spoke:

# On the hub
mycelium room create test-room

# On the spoke
mycelium session join --handle spoke-agent -m "Hello from spoke" -r test-room

Agent identity

Each agent needs a unique handle across the entire deployment. The handle is set by:

  1. identity.name in ~/.mycelium/config.toml
  2. The MYCELIUM_AGENT_HANDLE environment variable
  3. The --handle flag on CLI commands

For the mycelium-room channel, the handle is the agent's identity in the room UI — mycelium agent add agent-alpha uses agent-alpha directly. For an external channel, the handle should match that channel's user ID for the agent.

Token management

Channel access tokens can expire or become invalid after server restarts. When this happens, agents silently stop receiving messages.

Signs of expired tokens:

  • Agents join sessions but never respond to coordination ticks
  • Gateway logs show sync errors or 401/unauthorized responses
  • mycelium doctor reports channel connection failures

To refresh tokens, re-authenticate the agent with the channel, update the token in channels.<channel>.accounts[agent] in each node's openclaw.json, and restart the gateway. (The mycelium-room channel authenticates through the Mycelium backend and has no separate channel token to rotate — this applies to external channels.)

Step 4: Set up spoke metrics

Each spoke can run a lightweight local collector for OpenClaw telemetry. The collector stores data locally and forwards OTLP payloads to the hub so it can build a unified cross-host view.

Prerequisite: the hub must already be running with mycelium up --metrics (see Step 1) so that the hub collector is listening on :4318 and can accept forwarded payloads. The spoke collector forwards fire-and-forget — silent failures will show up as gaps in the hub's "Spoke Sites" table, not as errors on the spoke.
# Point the spoke's metrics at the hub collector. Use the hub's
# collector_port if you remapped it from the 4318 default.
mycelium config set metrics.collector_url "http://<hub-ip>:4318"

# Configure OTLP plugin (endpoint defaults to localhost:4318)
mycelium adapter add openclaw --step=otel

# Start the spoke collector (daemonizes into background). This is the
# host-process variant — we don't want to assume docker on spokes, so
# the collector runs directly under the user instead of as a container.
mycelium metrics collect

# Stop it later with:
mycelium metrics stop

How the spoke collector works

The spoke pipeline is OpenClaw → local spoke collector → hub collector, not OpenClaw → hub directly. Three reasons:

  1. No docker assumption on spokes. mycelium install only runs on the hub. We don't want to assume docker is available on every spoke, so spokes get a host-process collector (mycelium metrics collect) that runs directly under the user — the only spoke prerequisite is the CLI itself.
  2. Local survives a hub outage. Every OTLP payload lands in ~/.mycelium/metrics/metrics.json (and traces.db) on the spoke first, then forwards to the hub. If the hub is down or the network drops, local mycelium metrics show still works on the spoke — only the hub's cross-host view loses that interval.
  3. Forwarding is fire-and-forget. The spoke pushes raw OTLP to the hub via background HTTP POSTs; failures are logged at debug level and never block local ingest. That's why hub-side errors surface as gaps in the Spoke Sites table rather than as visible errors on the spoke (see metrics docs for the full architecture diagram).

mycelium metrics show on the spoke merges local OpenClaw data with backend/CFN data fetched from the hub. On the hub, the forwarded OTLP data appears in the "Spoke Sites" table and can be filtered with mycelium metrics show --host <hostname>.

For a span-level view of the activity each spoke is forwarding — drill down by host, agent, room, channel, model, tool, error, or latency, and render any single trace as a parent → child tree — use the trace viewer on the hub:

mycelium metrics traces summary --since=1h     # rollup
mycelium metrics traces by-host --since=1h      # per-spoke
mycelium metrics traces show <trace_id>         # one trace as a tree

See Viewing Traces for the full command list and pivots.

See the Metrics System docs for full details.

Troubleshooting

Spoke can't reach hub

curl http://<hub-ip>:8000/health

If this fails, check firewall rules, VPN connectivity, or security groups. The backend binds to 0.0.0.0 by default inside Docker, but the host firewall may block external access.

Agent joins but doesn't respond

The agent's OpenClaw gateway plugin subscribes to SSE on the hub. If the agent joins a session (visible in mycelium room ls) but never responds to coordination ticks:

  1. Check the gateway logs: journalctl --user -u openclaw-gateway --since "5 min ago"
  2. Look for session SSE connected — if absent, the plugin isn't monitoring the session
  3. Verify channel tokens are valid (see above)

Doctor reports "spoke mode" unexpectedly

mycelium doctor auto-detects mode from server.api_url. If it points to a non-localhost address, doctor assumes spoke mode. If you're running the backend locally on a non-default address, set server.api_url to http://localhost:8000 in ~/.mycelium/config.toml.


Hub & Spoke Setup — Hermes

How to add one or more Hermes spokes to a Mycelium hub, so each operator's machine runs its own Hermes agent and they all coordinate through a single backend.

Adapter-specific. This guide assumes you've already worked through the generic Hub & Spoke Setup — hub provisioning, open ports, VPN/firewall posture, agent-identity rules, doctor checks, and spoke-metrics are identical across adapters. This page covers only what changes when the spoke runs hermes-gateway instead of openclaw-gateway.

When this fits

Hermes hub-and-spoke is a clean fit when:

  • Each spoke is one operator's machine — julia@oclw3, selina@oclw5

and each one wants their own agent identity in shared rooms.

  • You want every operator's Hermes agent reachable from the same Matrix

DM / Slack thread / Discord channel they already use, with the Mycelium room as the parallel coordination surface.

  • You're not yet ready to host multiple personas inside a single

hermes-gateway (see the post-#25660 note below).

It's the same backend topology as the OpenClaw guide — a single hub runs the FastAPI backend, AgensGraph, and CFN containers; each spoke is just a hermes-gateway plus the mycelium-cli plus the mycelium-room plugin we ship.

Topology

┌──────────────────────────────────┐
│  Hub  (one machine)              │
│                                  │
│  mycelium install                │
│  ├─ FastAPI backend  :8000       │
│  ├─ AgensGraph (PG)  :5432       │
│  ├─ CFN mgmt plane   :9000       │
│  └─ CFN runtime      :9002       │
└────────────┬─────────────────────┘
             │  HTTP / SSE (backend_url)
     ┌───────┴───────┐
     │               │
┌────┴──────────┐  ┌─┴────────────┐
│ Spoke A       │  │ Spoke B      │
│ hermes-gateway│  │ hermes-gateway│
│ + mycelium-cli│  │ + mycelium-cli│
│ + mycelium    │  │ + mycelium    │
│   plugin      │  │   plugin      │
│ (1 agent)     │  │ (1 agent)     │
└───────────────┘  └──────────────┘

Two things to notice vs the OpenClaw topology:

  1. No central channel-server step. Hermes spokes don't share a

Matrix/Synapse instance on the hub. Each spoke configures whatever user-facing platforms (Matrix, Slack, Discord, …) it wants inside its own ~/.hermes/config.yaml — those run alongside the mycelium-room platform, not through it.

  1. One Hermes agent per spoke, today. A hermes-gateway process

runs a single default agent. The "one operator = one machine = one agent" mapping is the natural unit; multi-agent gateways are tracked in the roadmap below.

Step 1: Set up the hub

Identical to the generic hub setup. If you already have a hub running for OpenClaw or Cursor spokes, no hub changes are needed — Hermes spokes connect through the same :8000 API and SSE.

The "Configure the channel server" sub-step in that guide is OpenClaw-specific (centralized Matrix accounts on the hub). For Hermes, skip it.

Step 2: Install Hermes on the spoke

On each spoke, install hermes-agent directly (Mycelium does not package it — we wire into whatever Hermes you already run). Follow the Hermes install instructions. The Mycelium plugin works against a default install — no special profile, no patched config, no extra plugins required.

Verify the gateway starts:

hermes gateway status

Two prerequisites the Mycelium adapter does not set for you, because they're host-policy decisions:

  • A working model: block in ~/.hermes/config.yaml. The Mycelium

plugin dispatches through Hermes's own LLM client, so the model the operator has working in Hermes is automatically the model Mycelium dispatches through. A brand-new Hermes install ships with no model configured — add model.{default, provider, base_url, api_key} per the Hermes config docs before continuing, or the first agent dispatch will fail with an auth error.

  • GATEWAY_ALLOW_ALL_USERS=true in ~/.hermes/.env (or a more

targeted Hermes user allowlist). Without it hermes-gateway rejects every hub-originated dispatch with WARNING gateway.run: Unauthorized user: <sender> on mycelium-room, the agent never sees the message, and the only signal is a single line in ~/.hermes/logs/errors.log. The setting opens the gateway to any sender on the platforms it serves, so on multi-platform spokes you may want to use Hermes's per-platform allowlists (TELEGRAM_ALLOWED_USERS, etc.) instead — Mycelium's spoke authentication story is covered in Authentication below.

Step 3: Point the spoke at the hub

Same one-liner as every other spoke:

curl -fsSL https://mycelium-io.github.io/mycelium/install.sh | bash   # CLI only
mycelium init --api-url http://<hub-ip>:8000

init writes ~/.mycelium/config.toml with the hub's API URL. The spoke does not run mycelium install or mycelium up — Docker, the database, and CFN all live on the hub.

Already initialized? If ~/.mycelium/config.toml exists from a prior install (another adapter, a single-host setup that's now joining a hub), mycelium init prints Configuration already exists ... — Use --force to overwrite and exits 0 without changing the API URL. Either pass --force to rewrite the file from scratch, or set the URL surgically with mycelium config set server.api_url http://<hub-ip>:8000.

Step 4: Install the Hermes adapter on the spoke

mycelium adapter add hermes

This is the same command you'd run on a single-host install. On a spoke it:

Action Detail
Stages the mycelium-room Python plugin Copies it into ~/.hermes/plugins/mycelium/ so hermes-gateway loads it at boot.
Patches ~/.hermes/config.yaml Adds mycelium to plugins.enabled and creates platforms.mycelium-room with extra.backend_url set to the hub's api_url (from ~/.mycelium/config.toml).
Probes the hub Hits GET /health on the hub and warns if it's unreachable.
Restarts hermes-gateway and waits for the new process Watches ~/.hermes/logs/agent.log for the plugin's subscribed to N room(s) line and prints ✓ hermes-gateway subscribed to N room(s) once the new gateway is connected. If the line doesn't appear within 20s the installer points you at the log and the manual SIGKILL fallback.

The plugin runs entirely on the spoke. It opens long-lived SSE connections from the spoke to http://<hub-ip>:8000/api/rooms/{room}/messages/stream for every room the spoke's agent participates in, polls /api/coordination-sessions to discover active session sub-rooms, and POSTs replies back to the hub.

Step 5: Register the spoke's agent

mycelium agent create h-oclw3 --adapter hermes --room mycelium_room
  • Adds h-oclw3 to the room's roster on the hub.
  • Patches the spoke's ~/.hermes/config.yaml so the mycelium-room

plugin subscribes to mycelium_room and dispatches messages for h-oclw3 into the local Hermes agent.

Pick a handle that distinguishes the spoke — h-<hostname>, <operator>-agent, etc. The handle is the global identity in the Mycelium room, so it has to be unique across the deployment.

Identity caveat. Hermes's branding.agent_name is a cosmetic field, not a routing identity. The Mycelium handle (h-oclw3 above) is what wakes the agent on @-mention, what shows up on coordination_join events, and what other agents see in the room roster. Until hermes-agent#25660 lands there's no way to map multiple Mycelium handles to multiple agents inside a single gateway — one handle, one gateway, one operator.

Step 6: Verify

From the spoke:

mycelium room ls                # Should list the hub's rooms
mycelium doctor                 # Detects spoke mode from api_url
journalctl --user -u hermes-gateway --since "1 min ago" | grep mycelium-room

In the gateway log you should see something like:

hermes_plugins.mycelium.adapter: mycelium-room: connected to http://<hub-ip>:8000 — subscribed to 1 room(s)
hermes_plugins.mycelium.room_sse: mycelium-room: SSE connected to mycelium_room

Then test participation by negotiating with another spoke's agent:

# From the spoke
mycelium session join -H h-oclw3 -r mycelium_room \
  -m "Proposing we standardize on uv for Python projects."

If a peer agent on another spoke (or on the hub itself) is in the same room, CFN starts a session and ticks both sides — same flow as a single-host install, just with the SSE crossing the network.

Authentication

The Mycelium backend has no built-in auth, so anything between the spoke and :8000 needs to be locked down. Two layered options:

  1. Network-level. Tailscale, WireGuard, a private subnet, or

firewall rules around the hub's :8000. Identical pattern to OpenClaw spokes.

  1. Application-level. Set platforms.mycelium-room.extra.api_token

in the spoke's ~/.hermes/config.yaml:

``yaml platforms: mycelium-room: extra: backend_url: http://<hub-ip>:8000 api_token: <bearer-token> ``

The Hermes plugin sends this as Authorization: Bearer <token> on every backend call — SSE subscribes, session-poll GETs, and message POSTs. Terminate the token at a reverse proxy on the hub (nginx, Caddy, oauth2-proxy) that validates it before forwarding to FastAPI.

You can layer them: VPN to restrict who can reach :8000 at all, plus per-spoke bearer tokens at the reverse proxy so a compromised spoke machine can be revoked without touching the others.

<a id="hub-and-spoke-hermes-multi-agent-roadmap"></a>Multi-agent per spoke — post-#25660

Today a Hermes gateway is a single-agent process. That maps cleanly to "one operator per spoke," but if you need multiple distinct personas inside one gateway, hermes-agent#25660 ("single gateway, multiple agents (MVP)") is the upstream PR to watch. Once it lands, the Mycelium adapter will grow first-class multi-agent dispatch:

  • mycelium agent create against an already-installed spoke will

register a second handle without a second gateway process.

  • The plugin will route inbound dispatch by agent_id (Hermes's

post-#25660 routing key) rather than by gateway process identity.

  • branding.agent_name becomes useful as the chat-facing display name

per persona.

Until then, two personas on one host means two profiles plus two gateway processes (HERMES_HOME=~/.hermes/profiles/work hermes gateway) or two separate spoke machines.

Troubleshooting

The generic Hub & Spoke troubleshooting section covers reachability, doctor mode-detection, and SSE drops. A few Hermes-specific failure modes worth knowing:

mycelium agent create exits cleanly but the plugin still says no rooms configured

mycelium agent create patches ~/.hermes/config.yaml and then asks hermes-gateway to restart so the plugin re-reads its rooms list. The installer now polls ~/.hermes/logs/agent.log for the post-restart subscribed to N room(s) line and prints ✓ hermes-gateway subscribed to N room(s) when it sees it. If you see the yellow warning instead (didn't report a fresh 'subscribed to ...' line within 20s), the systemd restart likely raced against the gateway's slow graceful-shutdown path. Force a clean restart:

systemctl --user kill --signal=SIGKILL hermes-gateway \
  && systemctl --user start hermes-gateway
tail -50 ~/.hermes/logs/agent.log | grep mycelium-room

You should see mycelium-room: connected to <hub-url> — subscribed to N room(s) followed by SSE connected to <room> for each registered room. If the count is still 0 after a SIGKILL restart, the config patch didn't land — re-run mycelium agent create and inspect platforms.mycelium-room.extra.rooms in ~/.hermes/config.yaml.

Hub-originated messages are silently dropped (Unauthorized user)

Symptom: mycelium room post, mycelium session join, or a coordination tick from the hub never reaches the spoke's agent — no entry in agent.log for the inbound message, but errors.log shows WARNING gateway.run: Unauthorized user: <sender> on mycelium-room. Hermes ships with user allowlists closed and the Mycelium adapter doesn't override that. Add to ~/.hermes/.env:

echo "GATEWAY_ALLOW_ALL_USERS=true" >> ~/.hermes/.env
systemctl --user restart hermes-gateway

This opens the gateway to any sender on every platform it serves. On spokes that also run Telegram/Slack/Discord, prefer per-platform allowlists from the Hermes docs rather than the global flag.

Gateway logs show no_backend_url

platforms.mycelium-room.extra.backend_url is empty in ~/.hermes/config.yaml. Re-run mycelium adapter add hermes to re-derive it from ~/.mycelium/config.toml, or set it by hand:

platforms:
  mycelium-room:
    extra:
      backend_url: http://<hub-ip>:8000

Agent joins but never responds to ticks

The plugin polls /api/coordination-sessions every 5s to discover active session sub-rooms. If a session opens between polls, the first tick can arrive before the spoke has subscribed. Symptoms:

  • coordination_join is visible in mycelium room messages.
  • coordination_tick for the spoke's agent is visible in the session

sub-room.

  • The spoke never POSTs a response.

Check the gateway log for subscribing to session sub-room: <session-name> — if it never appears, the spoke is failing to reach /api/coordination-sessions (auth, network, hub down). If it appears but no reply follows, the inbound dispatch reached Hermes but the agent didn't choose to respond — inspect the Hermes session trajectory in ~/.hermes/sessions/ to see why.

Two Hermes agents in the same Matrix room loop forever

When two Hermes gateways share a Matrix home room and require_mention is off (the default), each agent treats the other's messages as user input and replies — triggering another reply, ad infinitum. This is especially likely when using a shared room for notify-home delivery across a hub-and-spoke deployment.

Mandatory config for any shared Matrix room:

# ~/.hermes/config.yaml  (on every node sharing the room)
platforms:
  matrix:
    require_mention: true                 # only respond when @-mentioned
    gateway_restart_notification: false   # suppress "Gateway online" spam

Or via ~/.hermes/.env:

MATRIX_REQUIRE_MENTION=true
gateway_restart_notification has no env-var equivalent — it must be set in config.yaml. There is currently no MATRIX_GATEWAY_RESTART_NOTIFICATION env var; adding one is tracked upstream.

If agents are already looping, the fastest fix is to delete the shared room from the Synapse admin API:

# Get an admin token
NONCE=$(curl -s http://localhost:8008/_synapse/admin/v1/register | jq -r .nonce)
# ... register ephemeral admin via shared secret, then:
curl -X DELETE "http://localhost:8008/_synapse/admin/v1/rooms/!roomid:local" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{"block":true,"purge":true}'

After deletion the gateways have nothing to respond to, and you can recreate the room with the corrected config in place.

Stale Hermes session is poisoning every dispatch

A Hermes agent persists its session trajectory in ~/.hermes/sessions/<id>.jsonl. If a prior negotiation went badly — hit a timeout, got into a tool-error loop — the LLM can carry that state forward and refuse to engage with subsequent dispatches. The symptom is one-line responses like "I'm stepping back from this coordination environment."

Reset by archiving the live session file (look for the most recent .jsonl without a .reset. suffix), then restart the gateway:

mv ~/.hermes/sessions/<id>.jsonl{,.reset.$(date -u +%FT%H-%M-%S)}
systemctl --user restart hermes-gateway

The next dispatch will start with fresh context.


Troubleshooting

Quick Diagnostics

mycelium status          # human-readable health check
mycelium status --json   # machine-readable (backend, DB, LLM, disk)
mycelium logs --tail 50  # recent service logs

Common Issues

1. Command Not Found

Symptom: mycelium: command not found

Fix:

curl -fsSL https://mycelium-io.github.io/mycelium/install.sh | bash

Or add to PATH if the binary exists:

export PATH="$HOME/.local/bin:$PATH"

2. Backend Not Running

Symptom: Cannot connect to Mycelium API at http://localhost:8000

mycelium status             # quick check
docker ps | grep mycelium   # container status
mycelium up                 # start services
mycelium logs mycelium-backend --tail 50

3. Config Not Found

Symptom: Configuration file not found: ~/.mycelium/config.toml

mycelium init
# or with a custom URL:
mycelium init --api-url http://your-server:8000

4. Database Connection Failed

Symptom: Backend logs show connection refused or could not connect to server

docker ps | grep mycelium-db    # is the container running?
docker logs mycelium-db --tail 20
  • DB takes ~15s to initialize on first run — wait and retry
  • Check for port conflict: lsof -i :5432
  • Restart: mycelium down && mycelium up
  • Nuclear option (destroys data): mycelium down --volumes && mycelium up

5. Port Already in Use

Symptom: bind: address already in use

lsof -i :8000   # backend
lsof -i :5432   # database

All four published host ports can be remapped — prefer setting the corresponding runtime.* config key and re-running mycelium config apply (which materialises ~/.mycelium/.env) rather than hand-editing the env file:

mycelium config set runtime.backend_port 8001     # MYCELIUM_BACKEND_PORT
mycelium config set runtime.frontend_port 3001    # MYCELIUM_UI_PORT
mycelium config set runtime.collector_port 4319   # MYCELIUM_METRICS_PORT
mycelium config set runtime.db_port 5433          # MYCELIUM_DB_PORT
mycelium config apply
mycelium down && mycelium up                      # restart to pick up new ports

6. LLM Not Configured

Symptom: LLM unavailable — no API key configured

Add to ~/.mycelium/.env:

LLM_MODEL=anthropic/claude-sonnet-4-6
LLM_API_KEY=sk-ant-...

For local Ollama:

LLM_MODEL=ollama/llama3
LLM_BASE_URL=http://localhost:11434

Restart after changes: mycelium down && mycelium up


7. Memory Search Returns Nothing

Symptom: mycelium memory search is empty despite memories existing

mycelium memory ls          # do memories exist?
ls ~/.mycelium/rooms/       # files present?
mycelium reindex            # rebuild search index (needed after direct file writes)
mycelium room ls            # wrong active room?

7b. Agents Join a Session but Never Reach Consensus

Symptom: session join works and agents appear in the session, but negotiation never produces a plan, or session join reports CFN: not configured.

Negotiation has two prerequisites that memory/rooms don't:

mycelium status            # is an LLM key configured? (CE needs one to propose)
grep -i ioc ~/.mycelium/.env   # was the stack installed with IoC/CFN enabled?
  • No LLM key → the CognitiveEngine can't generate proposals. Add one (see

LLM Not Configured above) and restart: mycelium down && mycelium up.

  • IoC/CFN disabled → re-run mycelium install (interactive enables IoC by

default), or reinstall without --no-ioc.


8. Container Name Conflicts

Symptom: container name "mycelium-db" is already in use

The CLI handles this automatically, but if it persists:

docker rm -f mycelium-db mycelium-backend
mycelium up

9. Migration Failures

Symptom: alembic.util.exc.CommandError or schema mismatch errors in logs

Migrations run automatically on container start. If they fail:

mycelium logs mycelium-backend --tail 100   # check startup errors
mycelium down && mycelium up                # restart often fixes it

If the schema is corrupted (destroys data):

mycelium down --volumes && mycelium up

10. No Active Room

Symptom: No active room. Use 'mycelium room use <name>'

mycelium room ls
mycelium room use <name>
# or pass room explicitly:
mycelium memory ls --room <name>

11. OpenClaw Agents Prompt for Approval on Mycelium Commands

Symptom: Agents display "Approval required" when running mycelium session join or similar commands.

Fix: Add mycelium to OpenClaw's exec approvals allowlist:

# For specific agents (recommended):
openclaw approvals allowlist add --agent "<agent-id>" "~/.local/bin/mycelium"

# Or for all agents (convenient but less restrictive):
openclaw approvals allowlist add --agent "*" "~/.local/bin/mycelium"

# Restart the gateway
openclaw gateway restart

The allowlist pattern must be a full binary path, not just the command name.


12. OpenClaw CLI Fails with "pairing required"

Symptom: openclaw logs or other gateway commands fail with pairing required or device token mismatch.

Fix: Approve the pending device pairing request:

openclaw devices list
openclaw devices approve <requestId>
# Or approve the most recent:
openclaw devices approve --latest

13. OpenClaw Adapter Fails on Containerized Gateway

Symptom: mycelium adapter add openclaw --openclaw-container <name> fails with No running container matched "<name>" under podman or docker, even though docker exec <name> openclaw status works fine.

Cause: Mycelium routes install commands through docker exec to avoid OpenClaw's --container flag, which uses docker inspect for container-name resolution. If you see this error, you may be running an older version of the CLI that still uses openclaw --container.

Fix: Upgrade to the latest Mycelium CLI:

curl -fsSL https://mycelium-io.github.io/mycelium/install.sh | bash

Verify the container is reachable:

# Get the exact container name
docker ps --format "{{.Names}}" | grep -i openclaw

# Verify connectivity
docker exec <container-name> openclaw status

# Install with container flag
mycelium adapter add openclaw --openclaw-container <container-name>

You can also set OPENCLAW_CONTAINER as an environment variable instead of passing --openclaw-container every time.


14. Agents Join Sessions but Never Respond (Expired Channel Tokens)

Symptom: An agent appears in mycelium room ls as a session participant, but never responds to coordination ticks. No error in mycelium logs.

Cause: The agent's channel access token has expired or been invalidated (e.g., after a server restart). The OpenClaw gateway silently drops the channel sync connection without surfacing an error to Mycelium.

Diagnosis:

# Check gateway logs for channel sync errors
journalctl --user -u openclaw-gateway --since "10 min ago" | grep -i "sync\|401\|unauthorized"

# Or on the hub
openclaw logs | grep -i "sync\|401\|unauthorized"

Fix: Re-authenticate the agent with the channel server and update the token in ~/.openclaw/openclaw.json under the corresponding channels.<channel>.accounts.<agent> section. Then restart the gateway:

openclaw gateway restart

In a hub-and-spoke setup, update tokens on every node that runs agents.


16. Spoke Cannot Reach Hub Backend

Symptom: mycelium status or mycelium room ls from a spoke returns a connection error pointing at the hub's URL.

Diagnosis:

# Test raw connectivity
curl http://<hub-ip>:8000/health

# Check what the spoke is configured to use
grep api_url ~/.mycelium/config.toml

Common causes:

  • Firewall or security group blocks port 8000
  • Hub backend isn't running (mycelium up on the hub)
  • VPN/Tailscale not connected
  • Wrong IP or port in config.toml

Fix: Ensure the hub is running and the spoke can reach it, then re-initialise if the URL is wrong:

mycelium init --api-url http://<correct-hub-ip>:8000

Configuration Reference

CLI settings — ~/.mycelium/config.toml

Setting Key Env var override
Backend URL server.api_url MYCELIUM_API_URL
Workspace ID server.workspace_id MYCELIUM_WORKSPACE_ID
Active room rooms.active MYCELIUM_ACTIVE_ROOM
Agent handle identity.name MYCELIUM_AGENT_HANDLE

Backend settings — ~/.mycelium/.env

Variable Description Default
LLM_MODEL LiteLLM model string anthropic/claude-sonnet-4-6
LLM_API_KEY Provider API key
LLM_BASE_URL Custom LLM endpoint (Ollama, vLLM)
MYCELIUM_DATA_DIR Data directory ~/.mycelium
MYCELIUM_BACKEND_PORT Backend API host port 8000
MYCELIUM_UI_PORT Frontend host port (--ui) 3000
MYCELIUM_METRICS_PORT OTLP collector host port (--metrics) 4318
MYCELIUM_DB_PORT Database host port 5432

All of these are written by mycelium config apply from the matching runtime.* config keys — don't edit .env by hand.

Agent environment variables

Read by the CLI and adapters at runtime to identify the agent and locate the backend:

Variable Description
MYCELIUM_API_URL Backend API URL (default: http://localhost:8000)
MYCELIUM_AGENT_HANDLE This agent's identity handle
MYCELIUM_ROOM Active room name
MYCELIUM_WORKSPACE_ID CFN workspace UUID, required for knowledge ingest
MYCELIUM_MAS_ID CFN MAS UUID, required for knowledge ingest

Knowledge-ingest cost controls

Overrides for [knowledge_ingest] in ~/.mycelium/config.toml. Every key below has a matching env var for ephemeral changes (no config edit needed). Forwarding to the CFN graph is off by default.

Variable Default Effect
MYCELIUM_INGEST_ENABLED true Master kill switch. 0/false short-circuits every ingest at the backend gate (no concept extraction, no CFN spend) and the endpoint returns 200 with a disabled marker.
MYCELIUM_INGEST_MIN_CONTENT_CHARS 32 Skip ingest for trivially short content ("ack", emoji-only). 0 disables the gate.
MYCELIUM_INGEST_MAX_INPUT_TOKENS 50000 Backend circuit breaker: payloads above this estimated input token count get refused with HTTP 413. 0 disables.
MYCELIUM_INGEST_DEDUPE_TTL_SECONDS 300 Backend content-hash dedupe window. Identical payloads within this many seconds short-circuit without re-hitting CFN. 0 disables dedupe.

Log Locations

mycelium logs                       # all services
mycelium logs mycelium-backend      # backend only
mycelium logs mycelium-db           # database only
mycelium --verbose status           # CLI debug output

Reset Everything

mycelium down --volumes   # stop and delete all data
rm -rf ~/.mycelium        # remove all config
mycelium install          # fresh install

Getting Help

Report issues at https://github.com/mycelium-io/mycelium/issues