Threadline is an audit platform that captures database row changes and links them to application actions and execution context. Threadline combines trigger-backed row-change capture, rich action semantics (actor/intent/context), and operator-grade exploration.
Jobs
- Capture & Transactions: read a captured transaction with
audit_transaction/2,transaction_context/2, orincident_bundle/2. - Querying & Timelines: inspect all changes with
timeline/2, page through them withtimeline_page/2, or reconstruct a row withrow_history/3. - Actions & Context: record semantic intent with
record_action/2, follow an actor withactor_history/2, or inspect a correlation withcorrelation_bundle/3. - Operations: export captured changes with
export_csv/2orexport_json/2; retention, health and coverage live in their dedicated modules and tasks.
The older names (history/3, row_history/4, row_history_page/4,
actor_window_page/3, correlation_bundle_page/3) remain as deprecated
delegates through Threadline 1.x.
Each single-subject lookup (audit_transaction/2, transaction_context/2,
incident_bundle/2) returns {:ok, value} or {:error, :not_found} and
has a ! sibling that raises Threadline.NotFoundError.
Composing your own Ecto query
Threadline.Query.timeline_query/1 is the one supported escape hatch for
composing your own Ecto query on top of the timeline. It returns the
unexecuted AuditChange Ecto.Query behind timeline/2 — same filters,
same captured_at descending, id descending order — so you can add your
own where/select/pagination before calling Repo. Unlike timeline/2,
it does not validate filter keys, so pass only the documented timeline/2
filter keys.
Summary
Capture & Transactions
Returns every %Threadline.Capture.AuditChange{} for a single audit_transactions.id.
Returns {:ok, %Threadline.Capture.AuditTransaction{}} for one visible
transaction, or {:error, :not_found} when it is absent or out of scope.
Returns the visible %Threadline.Capture.AuditTransaction{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Returns {:ok, %Threadline.Investigation.IncidentBundle{}} with linked
transaction/action context and ordered changes, or {:error, :not_found}.
Returns the visible %Threadline.Investigation.IncidentBundle{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Returns {:ok, %Threadline.Investigation.LinkedTransaction{}} for one
visible transaction and its visible changes, or {:error, :not_found}.
Returns the visible %Threadline.Investigation.LinkedTransaction{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Actions & Context
Returns a %Threadline.Page{} of AuditTransaction rows for one actor,
newest first by occurred_at and id.
Returns %Threadline.Investigation.LinkedChange{} rows made by one actor
across audited tables, newest first, as a list or %Threadline.Page{}.
Returns a %Threadline.Page{} of linked changes made by one actor using the retired first-page convention.
Returns linked change rows for one correlation_id, newest first, as a list
or %Threadline.Page{}. Correlation uses strict inner-join semantics.
Returns a %Threadline.Page{} of linked changes for one correlation using the retired first-page convention.
Records an %AuditAction{} that names the application event associated with a database transaction and returns {:ok, AuditAction} on success or {:error, reason} for a changeset failure, missing actor, invalid actor reference, or missing repository.
Use actor_history/2 or correlation_bundle/3 to explore captured changes associated with the actor or correlation after recording the action.
Operations
Returns CSV iodata and truncation metadata for matching captured changes.
Returns JSON iodata and truncation metadata for matching captured changes.
Types
An option accepted by actor_history/2; :after, :before, and :limit are deprecated aliases.
A filter for changes made by one actor.
A filter accepted by the legacy paged actor-window shape.
An option accepted by the legacy paged actor-window shape.
An option accepted by as_of/4.
An option accepted by audit_changes_for_transaction/2.
An option accepted by change_diff/2.
A filter for changes linked to one correlation id.
A filter accepted by the legacy paged correlation-bundle shape.
An option accepted by the legacy paged correlation-bundle shape.
An option accepted by export_csv/2.
An option accepted by export_json/2.
An option accepted by the legacy unbounded history read.
A string-keyed JSON map decoded from jsonb or bound for JSON encoding; owning types list guaranteed keys and new keys may be added.
A JSON value supported in a captured or exported JSON map.
An option accepted by the single-transaction lookup functions.
An option accepted by record_action/2; supply either :actor or :actor_ref and a repository.
An option that selects the Ecto repository used for a read.
A row-history filter accepted by the legacy filters-and-options shapes.
An option accepted by the legacy row-history read shape.
An option accepted by row_history/3.
An option accepted by the legacy paged row-history read shape.
A single host key or every field of a composite key as a map or keyword list.
A scalar host-table key value supported by Ecto.
A caller-owned scope value or callback; the scope value is opaque to Threadline.
A query callback that applies the caller's scope; the scope value is opaque to Threadline.
An option that selects a Threadline storage schema override.
A timeline filter for captured changes.
An option accepted by timeline/2.
An option accepted by timeline_page/2.
An option accepted by actor_window/3 and correlation_bundle/3.
Capture & Transactions
Capture is installed by the triggers your migrations generate with mix threadline.gen.triggers; these functions read one captured AuditTransaction by id.
@spec audit_changes_for_transaction(Ecto.UUID.t(), [audit_changes_opt()]) :: [ Threadline.Capture.AuditChange.t() ]
Returns every %Threadline.Capture.AuditChange{} for a single audit_transactions.id.
Use transaction_context/2 when the transaction, action, and linked changes belong in one result. The query applies the transaction visibility predicate supplied through :scope_query_fn.
transaction_id is audit_transactions.id. Accepts UUID strings or 16-byte
binaries; raises ArgumentError when the id is not a valid UUID. Returns []
when the UUID is well-formed but no matching rows exist. Ordered by
captured_at descending, then id descending — the same total order as
timeline/2.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— storage schema override. Optional.:scope— caller-owned scope value. Optional and opaque to Threadline.:scope_query_fn— callback that adds caller scope predicates. Optional.:preload— optional association list, e.g.[:transaction], forwarded torepo.preload/3when non-empty. Preloading:actionunder:transaction(transaction: :action/transaction: [:action, ...]) is deprecated: it still hydratestransaction.action, but emits one warning per call and will be removed no earlier than Threadline 2.0. Prefertransaction_context/2.
Returns
- A list of
%Threadline.Capture.AuditChange{}rows ordered bycaptured_atandiddescending. - Raises
ArgumentErrorfor an invalid UUID or preload value, andKeyErrorwhen:repois missing.
Examples
Threadline.audit_changes_for_transaction(transaction_id, repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec audit_transaction(Ecto.UUID.t(), [lookup_opt()]) :: {:ok, Threadline.Capture.AuditTransaction.t()} | {:error, :not_found}
Returns {:ok, %Threadline.Capture.AuditTransaction{}} for one visible
transaction, or {:error, :not_found} when it is absent or out of scope.
Use audit_transaction!/2 when absence is a bug. The linked action is
hydrated when present; a malformed UUID string is treated as not found.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Receives the transaction header query and context.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
{:ok, %Threadline.Capture.AuditTransaction{}}— the visible transaction, with its action hydrated when present.{:error, :not_found}— no visible transaction matches the UUID.- Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.audit_transaction(transaction_id, repo: MyApp.Repo)
@spec audit_transaction!(Ecto.UUID.t(), [lookup_opt()]) :: Threadline.Capture.AuditTransaction.t()
Returns the visible %Threadline.Capture.AuditTransaction{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Use audit_transaction/2 when absence should be returned as
{:error, :not_found}. Options and UUID handling follow that function.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Receives the transaction header query and context.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
%Threadline.Capture.AuditTransaction{}— the visible transaction.- Raises
Threadline.NotFoundErrorwhen no visible transaction matches the UUID. - Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.audit_transaction!(transaction_id, repo: MyApp.Repo)
@spec incident_bundle(Ecto.UUID.t(), [lookup_opt()]) :: {:ok, Threadline.Investigation.IncidentBundle.t()} | {:error, :not_found}
Returns {:ok, %Threadline.Investigation.IncidentBundle{}} with linked
transaction/action context and ordered changes, or {:error, :not_found}.
Use incident_bundle!/2 when absence is a bug. An existing transaction with
no visible changes returns changes: []; its row and changes come from two
independent queries.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Applies predicates to both read queries.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
{:ok, %Threadline.Investigation.IncidentBundle{}}— the transaction, action context, and visible changes.{:error, :not_found}— no visible transaction matches the UUID.- Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.incident_bundle(transaction_id, repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec incident_bundle!(Ecto.UUID.t(), [lookup_opt()]) :: Threadline.Investigation.IncidentBundle.t()
Returns the visible %Threadline.Investigation.IncidentBundle{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Use incident_bundle/2 when absence should be returned as
{:error, :not_found}. Options and UUID handling follow that function.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Applies predicates to both read queries.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
%Threadline.Investigation.IncidentBundle{}— the transaction, action context, and visible changes.- Raises
Threadline.NotFoundErrorwhen no visible transaction matches the UUID. - Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.incident_bundle!(transaction_id, repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec transaction_context(Ecto.UUID.t(), [lookup_opt()]) :: {:ok, Threadline.Investigation.LinkedTransaction.t()} | {:error, :not_found}
Returns {:ok, %Threadline.Investigation.LinkedTransaction{}} for one
visible transaction and its visible changes, or {:error, :not_found}.
Use transaction_context!/2 when absence is a bug. The transaction and its
changes are read by two independent queries; an existing transaction with
no visible changes returns changes: [].
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Applies predicates to both the transaction and changes queries.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
{:ok, %Threadline.Investigation.LinkedTransaction{}}— the transaction and visible changes.{:error, :not_found}— no visible transaction matches the UUID.- Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.transaction_context(transaction_id, repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec transaction_context!(Ecto.UUID.t(), [lookup_opt()]) :: Threadline.Investigation.LinkedTransaction.t()
Returns the visible %Threadline.Investigation.LinkedTransaction{} or raises
Threadline.NotFoundError when the transaction is absent or out of scope.
Use transaction_context/2 when absence should be returned as
{:error, :not_found}. Options and UUID handling follow that function.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Applies predicates to both read queries.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
%Threadline.Investigation.LinkedTransaction{}— the transaction and visible changes.- Raises
Threadline.NotFoundErrorwhen no visible transaction matches the UUID. - Raises
ArgumentErrorfor a non-binary id andKeyErrorwhen:repois missing.
Examples
Threadline.transaction_context!(transaction_id, repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
Querying & Timelines
Read captured changes across tables, follow a row through time, or inspect its state at a point in time.
Actions & Context
Record semantic actions and connect captured changes to actors, requests, jobs, and correlations.
@spec actor_history(Threadline.Semantics.ActorRef.t(), [actor_history_opt()]) :: Threadline.Page.t(Threadline.Capture.AuditTransaction.t())
Returns a %Threadline.Page{} of AuditTransaction rows for one actor,
newest first by occurred_at and id.
Use actor_window/3 for the change rows made by an actor across tables.
Walk pages with :cursor; stop when has_more is false.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Adds the caller's scope predicates to the read.:from—DateTime. Optional. Inclusive lower bound onoccurred_at.:to—DateTime. Optional. Inclusive upper bound onoccurred_at.:cursor—:startor an actor-history cursor. Optional.nilraisesArgumentError.:page_size— positive integer. Defaults to50.
Deprecated options
:after— actor-history cursor. Deprecated; use:cursorinstead.:before— actor-history cursor. Deprecated; usecursor: {:before, cursor}instead.:limit— positive integer. Deprecated; use:page_sizeinstead.
Each still works, and each emits one deprecation warning per call. Removal
is no earlier than Threadline 2.0. :cursor combined with :after or
:before raises ArgumentError, as does :page_size combined with
:limit.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
- A
%Threadline.Page{}of matching transactions. - Raises
ArgumentErrorfor an invalid cursor or conflicting pagination options, andKeyErrorwhen:repois missing.
Examples
Threadline.actor_history(actor_ref, repo: MyApp.Repo)
@spec actor_window(Threadline.Semantics.ActorRef.t(), [actor_window_filter()], [ window_opt() ]) :: [Threadline.Investigation.LinkedChange.t()] | Threadline.Page.t(Threadline.Investigation.LinkedChange.t())
Returns %Threadline.Investigation.LinkedChange{} rows made by one actor
across audited tables, newest first, as a list or %Threadline.Page{}.
Use actor_history/2 for one row per transaction. Pass :cursor to walk the
changes in keyset pages.
Filters
:table— string or atom. Optional. Matchestable_name.:from—DateTime. Optional. Inclusive lower bound oncaptured_at.:to—DateTime. Optional. Inclusive upper bound oncaptured_at.:correlation_id— non-empty string. Optional. Matches linked action correlation.:repo—Ecto.Repomodule. Optional when supplied inopts.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Adds the caller's scope predicates to the read.:cursor—:startor a prior page cursor. Optional. Returns a page instead of a list.:page_size— positive integer. Defaults to1000; applies when:cursoris set.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
- A list of
%Threadline.Investigation.LinkedChange{}rows, or a%Threadline.Page{}when:cursoris set. - Raises
ArgumentErrorfor invalid filters, cursor values, or a missing:repo.
Examples
Threadline.actor_window(actor_ref, [table: "members"], repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec actor_window_page( Threadline.Semantics.ActorRef.t(), [actor_window_page_filter()], [ actor_window_page_opt() ] ) :: Threadline.Page.t(Threadline.Investigation.LinkedChange.t())
Returns a %Threadline.Page{} of linked changes made by one actor using the retired first-page convention.
Use actor_window/3 with cursor: :start and optional page_size:. An absent or nil cursor here still begins the first page.
Filters
:table— string or atom. Optional; matchestable_name.:from— inclusive lower bound oncaptured_at. Optional.:to— inclusive upper bound oncaptured_at. Optional.:correlation_id— string. Optional; matches the linked action correlation.:repo—Ecto.Repomodule. Optional when supplied inopts.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— storage schema override. Optional.:scope— caller-owned scope value. Optional and opaque to Threadline.:scope_query_fn— callback that adds caller scope predicates. Optional.:cursor—:startor a prior change cursor. Optional; an absent ornilvalue begins the first page.:page_size— positive integer. Optional; defaults to1000.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
- A
%Threadline.Page{}of matching linked changes. - Raises
ArgumentErrorfor invalid filters, options, or cursor, andKeyErrorwhen:repois missing.
Examples
Threadline.actor_window_page(actor_ref, [table: "members"], repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec correlation_bundle(String.t(), [correlation_bundle_filter()], [window_opt()]) :: [Threadline.Investigation.LinkedChange.t()] | Threadline.Page.t(Threadline.Investigation.LinkedChange.t())
Returns linked change rows for one correlation_id, newest first, as a list
or %Threadline.Page{}. Correlation uses strict inner-join semantics.
Use actor_window/3 to read changes for one actor across tables. Pass
:cursor to walk correlation results in keyset pages.
Filters
:table— string or atom. Optional. Matchestable_name.:actor_ref—%ActorRef{}. Optional. Matches the transaction actor.:from—DateTime. Optional. Inclusive lower bound oncaptured_at.:to—DateTime. Optional. Inclusive upper bound oncaptured_at.:repo—Ecto.Repomodule. Optional when supplied inopts.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Adds the caller's scope predicates to the read.:cursor—:startor a prior page cursor. Optional. Returns a page instead of a list.:page_size— positive integer. Defaults to1000; applies when:cursoris set.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
- A list of
%Threadline.Investigation.LinkedChange{}rows, or a%Threadline.Page{}when:cursoris set. - Raises
ArgumentErrorfor invalid filters, cursor values, or a missing:repo.
Examples
Threadline.correlation_bundle("member-42", [table: "members"], repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec correlation_bundle_page(String.t(), [correlation_bundle_page_filter()], [ correlation_bundle_page_opt() ]) :: Threadline.Page.t(Threadline.Investigation.LinkedChange.t())
Returns a %Threadline.Page{} of linked changes for one correlation using the retired first-page convention.
Use correlation_bundle/3 with cursor: :start and optional page_size:. An absent or nil cursor here still begins the first page.
Filters
:table— string or atom. Optional; matchestable_name.:actor_ref—%ActorRef{}. Optional; matches the transaction actor.:from— inclusive lower bound oncaptured_at. Optional.:to— inclusive upper bound oncaptured_at. Optional.:repo—Ecto.Repomodule. Optional when supplied inopts.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— storage schema override. Optional.:scope— caller-owned scope value. Optional and opaque to Threadline.:scope_query_fn— callback that adds caller scope predicates. Optional.:cursor—:startor a prior change cursor. Optional; an absent ornilvalue begins the first page.:page_size— positive integer. Optional; defaults to1000.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
- A
%Threadline.Page{}of matching linked changes. - Raises
ArgumentErrorfor invalid filters, options, or cursor, andKeyErrorwhen:repois missing.
Examples
Threadline.correlation_bundle_page("member-42", [table: "members"], repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec record_action(atom(), [record_action_opt()]) :: {:ok, Threadline.Semantics.AuditAction.t()} | {:error, Ecto.Changeset.t()} | {:error, :missing_actor | :invalid_actor_ref | :missing_repo}
Records an %AuditAction{} that names the application event associated with a database transaction and returns {:ok, AuditAction} on success or {:error, reason} for a changeset failure, missing actor, invalid actor reference, or missing repository.
Use actor_history/2 or correlation_bundle/3 to explore captured changes associated with the actor or correlation after recording the action.
Options
:actor—%ActorRef{}identifying who performed the action. Required unless:actor_refis supplied.:actor_ref—%ActorRef{}identifying who performed the action. Required unless:actoris supplied.:repo—Ecto.Repomodule used for insertion. Required.:storage_schema— Threadline storage schema override. Optional.:status—:okor:error. Optional; defaults to:ok.:verb— string or atom describing the operation, such as"update". Optional.:category— string or atom grouping the action, such as"membership". Optional.:reason— string or atom explaining the action. Optional.:comment— free-text explanation. Optional.:correlation_id— string connecting work across boundaries. Optional.:request_id— request ID, such as one fromPlug.RequestId. Optional.:job_id— Oban job ID. Optional.
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:repois not provided
Examples
{:ok, actor} = Threadline.Semantics.ActorRef.new(:user, "u-42")
Threadline.record_action(:member_role_changed,
actor: actor, repo: MyApp.Repo,
category: "membership", verb: "update",
correlation_id: "request-8f2"
)
Operations
Export captured changes; retention, health, and coverage live in Threadline.Retention, Threadline.Health, and mix threadline.verify_coverage.
@spec export_csv([timeline_filter()], [export_csv_opt()]) :: {:ok, %{ data: iodata(), truncated: boolean(), returned_count: non_neg_integer(), max_rows: non_neg_integer() }}
Returns CSV iodata and truncation metadata for matching captured changes.
Use export_json/2 when consumers need JSON; both functions share the
timeline filter vocabulary and bounded export behavior.
Filters
:repo—Ecto.Repomodule. Optional when supplied inopts.:table— string or atom. Optional. Matchestable_name.:table_schema— string or atom. Optional. Matches the captured host schema.:actor_ref—%ActorRef{}. Optional. Matches the transaction actor.:from—DateTime. Optional. Inclusive lower bound oncaptured_at.:to—DateTime. Optional. Inclusive upper bound oncaptured_at.:correlation_id— non-empty string. Optional. Matches linked action correlation with strict inner-join semantics.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Adds the caller's scope predicates to the export read.:max_rows— non-negative integer. Defaults to10_000; controls truncation.:include_action_metadata— boolean. Defaults tofalse; appends action columns when true.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
{:ok, %{data: iodata, truncated: boolean, returned_count: non_neg_integer, max_rows: non_neg_integer}}— CSV output and export counts.- Raises
ArgumentErrorfor invalid filters or missing:repo; repository errors are reraised.
Examples
Threadline.export_csv([table: "members"], repo: MyApp.Repo)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
@spec export_json([timeline_filter()], [export_json_opt()]) :: {:ok, %{ data: iodata(), truncated: boolean(), returned_count: non_neg_integer(), max_rows: non_neg_integer() }}
Returns JSON iodata and truncation metadata for matching captured changes.
Use export_csv/2 when consumers need CSV; both functions share the
timeline filter vocabulary and bounded export behavior.
Filters
:repo—Ecto.Repomodule. Optional when supplied inopts.:table— string or atom. Optional. Matchestable_name.:table_schema— string or atom. Optional. Matches the captured host schema.:actor_ref—%ActorRef{}. Optional. Matches the transaction actor.:from—DateTime. Optional. Inclusive lower bound oncaptured_at.:to—DateTime. Optional. Inclusive upper bound oncaptured_at.:correlation_id— non-empty string. Optional. Matches linked action correlation with strict inner-join semantics.
Options
:repo—Ecto.Repomodule. Required.:storage_schema— string. Optional. Selects a Threadline storage schema override.:scope— caller-owned value. Optional. Opaque to Threadline and passed to:scope_query_fn.:scope_query_fn— function. Optional. Adds the caller's scope predicates to the export read.:max_rows— non-negative integer. Defaults to10_000; controls truncation.:json_format—:wrappedor:ndjson. Defaults to:wrapped.
Unknown keys raise ArgumentError naming the allowed keys.
Returns
{:ok, %{data: iodata, truncated: boolean, returned_count: non_neg_integer, max_rows: non_neg_integer}}— JSON output and export counts.- Raises
ArgumentErrorfor invalid filters or missing:repo,CaseClauseErrorfor an unsupported format, and reraises repository errors.
Examples
Threadline.export_json([table: "members"], repo: MyApp.Repo, json_format: :ndjson)Results contain column values as captured; redaction is applied when triggers are generated, not on read. Authorize reads with :scope_query_fn.
Types
@type actor_history_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:from, DateTime.t()} | {:to, DateTime.t()} | {:cursor, :start | Threadline.Page.actor_cursor() | {:before, Threadline.Page.actor_cursor()}} | {:page_size, pos_integer()} | {:after, Threadline.Page.actor_cursor()} | {:before, Threadline.Page.actor_cursor()} | {:limit, pos_integer()}
An option accepted by actor_history/2; :after, :before, and :limit are deprecated aliases.
@type actor_window_filter() :: {:table, atom() | String.t()} | {:from, DateTime.t()} | {:to, DateTime.t()} | {:correlation_id, String.t()} | repo_opt()
A filter for changes made by one actor.
@type actor_window_page_filter() :: actor_window_filter()
A filter accepted by the legacy paged actor-window shape.
@type actor_window_page_opt() :: window_opt()
An option accepted by the legacy paged actor-window shape.
@type as_of_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:cast, boolean()}
An option accepted by as_of/4.
@type audit_changes_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:preload, [atom() | {atom(), atom() | [atom()]}] | nil}
An option accepted by audit_changes_for_transaction/2.
@type change_diff_opt() :: {:format, :primary | :export_compat} | {:expand_insert_fields, boolean()}
An option accepted by change_diff/2.
@type correlation_bundle_filter() :: {:table, atom() | String.t()} | {:actor_ref, Threadline.Semantics.ActorRef.t()} | {:from, DateTime.t()} | {:to, DateTime.t()} | repo_opt()
A filter for changes linked to one correlation id.
@type correlation_bundle_page_filter() :: correlation_bundle_filter()
A filter accepted by the legacy paged correlation-bundle shape.
@type correlation_bundle_page_opt() :: window_opt()
An option accepted by the legacy paged correlation-bundle shape.
@type export_csv_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:max_rows, non_neg_integer()} | {:include_action_metadata, boolean()}
An option accepted by export_csv/2.
@type export_json_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:max_rows, non_neg_integer()} | {:json_format, :wrapped | :ndjson}
An option accepted by export_json/2.
@type history_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:from, DateTime.t()} | {:to, DateTime.t()} | {:limit, pos_integer() | :infinity | nil}
An option accepted by the legacy unbounded history read.
@type json_map() :: %{optional(String.t()) => json_value()}
A string-keyed JSON map decoded from jsonb or bound for JSON encoding; owning types list guaranteed keys and new keys may be added.
@type json_value() :: nil | boolean() | number() | String.t() | [json_value()] | json_map()
A JSON value supported in a captured or exported JSON map.
@type lookup_opt() :: repo_opt() | storage_schema_opt() | scope_opt()
An option accepted by the single-transaction lookup functions.
@type record_action_opt() :: repo_opt() | storage_schema_opt() | {:actor, Threadline.Semantics.ActorRef.t()} | {:actor_ref, Threadline.Semantics.ActorRef.t()} | {:status, :ok | :error} | {:verb, atom() | String.t()} | {:category, atom() | String.t()} | {:reason, atom() | String.t()} | {:comment, String.t()} | {:correlation_id, String.t()} | {:request_id, String.t()} | {:job_id, String.t()}
An option accepted by record_action/2; supply either :actor or :actor_ref and a repository.
@type repo_opt() :: {:repo, module()}
An option that selects the Ecto repository used for a read.
@type row_history_filter() :: repo_opt() | {:from, DateTime.t()} | {:to, DateTime.t()}
A row-history filter accepted by the legacy filters-and-options shapes.
@type row_history_legacy_opt() :: row_history_opt()
An option accepted by the legacy row-history read shape.
@type row_history_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:from, DateTime.t()} | {:to, DateTime.t()} | {:limit, pos_integer() | :infinity} | {:cursor, :start | Threadline.Page.change_cursor()} | {:page_size, pos_integer()}
An option accepted by row_history/3.
@type row_history_page_opt() :: row_history_opt()
An option accepted by the legacy paged row-history read shape.
@type row_id() :: row_key_scalar() | %{optional(atom() | String.t()) => row_key_scalar()} | [{atom() | String.t(), row_key_scalar()}]
A single host key or every field of a composite key as a map or keyword list.
@type row_key_scalar() :: String.t() | integer() | float() | boolean() | Date.t() | DateTime.t() | NaiveDateTime.t() | Decimal.t()
A scalar host-table key value supported by Ecto.
@type scope_opt() :: {:scope, term()} | {:scope_query_fn, scope_query_fn()}
A caller-owned scope value or callback; the scope value is opaque to Threadline.
@type scope_query_fn() :: (Ecto.Query.t(), term(), %{surface: atom(), params: map()} -> Ecto.Query.t())
A query callback that applies the caller's scope; the scope value is opaque to Threadline.
@type storage_schema_opt() :: {:storage_schema, String.t()}
An option that selects a Threadline storage schema override.
@type timeline_filter() :: repo_opt() | {:table, atom() | String.t()} | {:table_schema, atom() | String.t()} | {:actor_ref, Threadline.Semantics.ActorRef.t()} | {:from, DateTime.t()} | {:to, DateTime.t()} | {:correlation_id, String.t()}
A timeline filter for captured changes.
@type timeline_opt() :: repo_opt() | storage_schema_opt() | scope_opt()
An option accepted by timeline/2.
@type timeline_page_opt() :: timeline_opt() | {:page_size, pos_integer()} | {:cursor, :start | Threadline.Page.change_cursor()}
An option accepted by timeline_page/2.
@type window_opt() :: repo_opt() | storage_schema_opt() | scope_opt() | {:cursor, :start | Threadline.Page.change_cursor()} | {:page_size, pos_integer()}
An option accepted by actor_window/3 and correlation_bundle/3.