Simple presence tracking system for PhoenixKit admin interface.
This is a lightweight alternative to Phoenix.Presence that works without requiring a full OTP application supervision tree.
Keys and multi-tab tracking
An authenticated visitor is tracked under user_key(user_uuid, session_id)
("user:<uuid>:<session_id>"), an anonymous one under
"anonymous:<session_id>" — one ETS row per (identity, session), not per
process. session_id is a stable per-login identifier that is the same
across every LiveView mount sharing one login or one anonymous browser
session, so opening a second tab, or mounting a second LiveView under the
same session, tracks the SAME row instead of creating a second one: each
track_* call adds its calling process's monitor to that row's monitor set
rather than replacing the previous tab's. The row is deleted — and a
*_session_disconnected event broadcast — only once its LAST monitor goes
down; closing one tab while another is still open just shrinks the monitor
set.
For an authenticated visitor tracked through PhoenixKitWeb.Users.Auth's
on_mount hooks, session_id is a SHA-256 digest of the session token
(Base.url_encode64/2, not the raw token or the raw live_socket_id
itself) — stable per login for idempotent keying, but not reversible if it
ends up somewhere it shouldn't (a log line, a PubSub event payload). A call
site that still passes a raw session["live_socket_id"]
(PhoenixKitWeb.Live.Dashboard.Overview's own local tracking call, which
predates this scheme, and phoenix_kit_entities's own hook, a separate
package) produces a DIFFERENT session_id for the same login until it is
updated to match — a second, otherwise-idempotent row rather than a merge.
connected_at is pinned to the row's first appearance: a second tab
merges its metadata into the existing row without resetting the timestamp.
This table is local to the BEAM node it runs on — presence does not merge across nodes in a multi-node deployment, so a visitor connected to node A is invisible to a "Live sessions" page served from node B.
Summary
Functions
Returns a specification to start this module under a supervisor.
Gets presence statistics.
Gets the presence topic name.
Lists all active sessions.
Lists anonymous sessions only.
Lists authenticated sessions only.
Starts the simple presence system.
Subscribes to presence events.
Tracks an anonymous session.
Tracks an authenticated user session.
Updates metadata for an existing presence.
Builds the idempotent ETS key for a user's presence row.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Gets presence statistics.
Gets the presence topic name.
Lists all active sessions.
Lists anonymous sessions only.
Lists authenticated sessions only.
Starts the simple presence system.
Subscribes to presence events.
Tracks an anonymous session.
Tracks an authenticated user session.
Idempotent by (user.uuid, metadata.session_id) — a second track_user/2
call for the same user and session (e.g. a second open tab, or another
LiveView mounted under the same login) merges into the existing presence
row instead of creating a second one or replacing the first tab's monitor.
Updates metadata for an existing presence.
Builds the idempotent ETS key for a user's presence row.
session_id: nil falls back to one row per user (pre-session-keying
behavior) — a caller that tracks a user without a session_id still collides
across tabs on that single row.