We've been spawning dozens of Claude Code sessions a day across every Renkara repository — building new features, fixing bugs, writing specs, provisioning infrastructure. Each session lives in a terminal, posts its progress to stdout, and vanishes when the work is done. If you want to know what an agent is doing right now, you have to be at the keyboard staring at its transcript. If you want to course-correct mid-task, you have to interrupt the session, type a new prompt, and hope you didn't break something. There had to be a better way to work with agents. So we built Chirp.
Chirp is a self-hosted team chat platform with full Slack feature parity: channels, DMs, threads, mentions, reactions, search, files, voice and video huddles. But its defining feature isn't any of that. It's the part nobody else has built yet — a first-class, bidirectional bridge between the chat platform and every Claude Code session you spawn.
The Bridge
When you start a Claude Code session, a session_init hook fires before the model does anything. That hook calls Chirp, which provisions a dedicated thread inside your #agents channel, returns a session token, and writes the session identity to ~/.claude/state/<id>/chirp.json. From that moment on, every PreToolUse and PostToolUse hook streams a compact status update into the thread: "Running Bash: migrate the webhook retry table...", "Done. Exit 0." When sub-agents spawn inside the session, their events attach to the same thread with a sub-agent name prefix. You see the full agent tree live.
That part alone is useful. You can kick off a long refactor, go make coffee, and glance at your phone to see the session is still running, what it's doing right now, and what it's accomplished so far. But the killer feature is the other direction.
You can reply in the thread. From anywhere.
"Also make sure the migration is idempotent." "Don't touch the beacon schema." "Skip the test suite and just deploy." Your message lands in the session's inbox. The next hook invocation polls the inbox, receives your message, and emits it to stdout wrapped in a <chirp-inbox from="@user">...</chirp-inbox> block. The Claude Code harness injects that block as a system reminder into the model's next turn. The running agent sees your guidance and acts on it — without the session ever being interrupted, without you ever opening a terminal.
This single capability changes the relationship between the human and the agent. The agent is no longer a fire-and-forget process. It's an ongoing collaboration you can steer asynchronously. Every running task becomes a thread you can contribute to, mid-flight, from whichever device happens to be near you.
Starting tasks from your phone
Replying to a running session is one half of the story. The other half is starting one from a device that isn't your laptop.
The obvious answer — run the agent on Chirp's server — is a dead end. A server-hosted agent has no access to your filesystem, your local dev servers, your AWS profile, your Claude Code hooks, or the state of your working tree. It can query fleet MCPs and generate text (that's Chirpy's lane), but it can't edit webhook.py, can't run pytest, can't git push. For real engineering work, the agent must execute where your code actually lives.
So Chirp ships a small daemon for your Mac. chirp-agent-listener runs as a launchd LaunchAgent, long-polls the queue endpoint, and spawns claude -p "<task>" in the right repo whenever a task is queued from anywhere else:
- You're on the couch. Open Chirp on your phone, hit "Start new task", type "fix the webhook retry idempotency bug", select
project: chirp. - Chirp writes a queued-task row and creates a new
#agentsthread with statusQUEUED. - Your Mac — sitting on the desk across the room, lid closed, on battery — polls the queue every few seconds. It picks up the task, looks up
project: chirpin~/.chirp/projects.jsonto findcwd: ~/AVIAN/tools/chirp, and spawnsclaude -p "fix the webhook retry idempotency bug"in that directory. - The normal
session_inithook fires. The thread flips fromQUEUEDtoRUNNING. Events stream. You steer from the phone via the bidirectional inbox. You fall asleep. The Mac finishes the task and posts the final summary.
The daemon token is scoped narrowly — agent:queue.read + agent:queue.ack. It can dequeue tasks and mark them started. It can't post events, mint session tokens, or read messages. It's machine-bound (hardware UUID + hostname) so a stolen token fails immediately on any other device. No inbound ports on the Mac, no tunnels, no ngrok — outbound HTTPS only.
For sensitive projects, you can flip a requires_confirmation: true switch in projects.json. The daemon then posts a "react 👍 to run" prompt in the thread before spawning, so a fat-fingered task at 2am doesn't auto-deploy something.
The key insight: the phone isn't where the agent lives. It's just a remote control. The agent still runs with your local context, your credentials, your tools, your filesystem — it just started its life because you pressed a button somewhere else.
Chirpy
The other AI in Chirp lives in the workspace itself. Chirpy is a bot user in every workspace with a direct connection to Claude 4.7 and MCP access to every fleet tool you're allowed to use. You invoke Chirpy by @Chirpy or /ask, and a thread becomes a multi-turn conversation with an assistant that can read the channel, summarize, act across the fleet, and confirm destructive operations via reaction.
Some things Chirpy does every day at Renkara:
- "@Chirpy summarize the last 50 messages in
#incidents" — returns a tight brief with links to the original messages. - "@Chirpy catch me up on
#engineeringsince 9 AM" — narrative summary of what the team has been doing. - "@Chirpy what action items came out of this thread?" — posts a numbered checklist with inferred owners.
- "@Chirpy create a Docket card for this bug on the Chirp board with Kei as assignee" — drafts a card, asks for thumbs-up confirmation, files it.
- "@Chirpy prep me for my 3pm with Sloane" — pulls Cadence event details, Tribe profile, recent Docket activity, relevant Codex pages, and composes a briefing card.
Destructive operations (deleting messages, archiving channels, sending invoices, publishing content) are always confirmed via 👍 reaction before they execute. Chirpy never holds more permission than the user who invoked it.
Fleet Integration Is the Point
Chirp is built to be the nervous system of the rest of our tool fleet. Every Renkara tool (Docket, Fulcrum, Meridian, Herald, Beacon, Tribe, Cadence, Courier, Narrative, Pulse, Vigil, Codex, Slate, Trellis) connects in both directions.
- Docket cards can be created from any message via action menu or
/docket. Card status changes post back to a channel. Card URLs unfurl into rich cards. - Meridian time can be logged directly from a thread (
/time <project> <minutes>). Invoice status changes fire to#finance. - Herald newsletters can be drafted from an entire thread's content.
- Beacon experiments post results to a channel when they conclude.
- Cadence events can be scheduled from a DM: "Find time with Charles this week" queries both calendars and proposes slots.
- Courier acts as email fallback if you DM someone who has been offline in Chirp for more than 24 hours.
- Vigil incidents route by severity: SEV1 and SEV2 to
#alerts, SEV3 to#noise. Incident state changes update the existing thread in place. - Chronicle receives every log, metric, and trace Chirp emits; its pre-built dashboard shows live agent sessions, inbox injection rates, webhook queue depth, and integration circuit-breaker state.
- Slate embeds Chirp chat inline for shared lists. When you share a Slate project to a Tribe contact, Chirp auto-provisions a scoped channel, mints a guest session for the contact, and the chat renders inside Slate via an iframe. Every checklist item can have its own thread. The guest never needs a Chirp account.
The Embed Surface
The Slate integration required a new primitive: a way for external fleet tools to render Chirp chat inside their own UI, for users who don't have Chirp accounts, scoped strictly to a specific channel. We built it as a guest session system. A fleet tool with a service token can POST /api/v1/guest-sessions with a Tribe contact ID and a target channel; Chirp provisions (or reuses) a guest user bound to that contact and returns a short-lived session token. The tool embeds chirp.renkara.com/embed/channel/<id>?session=<token> in an iframe; the widget renders a sidebar-less message view that authenticates its own WebSocket. A postMessage contract keeps theme, unread counts, and composer state synchronized with the parent.
When the Slate share is revoked, Chirp revokes the guest session: the WebSocket closes within two seconds, tokens invalidate, and the embed shows a clean "session ended" state. No orphaned chats, no leaked tokens.
The same primitive will power future integrations. Any fleet tool that wants chat about a specific record — a Meridian invoice, a Beacon experiment, a Docket card — can embed a Chirp thread and invite Tribe contacts to participate without any of them needing an account.
Observability, by Design
Chirp is instrumented for Chronicle from the first line of code. Structured JSON logs with trace, span, workspace, user, channel, and session context; Prometheus metrics for every subsystem (messages per second, WebSocket frames, active agent sessions, inbox pending depth, webhook DLQ depth, circuit-breaker states); OpenTelemetry traces spanning request entry through service layer, database, NATS publish, and outbound integration calls. Agent session hooks reattach their spans to the session's root span via a trace carrier persisted in the session token record — so a full agent lifecycle, including nested sub-agents, visualizes as one trace in Chronicle.
Vigil monitors are registered at deploy time by a CodeDeploy post-deploy hook that runs a provisioning script against the Vigil API. Thresholds, probes, and alert routing are committed to the repo. Fresh deploys are monitored from minute one.
Bug reports go through @avian/bug-reporter-react. Every authenticated page has a <BugReportButton> in the top bar with Cmd/Ctrl+Shift+B as a shortcut. Submissions file Docket cards on the Chirp board with the current route, Zustand store snapshot (sensitive keys redacted), last 50 console lines, and an optional html2canvas screenshot.
Why Not Slack
We evaluated staying on Slack. The arguments for keeping it were real — it works, everyone knows how to use it, the integration ecosystem is broad. The arguments against were more real. Slack charges per-seat and we're growing. Slack's API has rate limits we were already hitting during synthesis pipelines. Slack's bot model requires external apps we don't want to manage. Slack has no agent-session concept at all — you can post to a channel from a script, but there's no primitive for a persistent bidirectional thread bound to an agent identity with inbox semantics. And Slack's data is on Slack's servers.
Chirp is ours. The code is ours, the data is ours, the rate limits are ours to set, and the roadmap answers to what we actually need.
Technical Specifications
| Component | Detail |
|---|---|
| Frontend | React 19, Vite 6, TypeScript 5.6, @avian/design-system, Zustand, React Query, react-virtuoso |
| Backend | FastAPI, SQLAlchemy 2.0 (async), asyncpg, FastMCP |
| Database | PostgreSQL with pg_vector extension for semantic message search |
| Cache | Valkey 8 (presence, typing indicators, unread counters) |
| Real-time | WebSocket primary with SSE fallback; NATS JetStream for cross-pod fan-out |
| Storage | S3 (chirp-attachments) with KMS encryption and lifecycle tiering |
| Huddles | LiveKit (self-hosted) with Whisper transcription |
| Auth | mTLS → service tokens → JWT via auth-service → bot tokens → guest sessions → magic-link |
| MCP Tools | 69 tools at launch across 13 categories |
| Remote trigger | chirp-agent-listener — Python daemon on the user's Mac (launchd LaunchAgent) that long-polls a queued-task endpoint and spawns claude -p in the right repo. Machine-bound daemon tokens scoped to agent:queue.read + agent:queue.ack only; outbound HTTPS only, no inbound ports. |
| Observability | Chronicle (logs + metrics + traces) + Vigil (monitoring + alert routing) + Bug Reporter (Docket) |
| Ports | Frontend 3440, Backend 3441, LiveKit 3442 |
| Theme | Light and dark mode with system preference detection |
| Deployment | AWS ECS Fargate, CloudFront frontend, ALB-fronted API, shared RDS + ElastiCache |
Chirp is part of Renkara's internal tool fleet. It ships alongside Docket, Fulcrum, Beacon, Herald, Meridian, Trellis, Narrative, Codex, Cadence, Courier, Tribe, Pulse, Vigil, Chronicle, Envoy, and Slate. Every tool in the fleet follows the same architectural conventions: FastAPI backends, React 19 frontends, PostgreSQL storage, Valkey caching, MCP integration for AI-native workflows, and Chronicle-native observability.
Full spec and build plan live at tools/chirp/docs/ in the AVIAN monorepo.