Threadline
CI: Runs on GitHub Actions.
Auditing for Phoenix.
Threadline is an open-source audit library for Elixir teams using Phoenix, Ecto, and PostgreSQL. It combines PostgreSQL trigger capture with semantic actions, then exposes the audit trail through Threadline.Plug, Threadline.record_action/2, Threadline.history/3, Threadline.timeline/2, Threadline.timeline_page/2, Threadline.incident_bundle/2, Threadline.export_json/2, and Threadline.as_of/4.
Use it when you want the audit layer in your app, not a separate event system or a black box.
Start here
- Evaluating: open the HexDocs for the full API.
- Adopting in Phoenix SaaS: read guides/getting-started-saas.md.
- Understanding the integration seams: read guides/integration-contracts.md.
- Checking the named support lanes: read guides/upgrade-path.md for the canonical
capture-only,phoenix-surface, andsigra-referencematrix. - Using Sigra: read guides/integrations/sigra.md.
- Contributing: follow
CONTRIBUTING.mdand runmix ci.all.
What you get
- Capture: trigger-backed row-change history in PostgreSQL with
Threadline.Plug. - Semantics: actor, intent, correlation, and request context via
Threadline.record_action/2. - Exploration: timelines and history with
Threadline.timeline/2,Threadline.timeline_page/2, andThreadline.history/3. - Operations: exports, snapshots, coverage checks, retention, redaction, and health tooling via
Threadline.export_json/2andThreadline.as_of/4.
Quick Start
Add
threadlineto your dependencies:def deps do [ {:threadline, "~> 0.5"} ] endInstall and migrate:
mix threadline.install mix ecto.migrateRegister triggers for the tables you want to audit:
mix threadline.gen.triggers --tables users,posts,comments mix ecto.migrateAdd the plug and set an actor inside your transaction:
# lib/my_app_web/router.ex pipeline :browser do plug Threadline.Plug, actor_fn: &MyApp.Auth.to_actor_ref/1 end alias Threadline.Semantics.ActorRef actor_ref = ActorRef.user("user:123") json = ActorRef.to_map(actor_ref) |> Jason.encode!() MyApp.Repo.transaction(fn -> MyApp.Repo.query!("SELECT set_config('threadline.actor_ref', $1::text, true)", [json]) MyApp.Repo.insert!(%MyApp.Post{title: "Ship audit logs"}) end) {:ok, _action} = Threadline.record_action(:post_published, repo: MyApp.Repo, actor: actor_ref )Query the audit trail:
Threadline.history(MyApp.Post, post.id, repo: MyApp.Repo) Threadline.timeline([table: "posts"], repo: MyApp.Repo) Threadline.timeline_page([table: "posts"], repo: MyApp.Repo, page_size: 200) Threadline.export_json([table: "posts"], repo: MyApp.Repo) Threadline.as_of(MyApp.Post, post.id, DateTime.utc_now(), repo: MyApp.Repo)
Use Threadline.timeline/2 for smaller eager slices. When the window is too
large to read eagerly, switch to Threadline.timeline_page/2 and continue with
next_cursor instead of offset pagination.
Keep the root README as the map, then use guides/domain-reference.md for the canonical "which public API first?" table, guides/getting-started-saas.md for the canonical first-hour Phoenix walkthrough, and guides/incident-playbook.md for operator recipes.
Operator Surface
Threadline provides an optional, drop-in LiveView UI to investigate the audit
trail natively in your app, including a polled coverage dashboard at
/audit/coverage, a read-only redaction drift viewer at
/audit/policy/redaction, and parity Mix tasks
mix threadline.health.coverage plus mix threadline.policy.show. Ensure you
have the optional Phoenix surface dependencies declared in mix.exs. Threadline
stays in-tree for now rather than splitting the UI into a separate package; keep
the closeout rationale and exact current proof pins in
guides/upgrade-path.md rather than copying them into
generic install docs.
1-Minute Mount
defmodule MyAppWeb.Router do
use MyAppWeb, :router
import Threadline.OperatorSurface.Router
# Must pipe through your own authentication
scope "/audit", MyAppWeb do
pipe_through [:browser, :require_authenticated_admin]
threadline_operator_surface "/",
actor_fn: &MyApp.Audit.current_actor/1,
authorize_fn: &MyApp.Audit.authorize_operator/1
end
end
For the full first-hour mounted walkthrough, read
guides/getting-started-saas.md. For the
"fail-closed" security default, authorization setup, and screen inventory, read
the Operator Surface guide. For the broader host
and framework contract across Threadline.Plug, Threadline.Job,
Threadline.Integrations.*, and operator-surface auth/export auth, read
guides/integration-contracts.md. For the
current support claims, stay with
guides/upgrade-path.md rather than inferring broader
compatibility from the README.
Notes
- Threadline works with PgBouncer transaction pooling.
-
Redaction drift uses three states:
Config matches deployed,Drift detected, andCould not introspect; rerunmix threadline.gen.triggersif the latter two appear. - Redaction, retention, export, and continuity live in the guides and HexDocs.
- Next operator reads after the first install are guides/performance.md and guides/incident-playbook.md.