Threadline.Audit runs a domain write inside one database transaction and optionally links the write to a semantic action.
Call from Phoenix context modules (or Oban workers) after Threadline.Plug /
Threadline.Job have resolved %Threadline.Semantics.AuditContext{} and
%Threadline.Semantics.ActorRef{}. The helper does not replace those edge modules.
Options
:actor_ref—%Threadline.Semantics.ActorRef{}(required unlessaudit_context:orallow_missing_actor: trueon capture-only paths):audit_context—%Threadline.Semantics.AuditContext{}; suppliesactor_ref,correlation_id, andrequest_id(top-level opts override):action— atom or{:name, action_opts}forThreadline.record_action/2after the callback succeeds; linksaudit_transactions.action_idviatxid_current()(correlation-ready path):capture_only— same as omitting:action(capture-only path):allow_missing_actor— when true and:actionis absent, permits nilactor_ref(non-recommended for multi-tenant SaaS):transaction_meta— map stored onaudit_transactions.metaon both correlation-ready (:actionpresent) and capture-only paths:correlation_id,:request_id,:job_id— forwarded torecord_action/2when:actionis an atom
Capture-only vs correlation-ready
:action | Strict :correlation_id timeline/export |
|---|---|
| present | correlation-ready — action_id linked |
| absent | capture-only — strict filters won't match |
See Threadline.timeline/2 for strict :correlation_id semantics.
Callback contract
Pass fn -> domain_result end with Repo captured in the closure.
Allowed: domain inserts/updates/deletes; Repo.rollback/1 on failure.
Forbidden inside the callback: set_config for threadline.actor_ref,
Threadline.record_action/2, and nested Repo.transaction/1 (breaks GUC and
txid_current() linkage).
Example
Threadline.Audit.transaction(MyApp.Repo, [
actor_ref: actor_ref,
action: :post_created,
transaction_meta: %{"organization_id" => organization_id}
], fn ->
MyApp.Repo.insert!(Post.changeset(%Post{}, attrs))
end)Return shape
Returns {:ok, result} | {:error, reason}. result is the callback result,
treated as caller-owned and opaque to Threadline. On success, it is the
callback return with
:audit_transaction_id merged when capture produced an audit_transactions row
(map callback) or wrapped as %{result: value, audit_transaction_id: id} for
non-map returns. On failure, {:error, reason} (:missing_actor,
:missing_audit_transaction_for_link, changesets, or rollback reasons).
Errors
{:error, :missing_actor}— nilactor_refwhen required{:error, :missing_audit_transaction_for_link}— linkageupdate_allcount != 1
Summary
Types
An action name passed to Threadline.record_action/2, optionally paired with its typed options.
An option accepted by transaction/3.
Functions
Runs fun inside repo.transaction/1 after setting the transaction-local
threadline.actor_ref GUC and optionally recording a semantic action, then returns
{:ok, result} or {:error, reason}. On success, result may include an
:audit_transaction_id when capture creates an audit_transactions row: Threadline merges the
id into map results and wraps non-map results as %{result: value, audit_transaction_id: id}.
Types
@type action_opt() :: atom() | {atom(), [Threadline.record_action_opt()]}
An action name passed to Threadline.record_action/2, optionally paired with its typed options.
@type transaction_opt() :: {:actor_ref, Threadline.Semantics.ActorRef.t() | nil} | {:audit_context, Threadline.Semantics.AuditContext.t() | nil} | {:action, action_opt() | nil} | {:capture_only, boolean()} | {:allow_missing_actor, boolean()} | {:transaction_meta, Threadline.json_map() | nil} | {:correlation_id, String.t() | nil} | {:request_id, String.t() | nil} | {:job_id, String.t() | nil} | Threadline.storage_schema_opt()
An option accepted by transaction/3.
Functions
@spec transaction(module(), [transaction_opt()], (-> result)) :: {:ok, result} | {:error, term()} when result: term()
Runs fun inside repo.transaction/1 after setting the transaction-local
threadline.actor_ref GUC and optionally recording a semantic action, then returns
{:ok, result} or {:error, reason}. On success, result may include an
:audit_transaction_id when capture creates an audit_transactions row: Threadline merges the
id into map results and wraps non-map results as %{result: value, audit_transaction_id: id}.
See module doc for options, callback rules, and return envelope.