Agent skill

Phoenix Channels Essentials

by j-morgan6 in j-morgan6/elixir-phoenix-guide

A skill your agent uses when building WebSocket features with Phoenix Channels — socket auth, join authorization, handlein/push/broadcast, Presence.

MITAuto-check passedBackend & APIs

Install Phoenix Channels Essentials

skills CLI
$ npx skills add j-morgan6/elixir-phoenix-guide --skill phoenix-channels-essentials -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install j-morgan6/elixir-phoenix-guide phoenix-channels-essentials --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/j-morgan6/elixir-phoenix-guide.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/phoenix-channels-essentials .claude/skills/phoenix-channels-essentials && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
phoenix-channels-essentials
GitHub stars
167
Token cost
~2.3k tokens
SKILL.md length
357 words
Files
1
Skills in repo
19
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when building WebSocket features with Phoenix Channels — socket auth, join authorization, handlein/push/broadcast, Presence.

  • Works in 6 steps: Always authenticate in connect/3 —… → Authorize in join/3 — verify the user… → Use handle_in for client-to-server, push… → …
  • Building WebSocket features with Phoenix Channels — socket auth
  • SKILL.md covers RULES — Follow these with no…, Socket Authentication, Topic Authorization and Channel Message Patterns, plus 4 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Phoenix Channels Essentials is an agent skill from j-morgan6/elixir-phoenix-guide. Use when building WebSocket features with Phoenix Channels — socket auth, join authorization, handlein/push/broadcast, Presence.

Its SKILL.md is about 2.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Backend & APIs, covering LLM observability and Realtime and WebSockets. The licence is MIT.

When your agent uses it

  • Building WebSocket features with Phoenix Channels — socket auth
  • Join authorization
  • Handlein/push/broadcast

Example prompts

  • “/phoenix-channels-essentials”

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. Always authenticate in connect/3 — channels bypass the Plug pipeline; tokens must be verified in the socket
  2. Authorize in join/3 — verify the user can access the requested topic before allowing the connection
  3. Use handle_in for client-to-server, push for server-to-client, broadcast for server-to-all — never confuse the direction
  4. Keep channel modules thin — delegate business logic to context modules; channels are the transport layer
  5. Use Presence for tracking connected users — don't roll your own presence tracking; Phoenix.Presence handles node distribution
  6. Return {:reply, :ok, socket} or {:reply, {:error, reason}, socket} from handle_in — don't silently drop messages

What it can do on your machine

Read from SKILL.md and the folder at commit cdfddac. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are elixir).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Phoenix Channels Essentials loads about 2.3k tokens when it runs. Until then it costs about 39 tokens; SKILL.md has 357 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~39
When it runs · the whole SKILL.md, loaded when a task matches
~2.3k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from j-morgan6/elixir-phoenix-guide at commit cdfddac, republished under its MIT licence (© j-morgan6). 357 words, ~2,283 tokens.

