An agent mesh control plane and dashboard server for coordinating local Claude Code sessions. Agents register, discover each other, and exchange messages through Drasill — using heimdall stdin injection for message delivery.
In Norse mythology, Yggdrasill is the world tree — an immense ash that connects the nine realms, carrying messages between gods, humans, and the dead through its branches and roots. The squirrel Ratatoskr runs up and down its trunk delivering messages (and insults) between realms.
Drasill is your local world tree. It connects Claude Code agents running in separate terminals, letting them discover each other, exchange tasks, and coordinate work — all through a single trunk. Each agent is a Ratatoskr, checking in at the tree for new messages whenever it finishes its current task.
The name drops the "Ygg-" prefix (meaning "terrible" or "dreadful") because there's nothing terrible about well-coordinated agents.
The mesh turns Drasill into a control plane for local Claude Code sessions:
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Claude Code │ │ Claude Code │ │ Claude Code │
│ (alpha) │ │ (beta) │ │ (gamma) │
└──────┬────────┘ └──────┬────────┘ └──────┬────────┘
│ │ │
│ register │ send msg │ inbox
│ send msg │ check inbox │ consume
│ check inbox │ │ send msg
│ │ │
└────────────────────┼────────────────────┘
│
┌───────┴────────┐
│ Drasill │
│ :8400 │
│ │
│ /mesh/* │
│ SQLite │
│ SSE + async │
└────────────────┘
- Register —
POST /mesh/agentsjoins the mesh with a name and optional session ID - Discover —
GET /mesh/agentslists all active agents - Message —
POST /mesh/agents/{name}/inboxsends a message (with optional heimdall wake) - Consume —
GET /mesh/agents/{name}/inboxreturns the oldest unread message - Pruning — Stale agents auto-transition: active → idle → dead → removed
All /mesh/* endpoints are localhost-only (middleware rejects non-loopback requests), so exposure via Cloudflare Tunnel or similar is safe.
A live-updating control panel showing agent status, recent messages, and a send-message form. SSE events trigger HTMX partial reloads, Alpine.js handles client-side interactivity. Web terminal gives browser access to agent pty sessions.
No plugins or hooks needed. Drasill injects mesh awareness into each agent's system prompt at launch and delivers messages via heimdall stdin injection when agents go idle. Agents communicate with peers using curl to the mesh API.
| Tool | Required | Purpose |
|---|---|---|
| uv | Yes | Python package management and virtualenv |
| heimdall (hm) | Yes | Rust pty supervisor — agents run inside heimdall sessions |
| Claude Code | Yes | The AI coding agent that Drasill coordinates |
| git | Yes | Used for session naming (repo + branch) and general dev |
| curl | Recommended | Agents use curl to communicate with the mesh API |
| Tailscale | Optional | Access Drasill from other devices on your tailnet |
# Verify prerequisites
uv --version && hm --version && claude --version && git --version && curl --version# Install dependencies
uv sync
# Start the server
uv run uvicorn drasill.app:create_app --factory --reload --port 8400
# Open the dashboard
open http://localhost:8400./deploy/install.shAgents are mesh-aware at launch — no setup needed. Send a message from any terminal:
curl -s -X POST "http://localhost:8400/mesh/agents/alpha/inbox" \
-H 'Content-Type: application/json' \
-d '{"from_agent": "human", "body": "Run the tests and report results"}'config.toml at the project root (or set DRASILL_CONFIG env var):
[server]
host = "0.0.0.0"
port = 8400
[db]
path = "~/.local/share/drasill/dash.db"
# Per-dashboard overrides
[dashboards.agent_mesh]
enabled = true
schedule = "5s"uv sync # install deps
uv run pytest # run tests (95% coverage gate)
uv run ruff check py_src py_tests # lint
uv run ruff format py_src py_tests # format
uv run nox -s all_checks # lint + format + typecheck + test150 tests, 96% coverage, zero lint warnings.
| Layer | Tech |
|---|---|
| Language | Python 3.13+ |
| Framework | FastAPI + uvicorn |
| Database | SQLite via aiosqlite |
| Realtime | SSE event bus + asyncio background tasks |
| Frontend | Jinja2 + HTMX + Alpine.js + Tailwind CDN |
| Validation | Pydantic v2 |
| Linting | Ruff (ALL rules) |
| Type checking | ty (strict) |
| Testing | pytest + pytest-asyncio, 95% coverage gate |
| Build | hatchling, managed by uv |
py_src/drasill/
├── app.py # FastAPI app factory + lifespan
├── config.py # TOML → Pydantic config loader
├── db.py # SQLite schema + init
├── claude.py # Async claude CLI wrapper
├── doctor.py # Dependency health checks
├── supervisor_client.py # Async heimdall socket client
├── mesh/
│ ├── router.py # /mesh/* API endpoints
│ ├── models.py # Pydantic request/response models
│ ├── events.py # SSE event bus (asyncio.Queue fanout)
│ ├── middleware.py # Localhost-only security middleware
│ ├── uploads.py # File attachment storage
│ └── pruning.py # Agent lifecycle management
├── terminal/
│ └── router.py # WebSocket heimdall proxy + session creation
├── templates/ # Jinja2 + HTMX templates
└── static/ # PWA assets + CSS
Apache 2.0