Built on Switch, by SandboxAQ

Take Switch away,and Ferri is five separate bots.

Every group on every app is a Switch room. Ferri is one Switch agent in all of them. Every approval is a Switch card, every message passes Switch Trust, and the Builder is a Switch-managed agent. Ferri has no code for any chat app: it speaks only to Switch.

  • One agent connection, five apps
  • No chat-app SDK in Ferri
  • Two additions to Switch core

The spine

Apps on one side. Ferri on the other. Switch in the middle.

Members and crew stay on their own apps. Switch turns each of their groups into a room and hands Ferri one stream of all of them. Ferri, the Builder and the crew's Desk and Pocket all work through it.

  • discord
  • telegram
  • slack
  • WhatsApp
  • mattermost
Switchthe spine
Rooms
Every chat on every app is one room. Ferri reads one stream.
Cards
Question and approval cards with buttons, answered where the crew already is.
Trust
Every message passes Shield, our Switch Trust endpoint, before it is relayed.
Templates
Desks and team rooms become real private channels.
Managed agent
The Builder: a Claude Code agent with permission cards.
Spending cap
Switch caps the Builder's tokens and turns per 24 hours; Ferri holds jobs until it resets.
Our two changes to Switch
Agent message edit
Announcements are edited in place on every platform.
WhatsApp bridge
WhatsApp groups through a linked device.
  • Ferrianswers, cards, Shield
  • Buildersite, posters, listings
  • CrewDesk and Pocket

Every Switch feature Ferri uses

What Ferri does with each part of Switch.

This is the table from docs/architecture.md, with the file in the repo where each one lives.

Switch Rooms and the agent protocol

  • Switch Rooms, bridged to Mattermost, Slack, Discord, Telegram and WhatsApp
    collaboration bridges

    Every channel becomes a room. One agent connection serves every platform, and one answer fans out to askers on four apps with native mentions.

    apps/ferri/src/flow.ts, rooms.ts

  • The always-on agent protocol
    SSE down, HTTP up

    Our own small TypeScript client: the event stream with a string cursor, heartbeat, typing, posting, ops, and catch-up after a restart.

    packages/switch

  • Addressing and follow_the_conversation

    Ferri tells a real mention from a follow-up, and runs a fast triage model on room chatter it was not addressed in.

    apps/ferri/src/mentions.ts, packages/knowledge/src/triage.ts

  • Gateway
    auth, rooms, agents, collaborations

    Desk and Pocket sign in with Switch accounts. Desk adds Ferri to rooms, discovers spaces and connects Shield. ./ferri connect adds platform connections.

    apps/desk, scripts/

Question and approval cards

  • Question cards
    /agent-sessions/{sid}/approvals

    Every step that needs a person is a card: unknown questions, replies waiting in Approve spaces, announcements, plan decisions, care cases, guest emails, social posts, desk packs, outreach emails and closing an event. The ladder (nudge, escalate, close) runs on them. Buttons on Slack, Discord and Telegram; a typed A1 "text" works everywhere, WhatsApp included.

    apps/ferri/src/loop.ts, launch.ts, care.ts, guests.ts, social.ts, desks.ts, wrap.ts

  • The card text limit

    Switch's text renderer shows at most 800 characters of a card's prompt. Ferri posts any longer prompt as a message just above the card, so long drafts stay answerable.

    packages/switch/src/client.ts (openQuestionsCard)

  • Answering a card as a person
    /gateway/agent-sessions/.../answer

    Desk and Pocket answer cards through Switch, under the signed-in organizer's name.

    apps/desk/lib/cards.ts

Switch Trust

  • Switch Trust
    /guardrails/check

    Shield is our own Trust endpoint inside the Ferri service. It blocks scam links and floods, redacts leaked secrets and alerts the crew, in 2 to 9 ms with no model in the path.

    packages/shield, apps/ferri/src/shield.ts

