Skip to content

Migration & Releases

beryl uses a fully automated release pipeline:

  1. Changelog fragments are authored with changie new (or just change) and committed alongside the code change. Fragments live in .changes/unreleased/ until a release is cut.
  2. changie-release collects unreleased fragments, bumps the version in gleam.toml, and opens a release PR.
  3. Merging the release PR triggers auto-tag, which creates a GitHub release and the version tag.
  4. The publish workflow fires on the tag and publishes the new version to Hex.pm.

Where to find changelogs:

  • GitHub Releases — the canonical per-version changelog
  • .changes/unreleased/ in the repository — what is staged for the next release

The following changes have accumulated since the last release. They are described at a high level here; see GitHub for exact signatures.

Channels can now receive server-originated OTP messages. Add a handle_info callback with channel.with_handle_info/2, then deliver messages from anywhere using beryl.send_info/4. The callback receives the typed message you sent (see the info type parameter below) — no Dynamic decode and no unsafe cast. A Reply returned from handle_info becomes a push (no client ref exists, so no phx_reply is sent).

beryl.broadcast_presence_diff/3 sends Phoenix-shaped presence diffs — joins/leaves objects keyed by presence key with metas arrays. Local track and untrack operations also invoke on_diff with concrete diff values. Anonymous entries are excluded from encoded diffs.

topic.parse_pattern/1 now recognises multi-segment wildcard patterns like "document:*:*". Use topic.extract_wildcards/2 to pull out the matched segments. Single trailing * patterns retain existing prefix-wildcard behaviour.

The core WebSocket transport now uses Mist directly instead of Wisp. If your application used beryl/transport/wisp, migrate to beryl/transport/mist. Examples also use Mist for HTTP routing and static assets. See the WebSocket Transport guide for the updated setup.

broadcast_from now correctly excludes the originating socket when a channel is PubSub-enabled. Previously, broadcasts through PubSub could echo back to the sender.

The rate limiter now denies requests when the token bucket actor cannot be started, rather than silently allowing them through. If you rely on rate limiting for security, this change removes a potential bypass under resource exhaustion.

Removed: socket.topics() and socket.is_subscribed()

Section titled “Removed: socket.topics() and socket.is_subscribed()”

These functions always returned empty/false because subscriptions are tracked internally by the coordinator. They have been removed from the public API.

Type-safe handle_info: info type parameter on Channel

Section titled “Type-safe handle_info: info type parameter on Channel”

Channel is parameterized as Channel(assigns, info). The info type is the server-originated message delivered to handle_info, so with_handle_info wires up fn(info, Socket(assigns)) -> HandleResult(assigns) and send_info/4 delivers a value of that type — end to end with no Dynamic and no unsafe identity FFI cast in application code. Channels that do not use handle_info leave info generic, so existing channel definitions keep compiling; only explicit Channel(MyAssigns) type annotations need to become Channel(MyAssigns, info) (or a concrete info type). This is a breaking type-signature change.

The Mist transport config now accepts an on_connect callback. Return Error(Nil) to reject the upgrade with a 403 before the WebSocket handshake completes, or Ok(assigns) to allow it and seed initial socket-level assigns visible to channels at join time (return Ok(Nil) for none). See WebSocket Transport → Seeding initial assigns.


When upgrading across a minor version boundary:

  1. Read the GitHub Release notes for the target version.
  2. Check removed or changed exports against the module map.
  3. Run gleam check — the Gleam compiler will surface type mismatches caused by API changes.
  4. Run just test to verify runtime behaviour.

If you hit a regression not covered by the release notes, please open an issue.