Download SKILL.mdSave it as .claude/skills/phoenix-channels-essentials/SKILL.md (or your agent's skills folder).
name
phoenix-channels-essentials
description
Use when building WebSocket features with Phoenix Channels — socket auth, join authorization, handle_in/push/broadcast, Presence.
file_patterns
**/*_socket.ex, **/*_channel.ex, **/channels/**/*.ex
auto_suggest
true

Phoenix Channels Essentials

For non-LiveView real-time features: mobile clients, SPAs, external APIs, inter-service communication.

RULES — Follow these with no exceptions

  1. Always authenticate in connect/3 — channels bypass the Plug pipeline; tokens must be verified in the socket
  2. Authorize in join/3 — verify the user can access the requested topic before allowing the connection
  3. Use handle_in for client-to-server, push for server-to-client, broadcast for server-to-all — never confuse the direction
  4. Keep channel modules thin — delegate business logic to context modules; channels are the transport layer
  5. Use Presence for tracking connected users — don't roll your own presence tracking; Phoenix.Presence handles node distribution
  6. Return {:reply, :ok, socket} or {:reply, {:error, reason}, socket} from handle_in — don't silently drop messages

Socket Authentication

Channels bypass the Plug pipeline, so session-based auth doesn't work. Use token-based authentication.

Generating Tokens (Server Side)
elixir
# In a controller or LiveView — generate a token for the current user
defmodule MyAppWeb.UserAuth do
  def generate_socket_token(conn) do
    Phoenix.Token.sign(conn, "user socket", conn.assigns.current_scope.user.id)
  end
end

# In your layout or root template
<script>
  window.userToken = "<%= Phoenix.Token.sign(@conn, "user socket", @current_scope.user.id) %>"
</script>
Verifying Tokens (Socket)
elixir
defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket

  channel "room:*", MyAppWeb.RoomChannel
  channel "notifications:*", MyAppWeb.NotificationChannel

  @impl true
  def connect(%{"token" => token}, socket, _connect_info) do
    # Phoenix.Token.verify/4 defaults max_age to 86_400 (1 day); this example
    # passes max_age: 1_209_600 to extend it to 2 weeks.
    case Phoenix.Token.verify(socket, "user socket", token, max_age: 1_209_600) do
      {:ok, user_id} ->
        {:ok, assign(socket, :user_id, user_id)}

      {:error, _reason} ->
        :error
    end
  end

  def connect(_params, _socket, _connect_info), do: :error

  @impl true
  def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end

Bad:

elixir
# No authentication — anyone can connect
def connect(_params, socket, _connect_info) do
  {:ok, socket}
end

Topic Authorization

Verify in join/3 that the user is allowed to access the topic.

elixir
defmodule MyAppWeb.RoomChannel do
  use MyAppWeb, :channel

  @impl true
  def join("room:" <> room_id, _payload, socket) do
    user_id = socket.assigns.user_id

    if Rooms.member?(room_id, user_id) do
      {:ok, assign(socket, :room_id, room_id)}
    else
      {:error, %{reason: "unauthorized"}}
    end
  end
end

Bad:

elixir
# No authorization — any authenticated user can join any room
def join("room:" <> room_id, _payload, socket) do
  {:ok, assign(socket, :room_id, room_id)}
end

Channel Message Patterns

Client-to-Server (handle_in)

Always reply so the client knows the result.

elixir
@impl true
def handle_in("new_msg", %{"body" => body}, socket) do
  user_id = socket.assigns.user_id
  room_id = socket.assigns.room_id

  case Chat.create_message(room_id, user_id, body) do
    {:ok, message} ->
      broadcast!(socket, "new_msg", %{
        id: message.id,
        body: message.body,
        user_id: message.user_id,
        inserted_at: message.inserted_at
      })
      {:reply, :ok, socket}

    {:error, changeset} ->
      {:reply, {:error, %{errors: format_errors(changeset)}}, socket}
  end
end

Bad:

elixir
# No reply — client doesn't know if message was received
def handle_in("new_msg", %{"body" => body}, socket) do
  broadcast!(socket, "new_msg", %{body: body})
  {:noreply, socket}
end
Server-to-Client (push)

Send a message to a specific client, not everyone.

elixir
# Push to this specific client only
push(socket, "typing", %{user_id: other_user_id})

# Broadcast to all clients on the topic (including sender)
broadcast!(socket, "new_msg", payload)

# Broadcast to all clients except the sender
broadcast_from!(socket, "user_joined", %{user_id: user_id})
External Messages (handle_info)

For messages from PubSub, timers, or other processes.

elixir
@impl true
def handle_info({:new_notification, notification}, socket) do
  push(socket, "notification", %{
    title: notification.title,
    body: notification.body
  })
  {:noreply, socket}
end

Topic Naming Conventions

elixir
# Resource-specific — one room
"room:42"

# User-scoped — all notifications for a user
"notifications:user_123"

# Collection-wide — all public updates
"updates:all"

# Subtopic — specific channel within a room
"room:42:typing"

Pattern match in join to extract IDs:

elixir
def join("room:" <> room_id, _payload, socket) do
  # room_id is a string — parse if needed
  room_id = String.to_integer(room_id)
  # ...
end

Presence Tracking

Use Phoenix.Presence for tracking who is online. It handles distributed nodes automatically.

Show full SKILL.md (142 more words)Show less
Setup
elixir
# lib/my_app_web/channels/presence.ex
defmodule MyAppWeb.Presence do
  use Phoenix.Presence,
    otp_app: :my_app,
    pubsub_server: MyApp.PubSub
end
Tracking in a Channel
elixir
defmodule MyAppWeb.RoomChannel do
  use MyAppWeb, :channel
  alias MyAppWeb.Presence

  @impl true
  def join("room:" <> room_id, _payload, socket) do
    send(self(), :after_join)
    {:ok, assign(socket, :room_id, room_id)}
  end

  @impl true
  def handle_info(:after_join, socket) do
    # Track this user's presence
    {:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
      online_at: inspect(System.system_time(:second)),
      typing: false
    })

    # Send current presence state to the joining client
    push(socket, "presence_state", Presence.list(socket))

    {:noreply, socket}
  end
end
Updating Presence Metadata
elixir
@impl true
def handle_in("typing", %{"typing" => typing}, socket) do
  Presence.update(socket, socket.assigns.user_id, fn meta ->
    Map.put(meta, :typing, typing)
  end)
  {:reply, :ok, socket}
end

When to Use Channels vs LiveView vs PubSub

