Skip to content

How beryl works

beryl provides a socket runtime on OTP actors and Erlang pg, with a built-in Phoenix-compatible codec. It supports pluggable wire codecs and WebSocket transports. An app can pass an init and update pair to beryl.child_spec. It can also pass a typed handler table to channel.child_spec. The runtime dispatches decoded messages and applies the returned effects. Separate actors manage presence and groups. Only PubSub sends data across nodes.

Here, runtime means the router, socket actors, and optional channel workers together, not one process. A topic worker belongs to one socket/topic join, not to every subscriber of a topic name.

Each page describes one subsystem and lists its source files.

flowchart TB
  T["WebSocket Transport<br/>beryl_mist · beryl_ewe"]
  W["Wire Protocol<br/>beryl/wire · beryl/wire/codec"]
  RT["Runtime<br/>router + one actor per socket<br/>+ one worker per channel<br/>beryl/runtime"]
  subgraph App["Your app"]
    C["channel handlers<br/>beryl/channel"]
    U["init / update<br/>beryl/socket"]
    C --> U
  end
  subgraph Domain["Domain actors"]
    P["Presence<br/>beryl/presence"]
    G["Groups<br/>beryl/group"]
  end
  PS["PubSub (Erlang pg)<br/>beryl/pubsub"]
  T --> W --> RT --> App
  RT --> Domain
  RT --> PS
ModuleResponsibilityPage
berylPublic entry-point: config/1, child_spec/3, broadcast/4, broadcast_from/5, stop/1—
beryl/channelRecommended channel layer: supervised startup, handler validation, typed per-topic state, join and close callbacks, senders, and ordered actionsChannels
beryl/socketThe app-facing dispatch types: Input, Next, Effect, JoinRef/ReplyRef, ConnectInfo/ConnectSeed, typed Sender/notifyRuntime
beryl/runtimeRouter, socket actors, and topic workers: subscriber index, per-socket models, per-topic channel processes, event delivery, returned effects, and heartbeat enforcementRuntime
beryl/pubsubDistributed publish and subscribe through Erlang pgBroadcasts Across Nodes
beryl/presenceOTP actor wrapping an add-wins OR-set CRDT; track/untrack, replication, scoped on_diff callbacksPresence
beryl/presence/wirePhoenix-compatible JSON encoding for presence diffs (joins/leaves maps)Presence
beryl/wireMessage encoding; includes phoenix_codec() for [join_ref, ref, topic, event, payload] framesWebSocket Frames & Transports
beryl/wire/codecCodec functions such as decode_text, decode_binary, and encode_*WebSocket Frames & Transports
beryl/transportPublic interface used by WebSocket transport packagesWebSocket Frames & Transports
beryl_mist / beryl_eweWebSocket adapters that assign socket IDs, register send functions, decode frames, and route messagesWebSocket Frames & Transports
beryl/groupNamed topic collections managed by an OTP actor; supports grouped broadcast(none)
beryl/topicTopic pattern matching: exact strings, "ns:*" prefix wildcards, and segment wildcards ("document:*:ops")(none)
beryl/errorOpaque StartFailure type that hides OTP's actor.StartError from public APIs(none)
beryl/rate_limitToken-bucket rate limiter; keyed per socket, per socket+topic, or per topic pattern(none)
beryl/logInternal named loggers built on palabres; not public API(none)
beryl/internalShared internal utilities (logging config, crash rescue); not public API(none)
flowchart TB
  S["beryl internal supervisor<br/>one-for-one, 3 restarts / 5s"]
  S --> SF["socket factory supervisor (Permanent)"]
  SF --> SA["socket actors<br/>one per connection, Temporary"]
  S --> RT["router actor (significant Transient)"]
  S -. optional .-> CL["connection limiter"]
  RT <-. "mutual monitors" .-> SA
  SA --> TS["topic worker supervisor<br/>channel layer, linked"]
  TS --> TW["topic workers<br/>one per socket/topic join, Temporary"]
  SA -. "ordered send requests" .-> T["transport connection process<br/>WebSocket writes"]
  PR["presence (app-started)"]
  GR["groups (app-started)"]
  SA -. "async mutation" .-> PR

child_spec supervises the router and socket factory. Transport connections request temporary socket children from the factory. Socket actors preserve protocol/effect order; transport connection processes perform network writes. Channel workers own per-topic state and callbacks.

A socket actor fault closes that socket. A worker fault closes its topic. A router or factory fault closes the affected runtime's connections. Temporary children are not restarted: clients reconnect and rejoin rather than recover an old session. The application starts and owns the presence and group actors. See the Supervision guide.

Slow channel callbacks do not block sibling workers, but joins, ordered closes, and presence work can delay the socket's effect processing. The router and presence actor remain shared resources, and transport queues can grow behind slow readers. See Queue limits and overload and Performance evidence for the current limits and the work tracked in #397 and #400.

Core source files are under packages/beryl/src/beryl/. Transport source files are under packages/beryl_mist/ and packages/beryl_ewe/. Start with Socket Processes & Restarts to learn how beryl runs sockets. See How beryl handles a message for the message path. See Broadcasts Across Nodes for cross-node behavior.