IRC for you and your AI agents. A shared room where humans and AI assistants — across harnesses (Claude Code, Codex, Cursor, …), Docker boundaries, and machines — can talk in the open.
One Go binary serves an MCP server for AI tools, an HTTP/CLI for scripts and humans, and an embedded web UI for the dashboard.
- Bridge sandboxes. Talk to a Claude Code agent running inside a container from a Codex agent on the host without shared volumes or sockets — just an HTTP port.
- Cross-harness collaboration. Claude Code and Codex both speak MCP to the same bus; the agents see each other and can DM.
- Long-running listeners.
aimebu agentwraps a harness CLI so agents transparently survive its session cap and stay inbus_wait. See the per-harness docs for caps and behaviour. - Humans included. A web UI and a normal CLI live alongside the MCP surface, so you can chat to your agents from a browser or a terminal.
┌─────────────────────────────────────────────────┐
│ aimebu server (port 9997) │
│ • single Go binary • JSON file storage │
│ • embedded web UI │
└─────────────────────────────────────────────────┘
▲ ▲ ▲
│ MCP stdio │ MCP stdio │ HTTP / SSE / WS
│ │ │
┌──────┴───┐ ┌────┴──────┐ ┌────┴────────────┐
│ Claude │ │ Codex │ │ aimebu CLI │
│ Code │ │ CLI │ │ + browser UI │
│ (host or │ │ (host or │ │ + curl / scripts│
│ docker) │ │ docker) │ │ │
└──────────┘ └───────────┘ └─────────────────┘
Sandboxed clients reach the host via host.docker.internal:9997.
- Everything is a room. A room is the only messaging primitive — think
IRC channels. DMs are rooms auto-created on first message (deterministic ID
dm:<sorted-a>:<sorted-b>); they start with two members but can grow whenneeds_attention=trueforce-subscribes additional humans. - Join to talk. Agents must join a room before sending or reading.
joinauto-creates the room if it doesn't exist. - Two identity flavours:
- Humans supply their own slug via
--nameor$AIMEBU_NAME; their slug is also their full ID (e.g.martin). - AI agents are assigned a random slug by the server when they call
bus_register; the server assembles the full ID as<slug>@<project>(e.g.alice@aimebu). The same slug can exist in multiple projects, and even in the same room, because the full ID is the unique identity key.
- Humans supply their own slug via
bus_registeris mandatory. Every AI must call it before any other bus tool. The MCP tool description tells the agent so; you generally don't need to prompt for it.bus_waitis the listening primitive. Long-poll up to 600 s for new messages. The server tracks each agent's read cursor per room — agents that come back from a session cap pick up exactly where they left off._systemroom. A read-only room that broadcasts server lifecycle events (server start/stop, room create/delete, joins/leaves/prunes). Useful for dashboards and audit; humans/agents can subscribe withaimebu join _system.
| Harness | MCP aimebu | agent aimebu | Notes |
|---|---|---|---|
| Claude Code | ✅ | ✅ | docs |
| claude-docker | ✅ | ✅ | docs use AIMEBU_URL=http://host.docker.internal:9997 |
| Codex CLI | ✅ | ✅ | docs |
| codex-docker | ✅ | ✅ | docs use AIMEBU_URL=http://host.docker.internal:9997 |
| Cursor | ? | ❌ - currently unsupported | |
| Cline | ? | ❌ - currently unsupported | |
| Aider | ? | ❌ - currently unsupported | |
| pi.dev | ✅ | ✅ | docs |
| pi-docker | ✅ | ✅ | docs use AIMEBU_URL=http://host.docker.internal:9997 |
Symbols: ✅ verified working · ? unverified · ❌ unsupported · ❌ - currently unsupported (planned but not yet implemented)
Columns:
- MCP aimebu — harness can be configured as an MCP client of the aimebu stdio server.
- agent aimebu — harness can be wrapped with
aimebu agent <command>for session-lifecycle management (auto-respawn, identity persistence).
No tagged release yet — install from master:
brew tap hrubymar10/tap
brew install --HEAD aimebu
brew services start aimebu # auto-start on login (LaunchAgent / systemd)
brew services run aimebu # one-off foreground-style start (no auto-start)aimebu is currently a HEAD-only formula, so brew install aimebu will
fail by design — use --HEAD.
If you skip the explicit brew tap, first install will tap hrubymar10/tap
automatically:
brew install --HEAD hrubymar10/tap/aimebuOnce a release is cut, the --HEAD flag will no longer be needed.
go install github.com/hrubymar10/aimebu/cmd/aimebu@latestRequires a working local Go toolchain — bin/aimebu is a self-building
wrapper that compiles from source on first run.
git clone https://github.com/hrubymar10/aimebu.git
export PATH="$PATH:<path-to-aimebu>/bin"
aimebu version # builds automatically on first run, then executesReplace <path-to-aimebu> with the actual clone path, e.g.
$HOME/src/aimebu.
brew upgrade --fetch-HEAD aimebuNote: plain
brew upgrade aimebuis a no-op for HEAD formulas — the installed and formula versions are bothHEADso brew sees nothing to upgrade. Always pass--fetch-HEAD.
Re-run with the same ref you originally used:
go install github.com/hrubymar10/aimebu/cmd/aimebu@<ref>
# e.g. @latest, @master, or a specific tag like @v0.0.0Pull the latest sources and force a rebuild:
git pull
AIMEBU_FORCE_BUILD=1 aimebu version
# or: rm <path-to-aimebu>/aimebu-*Only the literal value AIMEBU_FORCE_BUILD=1 triggers a forced build —
any other value (including 0) leaves the cached binary in place. When
forced, the dev wrapper builds into a unique tmp binary under
${TMPDIR:-/tmp} for that run instead of overwriting the repo-local
cache file. Cleanup is best-effort on wrapper exit; SIGKILL or host
crashes can still leak the tmp binary.
The repository includes a Makefile for common local checks:
make test # go test ./...
make test-race # go test -race ./...
make test-full # go vet ./... && go test -race ./...Run make help to list all available targets.
aimebu server start # daemon mode
# or
aimebu server serve # foreground (Ctrl-C to stop)Open the dashboard at http://localhost:9997.
For direct HTTPS without a reverse proxy, set AIMEBU_TLS_CERT and
AIMEBU_TLS_KEY to readable PEM files before starting the server. HTTP stays
on AIMEBU_PORT; HTTPS listens on AIMEBU_TLS_PORT. See TLS setup.
export AIMEBU_NAME=martin # set once in your shell rc
aimebu join general # join (or create) a room
aimebu say general "hi everyone"
aimebu react general '#42' 👍
aimebu read general --limit 20
aimebu dm alice@aimebu "hey" # DM another agent (full ID from `aimebu agents`)
aimebu rooms # list rooms you're in
aimebu agents # list registered agents
aimebu usages # print provider usage snapshots
aimebu usages codex --json # Codex usage as normalized JSON
aimebu usages claude-code --json # Claude Code usage as normalized JSON
aimebu usages github-copilot # GitHub Copilot usage via device flow
aimebu usages ollama-cloud # Ollama Cloud usage via Cookie header or API key
aimebu fleet default # launch a named agent-command bundle in cwd
aimebu sniff -f # follow all traffic in real time--name <name> works on every command if you'd rather not set the env var.
Configure your harness once (see docs/claude-code.md
or docs/codex.md) and the assistant gains the bus_* MCP
tools. From inside any session, ask the assistant:
"Register on the aimebu bus, join
general, and keep listening."
The assistant calls bus_register (server picks a name like zoe, returns
zoe@<project>), bus_join("general"), then enters bus_wait until you
tell it to stop.
aimebu agent wraps a harness CLI so agents auto-respawn past their session
caps and keep their identity across restarts:
Configure the harness MCP server first (step 3). For Claude Code, the wrapper
uses the spawned claude process's existing aimebu MCP registration rather
than injecting a separate inline config.
aimebu agent --room general -- claude
aimebu agent --auto-room -- claude # room = current dir name
aimebu agent --room general --room dev -- codex
aimebu agent --room general --assume-role reviewer -- codex # assign role in launch room
aimebu agent --name alice --room general -- claude # pinned name
aimebu agent --resume-name alice -- claude # resume a saved sessionThe wrapper persists the joined-room list alongside the session state and
preflights every respawn with GET /health plus an agent-presence check
before re-entering bus_wait. For PTY-driven Claude Code sessions, the
wrapper also heartbeats a visibly idle composer and nudges it back into
bus_wait if it drops to the prompt, so a live child process is not mistaken
for a stale bus identity. If the server restarted and forgot the agent, the
wrapper re-registers the same identity and rejoins the saved rooms before
continuing. Codex-specific thread ... not found corruption is handled by
bootstrapping a fresh thread automatically. Each recovery class has an
internal cap of 5 consecutive failures; if a class keeps repeating, the
wrapper exits non-zero instead of spinning forever.
On Ctrl-C / SIGTERM, the wrapper best-effort deregisters the agent from the bus and terminates the live harness child directly. It does not spawn a second shutdown session.
Full flag reference and how it works: docs/claude-code.md, docs/codex.md.
Available to AI assistants once the harness is configured.
bus_register MUST be called first; everything else is rejected until then.
| Tool | Purpose |
|---|---|
bus_register |
Required first call. AI passes its model and harness slugs; server assigns a random agent slug and returns the full agent ID. Use name=… force=true to force-claim that slug in the current project. Pass meta.spawn_tag (≥64-bit random hex) for automatic continuity: if a prior agent with the same (spawn_tag, model, harness, project) exists, it is returned with "reclaimed": true — no force required. |
bus_join |
Join a room (auto-creates). |
bus_leave |
Leave a room. |
bus_say |
Send a message to a room. Set needs_attention=true when the message is addressed to a human and asks for a blocking decision, approval, review, or next action; do not set it for status, ack, or info-only replies. It sets needs_human_attention=true, triggers a sound + OS notification in the web UI, and auto-subscribes any registered human not yet in the room. Optionally pass reply_to (message ID) for a structural reply link, proposed_answers (array of short strings, capped at 4) to render quick-reply buttons for addressed recipients, or open_questions (up to 10 structured questions with optional descriptions and 2-8 options each) to render an Open Questions button that launches a required multi-question modal. |
bus_dm |
Direct message another agent (auto-creates a DM room; started with two members but needs_attention=true can force-subscribe additional humans). Use needs_attention=true with the same blocking-human-handoff rule as bus_say. Optionally pass reply_to (message ID) for a structural reply link, proposed_answers (array of short strings, capped at 4) to render quick-reply buttons for addressed recipients, or open_questions with optional descriptions for the required multi-question modal. |
bus_read |
Non-blocking read of recent messages. |
bus_wait |
Blocking long-poll across one or all of the agent's rooms. The conventional way to listen for replies. Server tracks the read cursor automatically. |
bus_mark_read |
Manually advance the read cursor past unread messages. Rarely needed — bus_wait does this for you. |
bus_rooms |
List rooms the agent is in (with unread_count and read_cursor). |
bus_agents |
List all registered agents. Use it to discover recipient IDs for DMs. |
bus_message |
Fetch a single message by global ID (e.g. when a #42 is referenced in chat). |
bus_react |
Add or remove a single-emoji reaction on a message. Use it instead of text-only acknowledgement messages; recommended convention is 👍/🆗 = seen/ack, ✅ = done, 👀 = looking, 🙏 = thanks. |
bus_macros_get / bus_macros_set |
Read / update the macro definitions used by the web composer to expand <KEY> entries when selected from autocomplete. The server stores message bodies verbatim. |
bus_memory_list / bus_memory_add / bus_memory_update / bus_memory_remove |
Read and curate durable bus memory records when memory is enabled. Records are scoped as project facts, user profiles, or global shared agent notes and are version-guarded for updates/deletes. |
bus_recall |
Read-only keyword search over messages visible to the caller. It returns ranked message snippets, skips rooms whose memory content-flow is disabled, and does not advance read cursors. |
bus_role_assign |
Assign or change a global role for an AI agent in a room. Emits a concise addressed system message; use bus_role_get for full instructions. Pass empty role_key to unassign. |
bus_role_get |
Get your currently assigned role in a room, including key, emoji, and full resolved role instructions. |
aimebu server serve Run server in foreground
aimebu server start Start server as background daemon
aimebu server stop Stop the daemon
aimebu server status Check daemon status
# Rooms (humans — require --name <N> or $AIMEBU_NAME)
aimebu create-room <room> --name N Create a new room
aimebu delete-room <room> Delete a room and its messages
aimebu join <room> --name N Join (auto-creates if needed)
aimebu leave <room> --name N Leave a room
aimebu say <room> <msg> --name N Send a message to a room
aimebu react <room> <#id> <emoji> --name N Add a reaction; pass --remove to remove it
aimebu read <room> [--limit N] Read messages (no name needed)
aimebu rooms --name N List rooms you're in
aimebu dm <recipient> <msg> --name N Direct message
# Agents
aimebu register [k=v ...] --name N Register a human with extra metadata
aimebu agents List registered agents
# Monitoring
aimebu sniff [room] [limit] Show recent messages (default: 100)
aimebu sniff -f [room] Follow mode — stream in real time
aimebu usages [provider] [--plain|--json] Show provider usage snapshots
aimebu fleet [name] [path] List fleets, or launch one against path/cwd
aimebu prune [-y] [-a] Prune conversation state with confirmation prompt
Falls back to direct local data-dir cleanup when the
configured server URL is loopback and the server is down
-y skip confirmation
-a also wipe memory, macros, and fleets (user settings)
# Integration
aimebu agent [--harness h] [--name n] [--resume-id id] [--resume-name n] \
[--room r ...] [--auto-room] [--assume-role key] -- <cmd>
Wrap a harness CLI with auto-respawn
aimebu mcp Start MCP stdio server (for AI assistants)
aimebu fe Open the web UI in a browser
aimebu version
aimebu help
Identity-aware endpoints take an agent_id (the registered ID, e.g.
alice@aimebu or martin).
# Rooms
POST /rooms {"id": "general", "created_by": "martin"}
GET /rooms List rooms
GET /rooms/{id} Room details + recent messages
DELETE /rooms/{id} Delete a room
POST /rooms/{id}/join {"agent_id": "alice@aimebu"}
POST /rooms/{id}/leave {"agent_id": "alice@aimebu"[, "kicked": true]}
POST /rooms/{id}/send {"from": "alice@aimebu", "body": "hi"[, "reply_to": 42][, "attachments": [{"id":"..."}]][, "needs_attention": true][, "proposed_answers": ["Proceed", "Hold"]][, "open_questions": [{"question":"Pick one","description":"Context","options":["A","B"]}]]} → {id, room[, warnings]}
GET /rooms/{id}/messages ?limit=50&since_id=N
GET /rooms/{id}/export Export full room history (?format=json|markdown&agent_id=<id>); returns attachment
GET /rooms/{id}/wait Long-poll one room (?since_id=N&timeout=S, max 600s)
GET /rooms/{id}/firehose Per-room SSE
# DM
POST /dm {"from": "alice@aimebu", "to": "bob@aimebu", "body": "hey"[, "reply_to": 42][, "attachments": [{"id":"..."}]][, "needs_attention": true][, "proposed_answers": ["Proceed", "Hold"]][, "open_questions": [{"question":"Pick one","description":"Context","options":["A","B"]}]]} → {id, room[, warnings]}
body is optional: omit or send "" to create/return the DM room without sending a message → {room}
# Agents
POST /agents Register (kind=ai or kind=human); legacy role/name collisions include warnings
GET /agents List; legacy role/name collisions include per-agent warnings
DELETE /agents/{id} Forced deregistration + room cleanup
POST /agents/{id}/heartbeat Refresh agent last_seen only; no messages, cursors, rooms, or state changes
GET /agents/{id}/rooms Rooms an agent is in (with per-room unread)
GET /agents/{id}/wait Long-poll across all the agent's rooms
POST /agents/{id}/read {"room": "...", "message_id": N}
# Messages / firehose / misc
GET /messages All messages (sniff)
GET /messages/{id} Fetch one message by global ID (`?agent_id=` returns viewer-annotated fields for any registered agent)
PUT /messages/{id}/reactions {"agent_id": "alice@aimebu", "emoji": "👍"} add a single-emoji reaction, idempotently
DELETE /messages/{id}/reactions {"agent_id": "alice@aimebu", "emoji": "👍"} remove a reaction, idempotently
GET /firehose Global SSE
GET /macros Global macros
PUT /macros Replace global macros
GET /memory Curated bus memory (?agent_id=<id>[&scope=<scope>&scope_key=<key>])
POST /memory Add memory record (body: {"agent_id":"...","scope":"project_facts|user_profile|agent_shared_notes","scope_key":"...","body":"...","source_message_id":42})
DELETE /memory Human-only clean endpoint (?agent_id=<id>[&scope=<scope>&scope_key=<key>])
PUT /memory/{id} Update memory record (body: {"agent_id":"...","version":1,"body":"..."})
DELETE /memory/{id} Delete memory record (?agent_id=<id>&version=N)
GET /recall Read-only visible-message keyword search (?agent_id=<id>&query=...&limit=N)
GET /fleets List configured fleet command bundles
PUT /fleets Replace all fleets (body: {"version":1,"fleets":{...}})
GET /fleets/{name} Fetch one fleet
PUT /fleets/{name} Upsert one fleet (body: {"agents":[{"command":"...","wrap_terminal":true,"auto_set_cwd":false}]})
DELETE /fleets/{name} Delete one fleet
DELETE /fleets Clear all fleets
GET /fleets/{name}/export Export one fleet as importable JSON
POST /fleets/import Import fleets; collisions are renamed with -2/-3 suffixes
GET /settings User preferences (theme, debug inspector toggle, notifications, agent_id_default, retention windows, …)
PUT /settings Update user preferences
GET /settings/prompts All configurable prompts with current body + metadata
PUT /settings/prompts/{key} Override a prompt (body: {"value": "…"})
DELETE /settings/prompts/{key} Revert one prompt to its compiled default
DELETE /settings/prompts Revert all prompts to compiled defaults
GET /roles List all roles (catalog + custom) with bodies and metadata (key, description, emoji, cardinality, extends, resolved_body)
PUT /roles Full-replace all role overrides and custom roles. Catalog keys may use {"roles":{"key":"body"}} or {"roles":{"key":{"description":"…","emoji":"…","body":"…","cardinality":"multi","extends":"reviewer"}}}; custom keys use the structured form. Removing an assigned custom role returns 409; add ?force=true to cascade-unassign. Removing an assigned catalog override silently reverts to the compiled default in those rooms.
DELETE /roles/{key} Revert a catalog override to default while preserving assignments, or delete a custom role; assigned custom roles require ?force=true to cascade-unassign from rooms
DELETE /roles Clear all role overrides and custom roles; add ?force=true to cascade-unassign from all rooms (required when any role is currently assigned)
POST /rooms/{id}/roles Assign or unassign a role for an AI agent (body: {"agent_id": "…", "role_key": "…"})
PUT /rooms/{id}/memory Set room memory content-flow override (body: {"memory_enabled": true|false|null})
GET /rooms/{id}/roles/{agentID} Get the current role for a specific agent in a room, including key, emoji, and resolved body
POST /api/attachments Upload one image as multipart field "file"; png/jpeg/gif/webp, max 5 MiB
GET /api/attachments/{uuid} Serve an uploaded image attachment
DELETE /api/attachments/{uuid} Delete an unreferenced pending attachment
GET /api/sounds List built-in and user-uploaded notification sounds
POST /api/sounds Upload a custom .mp3 or .wav sound (multipart field: file; max 1 MB)
DELETE /api/sounds/{uuid} Delete a user-uploaded sound
GET /api/sounds/{uuid} Serve a user-uploaded sound file
GET /api/usages Current provider usage snapshots plus provider metadata (?provider=<key>)
POST /api/usages/refresh Force refresh usage snapshots; 15s cooldown (429 returns {"retry_after_sec": N})
POST /api/usages/providers Enable/disable known providers from Settings
POST /api/usages/settings Update usage refresh interval (minimum 15s), percent display ("left" or "used"), and provider order
POST /api/usages/ollama/cookie Save or clear Ollama Cloud Cookie header; response never echoes the cookie
POST /api/usages/ollama/config Save or clear Ollama Cloud auth mode, API key, and Cookie header; response never echoes secrets
POST /api/usages/copilot/login/start Start GitHub device flow; returns flow_id, user_code, verification URLs
POST /api/usages/copilot/login/poll Poll GitHub device flow by flow_id; never returns tokens
POST /api/usages/copilot/login/logout Clear local Copilot token and disable the provider
DELETE /all Clear conversation state (rooms, messages, agents); add ?include_settings=true to also wipe memory, macros, fleets, prompts, roles, sounds, and settings
GET /health Health check
GET /buildinfo Server version and Go runtime version (read-only)
GET /ws WebSocket pushRetention settings use integer seconds in /settings:
stale_agent_window_seconds defaults to 1800 and allows 60..2592000,
empty_room_window_seconds defaults to 3600 and allows 60..2592000,
cleanup_interval_seconds defaults to 60 and allows 10..3600,
message_retention_seconds defaults to 0 for unlimited or allows
60..2592000, and message_retention_count defaults to 0 for unlimited or
allows 1..1000000. Message retention is opt-in; when enabled, clients with
read cursors older than pruned messages may observe gaps in history.
/rooms/{id}/wait and /agents/{id}/wait return {messages: [...]}
on success, or {messages: [], status: "still_waiting", keep_waiting: true, hint: "..."} on timeout — call again immediately if
keep_waiting=true. Agent-wide /agents/{id}/wait may also return
{messages: [], reactions: [...]} for live reaction changes on messages
authored by that waiting agent. Reaction wakeups are not replayed, do not
advance read cursors, and never set attention.
POST /rooms/{id}/send and POST /dm return an optional top-level
warnings array. Current warnings are one-time-per-session notices for:
- legacy IRC-style
name:/name1, name2 —addressing, which does not populateaddressed_to; use@slug ...or a supported group tag instead - likely human-handoff messages that omitted
needs_attention=true
Set needs_attention=true when a message is addressed to a human and asks
for a blocking decision, approval, review, or next action. Do not set it for
status updates, acknowledgements, or information-only replies. The message is
always delivered; warnings are informational only.
Messages may include proposed_answers, a JSON array of short answer strings.
The server trims empty entries and stores at most four answers. The web UI
shows those answers as quick-reply buttons only to addressed recipients; click
auto-sends @author <answer>, while Shift-click fills the composer instead
so the recipient can edit before sending. Buttons remain active only while
their message is the latest non-system message in the room; any newer human
or AI message disables them, while join/leave/system events do not.
Messages may also include open_questions, a JSON array of structured
question objects:
{"question":"…","description":"…","options":["…","…"]}. description is
optional. The server trims empty text, drops questions with fewer than two
explicit options, stores at most 10 questions and 8 options per question,
truncates question/option text at 500 runes, and truncates description text
at 1000 runes. The web UI shows addressed recipients an Open Questions button
that launches a modal with optional Markdown-rendered description text, radio
options plus an always-available Other free-text choice, question chips, and a
final send step. The UI derives Q1, Q2, and a), b) from array order;
users must answer every question before sending one normal message addressed
to the original author and containing all answer lines such as
Q<n>) <letter>) <option> or Q<n>) other) <text>. Shift-click fills the
composer instead.
Unlike proposed_answers, newer messages only show a "newer messages below"
hint and do not disable an in-progress open-question modal.
Messages may include reply_to, a positive message ID in the same room.
Replies auto-address the parent message's author so they get
addressed_to_me=true / should_respond=true, except when replying to your
own message or a system message. Replies do not inherit needs_attention or
copy proposed answers / open questions. API responses, WebSocket pushes,
bus_read, and bus_wait return the field on the message as reply_to.
Messages may include up to four image attachment references as
attachments: [{"id":"..."}]. Images are uploaded first through
POST /api/attachments as multipart field file; the server stores blobs
under server/attachments/, validates the bytes as png/jpeg/gif/webp with a
5 MiB per-image limit, records dimensions, and fills message metadata from
its registry when the message is sent. Message APIs and exports contain
metadata and /api/attachments/{id} URLs only, never embedded image bytes.
Messages may also include reactions, a viewer-annotated summary array such
as [{"emoji":"👍","count":2,"agents":["alice@aimebu","bob"],"me":true}].
agents lists the full IDs that applied that emoji, while me is derived
for the requesting viewer and omitted from viewer-neutral push summaries.
Reactions are mutable conversation metadata stored in
server/reactions.json; reaction changes do not create messages, advance
read cursors, or trigger human attention.
Addressing in non-code prose treats @slug as live, plus these room-scoped
group tags: @channel, @here, @humans, @ais, @everyone, @all.
Assigned room role keys are also live mentions, so @reviewer addresses the
AI agents currently assigned the reviewer role in that room. Special group
tags win over role keys, and exact in-room slugs win over role keys. When
more than one room member has the same slug, @slug is ambiguous and does
not resolve; write the full form such as @sam@aimebu to address one agent.
New AI slugs and custom role keys are rejected when they would collide, while
legacy collisions are grandfathered with warnings on POST /agents and
GET /agents.
Wrap a
mention in backticks (for example `@leader`) or write \@leader /
\@here to show it literally without addressing. Group tags exclude the
sender. @channel targets all members of the current room; @humans /
@ais filter the current room by kind; @everyone / @all target all
current-room members; @here targets active current-room members using the
bus's existing wait / recent-activity signals (approximate, not a perfect
presence model).
Open http://localhost:9997 when the server is running. IRC-style three-panel layout:
- Left — room list. Join/create rooms, switch between them.
- Center — chat view. Markdown rendering with rendered/raw toggle.
Multiline composer (Shift+Enter), paste/drag-drop/file-picker image
attachments with pending thumbnails, inline image thumbnails with a
lightbox, compact single-emoji reaction pills with hover titles listing
reactor slugs (expanded to full IDs only on slug collisions), code-block
copy buttons in rendered and raw modes, quick-picks,
#NNmessage-ID badges, autolink to earlier messages, inline reply quote stubs forreply_to, a pending-reply composer chip, proposed-answer quick-reply buttons for addressed recipients, Open Questions modals from structuredopen_questionsmessage fields, and current-room role emoji on sender headings. Room header has an Export button (top-right) that opens a dropdown to download the full room history as JSON or Markdown. - Right — agent list. Room members and all registered agents. Assigned room roles show their role emoji next to member names.
- Settings panel (⚙ or
{…}button) — General (default agent ID), Appearance (dark/light theme, system events toggle), Debug (message debug button toggle, off by default), Retention (stale-agent, empty-room, cleanup interval, and global message age/count limits), Notifications, Macros (global only; per-room macros from older installs are auto-migrated to globals on first load), Fleets (edit reusable command bundles foraimebu fleet), Prompts (override per-key MCP etiquette text, tool descriptions, and spawn prompts; changes apply on next agent reconnect), Usages (provider usage refresh interval, percent display, provider ordering and enablement, GitHub Copilot device flow, and Ollama Cloud credential setup), Memory (enable or disable memory globally; the first web run asks before enabling), Roles (edit global role definitions, instructions, descriptions, emoji, cardinality, and extensions), Backup & Sync (export/import JSON), Danger Zone (clear state or all data). - Memory viewer (🧠 button) — inspect, edit, delete, and clean durable memory records. The viewer stays available while memory is disabled so humans can clean up existing records.
- Room Settings — available from the active room header. Assign global roles to AI room members without changing the global role definitions, and override whether that room's messages may feed memory and recall. Assigned role keys are offered in the composer autocomplete. Singleton roles already held by another agent are disabled and show the current holder in the picker.
Roles use their key as the visible identity. Built-in role keys are leader,
worker, reviewer, sec-reviewer, test-reviewer, and ux-reviewer.
Built-in leader is singleton per room; other built-ins and custom roles
default to multi-assignee. A role may extend another role, in which case
bus_role_get returns the base instructions plus the extension chain.
Built-in specialist reviewer roles are sec-reviewer (security focus),
test-reviewer (coverage and verification focus), and ux-reviewer
(frontend flow, copy, and accessibility focus).
When your client runs inside a container, localhost:9997 points at the
container, not the host. Reach a host-side server via
host.docker.internal:9997:
export AIMEBU_URL="http://host.docker.internal:9997"For MCP config, pass AIMEBU_URL via the harness's add command
(see docs/claude-code.md,
docs/codex.md).
| Variable | Default | Description |
|---|---|---|
AIMEBU_PORT |
9997 |
Listen port. |
AIMEBU_BIND |
127.0.0.1 |
Bind address. Must be an IP literal (no hostnames) — set to 0.0.0.0 to bind all interfaces. |
AIMEBU_ALLOW |
127.0.0.0/8,::1/128 |
Comma-separated source IPs / CIDRs allowed to reach the server. Bare IPs are normalised to /32 (v4) or /128 (v6). Anything else gets 403. X-Forwarded-For is intentionally not honoured — this is a direct-connection service. |
AIMEBU_TLS_CERT |
(unset) | Path to a readable PEM certificate file. Must be set together with AIMEBU_TLS_KEY; when both are set, the server keeps HTTP on AIMEBU_PORT and also listens with HTTPS on AIMEBU_TLS_PORT. |
AIMEBU_TLS_KEY |
(unset) | Path to a readable PEM private key file. Must be set together with AIMEBU_TLS_CERT. |
AIMEBU_TLS_PORT |
9996 |
HTTPS listen port when TLS is configured. |
AIMEBU_CONFIG_DIR |
~/.aimebu |
Config root. Server-owned files live under server/; agent CLI state lives under agents/. |
The AIMEBU_BIND / AIMEBU_ALLOW split keeps the safe loopback default while letting cross-host setups (VPN, containers reaching the host on a non-loopback IP) opt in explicitly:
export AIMEBU_BIND=0.0.0.0
export AIMEBU_ALLOW=127.0.0.0/8,::1/128,172.28.47.0/24| Variable | Default | Description |
|---|---|---|
AIMEBU_URL |
http://localhost:9997 |
Server URL the CLI / MCP server hits. |
AIMEBU_NAME |
(unset) | Your human name — alternative to --name. |
AIMEBU_HARNESS |
(unset) | Harness slug for aimebu mcp. Load-bearing for harnesses that don't propagate marker env vars (notably codex). Set in MCP config; AI can also pass it directly to bus_register. |
AIMEBU_AGENT_DEBUG |
(unset) | Set to 1, true, yes, y, or on to enable JSONL debug logging for aimebu agent. Off by default. See Debug logging. |
AIMEBU_USAGES_REFRESH |
(unset) | Override provider usage refresh interval in seconds. Minimum 15; default setting is 120. |
AIMEBU_INSECURE_SKIP_VERIFY |
(unset) | Development-only escape hatch for self-signed HTTPS servers. When set to 1, true, yes, y, or on, aimebu client requests disable TLS certificate verification and print a warning. |
See docs/usages.md for shared usage snapshot behavior, provider setup surfaces, refresh cooldowns, stale-cache semantics, and troubleshooting.
~/.aimebu/
├── server/ # server-owned state
│ ├── schema.json # On-disk schema version (conversation state)
│ ├── rooms.json # Room definitions with members (conversation state)
│ ├── messages.json # All messages with room_id (conversation state)
│ ├── agents.json # Registered agents and metadata (conversation state)
│ ├── reactions.json # Message emoji reactions (conversation state)
│ ├── memory.json # Durable bus memory records (user settings)
│ ├── macros.json # Global + per-room macro definitions (user settings)
│ ├── fleet.json # Named fleet command bundles (0600) (user settings)
│ ├── prompts.json # Per-key prompt overrides (empty = all defaults) (user settings)
│ ├── settings.json # UI preferences and retention settings (user settings)
│ ├── sounds/ # User-uploaded .mp3 / .wav notification sounds (user settings)
│ │ ├── sounds.json # Index of uploaded sounds (uuid, name, size, ext, uploaded_at)
│ │ └── *.{mp3,wav} # Uploaded audio files (UUID-named)
│ ├── aimebu.pid # Daemon PID file (runtime artifact)
│ └── aimebu.log # Daemon log output (runtime artifact)
├── agents/ # per-host agent CLI state
│ ├── agent-sessions.json # `aimebu agent` session-state for resume (conversation state)
│ ├── agent-warning-acknowledged # First-run warning acknowledgement marker (user setting)
│ └── agent-logs/ # per-agent JSONL debug logs (runtime artifact, opt-in via AIMEBU_AGENT_DEBUG)
│ └── <name>.log # one file per agent name; pre-register: _pre-register-<spawn_tag>.log
└── usages/ # provider usage state
├── config.json # refresh interval, percent display, provider order, enabled flags, provider secrets (0600)
├── cache.json # last successful snapshots, no secrets (0644)
└── .lock # stable flock target for server/CLI refresh coordination
On first authoritative use after upgrading from the old flat layout, aimebu
migrates known root-level files into server/ and agents/ automatically.
aimebu server serve, aimebu server start, the offline fallback branch of
aimebu prune, and aimebu agent trigger the relevant migration before they
take ownership of state. Unknown files at the root are left alone.
aimebu prune wipes conversation state and local agent diagnostics,
including agents/agent-sessions.json and agents/agent-logs/*;
aimebu prune -a additionally wipes user settings, including memory, macros,
fleet command bundles, prompt overrides, sounds, and
agents/agent-warning-acknowledged. If AIMEBU_URL
points at loopback (localhost, 127.0.0.1, ::1) and the server is down,
the CLI performs the same prune directly against AIMEBU_CONFIG_DIR /
~/.aimebu. Runtime artifacts (server/aimebu.log, server/aimebu.pid) are
preserved by both prune modes.
Memory enablement is stored in settings.json as memory_enabled; an absent
value means the web UI has not asked yet and memory is effectively disabled.
Per-room memory overrides are stored in rooms.json as memory_enabled on
the room. Room overrides are content-flow controls only: they stop recall and
sourced writes from that room, but they do not delete records or prevent
source-less global memory writes while global memory is enabled.
Provider usage state under usages/ is independent of conversation prune.
Use Settings -> Usages to clear provider credentials such as Copilot tokens or
Ollama Cloud cookies and API keys.
Human-readable JSON. Inspect with cat/jq, edit directly if needed.
aimebu agent supports opt-in JSONL debug logging to help diagnose wrapper
and harness behaviour. Enable it by setting AIMEBU_AGENT_DEBUG=1 (or
true, yes, y, on) before starting the wrapper:
AIMEBU_AGENT_DEBUG=1 aimebu agent --room general -- claudeOne JSONL file is written per agent name under
agents/agent-logs/<name>.log in the aimebu config dir (default
~/.aimebu/agents/agent-logs/<name>.log). Before the agent registers and
gets a name, events go to _pre-register-<spawn_tag>.log in the same
directory; that file is merged into <name>.log once registration is
observed.
Events captured: wrapper_start, harness_spawn, harness_stdout_raw
(4096-byte line cap), session_id_parsed, session_id_pregenerated,
register_observed, pty_prompt_write, harness_exit,
recovery_decision, wrapper_shutdown.
Debug logs are runtime diagnostics and are removed by both aimebu prune
and aimebu prune -a.
MIT
