Threadline (Threadline v0.5.0)

Copy Markdown View Source

Audit platform for Elixir teams using Phoenix, Ecto, and PostgreSQL.

Threadline combines trigger-backed row-change capture, rich action semantics (actor/intent/context), and operator-grade exploration.

Summary

Functions

Returns a keyset page of AuditTransaction records for a given actor, ordered by occurred_at descending, then id descending.

Returns change rows across tables for one actor.

Returns one keyset page of change rows across tables for one actor.

Returns the row snapshot for a schema record at a point in time.

Returns every %Threadline.Capture.AuditChange{} for a single audit_transactions.id.

Projects a single %Threadline.Capture.AuditChange{} into deterministic, JSON-friendly maps.

Returns change rows linked to one correlation_id with strict correlation semantics.

Returns one keyset page of changes linked to one correlation_id.

Exports matching audit changes as CSV (same filters / opts as Threadline.Query.timeline/2).

Exports matching audit changes as JSON (same filters / opts as Threadline.Query.timeline/2).

Returns AuditChange records for a given schema record, ordered by captured_at descending.

Returns one transaction-focused incident bundle with linked transaction/action context and packaged JSON-ready diffs.

Records a semantic audit action.

Returns the investigation slice for one schema row.

Returns one keyset page of row history for a single schema row.

Returns AuditChange records across tables, filtered by the given options, ordered by captured_at descending, then id descending (same total order as Threadline.Query.audit_changes_for_transaction/2; see Threadline.Query.timeline/2).

Returns one explicit keyset page of timeline results without changing timeline/2.

Returns one transaction-oriented investigation slice with linked transaction and optional action metadata.

Functions

actor_history(actor_ref, opts)

Returns a keyset page of AuditTransaction records for a given actor, ordered by occurred_at descending, then id descending.

Options

  • :repo — required Ecto.Repo module
  • :limit — integer, maximum number of records to return (default 50)
  • :after — cursor to fetch older records
  • :before — cursor to fetch newer records
  • :from — inclusive lower bound on occurred_at
  • :to — inclusive upper bound on occurred_at

actor_window(actor_ref, filters \\ [], opts \\ [])

Returns change rows across tables for one actor.

actor_window_page(actor_ref, filters \\ [], opts \\ [])

Returns one keyset page of change rows across tables for one actor.

as_of(schema_module, id, timestamp, opts)

Returns the row snapshot for a schema record at a point in time.

Options

audit_changes_for_transaction(transaction_id, opts)

Returns every %Threadline.Capture.AuditChange{} for a single audit_transactions.id.

Delegates to Threadline.Query.audit_changes_for_transaction/2. Pass repo: (required). Optional preload: (e.g. [:transaction]) is forwarded when non-empty — see the Query function for ordering and UUID rules.

change_diff(audit_change, opts \\ [])

Projects a single %Threadline.Capture.AuditChange{} into deterministic, JSON-friendly maps.

Delegates to Threadline.ChangeDiff.from_audit_change/2. See that module for :format (including :export_compat) and :expand_insert_fields.

correlation_bundle(correlation_id, filters \\ [], opts \\ [])

Returns change rows linked to one correlation_id with strict correlation semantics.

correlation_bundle_page(correlation_id, filters \\ [], opts \\ [])

Returns one keyset page of changes linked to one correlation_id.

export_csv(filters \\ [], opts \\ [])

Exports matching audit changes as CSV (same filters / opts as Threadline.Query.timeline/2).

See Threadline.Export and Threadline.Query.timeline/2.

export_json(filters \\ [], opts \\ [])

Exports matching audit changes as JSON (same filters / opts as Threadline.Query.timeline/2).

Pass json_format: :ndjson in opts for newline-delimited objects. See Threadline.Export and Threadline.Query.timeline/2.

history(schema_module, id, opts)

Returns AuditChange records for a given schema record, ordered by captured_at descending.

Structs include :changed_from when present in the row (sparse prior values on UPDATE under an opt-in per-table capture function; nil when disabled or on INSERT/DELETE rows).

Options

incident_bundle(transaction_id, opts \\ [])

Returns one transaction-focused incident bundle with linked transaction/action context and packaged JSON-ready diffs.

Returns {:ok, bundle} when the parent AuditTransaction exists, even if it has no captured changes. Returns {:error, :not_found} when the transaction row does not exist.

record_action(name, opts \\ [])

Records a semantic audit action.

Required options

  • :actor or :actor_ref%ActorRef{} identifying who performed the action
  • :repo — the Ecto.Repo module to use for insertion

Optional options

  • :status:ok or :error (default: :ok)
  • :verb — string or atom (e.g., "update")
  • :category — string or atom (e.g., "membership")
  • :reason — atom (e.g., :insufficient_permissions)
  • :comment — free-text string explanation
  • :correlation_id — cross-boundary correlation ID string
  • :request_id — request ID string (from Plug.RequestId / x-request-id)
  • :job_id — Oban job ID string

Returns

  • {:ok, %AuditAction{}} on success
  • {:error, %Ecto.Changeset{}} if changeset validation fails
  • {:error, :missing_actor} if no actor was provided
  • {:error, :invalid_actor_ref} if the actor fails ActorRef validation
  • {:error, :missing_repo} if :repo is not provided

row_history(schema_module, id, filters \\ [], opts \\ [])

Returns the investigation slice for one schema row.

This is the discoverable row-history helper for operators who want one row's changes without assembling table and primary-key predicates manually.

row_history_page(schema_module, id, filters \\ [], opts \\ [])

Returns one keyset page of row history for a single schema row.

timeline(filters \\ [], opts \\ [])

Returns AuditChange records across tables, filtered by the given options, ordered by captured_at descending, then id descending (same total order as Threadline.Query.audit_changes_for_transaction/2; see Threadline.Query.timeline/2).

Use timeline/2 for eager, bounded slices where returning a full list is still the simple path. Use timeline_page/2 for larger investigation windows where stable keyset traversal matters.

Options

  • :table — string or atom; filters by table_name
  • :actor_ref%ActorRef{}; filters by actor via a JOIN to audit_transactions
  • :fromDateTime; inclusive lower bound on captured_at
  • :toDateTime; inclusive upper bound on captured_at
  • :correlation_id — non-empty binary; only changes whose transaction is linked to an audit_actions row with that correlation id (strict semantics — see Threadline.Query).
  • :repo — required Ecto.Repo module

timeline_page(filters \\ [], opts \\ [])

Returns one explicit keyset page of timeline results without changing timeline/2.

Uses the same filter vocabulary as timeline/2, but returns a page struct so large investigation windows can be traversed incrementally while timeline/2 stays eager for existing callers. Paging controls live in opts:

  • :page_size — positive integer, defaults to 1000
  • :cursor%{captured_at: %DateTime{}, id: uuid} from a prior page's next_cursor
  • :repo — required Ecto.Repo module

transaction_context(transaction_id, opts \\ [])

Returns one transaction-oriented investigation slice with linked transaction and optional action metadata.

This packages the existing transaction drill-down primitive into a reusable helper contract without adding Phase 55 diff or incident-bundle rendering.