FeatureChannelsLiveViewPubSub
ClientAny (mobile, SPA, IoT)Browser onlyServer-side only
ProtocolWebSocket + customWebSocket + HTMLErlang messages
RenderingClient rendersServer rendersNo rendering
Use whenNon-browser clients, custom protocolsBrowser UI with real-timeInter-process communication

Choose Channels when:

  • Mobile apps need real-time features
  • SPA frontend (React, Vue) needs WebSocket communication
  • External services need bidirectional communication
  • You need a custom binary protocol

Choose LiveView when:

  • Browser-based UI with real-time updates
  • Server-rendered HTML is acceptable
  • You want to avoid writing JavaScript

Choose PubSub when:

  • Server-side inter-process communication only
  • LiveView components need to communicate
  • Background jobs need to notify the web layer

Testing Channels

elixir
defmodule MyAppWeb.RoomChannelTest do
  use MyAppWeb.ChannelCase

  setup do
    user = user_fixture()
    room = room_fixture(members: [user])
    token = Phoenix.Token.sign(MyAppWeb.Endpoint, "user socket", user.id)
    {:ok, socket} = connect(MyAppWeb.UserSocket, %{"token" => token})
    {:ok, _, socket} = subscribe_and_join(socket, "room:#{room.id}", %{})

    %{socket: socket, user: user, room: room}
  end

  test "new_msg broadcasts to room", %{socket: socket} do
    ref = push(socket, "new_msg", %{"body" => "hello"})

    assert_reply ref, :ok
    assert_broadcast "new_msg", %{body: "hello"}
  end

  test "new_msg with invalid data returns error", %{socket: socket} do
    ref = push(socket, "new_msg", %{"body" => ""})

    assert_reply ref, :error, %{errors: _}
  end

  test "unauthorized user cannot join room", %{socket: socket, room: room} do
    other_user = user_fixture()
    token = Phoenix.Token.sign(MyAppWeb.Endpoint, "user socket", other_user.id)
    {:ok, socket} = connect(MyAppWeb.UserSocket, %{"token" => token})

    assert {:error, %{reason: "unauthorized"}} =
             subscribe_and_join(socket, "room:#{room.id}", %{})
  end

  test "presence is tracked on join", %{socket: socket, user: user} do
    user_id = to_string(user.id)
    assert %{^user_id => %{metas: [%{online_at: _}]}} =
             MyAppWeb.Presence.list(socket)
  end
end

See phoenix-pubsub-patterns skill for server-side PubSub patterns. See phoenix-liveview-essentials skill for LiveView real-time patterns. See testing-essentials skill for comprehensive testing patterns.

© j-morgan6, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/phoenix-channels-essentials of j-morgan6/elixir-phoenix-guide.

Open the folder on GitHubat commit cdfddac

Compare with similar skills

Phoenix Channels Essentials next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Phoenix Channels Essentials compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Phoenix Channels Essentials this skillj-morgan6/elixir-phoenix-guide167—~2.3kAutomated safety check: PassMIT
Gemini Live API Devgoogle-gemini/gemini-skills4.3k—~4.6kAutomated safety check: PassApache-2.0
Databuddydatabuddy-analytics/Databuddy1.2k—~2.1kAutomated safety check: PassAGPL-3.0
Phoenix GraphqlArize-ai/phoenix12k—~2.2kAutomated safety check: PassCustom licence
Building AI Agent On CloudflareCommandCodeAI/agent-skills133—~2.3kAutomated safety check: PassMIT
Iflytek Image Understandingiflytek/iFly-Skills209—~949Automated safety check: PassApache-2.0

Similar skills

  • Gemini Live API Dev

    google-gemini/gemini-skills

    Official

    A skill your agent uses when building real-time, bidirectional streaming applications with the Gemini Live API, or migrating legacy Live models (2.0/2.5/3.1) to Gemini 3.8 Live.

    4.3k GitHub stars~4.6k tokensUpdated 3 days ago
    Backend & APIsAuto-check passed
  • Databuddy

    databuddy-analytics/Databuddy

    Integrate Databuddy analytics using the SDK, REST API, or MCP.

    1.2k GitHub stars~2.1k tokensUpdated today
    Backend & APIsAuto-check passed
  • Phoenix Graphql

    Arize-ai/phoenix

    Write efficient GraphQL queries against the Phoenix API. An agent skill from Arize-ai/phoenix.

    12k GitHub stars~2.2k tokensUpdated today
    Backend & APIsAuto-check passed
  • Building AI Agent On Cloudflare

    CommandCodeAI/agent-skills

    Builds AI agents on Cloudflare using the Agents SDK with state management, real-time WebSockets, scheduled tasks, tool integration, and chat capabilities.

    133 GitHub stars~2.3k tokensUpdated 7 mo ago
    Backend & APIsAuto-check passed
  • Iflytek Image Understanding

    iflytek/iFly-Skills

    A skill your agent uses when user asks to analyze an image, describe image contents, or answer questions about a picture.

    209 GitHub stars~949 tokensUpdated 2 days ago
    Backend & APIsAuto-check passed
  • Azure AI Voicelive TS

    microsoft/skills

    Official

    Azure AI Voice Live SDK for JavaScript/TypeScript. An agent skill from microsoft/skills.

    3.1k GitHub starsUsed in 5 repos~3.4k tokens
    Backend & APIsAuto-check passed

