Provides LiveView routing for the Arcana dashboard.
Usage
Add to your router:
import ArcanaWeb.Router
scope "/" do
pipe_through :browser
arcana_dashboard "/arcana"
endOptions
:live_socket_path- The path to the LiveView socket. Defaults to"/live".:repo- The Ecto repo to use for Arcana operations. If not provided, falls back toApplication.get_env(:arcana, :repo).:on_mount- Optional list ofPhoenix.LiveView.on_mount/1callbacks to add to the dashboard's live_session.:live_session_name- The name of the dashboard's live_session. Defaults to:arcana_dashboard. Phoenix requires live_session names to be unique within a router, so mounting the dashboard more than once (say, a superuser dashboard plus a scoped one) needs a distinct name for each extra mount.:collections- Optional{module, function}pair that scopes the dashboard to a subset of collections. The function receives the%Plug.Conn{}of the page request and must return either:all(no restriction) or a list of collection names. Every dashboard surface (listings, search, ask, ingest, evaluation, maintenance, stats) is limited to those collections, and events naming any other collection are rejected server-side. A plain{module, function}tuple is required (not a function capture) so the route metadata stays serializable. See "Scoping collections" for when the decision is re-evaluated.
Example with options
arcana_dashboard "/arcana",
repo: MyApp.Repo,
on_mount: [MyAppWeb.Auth]Scoping collections
arcana_dashboard "/arcana",
collections: {MyAppWeb.ArcanaAccess, :allowed_collections}
defmodule MyAppWeb.ArcanaAccess do
def allowed_collections(conn) do
case conn.assigns.current_user do
%{admin: true} -> :all
%{tenant: tenant} -> ["#{tenant}-docs"]
end
end
endWhen the decision is made
The function runs while the page request is being served, and its result
is snapshotted into the LiveView session: Phoenix signs it into the
rendered data-phx-session payload, which stays valid for the session's
max age (14 days by default). The websocket connect, every reconnect,
and every live_patch/live_redirect inside the dashboard read back
that snapshot instead of calling the function again. Only a full page
request re-runs it.
So narrowing a user's permissions does not narrow an already-rendered
dashboard. Until the user loads a fresh page, they keep the scope they
mounted with. The on_mount hook cannot close that gap on its own —
there is no %Plug.Conn{} in a LiveView mount to re-resolve from.
The mitigation is to cut the socket when permissions change. Set a
live_socket_id per user in your session (Phoenix's
put_session(conn, :live_socket_id, "users_socket:#{user.id}")), then
broadcast a disconnect when their access changes:
MyAppWeb.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})The client reconnects through a full request, which re-runs the MFA with
the current %Plug.Conn{}. Pair it with a short session max age if you
need a hard upper bound on how long a stale scope can live.
Summary
Functions
Defines an Arcana dashboard route.