Broca is an agent action log and natural language narrator. It records what every agent in your system did, then translates raw events into human-readable narratives so you can read your stack like a story. Subscribe it to Axon to ingest events automatically, log actions directly via HTTP, or ask plain-English questions about recent activity.
- Port: 5000
- Stack: Node 22, libsql (SQLite-compatible embedded database)
- Stores every agent action with agent, service, action type, and arbitrary payload
- Generates template-based narratives synchronously on ingest, LLM-based on demand
- Subscribes to Axon channels and ingests events automatically via webhook
- Exposes a human-readable activity feed across all agents
- Answers natural language questions about recent activity via
/ask - Tracks aggregate stats: total actions, narration coverage, top services/agents/actions
docker run -d \
--name broca \
-p 5000:5000 \
-e BROCA_API_KEY=your-secret-key \
-e DB_PATH=/data/broca.db \
-v broca-data:/data \
ghcr.io/ghost-frame/broca:latestWithout BROCA_AUTH=disabled, all authenticated endpoints require Authorization: Bearer <BROCA_API_KEY>. The /ingest webhook endpoint is intentionally unauthenticated so Axon (or any upstream source) can push events to it -- gate it at the network layer if needed.
A minimal browser UI is served at / for inspecting the action log live.
| Variable | Default | Description |
|---|---|---|
PORT |
5000 |
Port to listen on |
HOST |
0.0.0.0 |
Bind address |
DB_PATH |
broca.db |
Path to the libsql database file |
BROCA_API_KEY |
(none) | Bearer token required for authenticated requests |
BROCA_AUTH |
(required) | Set to disabled to skip auth entirely (development only) |
CORS_ALLOW_ORIGIN |
(none) | Value for the Access-Control-Allow-Origin response header |
AXON_URL |
(none) | If set, subscribes to Axon at this URL on startup |
AXON_API_KEY |
(none) | Bearer token for Axon subscribe calls |
SELF_URL |
http://localhost:$PORT |
Public URL Axon uses to call back to /ingest |
- Action -- a single recorded event with
agent,service,action, andpayload - Narrative -- a human-readable sentence describing the action, generated from a template on ingest or via LLM on demand
- Feed -- the chronological activity stream with narratives filled in
- Ask -- natural language query that scans recent actions and returns a synthesized answer
Returns service status and aggregate stats.
{
"status": "ok",
"version": "0.1.0",
"total": 1042,
"narrated": 1042,
"by_service": [{ "service": "memory", "count": 502 }],
"by_agent": [{ "agent": "claude-code", "count": 320 }],
"by_action": [{ "action": "memory.store", "count": 410 }]
}Log a single action directly. Authenticated.
Request
{
"agent": "claude-code",
"service": "memory",
"action": "memory.store",
"payload": { "content": "...", "tags": ["session"] }
}Response 201
{
"id": 1042,
"agent": "claude-code",
"service": "memory",
"action": "memory.store",
"payload": { "content": "...", "tags": ["session"] },
"narrative": "claude-code stored a new memory",
"axon_event_id": null,
"created_at": "2026-03-22T12:00:00Z"
}A template-based narrative is generated synchronously when one exists for the action type.
Query recent actions. Authenticated.
Query params
agent-- filter by agent nameservice-- filter by service nameaction-- filter by action typesince-- ISO8601 timestamp lower boundnarrated_only--trueto return only actions with a narrativelimit-- default50, max500offset-- pagination offset
Response 200 -- array of action objects.
Get a single action by ID. Authenticated.
Get the narrative for a single action. Generates one via LLM if no template matched on ingest, then persists it. Authenticated.
Response 200
{
"id": 1042,
"narrative": "claude-code stored a new memory tagged session",
"action": "memory.store",
"agent": "claude-code",
"created_at": "2026-03-22T12:00:00Z"
}Bulk-narrate a batch of actions via LLM. Max 50 IDs per call. Authenticated.
Request
{ "ids": [1042, 1043, 1044] }Response 200
[
{ "id": 1042, "narrative": "..." },
{ "id": 1043, "narrative": "..." }
]Human-readable activity stream. Auto-fills missing narratives from templates. Authenticated.
Query params
agent-- filter by agent namesince-- ISO8601 timestamp lower boundlimit-- default20, max100offset-- pagination offset
Response 200
[
{
"id": 1042,
"narrative": "claude-code stored a new memory",
"agent": "claude-code",
"service": "memory",
"action": "memory.store",
"created_at": "2026-03-22T12:00:00Z"
}
]Natural language query over recent actions. Authenticated.
Request
{ "question": "What did claude-code do in the last hour?" }Response 200 -- synthesized answer with cited action IDs.
question is capped at 2000 characters.
/ask works without an LLM configured. When LLM_URL is unset, or the LLM call
fails, Broca falls back to keyword routing to pick a service and returns a plain
deterministic summary of the result rather than failing the request. The response
shape is the same either way, so the endpoint always answers 200 unless the
downstream service call itself fails.
Receives Axon webhook events. Unauthenticated by design -- protect at the network layer. Set AXON_URL to have Broca subscribe to Axon on startup and receive events automatically.
Request (Axon event shape)
{
"id": 9001,
"channel": "memory",
"source": "kleos",
"type": "memory.store",
"payload": { "...": "..." },
"created_at": "2026-03-22T12:00:00Z"
}Response 200
{ "ok": true }Returns the same aggregate counts as /health.
Broca is one piece of a larger agent infrastructure. Sister services:
- axon -- pub/sub event bus
- chiasm -- task coordination dashboard
- loom -- workflow orchestration
- soma -- agent registry and heartbeats
- thymus -- output evaluation and quality scoring
Broca runs standalone -- no upstream service is required -- but pairs naturally with Axon for zero-config event ingestion.