Threadline.Plug (Threadline v0.2.0)

Copy Markdown View Source

Plug that extracts AuditContext from a Plug.Conn and stores it in conn.assigns[:audit_context].

Usage

# In a Phoenix router pipeline or endpoint:
plug Threadline.Plug

# With actor extraction (recommended):
plug Threadline.Plug, actor_fn: &MyApp.Auth.to_actor_ref/1

Options

  • :actor_fn — a function (Plug.Conn.t() -> ActorRef.t() | nil) that extracts the current actor from the conn. Called during call/2. If omitted, audit_context.actor_ref will be nil.

What is extracted

  • actor_ref — result of :actor_fn (or nil)
  • request_id — from x-request-id header, then conn.assigns[:request_id], then nil
  • correlation_id — from x-correlation-id header, or nil
  • remote_ip — from conn.remote_ip, formatted as a dotted-decimal string

PgBouncer note

This Plug does not call SET / SET LOCAL on the database connection. Request metadata lives on conn.assigns only. This design is safe for PgBouncer transaction-mode pooling.

PostgreSQL bridge (CTX-03)

To populate audit_transactions.actor_ref from capture triggers, the host must set a transaction-local GUC inside the same Ecto.Repo.transaction/1 as audited writes, before the first row change in that transaction:

json = Threadline.Semantics.ActorRef.to_map(actor_ref) |> Jason.encode!()

Repo.transaction(fn ->
  Repo.query!("SELECT set_config('threadline.actor_ref', $1::text, true)", [json])
  # ... audited writes here ...
end)

The trigger reads threadline.actor_ref via current_setting only; it never calls set_config itself (see gate-01-01.md). See test/threadline/capture/trigger_context_test.exs for the contract example.