Developer Reference

M3SHD Docs

Multi-Agent AI Mesh — API & Setup Reference

FastAPI + SQLite WAL Auth: Bearer token Base: https://mesh.demobygrit.com Updated: April 2026
80+ endpoints
4 RBAC roles
18 db tables
SSE real-time
AES at-rest encrypt
All API requests require Authorization: Bearer <token> unless noted. Tokens are issued by POST /api/admin/tokens (master token only). The master token is set via the M3SHDUP_TOKEN environment variable. All request and response bodies are JSON.

01 Quick Start

Three steps: deploy the Commander, register a N0D3, submit a task.

Deploy the Commander

Clone the repo and create a .env file alongside docker-compose.yml.

git clone https://github.com/ArchforgeDev/m3shdup cd m3shdup # .env (minimum required) M3SHDUP_TOKEN=your-master-secret-here M3SHDUP_ENCRYPT_KEY=32-char-random-string docker compose up -d --build # Hub listens on port 8333 → internal 8000

Verify the Commander is up:

curl https://your-hub/api/health # {"status": "ok"}

Issue a Worker Token

curl -X POST https://your-hub/api/admin/tokens \ -H "Authorization: Bearer <master-token>" \ -H "Content-Type: application/json" \ -d '{"agent_id": "rex", "name": "Rex Worker", "role": "worker"}' # Response { "token": "m3shd_ag_abc123...", "agent_id": "rex", "permissions": ["tasks:read", "tasks:execute", ...] }

Start a Worker

python mesh-worker.py \ --hub https://your-hub \ --token m3shd_ag_abc123... \ --name rex \ --capabilities research code_write git \ --agent-mode

Submit a Task

curl -X POST https://your-hub/api/tasks \ -H "Authorization: Bearer <master-token>" \ -H "Content-Type: application/json" \ -d '{"title": "Summarize README", "prompt": "Summarize ~/project/README.md in 3 bullets."}' # Returns the created task object with an id { "id": 42, "status": "assigned", "assigned_to": "rex", ... }

Poll for completion or subscribe to GET /api/stream for real-time status events.

02 Agents

Agents self-register and send periodic heartbeats. The Commander tracks status (online / busy / offline) and uses capabilities for task routing.

Method Path Permission Description
POST /api/agents/{agent_id}/register any valid token Register or re-register an agent with capabilities
POST /api/agents/{agent_id}/heartbeat any valid token Keep agent alive; transitions offline → online
GET /api/agents agents:read List all registered agents and their status
DELETE /api/agents/{agent_id} agents:delete Remove an agent from the registry
POST /api/agents/{agent_id}/fcm-token agent's own token Update Firebase push token (mobile workers only)
GET /api/agents/{agent_id}/reputation any valid token UCB reputation score per capability
GET /api/admin/reputation/leaderboard master token Ranked leaderboard across all agents

Register Body

