Inter-agent IPC message queue for claude-code-event-listeners. SQLite-backed channels with per-agent watermarks, Ed25519 identity verification, HTTP long-poll, CLI mode, and optional ngrok exposure.
# From the marketplace (recommended)
claude plugin marketplace add mividtim/claude-code-el-ipc
claude plugin install el-ipc
# Or manually
git clone https://github.com/mividtim/claude-code-el-ipc.git- Python 3.6+
cryptographypackage for identity verification:pip install cryptography- claude-code-event-listeners plugin
- ngrok (optional, for remote agent communication)
Without identity verification, any agent can send and read messages:
# Start the server
python3 sources.d/ipc-server.py serve
# Send a message (CLI — no server needed for local agents)
python3 sources.d/ipc-server.py send my-channel agent-a "Hello from Agent A"
# Read messages
python3 sources.d/ipc-server.py read my-channel agent-b
# Long-poll for new messages
python3 sources.d/ipc-server.py read my-channel agent-b --waitIdentity verification uses Ed25519 asymmetric keys. Security activates automatically when the first operator is seeded — all messages must then be signed.
The operator is the admin who authorizes agents. Run this once per deployment:
python3 sources.d/ipc-server.py seed my-operator > operator.key
chmod 600 operator.keySave the private key file. It cannot be recovered. The public key is stored in the database.
For each agent that will send messages:
python3 sources.d/ipc-server.py keygen --out herald
# Creates: herald.key (private, chmod 600) and herald.pub (public)The operator authorizes each agent by signing their registration:
python3 sources.d/ipc-server.py register herald \
--pubkey herald.pub \
--as my-operator \
--key operator.key \
--identity "a9e6db87-b090-47e0-a509-2bc624896d0f"The --identity flag is optional — use it for UUIDs, descriptions, or any secondary identifier.
Once secure mode is active, all sends require the sender's private key:
# CLI (direct DB)
python3 sources.d/ipc-server.py send my-channel herald "Hello" --key herald.key
# HTTP
curl -X POST http://localhost:9876/send \
-H 'Content-Type: application/json' \
-d '{"channel":"my-channel","sender":"herald","content":"Hello","signature":"<base64>"}'For HTTP, the signature is base64(Ed25519_sign(private_key, "channel\nsender\ncontent")).
python3 sources.d/ipc-server.py agentsAgent A ---[CLI: direct SQLite write]---> ipc.db <---[CLI: direct read]--- Agent B
^
Agent C ---[HTTP POST /send]---> ipc-server.py ---[HTTP GET /messages?wait=true]--- Agent D
|
(ngrok)
|
Remote ---[HTTPS POST /send]--------'
- Local agents can read/write the SQLite DB directly via CLI (no server needed)
- Remote agents use HTTP endpoints through the server
- Hybrid — local CLI writes + remote HTTP reads work together on the same DB
| Endpoint | Method | Description |
|---|---|---|
/send |
POST | Send a message. Body: {channel, sender, content, signature?} |
/register |
POST | Register an agent. Body: {name, public_key, operator, operator_key, identity?} |
/messages |
GET | Read messages. Params: channel, agent, wait=true (long-poll, 30s) |
/channels |
GET | List all channels with message counts |
/agents |
GET | List registered agents (names, roles, identities) |
/health |
GET | Status + pending count + secure mode flag |
/.well-known/agent-card.json |
GET | A2A Agent Card — identity, capabilities, DID:key |
The system uses Ed25519 asymmetric cryptography:
- Operators are seeded with
seed— they authorize agent registrations - Agents are registered with
register— their public key is stored - Messages are signed with the sender's private key and verified against their stored public key
- Impersonation is impossible — signing as "herald" with a different key is rejected
- Progressive security — the system runs in open mode until the first operator is seeded
Messages are signed over the canonical payload channel\nsender\ncontent (newline-separated, UTF-8 encoded). The signature is base64-encoded Ed25519.
You can seed multiple operators. Each can independently register agents:
python3 sources.d/ipc-server.py seed ops-team-1 > ops1.key
python3 sources.d/ipc-server.py seed ops-team-2 > ops2.keyThe server hosts an Agent Card at /.well-known/agent-card.json, making it discoverable by other agents following the Agent2Agent Protocol.
# Via HTTP
curl http://localhost:9876/.well-known/agent-card.json
# Via CLI
python3 sources.d/ipc-server.py agent-cardIf an operator has been seeded, the card automatically includes a DID:key identifier derived from the operator's Ed25519 public key. Configure the card via environment variables:
IPC_AGENT_NAME="Herald" \
IPC_AGENT_DESC="Persistent bot with long-term memory" \
IPC_AGENT_URL="https://my-agent.ngrok.io" \
python3 sources.d/ipc-server.py serve| Variable | Default | Description |
|---|---|---|
IPC_DB_PATH |
/tmp/el-ipc.db |
SQLite database path |
IPC_PORT |
9876 |
HTTP server port |
IPC_API_KEY |
(none) | Legacy API key auth (pre-identity, still works alongside Ed25519) |
IPC_AGENT_NAME |
el-ipc agent |
Agent name in the A2A Agent Card |
IPC_AGENT_DESC |
(generic) | Agent description in the A2A Agent Card |
IPC_AGENT_URL |
http://localhost:{port} |
Public URL in the Agent Card (set to your ngrok URL) |
| Command | Description |
|---|---|
serve [port] |
Start HTTP server |
send <channel> <from> <msg> [--key <file>] |
Send a message |
read <channel> <agent> [--wait] |
Read unread messages |
channels |
List channels |
seed <name> |
Generate operator keypair, store in DB |
keygen [--out <prefix>] |
Generate Ed25519 keypair (utility) |
register <name> --pubkey <f> --as <op> --key <f> [--identity <s>] |
Register agent |
agents |
List registered agents |
agent-card |
Print A2A Agent Card JSON |
Listen for IPC messages as an event source:
event-listen.sh listen python3 sources.d/ipc-server.py read my-channel my-agent --waitTo allow remote agents to send messages:
ngrok http 9876 --subdomain my-ipc-serverRemote agents POST to https://my-ipc-server.ngrok.io/send with signed payloads.
- claude-code-event-listeners plugin
- Python 3.6+
cryptographypackage (pip install cryptography) for identity features- ngrok (optional, for remote communication)
MIT