mix threadline.gen.triggers (Threadline v0.10.2)

Copy Markdown View Source

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,comments

Each 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_score

That 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 sparse changed_from JSON on UPDATE (default: off)
  • --except-columns — comma-separated column names excluded from both changed_fields and changed_from when --store-changed-from is set (alphanumeric and underscore only). Merged with :except_columns from config.
  • --dry-run — print table=… 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.