Inter-user and human-to-agent messaging via org-workspace state machine.
- Team messaging: Send/receive messages between Datacore users
- Agent inbox:
@yourname-claudereceives tasks with full lifecycle governance - Task governance: Trust tiers, per-sender budgets, rate limiting, queue depth control
- org-workspace backed: All state in org-mode files under
org/messaging/ - Relay transport: Real-time delivery via configurable WebSocket relay
- Claude Code hook: Agent tasks surface on next prompt; Claude replies inline
- Python 3.10+
- org-workspace >= 0.3.0
- websockets, pyyaml, aiohttp, filelock
cd ~/Data/.datacore/modules
git clone https://github.com/datafund/datacore-messaging.git messaging
cd messaging
./install.shThe installer will:
- Install Python dependencies (see
requirements.txt) - Create
settings.local.yamlfrom template - Add Claude Code hook to
~/.claude/settings.json
Edit settings.local.yaml:
identity:
name: yourname # Your username (required)
messaging:
default_space: 1-datafund # Space where org/messaging/ lives
relay:
url: "wss://your-relay-host/ws" # Configurable relay endpoint
secret: "your-team-secret" # Shared secret for relay auth
trust_tiers: # Optional: override per-tier defaults
team:
daily_token_limit: 200000
unknown:
auto_accept: false
trust_overrides: # Optional: per-actor tier assignment
"tex@team.example.com": team
"stranger@other.com": unknownSee settings.local.yaml.example for all options.
All messaging state lives under the space's org/messaging/ directory:
{space}/org/messaging/
├── inbox.org # Universal inbox (all incoming messages)
├── outbox.org # Sent message log
└── agents/
└── {username}-claude.org # Agent task inbox (task state machine)
Messages are org-mode headings with property drawers. The agent inbox uses
org-workspace's state machine: WAITING → QUEUED → WORKING → DONE → ARCHIVED.
Known actors are stored in templates/contacts.yaml (copy to your space):
actors:
- id: "tex@team.example.com"
name: "Tex"
trust_tier: team
added: 2026-03-11Add actors by editing {space}/contacts.yaml directly, or set per-actor trust
overrides in settings.local.yaml under messaging.trust_overrides.
python3 datacore-msg.py send @gregor "Hey, can you review the PR?"
python3 datacore-msg.py send @tex-claude "Research competitor pricing"python3 datacore-msg.py inboxpython3 datacore-msg.py gui
# Or: ./start.shType in the input field:
| Command | Description |
|---|---|
/mine |
Show my unread messages |
/todos |
Show my TODO messages |
/tasks |
Show Claude task queue |
/context <id> |
Show thread context |
/online |
Show online users |
/status [val] |
Get/set status |
/relay |
Show relay connection info |
/clear |
Clear display |
/help |
Show available commands |
Claude Code doesn't maintain a persistent relay connection. Instead, a hook checks the agent inbox each time you submit a prompt.
WAITING -> sender submits a task request
QUEUED -> owner approves (auto-accept for trusted tiers)
WORKING -> Claude claims and begins execution
DONE -> execution complete, result posted
ARCHIVED -> owner confirms result
CANCELLED -> rejected or timed out at any stage
- Sender sends
@tex-claude do somethingvia GUI or CLI - Message stored in
{space}/org/messaging/agents/tex-claude.org - Governor checks trust tier — auto-accepts or holds for approval
- On next Claude Code prompt, hook surfaces the queued task
- Claude executes and replies via
hooks/send-reply.py
📬 New task for @tex-claude:
From @gregor (14:30) [QUEUED]:
Can you help debug the auth flow?
[msg-id: msg-20251212-143000-gregor]
---
To reply: hooks/send-reply.py <user> <message>
The governor enforces resource limits per sender before accepting tasks:
- Trust tiers:
owner,team,trusted,unknown - Daily token budgets: per-tier and global caps
- Rate limits: tasks per hour per actor
- Queue depth: max concurrent active tasks
- Auto-accept: trusted tiers bypass approval queue
Configure via trust_tiers and trust_overrides in settings.
# Reply to a sender
python3 hooks/send-reply.py gregor "Fixed! Check the PR."
# Reply to a specific message (creates thread)
python3 hooks/send-reply.py --reply-to msg-20251212-143000-gregor gregor "Follow-up"
# Mark task complete and reply
python3 hooks/send-reply.py --complete msg-20251212-143000-gregor gregor "Task done."# Approve a waiting task
python3 hooks/task-queue.py approve msg-20251212-143000-gregor
# Reject a task
python3 hooks/task-queue.py reject msg-20251212-143000-gregor
# Cancel a running task
python3 hooks/task-queue.py cancel msg-20251212-143000-gregorThe relay provides real-time delivery between team members. It is optional — the module works offline using the org-workspace store alone.
The relay server is implemented in lib/relay.py. TLS termination should be
handled by a reverse proxy (nginx, Caddy) in front of the relay process.
Run the relay:
RELAY_SECRET=your-secret python3 -m lib.relay --port 8080Or deploy with Docker (see fly.toml for Fly.io example):
echo "RELAY_SECRET=your-secret" > .env
docker build -t datacore-relay . && docker run -p 8080:8080 --env-file .env datacore-relayConfigure the URL in settings.local.yaml under messaging.relay.url.
datacore-msg.py # Unified CLI/GUI entry point
install.sh # Interactive installer
settings.local.yaml # Your settings (gitignored)
UPGRADING.md # Migration guide (v0.1.0 → v0.2.0+)
lib/
├── config.py # Settings, trust tiers, paths
├── message_store.py # org-workspace message CRUD
├── agent_inbox.py # Agent task state machine
├── governor.py # Task acceptance policy
└── relay.py # WebSocket relay server (consolidated)
hooks/
├── inbox-watcher.py # Claude Code hook (prompt check)
├── send-reply.py # Reply helper for Claude
├── mark-message.py # Mark messages read/todo/done
└── task-queue.py # Approve/reject/cancel tasks
templates/
└── contacts.yaml # Known actors template
MIT