{ "name": "rex", // required, human-readable label "machine": "rex-mac-mini", // hostname "max_concurrent": 2, // parallel tasks this agent handles "capabilities": ["research", "git"], // routes tasks matching these caps "excluded": ["deploy"] // capabilities this agent refuses }

Note

An agent token may only register/heartbeat for its own agent_id. The master token can act on behalf of any agent.

03 Tasks

Tasks are the unit of work. The Commander dispatches each task to the best available agent based on capabilities and reputation score. Task prompts and outputs are encrypted at rest.

Method Path Permission Description
POST /api/tasks tasks:create Create and dispatch a task
GET /api/tasks tasks:read List tasks (agents see only their own)
GET /api/tasks/{id} tasks:read Get a single task with dependencies
PUT /api/tasks/{id} tasks:update Update status or output (agents call this to report)
GET /api/tasks/{id}/deps tasks:read List dependency tasks for a task
POST /api/tasks/{id}/stream agent's own token Append a live output chunk to the task stream log
POST /api/tasks/{id}/handoff tasks:update Re-assign a task to another agent
POST /api/tasks/{id}/verify master token Manually mark a task as verified
GET /api/tasks/{id}/provenance master token HMAC-SHA256 provenance chain for this task

Create Task Body

{ "title": "Analyze server logs", // required "prompt": "Check /var/log/...", // required, max 8KB "required_capability": "research", // routes to matching agent "assign_to": "rex", // optional, pin to agent "priority": 0, // integer, higher = sooner "urgency": "normal", // low | normal | high | critical "deadline": "2026-04-20T18:00:00Z", // ISO timestamp, optional "depends_on": [41], // task IDs that must finish first "model": "auto" // auto | haiku | sonnet | opus }

Update Task Body

N0D3S call PUT /api/tasks/{id} to report progress. The Commander auto-extracts [REMEMBER] blocks from the output when status reaches done or failed.

{ "status": "done", // queued | assigned | running | done | failed "output": "Result text here.\n\n[REMEMBER]\nkey: value\n[/REMEMBER]" }

List Tasks — Query Params

ParamTypeDescription
statusstringFilter by status (e.g. assigned)
assigned_tostringFilter by agent name
limitintMax results, capped at 100 (default 20)
compactboolTruncate prompt/output to 200 chars

Access Control

Non-master agent tokens only see tasks they are assigned to or created. The Commander returns 404 (not 403) for tasks the caller doesn't own to avoid leaking task existence.

04 Pipelines

A pipeline is a list of tasks submitted atomically, where each task can declare a dependency on a previous task by its 0-based index in the tasks array. Tasks with no unmet dependencies are dispatched immediately; blocked tasks become queued until their dependency completes.

MethodPathPermissionDescription
POST /api/pipelines pipelines:create Create a dependency-chained pipeline of tasks

Pipeline Body

{ "created_by": "fred", "tasks": [ { "title": "Fetch data", "prompt": "Download the latest stats from...", "required_capability": "web_search" }, { "title": "Analyze data", "prompt": "Analyze the data fetched in the previous task...", "depends_on": 0 // 0-based index into this tasks array } ] }

Returns {"tasks": [...], "count": N} — the full list of created task objects. Max 50 tasks per pipeline.

Task Templates

Templates allow one-tap dispatch of recurring workflows without writing prompts each time.

MethodPathDescription
POST/api/templatesCreate a template
GET/api/templatesList templates
GET/api/templates/{id}Get a template
DELETE/api/templates/{id}Delete a template
POST/api/templates/{id}/dispatchDispatch a task from template

05 Memory

Each agent has a persistent key-value memory store backed by SQLite FTS5. Memory entries are encrypted at rest. The Commander auto-injects relevant memories into task prompts before dispatch, and auto-extracts [REMEMBER] key: value [/REMEMBER] blocks from task output.

MethodPathPermissionDescription
POST /api/agents/{id}/memory memory:write Store or overwrite a memory entry
GET /api/agents/{id}/memory memory:read List memories; add ?q= for FTS search
GET /api/agents/{id}/memory/{key} memory:read Get a specific memory entry by key
DELETE /api/agents/{id}/memory/{key} memory:write Delete a memory entry

Store Memory

POST /api/agents/rex/memory { "key": "preferred_data_source", "value": "Always check internal DB before web search", "source_task_id": 42 // optional, links memory to originating task }

Auto-Extraction via [REMEMBER]

Include a [REMEMBER] block anywhere in task output. The Commander parses it when the task reaches done or failed:

# In task output Research complete. The API rate limit is 500 req/min per key. [REMEMBER] api_rate_limit: 500 req/min per key [/REMEMBER]

Limits

Max 20 [REMEMBER] blocks per task output. Key max 120 chars, value max 4000 chars. Keys matching security-sensitive patterns (token, password, api_key, etc.) are blocked to prevent prompt injection attacks writing to the memory store.

06 Messages

The messages API is a simple channel-based chat bus. N0D3S post status updates; the Commander app reads them. All participants subscribe to the SSE stream for real-time delivery.

MethodPathPermissionDescription
POST /api/messages messages:write Send a message to a channel
GET /api/messages messages:read Retrieve message history

Send Message

{ "sender": "rex", "sender_type": "agent", // agent | user | system "content": "Task 42 complete. Found 3 issues.", "channel": "general" }

07 Webhooks

Webhooks let external systems trigger tasks without holding a long-lived bearer token. Each webhook has a generated secret and an optional template binding. Inbound webhooks are rate-limited per name.

MethodPathAuthDescription
POST /api/admin/webhooks master token Create a webhook
GET /api/admin/webhooks master token List webhooks
DELETE /api/admin/webhooks/{name} master token Delete a webhook
POST /api/webhooks/{name} X-Webhook-Secret header Trigger webhook (custom or Uptime Kuma)
POST /api/webhooks/{name}/github X-Hub-Signature-256 Trigger from GitHub event (HMAC-validated)

Create Webhook

{ "name": "pr-review", "template_id": 5, // optional, link to a task template "target_agent": "rex", // optional, pin dispatch to agent "required_capability": "code_write", // default: research "priority": 1 } # Response includes the generated secret — store it, it won't be shown again { "name": "pr-review", "secret": "b4e3a8...", ... }

08 Admin

Admin endpoints manage users, tokens, audit logs, peer federation, and system analytics. Most require the master token or admin:* permissions.

Users

MethodPathDescription
POST/api/admin/usersCreate a user account
GET/api/admin/usersList users
PUT/api/admin/users/{id}Update user (role, password, enabled)
DELETE/api/admin/users/{id}Delete a user

Tokens

MethodPathDescription
POST/api/admin/tokensIssue an agent token (master only)
GET/api/admin/tokensList tokens (hashes redacted)
PUT/api/admin/tokens/{hash}/permissionsUpdate token permissions
DELETE/api/admin/tokens/{hash}Revoke a token
GET/api/admin/rolesList role presets and their permissions

Audit Log

MethodPathDescription
GET/api/admin/auditQuery audit log (?limit=, ?actor=, ?action=)

Analytics

PathDescription
/api/admin/analytics/tasksTask volume and completion rates over time
/api/admin/analytics/costsDaily Claude API cost breakdown
/api/admin/analytics/agentsPer-agent task counts and success rates
/api/admin/analytics/metacognitionConfidence score distribution
/api/admin/analytics/summaryHigh-level system summary

Federation Peers

MethodPathDescription
POST/api/admin/peersRegister a peer hub
GET/api/admin/peersList peer hubs
DELETE/api/admin/peers/{name}Remove a peer
POST/api/admin/peers/{name}/pingTest peer connectivity
POST/api/federation/tasksAccept a task relayed from a peer
GET/api/federation/statusFederation health and peer list

09 Health & SSE

Health Check

GET /api/health # Unauthenticated — returns minimal response (used by Docker healthcheck) { "status": "ok" } # Authenticated — returns full agent status map { "status": "ok", "subscribers": 3, "agents": { "rex": "online", "crucible": "busy", "archon": "online" } }

SSE Stream

Subscribe to GET /api/stream to receive all Commander events in real-time. The connection is long-lived; the Commander sends a keepalive event every 30 seconds and caps the per-subscriber queue at 500 events.

GET /api/stream Authorization: Bearer <token> Accept: text/event-stream # Events emitted by the hub event: task_queued // task created, no worker yet event: task_assigned // dispatcher assigned task to agent event: task_updated // worker reported progress event: agent_registered // new agent joined event: agent_status // agent came online or went offline event: message // new chat message event: keepalive // sent every 30s to prevent timeout

Live task stream

POST /api/tasks/{id}/stream lets a worker push incremental output chunks. Clients subscribed to /api/stream receive each chunk as a task_stream event, enabling a live console view of long-running Claude tasks.

10 Worker Setup

mesh-worker.py is a zero-dependency Python script (stdlib only) that polls the Commander for assigned tasks and executes them via the Claude CLI. It self-registers on startup and sends a heartbeat every 30 seconds.

Requirements

  • Python 3.9+ (no third-party packages — stdlib only)
  • Claude CLI installed and authenticated (claude --version must work)
  • A valid worker or agent token from POST /api/admin/tokens
  • Network access to the Commander URL

CLI Reference

python mesh-worker.py [options] --hub Hub base URL (env: M3SHDUP_HUB, default: http://localhost:8333) --token Bearer token (env: M3SHDUP_AGENT_TOKEN or M3SHDUP_TOKEN) --name Agent name (env: M3SHDUP_AGENT_NAME, default: worker) --machine Hostname label (env: M3SHDUP_MACHINE, default: system hostname) --max-concurrent Parallel task limit (env: M3SHDUP_MAX_CONCURRENT, default: 1) --capabilities Space-separated caps (env: M3SHDUP_CAPABILITIES, default: research) --excluded Caps to refuse (env: M3SHDUP_EXCLUDED) --claude-timeout Seconds per task (env: M3SHDUP_CLAUDE_TIMEOUT, default: 1800) --agent-mode Use claude -p (tools) (env: M3SHDUP_AGENT_MODE)

Capabilities

Capabilities are free-form strings. The Commander routes tasks to N0D3S whose capability list contains the task's required_capability. The N0D3 also has per-capability timeout overrides:

CapabilityDefault TimeoutDescription
research30 minGeneral research, analysis, writing
web_search30 minInternet search tasks
code_write30 minCode generation and refactoring
file_ops15 minFile system operations
git15 minGit commits, PRs, rebases
docker15 minContainer builds and restarts
deploy15 minVPS deploys and config changes

Agent Mode vs. Print Mode

Without --agent-mode, the N0D3 runs claude --print <prompt> — Claude responds with text only. With --agent-mode, it runs claude -p <prompt> --dangerously-skip-permissions — Claude has full tool access (file reads, bash, etc.) and streams output chunks back to the Commander in real time.

Agent mode security

--dangerously-skip-permissions means Claude can run arbitrary shell commands on the worker machine. Only use agent mode on machines you trust and where the Claude CLI is the primary operator. Never run agent mode as root.

Failure & Escalation

When a task fails, the N0D3 retries up to 3 times. On the third failure it calls POST /api/escalations to notify the Commander and posts a message to the general channel. The Commander operator can then investigate via the audit log or reassign the task.

Running as a Service

# /etc/systemd/system/mesh-worker.service [Unit] Description=M3SHD Mesh Worker After=network.target [Service] User=ubuntu WorkingDirectory=/home/ubuntu/m3shdup Environment=M3SHDUP_HUB=https://mesh.demobygrit.com Environment=M3SHDUP_AGENT_TOKEN=m3shd_ag_... Environment=M3SHDUP_AGENT_NAME=rex Environment=M3SHDUP_CAPABILITIES=research,code_write,git Environment=M3SHDUP_AGENT_MODE=true ExecStart=/usr/bin/python3 mesh-worker.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable --now mesh-worker sudo journalctl -u mesh-worker -f

11 Pi Deployment

A Raspberry Pi 5 makes an effective edge N0D3 — low power, always-on, Tailscale-connected. The setup below produces a headless N0D3 that starts automatically on boot.

1 — Flash Pi OS Lite

Use Raspberry Pi Imager to flash Pi OS Lite 64-bit (no desktop). In the advanced settings, configure your SSH key, hostname, and Wi-Fi credentials before writing. This avoids needing a monitor at any point.

# From your Mac — verify the Pi is up after first boot ssh pi@raspberrypi.local # Update everything first sudo apt update && sudo apt upgrade -y

2 — Install Tailscale

curl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up --authkey=tskey-auth-... # The Pi now has a stable 100.x.x.x address reachable from any Tailscale node

3 — Install Claude CLI

# Install Node.js first (Claude CLI requires it) curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - sudo apt install -y nodejs # Install Claude CLI npm install -g @anthropic-ai/claude-code # Authenticate — follow the browser prompt claude /login

4 — Deploy the Worker

git clone https://github.com/ArchforgeDev/m3shdup /home/pi/m3shdup cd /home/pi/m3shdup # Create the environment file cat > /home/pi/.mesh-worker.env <<EOF M3SHDUP_HUB=https://mesh.demobygrit.com M3SHDUP_AGENT_TOKEN=m3shd_ag_... M3SHDUP_AGENT_NAME=pi-node-01 M3SHDUP_MACHINE=raspberrypi5 M3SHDUP_CAPABILITIES=research,file_ops M3SHDUP_MAX_CONCURRENT=1 M3SHDUP_AGENT_MODE=true EOF chmod 600 /home/pi/.mesh-worker.env

5 — Create systemd Service

sudo tee /etc/systemd/system/mesh-worker.service <<EOF [Unit] Description=M3SHD Mesh Worker After=network-online.target tailscaled.service Wants=network-online.target [Service] User=pi WorkingDirectory=/home/pi/m3shdup EnvironmentFile=/home/pi/.mesh-worker.env ExecStart=/usr/bin/python3 mesh-worker.py Restart=always RestartSec=15 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now mesh-worker sudo journalctl -u mesh-worker -f

Verify

Once the N0D3 starts, hit GET /api/agents on the Commander — the Pi node should appear as online within a few seconds of the service starting.

12 Security

Authentication

All API endpoints require Authorization: Bearer <token>. Three token types exist:

TypeHow CreatedScope
Master token M3SHDUP_TOKEN env var Full access — all endpoints, all agents
Agent token POST /api/admin/tokens RBAC-scoped — only what the role allows
Session cookie POST /api/login (browser) User-scoped, for the web dashboard

Agent tokens are stored as SHA-256 hashes in the database — the raw token is shown only once at creation time. Tokens are prefixed m3shd_ag_ and are 32 hex chars of entropy (128 bits).

RBAC Roles

Named roles are permission bundles. The master token can also issue tokens with a custom explicit permission list.

RolePermissions
worker tasks:read, tasks:execute, tasks:update, messages:read, messages:write, agents:read, agents:register, memory:read, memory:write, escalations:create
mobile Same as worker minus escalations:create
commander tasks:read, tasks:create, messages:read, messages:write, agents:read, escalations:read, admin:templates, pipelines:create
admin All permissions

Empty permissions = no access

A token with an empty permission list has zero access beyond basic auth. This is intentional — there is no implicit "read-all" grant. Every capability must be explicitly listed in the token's permission set.

Encryption at Rest

Task prompts, task outputs, and agent memory values are encrypted at rest using Fernet symmetric encryption (AES-128-CBC + HMAC-SHA256). The encryption key is derived from M3SHDUP_ENCRYPT_KEY. If that variable is not set, the system falls back to M3SHDUP_TOKEN — set a dedicated key in production.

The system supports key rotation via M3SHDUP_LEGACY_ENCRYPT_KEY. Rows encrypted under the old key are re-encrypted on next read using a MultiFernet approach.

Token Budgets

The Commander enforces a per-day Claude API cost cap configurable via M3SHDUP_DAILY_COST_CAP (default $5.00). Task creation and webhook dispatch return HTTP 429 when the cap is reached. N0D3S also track per-agent daily token usage, capped at 500k tokens/day per agent.

Audit Log

Every write operation — token creation, task creation, memory writes, agent registration, user changes — is appended to an immutable audit log. Query it via GET /api/admin/audit with optional ?actor= and ?action= filters.

Kill Switch

The kill switch halts all new task creation instantly without taking the Commander offline. Activate via the admin dashboard or directly via the kill switch API. Existing running tasks complete; no new tasks are accepted until the switch is cleared.

13 Configuration

The Commander is configured entirely via environment variables. Set them in a .env file at the project root — Docker Compose picks it up automatically.

Variable Required Default Description
M3SHDUP_TOKEN yes Master API token. All Commander management operations require this.
M3SHDUP_ENCRYPT_KEY recommended falls back to TOKEN Fernet encryption key for prompts, outputs, and memory. Use a random 32-char string.
M3SHDUP_LEGACY_ENCRYPT_KEY no Old key for transparent re-encryption during key rotation.
M3SHDUP_DAILY_COST_CAP no 5.00 USD daily Claude API spend ceiling. Task creation fails with 429 when hit.
M3SHDUP_NTFY_URL no https://ntfy.gritwerk.com ntfy server URL for push alerts (agent down, cost budget, escalations).
M3SHDUP_NTFY_TOPIC no mediaserver-b0c64542 ntfy topic name.
M3SHDUP_ALERTS_ENABLED no true Set to false to disable all ntfy notifications.
M3SHDUP_DEMO_ENABLED no false Enable the read-only demo endpoints (/api/demo/*) with synthetic data.

Worker Environment Variables

Variable Default Description
M3SHDUP_HUBhttp://localhost:8333Hub base URL
M3SHDUP_AGENT_TOKENAgent bearer token (preferred over M3SHDUP_TOKEN)
M3SHDUP_AGENT_NAMEworkerAgent name used for registration
M3SHDUP_MACHINEsystem hostnameMachine identifier label
M3SHDUP_MAX_CONCURRENT1Max parallel Claude invocations
M3SHDUP_CAPABILITIESresearchComma-separated capability list
M3SHDUP_EXCLUDEDComma-separated capabilities to refuse
M3SHDUP_CLAUDE_TIMEOUT1800Per-task Claude timeout in seconds
M3SHDUP_AGENT_MODEfalseSet to true to use claude -p with tool access

Database

SQLite WAL database at data/m3shdup.db relative to the project root. Mount ./data as a Docker volume to persist across container rebuilds. WAL mode is enabled on startup for concurrent read performance.

14 Webhook Integration

Custom Webhooks

Create a webhook via the admin API, then send a POST to /api/webhooks/{name} with the webhook secret in the X-Webhook-Secret header (or ?secret= query param).

# 1. Create webhook curl -X POST https://your-hub/api/admin/webhooks \ -H "Authorization: Bearer <master-token>" \ -d '{"name": "deploy-alert", "required_capability": "deploy"}' # Response includes generated secret { "name": "deploy-alert", "secret": "b4e3a8..." } # 2. Trigger from any system curl -X POST https://your-hub/api/webhooks/deploy-alert \ -H "X-Webhook-Secret: b4e3a8..." \ -d '{"title": "Deploy check", "prompt": "Verify the staging deployment is healthy."}'

GitHub Integration

Point a GitHub webhook at /api/webhooks/{name}/github. The Commander validates the X-Hub-Signature-256 HMAC header using the webhook secret. Supported events: pull_request, issues, push.

# GitHub repo settings → Webhooks → Add webhook Payload URL: https://mesh.demobygrit.com/api/webhooks/pr-review/github Content type: application/json Secret: <webhook secret from admin API> Events: Pull requests, Issues, Pushes # On PR open, the hub creates: # title: "Review PR #42: Add caching layer" # prompt: "GitHub PR #42 was opened: 'Add caching layer'..."

Uptime Kuma Integration

Uptime Kuma payloads are auto-detected on /api/webhooks/{name}. Point a Kuma monitor notification at the webhook URL with the secret in the header. The Commander parses the heartbeat and monitor fields and dispatches an alert task automatically.

# Uptime Kuma: Notification → Webhook URL: https://mesh.demobygrit.com/api/webhooks/kuma-alert Request headers: X-Webhook-Secret: <secret> # On monitor DOWN, hub creates: # title: "Alert: my-service is DOWN" # prompt: "Uptime Kuma alert — monitor 'my-service' changed to DOWN..."

Federation

Two M3SHD Commander instances can exchange tasks via federation. Each Commander registers the other as a peer with a shared token. The sending Commander calls POST /api/federation/tasks on the receiving Commander; the receiving Commander validates the federation token (constant-time compare), enforces the daily cost cap, and dispatches the task locally. Relay chains are capped at 3 hops to prevent loops.

# Register a peer on Hub A curl -X POST https://hub-a/api/admin/peers \ -H "Authorization: Bearer <master-token>" \ -d '{"name": "hub-b", "url": "https://hub-b.example.com", "token": "shared-secret"}' # Register Hub A as a peer on Hub B (same shared-secret) curl -X POST https://hub-b/api/admin/peers \ -H "Authorization: Bearer <master-token>" \ -d '{"name": "hub-a", "url": "https://hub-a.example.com", "token": "shared-secret"}'