Room templates, documents and media

  • Room templates (Templates v2)
    create_room_from_yaml

    “Make team rooms” and “make a judging desk” create real channels on the platform, private or public, with the people asked for, Ferri and a kickoff, attached to the event at once.

    apps/ferri/src/launch.ts, desks.ts, packages/knowledge/src/desks.ts

  • Library documents

    The live knowledge is mirrored into a Switch library document attached to the event's rooms. The plan, the recap, the starter kit and every desk sheet are documents too, and follow every change.

    packages/knowledge/src/mirror.ts, apps/ferri/src/plan.ts

  • Agent media and read_context
    /agents/{id}/rooms/{room}/media

    Posters go out as native images on every platform after one OK. Ferri reads the Builder's files from the room with read_context.

    packages/switch/src/client.ts (sendMedia), apps/ferri/src/builder.ts

Managed agents

  • Agent management and the headless agent controller
    create_agent

    Ferri creates its own Builder through Switch: a managed Claude Code agent on a Switch agent controller on the organizer's machine. Switch starts a session whenever it is addressed and gives it the Switch tools over MCP.

    scripts/builder-setup.ts, apps/ferri/src/builder.ts

  • Handing work to a coding agent
    invite_agent_to_room, send_targeted_message

    Ferri puts the Builder in the crew room and hands each job over as a targeted message bound to that room. The Builder reports back to Ferri the same way.

    packages/switch/src/client.ts (roomOp), apps/ferri/src/care.ts

  • Addressing policy
    PUT /gateway/agents/{id}/addressing-policy

    Decides who may address the Builder and answer its cards: the owner everywhere, the crew in crew rooms, and a member's room only while the Builder is helping that member.

    apps/ferri/src/builder.ts (syncPolicy)

  • Session activity and the agent's own cards

    The Builder's progress shows as Switch activity cards. Its Claude Code permission prompts become Switch cards in the room, answered there, in Desk or in Pocket. Public steps always ask.

    builder/.claude/settings.json, apps/desk/lib/builder.ts

  • Shipping to production behind a card

    The Builder creates the event site's Supabase project and deploys on Vercel. Claude Code's prompt for vercel deploy --prod is the Switch card the crew answers.

    builder/kits/event-site, builder/.claude/skills/ship-event-site

  • Budgets
    /gateway/tenants/{tid}/budgets

    The Builder has its own output-token and turn budget per 24 hours. Switch stops it when it is spent; Ferri holds new jobs until the reset, and Desk shows the spend.

    scripts/builder-setup.ts, apps/desk/app/(desk)/builder

  • Room documents as shared context
    load_internal_documents

    The hand-off points the Builder at the knowledge and plan documents attached to the crew room, so the brief stays short.

    packages/knowledge/src/builder.ts

One message, end to end

From a Telegram group to every room, through Switch.

The whole stack in one picture, then one question's trip through it.

How Ferri fits together: Discord, Telegram, Slack, WhatsApp and Mattermost on top; Switch, our fork, as the spine with rooms and bridges, approval cards, Trust, templates, library documents, media, managed agents, budgets, plus agent message edit and the WhatsApp bridge; below it the Ferri service with Shield, Ferri Builder and Claude; then outside services, Postgres with pgvector, and Desk and Pocket.

The same picture as text is in architecture.md.

  1. 1Telegram → Switch room

    A member asks on Telegram

    “@ferri can I bring my dog?” in the event's Telegram group. Switch's Telegram bridge makes that group a Switch room, like every other group on every other app.

  2. 2Switch Trust → Shield

    Switch Trust checks it

    Before anyone sees it, Switch sends the message to Shield at /guardrails/check. Scam links and floods are blocked, leaked keys become [redacted]. 2 to 9 ms, no model.

  3. 3SSE down, HTTP up

    Ferri reads it over the agent protocol

    One SSE stream brings Ferri every room's events. Addressing tells it this was a real mention. Posts, typing, heartbeat and ops go back up over HTTP.

  4. 4Card in the crew room

    It doesn't know, so it asks once

    The member hears “I'm checking with the team” at once. A Switch question card opens in the crew room for whoever owns that topic, and their phone buzzes through Pocket.

  5. 5One answer

    The crew answers the card

    A button on Slack, Discord or Telegram, a typed A1 “Yes, dogs are welcome” anywhere, or Desk and Pocket, which answer through Switch under the organizer's name.

  6. 6Fanned out to every room

    Everyone who asked gets it

    The answer reaches every member who asked the same thing, in their own room on their own app, with a real mention. Ferri saves it, so the next person gets it at once.

