Skip to content

feat: add broadcast messaging across registries - #77

Merged
ihorkatkov merged 10 commits into
mainfrom
feature/broadcast-messaging
Apr 13, 2026
Merged

ihorkatkov merged 10 commits into
mainfrom
feature/broadcast-messaging

Conversation

@ihorkatkov

@ihorkatkov ihorkatkov commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds registry-wide broadcast messaging so a sender can deliver one message to every agent in a target registry via REST, WebSocket, and all three plugin tools
  • Reuses the existing send_message path for per-recipient delivery, keeping message guarantees and routing unchanged
  • Enforces sender membership in the target registry; non-members get 403/error
  • Best-effort delivery with explicit partial-failure reporting (failed list)
  • Bumps plugin versions: Claude Code 1.1.0, OpenClaw 0.3.0, OpenCode 0.4.0

Design Overview

Request Flow

  Client (REST or WebSocket)
    |
    |-- POST /registry/:token/broadcast     (BroadcastController)
    |   OR
    |-- WS "broadcast_message" event        (AgentChannel)
    |
    v
  Viche.Agents.broadcast_message/1
    |
    |-- validate type, token, body
    |-- ensure_sender_registry_membership/2
    |   (reuses authorize_registry_join/2)
    |
    v
  agents_in_registry(token)  -->  [agent_info]
    |
    |-- for each recipient:
    |     |
    |     v
    |   Viche.Agents.send_message/1
    |     |-- AgentServer.receive_message/2  (GenServer inbox)
    |     '-- PubSub broadcast "new_message" (Channel push)
    |
    v
  {:ok, %{recipients, message_ids, failed}}

Impact Map

  +--------------------------------------------------+
  |                   Elixir Server                   |
  |                                                   |
  |  lib/viche/agents.ex                              |
  |    '-- broadcast_message/1                        |
  |    '-- ensure_sender_registry_membership/2        |
  |    '-- deliver_broadcast_messages/4               |
  |                                                   |
  |  lib/viche_web/                                   |
  |    +-- controllers/broadcast_controller.ex  [NEW] |
  |    +-- channels/agent_channel.ex (broadcast_msg)  |
  |    +-- controllers/well_known_controller.ex       |
  |    '-- router.ex                                  |
  +--------------------------------------------------+
               |                    |
     +---------+------+    +-------+--------+
     |  REST API      |    |  WebSocket     |
     |  POST /:token/ |    |  broadcast_msg |
     |   broadcast    |    |  event         |
     +---------+------+    +-------+--------+
               |                    |
  +------------+--------------------+-------------+
  |                    Plugins                     |
  |                                                |
  |  channel/claude-code-plugin-viche/tools.ts     |
  |    '-- viche_broadcast  (MCP tool)             |
  |                                                |
  |  channel/openclaw-plugin-viche/tools.ts        |
  |    '-- viche_broadcast  (OpenClaw tool)        |
  |                                                |
  |  channel/opencode-plugin-viche/tools.ts        |
  |    '-- viche_broadcast  (OpenCode tool)        |
  +------------------------------------------------+
               |
  +------------+-----------------------------------+
  |                    Tests                        |
  |                                                 |
  |  test/viche/agents_test.exs                     |
  |  test/viche_web/controllers/                    |
  |    broadcast_controller_test.exs         [NEW]  |
  |    well_known_controller_test.exs               |
  |  test/viche_web/channels/agent_channel_test.exs |
  |  test/viche_web/integration/                    |
  |    broadcast_flow_test.exs               [NEW]  |
  |  *.argus.test.ts (3 adversarial tests)   [NEW]  |
  +----------------------------------+--------------+
               |
  +------------+-----------------------------------+
  |                    Docs                         |
  |                                                 |
  |  AGENTS.md, README.md, SPEC.md                  |
  |  specs/03-messaging.md, specs/07-websockets.md  |
  |  Plugin READMEs, SKILL.md                       |
  +----------------------------------+--------------+

Changes

Elixir Server

File Change
lib/viche/agents.ex broadcast_message/1, ensure_sender_registry_membership/2, deliver_broadcast_messages/4, new types broadcast_result, broadcast_failure
lib/viche_web/controllers/broadcast_controller.ex New — REST POST /registry/:token/broadcast, derives sender from conn.assigns.current_agent_id
lib/viche_web/channels/agent_channel.ex handle_in("broadcast_message", ...) with field validation clauses
lib/viche_web/controllers/well_known_controller.ex Broadcast endpoint + broadcast_message client event in descriptor
lib/viche_web/router.ex Route post "/:token/broadcast" in registry scope

