The Threadline Operator Surface provides a suite of mountable, drop-in LiveView screens to investigate row mutations, actor histories, and transaction contexts directly in your host application.
It is designed to be fully optional: phoenix, phoenix_live_view, phoenix_html, and phoenix_pubsub are optional dependencies, so capture-only integrations aren't forced to bring in UI code.
1-Minute Mount
To enable the UI, first ensure you have the optional dependencies installed in your mix.exs:
def deps do
[
# ...
{:threadline, "~> 0.3.0"},
{:phoenix, "~> 1.7"},
{:phoenix_live_view, "~> 1.0"},
{:phoenix_html, "~> 4.0"},
{:phoenix_pubsub, "~> 2.1"}
]
endThen, use the threadline_operator_surface/2 macro in your host application's router:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
import Threadline.OperatorSurface.Router
pipeline :admin_auth do
# You MUST provide your own pipeline to authenticate admins.
plug :require_authenticated_admin
end
scope "/admin", MyAppWeb do
pipe_through [:browser, :admin_auth]
# Mount the operator surface with access control options
threadline_operator_surface "/audit",
actor_fn: {MyApp.Audit, :current_actor},
authorize_fn: {MyApp.Audit, :authorize_operator}
end
endSecurity and Authorization (Fail-Closed Default)
Threadline adopts a fail-closed security posture by default. The threadline_operator_surface/2 macro requires a secure mount. Multi-tenancy and authorization stay host-owned.
Unless explicitly bypassed, the macro will fail at compile time unless one of the following is true:
- The route
scopehas at least onepipe_through. - The
:authorize_fnoption is provided. - The
:adopter_acknowledges_unauthenticated: trueoption is explicitly supplied (this raises in test and loudly logs a warning in prod).
:authorize_fn
The :authorize_fn callback determines whether the current request is allowed to access the operator surface and can scope queries to a specific tenant. It expects a tuple of {Module, :function_name} that takes the Plug.Conn.t() and returns:
{:ok, scope}- Allowed. Thescopewill be passed into investigation queries (e.g., restricting to a specific organization or tenant).:error- Denied.
Telemetry event [:threadline, :operator_surface, :authorize] is emitted with the outcome (:granted, :denied, or :error).
:actor_fn
The :actor_fn acts just like the native Threadline.Plug configuration, determining the identity performing actions in the operator surface.
Available Screens (v1.17)
The surface provides three must-have workflows out of the box. Together they answer the vast majority of investigation questions on click 1.
Incident Drill-down (/audit/transactions/:id)
Answers: "What exactly changed in this transaction, and why?"
Shows all mutations that occurred within a single database transaction, visualizing what was added, removed, or changed. This uses Threadline.incident_bundle/2 under the hood.
Actor Window (/audit/actors/:kind/:id)
Answers: "What did this user/system do recently?" A time-windowed view of all transactions initiated by a specific actor identity. From here, you can deep-link into specific Incident Drill-down screens.
Row History / As-of Sub-view (/audit/rows/:table/:pk)
Answers: "When did this specific record change, and what did it look like at 2:00 PM yesterday?" Reachable directly from drill-down rows, this screen shows the full mutation lifecycle of a single record and reconstructs its exact state as-of any point in time.
mix threadline.incident Companion Task
For operators who rely on SSH or CLI access (and for projects not using Phoenix), Threadline provides parity via a Mix task.
You can query the exact same incident data natively in the terminal without mounting the LiveView surface:
mix threadline.incident <transaction_id>