What we added to Switch core

Two things Ferri needed that Switch didn't have yet.

Both live in our fork, RudraO2/switch, branch ferri/agent-message-edit, and ship in Ferri's Switch core image, ghcr.io/rudrao2/ferri-switch-core, which ./ferri up runs.

Agent message edit

“Fix the announcement everywhere” needs an agent that can edit its own post. Switch had no op and no route for it, though every platform adapter already had update_message. So we added the route:

POST /agents/{agent_id}/messages/{event_id}/edit
{ "room_id": "...", "content": "..." }
→ { "ok": true, "edited": true }
  • Checks room membership and budget like a post, and refuses any message that is not the agent's own (403).
  • Runs the new text through Switch Trust, so Shield checks edits too.
  • The room's bridge finds the platform post in its message map and redraws it in place on Mattermost, Slack, Discord and Telegram.
  • Without the image Ferri still works: an edit falls back to a new post that starts with “Update:”.

The whatsapp_web bridge

Community groups often live on WhatsApp, and the Switch release we build on had no WhatsApp bridge. We added a messaging platform the way Switch's own guide adds one: one adapter folder, one line in platforms.py, and a docs page.

core/switch_core/bridges/collaboration/
  whatsapp_web/adapter.py
  • Talks to a WAHA sidecar linked to the organizer's own number by QR code or pairing code. It dials out, so no public address is needed.
  • Only the chats named in the connection become rooms. The organizer's other chats never reach Switch.
  • Posts are paced like a person: typing first, at least 3 seconds apart in one chat, at most 30 an hour.
  • Quote replies are threads; edits, deletes and attachments go both ways; cards are answered by typing. 26 adapter tests in the fork.

One thin image, nothing else changes. Switch's published core 0.31.1-dev.339.g86d5928 plus the two additions, built for amd64 and arm64.

See the fork

A Switch-managed agent

The Builder: Claude Code, run by Switch.

Some event work is making things: the event site, posters, listings. Ferri hands that to Ferri Builder, a Claude Code agent that Switch itself runs on the organizer's machine.

  • Created and run through Switch
    Ferri creates the Builder with Switch's agent management. It runs on Switch's headless agent controller, and Switch starts a session whenever it is addressed.
  • Its own folder, its own Claude home
    It works in ~/.ferri/builder, seeded from builder/ in the repo, so the organizer's personal Claude settings never reach it.
  • Its own budget in Switch
    400k output tokens and 60 turns per 24 hours by default. When it is spent Switch stops the Builder, and Ferri holds new jobs until the reset.
  • Permission prompts become Switch cards
    Every public step (deploy, push, email, post) is a Claude Code permission prompt, which Switch shows as a card. A no means nothing is published.
Read builder.md
#crewa Switch room
Ferri Job B1 is with the Builder: ship the Build Night site.
Builder · activity
  • ✓ read knowledge and plan from the room
  • ✓ created the Supabase project
  • ✓ set the Vercel environment
Permission card

The Builder wants to run:

vercel deploy --prod
Allow onceDecline

Answer here, in Desk or in Pocket.

nothing goes live without the crew's yes

Run Switch and Ferri yourself.

One command starts Switch with our two additions, a Mattermost to try it in, Ferri and Desk, with a demo event already loaded.