Generates an Ecto migration that installs Threadline audit triggers on the specified tables.
Usage
mix threadline.gen.triggers --tables users
mix threadline.gen.triggers --tables users,posts,commentsEach invocation writes one migration whose statements create or replace the
audit trigger on each listed table (CREATE OR REPLACE TRIGGER, PostgreSQL 14
or later). Run mix ecto.migrate to apply.
The trigger calls threadline_capture_changes(), which must already be
installed via mix threadline.install.
With before-values capture for opted-in tables:
mix threadline.gen.triggers --tables posts --store-changed-from
mix threadline.gen.triggers --tables posts --store-changed-from --except-columns secret_token,internal_scoreThat emits per-table functions threadline_capture_changes_<table>() and wires
triggers to them. Migrations generated without --store-changed-from keep the
default global threadline_capture_changes() trigger body unless the table
has :exclude / :mask rules under config :threadline, :trigger_capture (see
README).
Redaction (config :threadline, :trigger_capture)
At task start the host app config is loaded (Mix.Task.run("app.config", [])).
Per-table entries under :tables may set :exclude, :mask, optional
:mask_placeholder, :store_changed_from, and :except_columns. Overlap
between :exclude and :mask is validated before writing the migration.
Rerunning
Run the task again for tables that already have a trigger migration, for
example after changing :trigger_capture redaction rules or to clear a
Drift detected status. It writes a new migration. When the table-derived
name is already taken, the migration gets a numbered name, such as
threadline_triggers_posts_2, and a matching numbered module. A rerun for a
different table set, such as posts after posts,users, keeps the
un-numbered threadline_triggers_posts. The new migration replaces the
trigger in place, so capture has no gap.
A table that returns to the default trigger also drops its leftover per-table
capture function. That drop never cascades. On a table that never had one,
PostgreSQL prints a harmless NOTICE that the function does not exist. The
drop is skipped when threadline_capture_changes_<table> is longer than
PostgreSQL's 63-byte identifier limit, because the truncated name can belong
to another table's function.
Rolling back a rerun migration keeps capture on for the tables it re-pointed.
Rolling back does not restore the earlier capture policy: the trigger keeps the
policy the rerun installed. If the rerun had removed redaction rules, a rolled
back rerun leaves capture running unredacted until you regenerate, and
mix threadline.policy.show flags the mismatch once your config lists those
rules again. The generated down says the
same in a comment. To stop capturing a table, write a migration that drops its
trigger.
Options
--tables— comma-separated list of table names (required)--store-changed-from— emit per-table capture functions that persist sparsechanged_fromJSON on UPDATE (default: off)--except-columns— comma-separated column names excluded from bothchanged_fieldsandchanged_fromwhen--store-changed-fromis set (alphanumeric and underscore only). Merged with:except_columnsfrom config.--dry-run— printtable=… exclude=… mask=…per table and skip writing a migration
Guards
The task exits non-zero if audit_transactions or audit_changes is in the
table list. Installing audit triggers on Threadline's own tables would cause
recursive loops in the audit tables themselves.