More from j-morgan6/elixir-phoenix-guide

All 19 skills in this repo
  • Code Quality

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when refactoring for duplication, complexity, or dead code — includes the plugin's on-demand analysis scripts.

    167 GitHub stars~1.4k tokensUpdated 3 mo ago
    Auto-check passed
  • Deployment Gotchas

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when preparing releases or deployment config — runtime.exs vs compile-time config, release migrations, PHXHOST/PHXSERVER, assets, health checks.

    167 GitHub stars~2.6k tokensUpdated 3 mo ago
    Auto-check passed
  • Ecto Changeset Patterns

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when a resource needs multiple changesets (registration vs update), conditional validation, field transforms, or uniqueness validation — changeset composition.

    167 GitHub stars~2.1k tokensUpdated 3 mo ago
    Auto-check passed
  • Ecto Essentials

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when defining schemas, writing queries, or creating migrations — schema design, Repo usage, indexes, query composition.

    167 GitHub stars~2.2k tokensUpdated 3 mo ago
    Auto-check passed
  • Ecto Nested Associations

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when a form or operation manages parent and child records together — castassoc/castembed, onreplace, Ecto.Multi across tables, FK cascade design.

    167 GitHub stars~2.2k tokensUpdated 3 mo ago
    Auto-check passed
  • Elixir Essentials

    j-morgan6/elixir-phoenix-guide

    A skill your agent uses when writing or refactoring core Elixir — pattern matching, case/cond/with, pipes, {:ok, }/{:error, } contracts.

    167 GitHub stars~2.2k tokensUpdated 3 mo ago
    Auto-check passed

Questions about Phoenix Channels Essentials

What does Phoenix Channels Essentials do?

A skill your agent uses when building WebSocket features with Phoenix Channels — socket auth, join authorization, handlein/push/broadcast, Presence. Phoenix Channels Essentials is an agent skill from j-morgan6/elixir-phoenix-guide. Use when building WebSocket features with Phoenix Channels — socket auth, join authorization, handlein/push/broadcast, Presence.

When should I use Phoenix Channels Essentials?

Phoenix Channels Essentials fits situations like: building WebSocket features with Phoenix Channels — socket auth; join authorization; handlein/push/broadcast.

How do I install Phoenix Channels Essentials in Claude Code?

Run `npx skills add j-morgan6/elixir-phoenix-guide --skill phoenix-channels-essentials -a claude-code`. Or copy the skill folder (skills/phoenix-channels-essentials in j-morgan6/elixir-phoenix-guide) into .claude/skills/phoenix-channels-essentials in your project. Claude Code loads it when a task matches its description.

How do I install Phoenix Channels Essentials in Codex?

Run `npx skills add j-morgan6/elixir-phoenix-guide --skill phoenix-channels-essentials -a codex`. Or copy the skill folder (skills/phoenix-channels-essentials in j-morgan6/elixir-phoenix-guide) into .agents/skills/phoenix-channels-essentials in your project. Codex loads it when a task matches its description.

Can I use Phoenix Channels Essentials in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add j-morgan6/elixir-phoenix-guide --skill phoenix-channels-essentials -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/phoenix-channels-essentials, .gemini/skills/phoenix-channels-essentials, .github/skills/phoenix-channels-essentials and .opencode/skills/phoenix-channels-essentials in your project.

What does Phoenix Channels Essentials need to run?

SKILL.md names no scripts, command-line tools or credentials: Phoenix Channels Essentials is instructions for the agent only.

Does Phoenix Channels Essentials access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Phoenix Channels Essentials safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Phoenix Channels Essentials use?

Phoenix Channels Essentials is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Phoenix Channels Essentials use?

About 2.3k tokens (SKILL.md is roughly 9.1k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Phoenix Channels Essentials?

Skills that share tags, products or a category with Phoenix Channels Essentials: Gemini Live API Dev (google-gemini/gemini-skills, 4.3k stars), Databuddy (databuddy-analytics/Databuddy, 1.2k stars), Phoenix Graphql (Arize-ai/phoenix, 12k stars) and Building AI Agent On Cloudflare (CommandCodeAI/agent-skills, 133 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Phoenix Channels Essentials?

j-morgan6 (a GitHub user) maintains it in j-morgan6/elixir-phoenix-guide, which has 167 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on July 6, 2026.

Source: j-morgan6/elixir-phoenix-guide on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.