Plugins (TypeScript)

File Change
channel/claude-code-plugin-viche/tools.ts viche_broadcast tool + input validation helpers (validRegistryToken, validMessageType)
channel/openclaw-plugin-viche/tools.ts viche_broadcast tool + validProtocolMessageType guard
channel/opencode-plugin-viche/tools.ts viche_broadcast tool + ProtocolMessageTypeSchema Zod enum
channel/opencode-plugin-viche/tools.ts Tightened viche_send type param from z.string() to z.enum(["task","result","ping"])

Tests

File Type
test/viche/agents_test.exs 6 unit tests for broadcast_message/1
test/viche_web/controllers/broadcast_controller_test.exs New — 4 controller tests (success, missing sender, forbidden, missing body, invalid token)
test/viche_web/channels/agent_channel_test.exs 5 channel tests for broadcast_message event
test/viche_web/controllers/well_known_controller_test.exs 3 tests for broadcast in well-known descriptor
test/viche_web/integration/broadcast_flow_test.exs New — 4 end-to-end tests (REST, WebSocket, mixed transport, message integrity)
*.argus.test.ts (x3) Adversarial validation tests for registry token + message type in all 3 plugins
channel/claude-code-plugin-viche/tools.test.ts 3 unit tests for viche_broadcast
channel/openclaw-plugin-viche/tools-websocket.test.ts 2 unit tests for viche_broadcast
channel/opencode-plugin-viche/__tests__/tools.test.ts 2 unit tests for viche_broadcast

Documentation

File Change
AGENTS.md Broadcast flow, API table, channel events, plugin contract
README.md Usage example, capability table, sequence diagram
SPEC.md Section 5.3a — broadcast endpoint spec with request/response schemas
specs/03-messaging.md Broadcast section with behavior, flow, and acceptance criteria
specs/07-websockets.md broadcast_message event, client code example, acceptance criteria
Plugin READMEs Tool tables, sequence diagrams
channel/claude-code-plugin-viche/skills/viche/SKILL.md viche_broadcast tool docs + example

Key Design Decisions

  • Thin loop over send_message — no broadcast-specific message schema; each recipient gets a normal Message with a unique ID routed through the existing delivery path
  • Registry-bound authorization — sender must belong to target registry to broadcast (reuses authorize_registry_join/2)
  • Self-delivery included — sender receives their own broadcast for consistency and observability
  • Best-effort delivery — partial failures do not cancel successful deliveries; failures are surfaced explicitly in the failed list
  • Sender identity enforced server-side — BroadcastController derives from from conn.assigns.current_agent_id; AgentChannel derives it from socket.assigns.agent_id. Client-supplied from is ignored
  • Input validation at plugin layer — all three plugins validate registry token format and message type enum before sending to the server

How to Test

# Full server validation
mix precommit

# Run broadcast-specific Elixir tests
mix test test/viche/agents_test.exs test/viche_web/controllers/broadcast_controller_test.exs test/viche_web/channels/agent_channel_test.exs test/viche_web/integration/broadcast_flow_test.exs

# Plugin tests
cd channel/claude-code-plugin-viche && bun test
cd channel/openclaw-plugin-viche && bun test
cd channel/opencode-plugin-viche && bun test

Version Bumps

Package Old New
claude-code-plugin-viche 1.0.0 1.1.0
@ikatkov/viche-plugin (OpenClaw) 0.2.1 0.3.0
opencode-plugin-viche 0.3.1 0.4.0

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my feature works
  • New and existing unit tests pass locally with my changes

ihorkatkov and others added 10 commits April 13, 2026 20:47
The claude-code-plugin-viche was sending empty params {} when joining
registry channels, causing the server to refuse with agent_id_required.
Also adds explicit Logger.warning calls on the server side so registry
join refusals include the reason in logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When agent_id is missing, we have no agent to name — so log the
socket ID and existing assigns to identify which connection attempted
the join.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…identity in logs

After register-on-join, the socket connects without an agent_id so
socket.id is nil. Explicitly set socket.id = "agent_socket:<id>" on the
returned socket so subsequent registry channel joins on the same socket
carry the agent identity in Phoenix logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ihorkatkov
ihorkatkov merged commit 20215c6 into main Apr 13, 2026
4 checks passed
@ihorkatkov
ihorkatkov deleted the feature/broadcast-messaging branch April 13, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant