Skip to content

Reference

The canonical function-level API reference is generated from Gleam's docs metadata and published here:

API Reference

beryl is not on Hex yet, so there is no hexdocs.pm listing.

This page provides a module map, broadcast cheatsheet, Phoenix wire protocol reference, and client compatibility notes.


ModuleWhat it doesWhen to use it
berylTop-level API: register channels, broadcast, send_infoEntry point for all applications
beryl/supervisorSupervised startup: builds beryl's child specification and resolves stable subsystem handlesStarting beryl — this is the only entry point
beryl/channelChannel builder, callback types, HandleResultDefining channel behaviour
beryl/socketSocket abstraction, assigns helpersInside channel callbacks
beryl/topicTopic parsing, wildcard matching, segment extractionDynamic routing, multi-tenant patterns
beryl/pubsubDistributed PubSub backed by Erlang pg, generic over payload typeMulti-node fan-out, cluster broadcasts
beryl/presenceOTP actor wrapping the presence CRDT, plus opaque Diff accessorsTracking who is online
beryl/groupNamed sets of topics for bulk broadcastRooms with multiple sub-topics
beryl/bridgeForwards an external OTP actor's stream to a socket channelPushing a domain actor's updates to clients
beryl/errorOpaque StartFailure type returned by subsystem start functionsHandling startup errors
beryl/wirePhoenix-compatible codec and JSON helpersPhoenix clients, custom transports, protocol debugging
beryl/wire/codecPluggable codec contract for text and binary framesCustom wire formats
beryl/transportTransport SPI: socket lifecycle, inbound routing, edge rate limitingWriting a custom WebSocket transport
beryl/statsPoint-in-time local coordinator snapshots and typed availability/timeout errorsOperational polling and application metrics
beryl_mistMist WebSocket upgrade and dispatch (separate beryl_mist package)Wiring beryl to a Mist server
beryl_eweEwe WebSocket upgrade and dispatch (separate beryl_ewe package); mirrors the beryl_mist APIWiring beryl to an Ewe server

For the stable :telemetry event taxonomy, snapshot semantics, and application-owned Prometheus/Grafana export pattern, see the Observability guide.


GoalAPINotes
Reply to an incoming messagechannel.Reply(event, payload, socket) from handle_inSends phx_reply with "status": "ok"; the event arg is ignored on the wire — reply is keyed by ref
Fail an incoming messagechannel.ReplyError(payload, socket) from handle_inSends phx_reply with "status": "error", firing the client's receive("error", ...) hook. Reply cannot do this — it is always "ok"
Push to the current socket onlychannel.Push(event, payload, socket) from handle_in or handle_infoServer-originated push on this socket's topic
No responsechannel.NoReply(socket)Use when the callback has no output
Broadcast to all sockets on a topicberyl.broadcast(channels, topic, event, payload)All subscribers including the sender
Broadcast, excluding senderberyl.broadcast_from(channels, socket.id(socket), topic, event, payload)Second arg is except_socket_id: String; use socket.id/1 to extract it when you have a Socket value. Skips the originating socket; works across PubSub nodes
Send an OTP message to a joined channel contextberyl.send_info(channels, socket_id, topic_name, message)Delivers the typed message to handle_info; the callback receives the concrete info value — no Dynamic decode and no unsafe cast required
Broadcast presence diffberyl.broadcast_presence_diff(channels, topic, diff)Encodes Phoenix-shaped joins/leaves; only named topic entries are included

beryl speaks the same JSON array wire format as Phoenix channels. All frames are JSON arrays with five elements:

[join_ref, ref, topic, event, payload]
FieldTypeDescription
join_refstring or nullReference from the original phx_join frame; null for server-initiated pushes
refstring or nullPer-message reference echoed in the reply; null for pushes
topicstringThe channel topic, e.g. "room:lobby"
eventstringEvent name
payloadobjectArbitrary JSON object
EventDirectionMeaning
phx_joinclient → serverRequest to join a topic
phx_leaveclient → serverUnsubscribe from a topic
phx_replyserver → clientReply to a client message, "status" of "ok" or "error". Rejected joins arrive this way, not as phx_error
phx_errorserver → clientThe channel terminated abnormally
phx_closeserver → clientChannel closed by server
heartbeatclient → serverKeep-alive ping (topic "phoenix")

Sent in response to any client message. The event arg passed to channel.Reply is not reflected on the wire — the frame always uses phx_reply and the original ref. status is what distinguishes success from failure, and it is set by which result you return, never by the payload:

// channel.Reply(event, payload, socket)
[join_ref, original_ref, "topic:name", "phx_reply", {"status": "ok", "response": <your_payload>}]
// channel.ReplyError(payload, socket)
[join_ref, original_ref, "topic:name", "phx_reply", {"status": "error", "response": <your_payload>}]

A join reply uses the join_ref as both join_ref and ref:

["1", "1", "room:lobby", "phx_reply", {"status": "ok", "response": {}}]

The client sends heartbeats on the "phoenix" topic; beryl replies immediately:

// client →
[null, "ref", "phoenix", "heartbeat", {}]
// server →
[null, "ref", "phoenix", "phx_reply", {"status": "ok", "response": {}}]

Follows the Phoenix presence diff format. Both joins and leaves are objects keyed by presence key (typically the user ID). Each value has a metas array:

{
"joins": {
"user:42": { "metas": [{ "phx_ref": "abc123", "online_at": 1234567890 }] }
},
"leaves": {
"user:99": { "metas": [{ "phx_ref": "xyz789" }] }
}
}

broadcast_presence_diff encodes only named topic entries (entries with an explicit key). Anonymous entries are excluded.


When started with wire.phoenix_codec(), beryl uses the standard Phoenix wire format, so any Phoenix-compatible WebSocket client works out of the box:

ClientNotes
phoenix.jsOfficial JS client; full support
phxGleam client; designed for beryl
Phoenix Swift / Kotlin clientsCommunity Phoenix clients; wire-compatible
Plain WebSocketUse the JSON array format directly; no reconnect logic

The WebSocket upgrade path is caller-provided — there is no default. Pass the path when constructing your transport config with mist_transport.default_config(path). The Phoenix JS client appends /websocket to the socket endpoint, so if you configure the client with "/socket", mount your handler at "/socket/websocket". See the WebSocket Transport guide for details.


beryl follows Semantic Versioning but is not yet 1.0. Until the 1.0 release:

  • Minor version bumps (0.x → 0.x+1) may include breaking changes to the public API.
  • Patch version bumps (0.x.y → 0.x.y+1) fix bugs without intentional breakage.
  • Public API is defined as the exports of the modules listed in the module map above.
  • Coordinator, rate-limit, and internal helper modules are intentionally hidden from downstream packages. Transports integrate through the public beryl/transport SPI; beryl_mist and beryl_ewe are the supported WebSocket transports.

Check GitHub releases before upgrading to a new minor version.