Use Threadline.Integrations.Sigra when your Phoenix host already uses Sigra for request authentication and impersonation.
This guide documents Threadline's current sigra-reference lane: a maintained
first-party reference path for a Phoenix host that already owns Sigra. It is a
reference claim, not a blanket support promise for arbitrary Sigra versions,
arbitrary auth layouts, or non-Phoenix hosts.
Install
Add Sigra to your host application's mix.exs as an optional dependency:
{:sigra, "~> 0.2", optional: true}This dependency is for hosts; never for the library.
In this lane, Sigra stays host-owned and soft-loaded. The root threadline
library keeps Sigra out of its dependency graph, while the reference path is
proven through the current example app, docs, and focused repo verification.
That host dependency shape is not a blanket support promise for every Sigra
0.2.x host.
Plug callback wire-up
Wire Threadline.Plug directly with both callbacks in the router pipeline
after your host has established request auth and any proxy-aware IP rewriting:
pipeline :api do
plug :accepts, ["json"]
plug Threadline.Plug,
actor_fn: &Threadline.Integrations.Sigra.actor_ref_from_conn/1,
context_overrides_fn: &Threadline.Integrations.Sigra.audit_context_overrides_from_conn/1
endactor_fn decides who acted. context_overrides_fn can add only additive
request metadata when the baseline conn extraction has no value.
Threadline.Plug always derives request_id from x-request-id first and
correlation_id from x-correlation-id first. The Sigra callback is therefore
supplemental: it fills missing values and never replaces an explicit header or
already-derived actor identity. If the callback returns unknown keys or any
non-map value, Threadline.Plug raises ArgumentError immediately.
Hosts still own transport normalization. If your deployment needs proxy-aware IP
handling, rewrite conn.remote_ip upstream before Threadline.Plug runs.
This remains a direct callback pair, not a second adapter layer: the host wires
Sigra state into Threadline.Plug, and Threadline keeps the additive-only
request metadata contract intact.
Sigra covers request capture only. It does not secure /audit, export routes,
tenancy, roles, or policy admin for you.
Surface and export auth stay host-owned
The auth split is intentional:
- request capture auth belongs in
actor_fnpluscontext_overrides_fn - LiveView surface auth belongs in
authorize_fn - export HTTP auth belongs in
export_authorize_fn, or it falls back toauthorize_fnwith a synthetic%{assigns: conn.assigns}mirror
Keep your browser/admin boundary in front of /audit, then let
authorize_fn fail closed inside the mounted surface. Keep export routes behind
the same host-owned posture. Sigra does not become the auth story for the
operator surface or exports.
The canonical operator-surface callback shape is one shared %{assigns: assigns}
function. Keep it host-owned, let it return :ok or {:ok, scope}, and treat
that scope as opaque host data. For a support-read-only lane, reuse the same
/audit tree, return a host-owned scope such as
%{access: :support_read_only, organization_id: "org_123"}, and default to
exports: false. Do not treat Sigra as a page-level authorization DSL or a
Threadline-owned roles system.
Behaviors locked by SPEC
- Impersonation maps to
:admin. Whencurrent_scope.impersonating_fromis non-nil,actor_ref_from_conn/1returns an admin actor and keeps the impersonated user encoded in correlation metadata. - API token maps to
:service_account. Whencurrent_scope.auth_methodis:api_tokenor:jwt,actor_ref_from_conn/1returns a service account actor usingcurrent_scope.id. - Active organization adds a suffix. When Sigra exposes an active organization, the adapter appends
:org:<id>to the derived correlation id. - Anonymous / Sigra-absent returns
nil. If the request has no supported Sigra actor shape,actor_ref_from_conn/1returns rawnil. x-correlation-idheader always wins. When the header is present,audit_context_overrides_from_conn/1returns%{}soThreadline.Plugpreserves the request value instead of replacing it.x-request-idand any existing actor identity also stay authoritative.context_overrides_fnis additive request metadata only; it is not a second actor path.- Plug-only adapter; no telemetry subscription in v1.
These behaviors are the supported reference semantics for the current guide and example app. They are not a statement that every Sigra-backed Phoenix host or every future Sigra release is automatically covered by Threadline.
correlation_id formats
- Impersonation:
sigra-imp:<session_id>:user:<imp_user_id> - Plain session:
sigra-session:<session_id> - API token:
sigra-token:<token_id> - Anonymous / Sigra absent: no override /
%{}
Soft-dep contract
Code.ensure_loaded?(Sigra.Session) is the single soft-dependency gate.
When that check is false:
actor_ref_from_conn/1returnsnilaudit_context_overrides_from_conn/1returns%{}
That soft-dep contract is part of the sigra-reference lane. The host owns
whether Sigra is present; Threadline only adapts that state when the dependency
is loaded.