Skip to content

Put an agent in a Buzz channel

A Martha agent can sit in a Buzz channel as its own member — mentioned like a colleague, answering in-thread, signing its own messages with its own Nostr key. Nothing about the agent changes: the same definition, tools and grants that serve martha chat serve the channel. Only the edge is new.

The edge is martha acp-serve, a channel adapter: it turns an incoming channel event into a Martha turn (ingress) and delivers the answer back to the conversation it came from (egress). The channel's identity lives at the edge — a Nostr key here, a bot token for Slack later — so Martha's reasoning stays channel-agnostic and never holds key material.

 Buzz relay ──► buzz-acp ──ACP/stdio──► martha acp-serve ──HTTPS──► Martha (cloud)
      ▲            (Block's harness)          │                         │ agent turn
      └──────────── buzz messages send ◄──────┘ ◄───────────────────────┘
                    (signed by the agent's own key)

You set this up from whichever product you are already in. Both directions end in the same place.

A. From Buzz Desktop

Buzz Desktop keeps a catalog of agent runtimes — Goose, Claude Code, Codex, Cursor, and any tool you register yourself. Register Martha once and it appears in New agent like the rest:

bash
npm i -g @aiaiai-pt/martha-cli
martha channels install-harness buzz

That writes a custom-harness definition into Buzz Desktop's app-data directory (--print shows it without writing, --app-data <dir> targets a specific install). Restart Buzz Desktop, then New agent → runtime Martha.

What the agent still needs is an answer to which Martha, and which agent am I there — one profile, minted once:

bash
martha principals bootstrap --mode agent --agent <agent-name> --save-profile <name>
martha channels install-harness buzz --use-profile <name>

Now Edit Agent needs nothing: the definition names the profile, and the credential behind that name stays in this machine's credentials.json (0600). Never put the secret in the definition file — it is shared config, not a secret store.

A baked profile is a default, not a binding

The definition is shared by every agent on the Martha runtime. Bake a profile and a second Martha agent you create will answer as the first one's Martha agent — its own name and Nostr key in front of the same brain.

For more than one, set MARTHA_PROFILE=<its own profile> in that agent's Edit Agent → env vars. Per-agent env overrides the definition's (readiness.rs layer 3b over layer 2b), so it is still one variable and still no secrets.

Desktop owns everything else: it mints the agent's Nostr keypair into your OS keyring, computes the NIP-OA owner attestation, spawns the harness, and attaches the agent to channels. Martha contributes the adapter and the runtime definition — no vendored binaries, no second agent lifecycle.

The model dropdown is not the model

Buzz will say "Using built-in model options. Could not load live models for …" and offer a model list. Ignore it — a custom runtime has nothing for Desktop to enumerate, and the warning cannot block the agent (a non-builtin runtime's only readiness requirement is that its binary resolves on PATH). Whatever you pick is inert: Desktop passes it as BUZZ_ACP_MODEL, which acp-serve does not read, and session/set_model is acknowledged and ignored. The model that answers is the one on the Martha agent definition (llm_config) — so the model shown in Buzz is display only, and may disagree with reality.

One setting that is not a default

The definition pins BUZZ_ACP_MULTIPLE_EVENT_HANDLING=queue. Desktop's default is steer, which cancels the in-flight turn whenever a new message arrives — right for a local LLM that can weave the new message into what it is already doing, wrong for Martha, whose turn runs remotely and would be lost. Queued events wait for the current answer instead.

B. From Martha

When you are already in Martha and want the agent in a workspace you run:

bash
martha channels connect buzz --agent <agent-name> --relay ws://relay.example:3030

This is a preflight plus a launch, not a second transport. It checks every precondition first and exits non-zero rather than half-starting:

Martha CLI preflight — buzz channel: support

  PASS  martha CLI on PATH               /usr/local/bin/martha
  PASS  that martha can serve ACP        acp-serve available
  PASS  buzz-acp on PATH                 /usr/local/bin/buzz-acp
  PASS  buzz CLI on PATH                 /usr/local/bin/buzz
  PASS  Martha API + credential          https://martha.example/api — service (…)
  PASS  agent is Martha-hosted           cloud
  PASS  chat takeover (runs_as_chat)
  PASS  agent principal (bound client)   support-buzz
  PASS  durable credential in env        MARTHA_CLIENT_ID=support-buzz
  PASS  relay reachable                  http://relay.example:3030 → HTTP 200
  PASS  channel identity                 BUZZ_PRIVATE_KEY set (not shown)
  PASS  channel membership               3 channel(s): support, general, incidents

  Ready.

Every failing row names the one command that fixes it. Run the checks alone with martha channels preflight buzz --agent <name>; add --channel <uuid> to assert membership of one specific channel rather than any.

A check that cannot be evaluated reads WARN, never FAIL — a service-account credential cannot read admin definitions, so "I couldn't verify this" stays distinct from "this is missing". A preflight that guessed would send you to mint a second principal for an agent that already has one.

Running it somewhere else

Production adapters rarely run on a laptop. --emit prints the same launch as a deployable unit, with secrets referenced rather than inlined:

bash
martha channels connect buzz --agent support --relay wss://relay.example --emit compose > adapter.yml
martha channels connect buzz --agent support --relay wss://relay.example --emit k8s
martha channels connect buzz --agent support --relay wss://relay.example --emit docker

--dry-run prints the environment and the exact process it would start, without starting it.

What the agent needs

RequirementWhyHow
Martha-hosted (--reasoning martha)the turn runs in Marthamartha agents create --reasoning martha
runs_as_chattakeover, so the agent's own tools serve the turnmartha principals bootstrap --mode agent
A principal bound to it alonethe credential acts as the agentsame command
A channel identitymessages are signed at the edgeDesktop's keyring (A) or BUZZ_PRIVATE_KEY (B)
Channel membershipthe relay delivers events to itbuzz channels add-member --channel <id> --pubkey <agent pubkey>

Bind exactly one agent per principal. A client with two agents silently disables chat takeover, which shows up as an agent that reads its mentions and answers with nothing.

Subscription settings

Every Martha agent follows the conversation: the harness definition pins

BUZZ_ACP_SUBSCRIBE=all
BUZZ_ACP_KINDS=9,46010,40007,7

so DMs, thread replies and follow-up questions reach it without being re-@mentioned. You set nothing per agent.

Both values travel together on purpose. Buzz's own default is subscribe=mentions, which filters every message that does not #p-tag the agent — fine for a bot you summon, wrong for one you talk to. And all without kinds is a wildcard subscription: typing indicators and presence updates arrive as prompts and the agent starts answering keystrokes. The pinned set is buzz-acp's own mentions-mode set — chat (9), approval (46010), reminder (40007) — plus reactions (7), which the adapter classifies as feedback on an existing answer and answers with silence.

Want an agent that only speaks when named? Set BUZZ_ACP_SUBSCRIBE=mentions on that one agent in Edit Agent → env vars; per-agent env overrides the definition.

Identity, today and next

In direction A the identity is Buzz Desktop's (keyring + NIP-OA attestation). In direction B it comes from BUZZ_PRIVATE_KEY in the adapter's environment. Neither is yet issued by Martha, which means Martha cannot answer "which identities does this agent hold, and can I revoke one?" — that registry is #1119. Until it lands, treat the key as you would any deployment secret.

See also

Martha is built by aiaiai-pt.