This guide is the canonical support-matrix and lifecycle reference for Threadline's named adoption lanes. guides/integration-contracts.md defines the reusable seams, and guides/operator-surface.md covers mount, auth, and screens. This guide answers a different set of questions: which lane you are on, what compatibility is actually supported, and how surface-only changes move between Threadline minors.
Threadline 0.6.0 landed Evidence, Audit.transaction/3, and aligned operator surfaces in-repo after 0.5.0; the later minors (0.7.0 through 0.9.0) added surface, DX, and proof-lane work only. 0.10.0 is the first bump in that run with adopter actions attached — see the 0.9.x → 0.10.x bullet below. 0.11.0 regenerated triggers to resolve real primary keys (see the 0.10.x → 0.11.x bullet), and 0.12.0 carries three breaking telemetry/config changes (see the 0.11.x → 0.12.x bullet). Upgrade steps are semver-scoped in CHANGELOG.md and this guide.
Who this guide is for
Use this guide if you are:
- deciding whether you are running
capture-only,phoenix-surface,phx-gen-auth-reference, orsigra-reference - upgrading Threadline across minors and need to know what the operator-surface contract includes
- checking whether a host path is
supported,reference, orunclaimed
If you only need router wiring, auth posture, or screen references, stay in guides/operator-surface.md.
How to tell which lane you are on
You are on the capture-only lane when your host application does not depend on Threadline's optional Phoenix surface dependencies and does not mount threadline_operator_surface/2. The proof point for this lane is mix verify.compile_no_optional.
You are on the phoenix-surface lane when your host application adds the optional Phoenix surface dependencies and mounts threadline_operator_surface/2 in a Phoenix router using the in-tree operator surface. The proof for this lane comes from the root package: mix.exs, mix.lock, root CI, and the root doc-contract tests.
You are on the sigra-reference lane when your Phoenix host already uses Sigra and composes Threadline.Integrations.Sigra into Threadline.Plug using the current example app and guide path. The proof for this lane comes from examples/threadline_phoenix/, its lockfile and README, guides/integrations/sigra.md, and mix verify.example. This is a narrower claim than generic Sigra compatibility.
You are on the phx-gen-auth-reference lane when your Phoenix host uses mix phx.gen.auth (or equivalent generated session auth) and wires Threadline.Plug with a host-owned actor module derived from conn.assigns[:current_scope] per guides/integrations/phx-gen-auth.md. Proof for this lane is guides/integrations/phx-gen-auth.md, test/threadline/integrations/phx_gen_auth_integration_test.exs, and mix verify.test — not a second example application. This lane is not Sigra-compatible and is narrower than generic phoenix-surface.
For 0.5.0 support-lane wording, read those lane claims together with
guides/operator-surface.md: current mounted proof covers the shared /audit
timeline, actor, transaction, support-scoped row history / as-of, and
export-auth seams on the current tree.
Mounted /audit/evidence is narrower than that broad /audit proof. Treat it
as a separately authorized capability under the phoenix-surface lane rather
than a surface that appears automatically everywhere /audit is mounted.
Threadline uses three support words intentionally:
supportedmeans the lane is documented and backed by current repo proof.referencemeans the repo maintains a first-party composition path inside a narrower host story.unclaimedmeans the combination may be plausible locally, but this repo does not currently prove it.
That distinction matters more than dependency rows alone:
capture-onlyissupportedwith no optional Phoenix dependencies installed and is enforced bymix verify.compile_no_optional.phoenix-surfaceissupportedonly for the exact optional dependency ranges Threadline declares and CI-covers in this release.sigra-referenceis areferencelane for Phoenix hosts already using Sigra; it is proven only by the current example app, example lockfile, docs, and focused verification in this repo.- phx-gen-auth-reference is a reference lane for Phoenix hosts on generated session auth; it is proven by guides/integrations/phx-gen-auth.md and focused root verification, not by examples/threadline_phoenix/ or Sigra.
- Anything outside these named lanes is
unclaimed, even if it may work.
Supported compatibility matrix
Support claims in this table come from current in-repo proof only:
- declared optional dependency ranges in
mix.exs - current lock resolution in
mix.lock - current CI coverage in
.github/workflows/ci.yml - focused guide, doc-contract, and example-app verification for the named lane
| Lane | Claim type | Declared support | Current tested resolution | Proof / CI coverage |
|---|---|---|---|---|
capture-only | supported | No optional Phoenix surface dependencies required | N/A | mix verify.compile_no_optional and CI job verify-compile-no-optional |
phoenix-surface | supported | phoenix ~> 1.7, phoenix_live_view ~> 1.0, phoenix_html ~> 4.0, phoenix_pubsub ~> 2.1 | Phoenix 1.8.7, Phoenix LiveView 1.1.30, Phoenix HTML 4.3.0, Phoenix PubSub 2.2.0 | Root mix.exs, root mix.lock, mix verify.test, mix ci.all, root doc-contract coverage, and CI jobs verify-test / verify-bump-rehearsal |
phx-gen-auth-reference | reference | Host-generated session auth (mix phx.gen.auth or equivalent) + phoenix-surface optional deps | Root mix.lock Phoenix/LV/HTML/PubSub versions (no Sigra) | guides/integrations/phx-gen-auth.md, test/threadline/integrations/phx_gen_auth_integration_test.exs, mix verify.test, verify-test |
sigra-reference | reference | Example host path uses {:sigra, "~> 0.2", optional: true} alongside the Phoenix reference app stack | Example app lock resolves Sigra 0.2.5, Phoenix 1.8.5, Phoenix LiveView 1.1.28, Phoenix HTML 4.3.0, Phoenix PubSub 2.2.0 | examples/threadline_phoenix/mix.lock, examples/threadline_phoenix/README.md, guides/integrations/sigra.md, mix verify.example, and focused doc-contract tests |
Threadline does not claim support for Phoenix, LiveView, HTML, PubSub, or Sigra combinations outside these named proofs. The {:sigra, "~> 0.2", optional: true} declaration is a host install shape, not a blanket promise covering every Sigra 0.2.x host. If your lockfile resolves to different versions within the declared ranges, or your host auth/layout differs from the reference path, treat that as your responsibility to verify locally unless and until the Threadline repo updates its own declared ranges, lock resolution references, docs, and CI coverage accordingly.
Backport policy. Security and critical fixes are backported as patch releases on the current minor (e.g. 0.9.1), which any ~> 0.9.0-style three-segment pin picks up automatically; crossing a minor stays a deliberate, changelog-reading act. This is why a tight three-segment pin never strands an install-once audit adopter on an unpatched line.
Upgrade by Threadline minor
When you upgrade by Threadline minor:
- read
CHANGELOG.mdfor upgrade notes and any announced surface-only deprecations - identify your lane before changing dependencies
- if you are
capture-only, runmix verify.compile_no_optional - if you are
phoenix-surface, confirm your Phoenix stack still fits the declared optional dependency ranges inmix.exs - if you are following the
sigra-referencelane, compare your host against the current example-app proof path before widening anything - run the lane-appropriate verification entrypoints in the upgraded application
CHANGELOG.md and this guide have different jobs. CHANGELOG.md is chronological and complete — the source of truth for what shipped in each release. This guide is curated and incomplete by design — it answers only "I'm bumping N → M — must I act, and how?" A no-action change appears in CHANGELOG.md in full and here only inside a "nothing required" line; every bump bullet below links its [x.y.0] anchor instead of restating the feature list.
What changed across the 0.6.x → 0.9.x era, by theme
Every adopter-visible change from 0.6.x through 0.9.x fell into one of four themes. None of them required a host migration or a config change for existing adopters — this table names where each theme landed and the action (if any) for you:
| Theme | Where it landed | Adopter action |
|---|---|---|
| storage-schema default | Nothing landed here in this era: the storage_schema seam does not exist before 0.10.0. Installs generated in the 0.6.x–0.9.x era put Threadline-owned tables, functions, and triggers wherever the installer wrote them, which is the host's public schema. | None in this era, and none arriving later either — 0.10.0 defaults to public, which is what these installs already have. The no-action outcome is settled by the 0.10.0 change, not by this era; see the 0.9.x → 0.10.x bullet and the migration callout below. |
| operator surface/theming | [0.8.0] operator-surface overhaul (dark "night infrastructure" theme, Home task-launcher, copy-to-clipboard, evidence verdicts) and [0.9.0] first-class positioning + accessibility pass. | Nothing required — capture-only is unaffected and phoenix-surface gets the improved /audit surface with no mount-shape change. |
| release proof lanes | [0.7.0] through [0.9.0] hardened the release/proof pipeline (flake-detection gate, doc-contract locks, release-please publishing). | Nothing required — these are maintainer/CI proofs, not host code. |
| migration expectations | No host-DB migration shipped in the 0.6.x → 0.9.x era. The one migration-shaped expectation this guide carries — the storage_schema freeze — belongs to 0.10.0, not to this era. | Nothing required for existing adopters — see the storage-schema callout. |
At a glance, per minor
| Bump | Breaking? | Migration required | Config touch | Reassurance |
|---|---|---|---|---|
| 0.6.x → 0.7.x | No | None | None | Nothing required — first-hour DX + reference-lane docs only. |
| 0.7.x → 0.8.x | No | None | None | Nothing required — operator-surface/theming + CI proof-lane work only. |
| 0.8.x → 0.9.x | No | None | None | Nothing required — operator-surface positioning + accessibility only. |
| 0.9.x → 0.10.x | No | None | Optional — storage_schema is a new opt-in whose default is what you already have | Not nothing required: four adopter actions (S3 export dependencies, two new operator-surface routes, a narrowed Storage callback, 25 newly undocumented modules). |
| 0.10.x → 0.11.x | Yes | Regenerate triggers + add the row-history index | Optional — a primary_key: override, only for a table with no usable primary key | Not nothing required: regenerate every audited table's trigger, then add the row-history index. Full procedure: Upgrading to 0.11. |
| 0.11.x → 0.12.x | Yes | None | Only if a capture table entry passes a bare atom to exclude:, mask: or except_columns: — wrap it in a list | Not nothing required: three adopter actions (telemetry handlers matching actor-ref keys, health-checked error metadata, non-list capture options). |
Current guidance by minor:
0.5.x → 0.6.x: Evidence plane (
Threadline.Evidence,mix threadline.evidence.show,/audit/evidence), recommended audited write path (Threadline.Audit.transaction/3); seeCHANGELOG.md[0.6.0]for deprecated manual GUC +record_action/2recipe.0.6.x → 0.7.x: First-hour DX and reference-lane docs — a Configure Threadline subsection,
:schemasmount wiring, and thephx-gen-auth-referencelane. Breaking changes: None. Required migration: None. Config changes: None. Forcapture-onlyandphoenix-surfaceadopters: nothing required. SeeCHANGELOG.md[0.7.0].0.7.x → 0.8.x: Operator-surface overhaul (dark "night infrastructure" theme, Home task-launcher, copy-to-clipboard, evidence verdicts) plus CI/quality hardening. Breaking changes: None. Required migration: None. Config changes: None (optional
operator_surface_embed_scripts: falseopts out of the embedded copy helper). Forcapture-onlyandphoenix-surfaceadopters: nothing required. SeeCHANGELOG.md[0.8.0].0.8.x → 0.9.x: Operator-surface first-class positioning and an accessibility pass. Breaking changes: None. Required migration: None. Config changes: None. For
capture-onlyandphoenix-surfaceadopters: nothing required. SeeCHANGELOG.md[0.9.0].0.9.x → 0.10.x: A documented public surface, the new
storage_schemaseam, and an operator surface with a theme lane and row-level deep links. Breaking changes: None. Required migration: None. Config changes: None required —storage_schemais a new opt-in and its default is the host'spublicschema, which is what your install already has. Unlike every earlier bump in this list, this one is not a nothing-required bump: four adopter actions remain.- S3 export adopters — the export HTTP client moved from
:hackneyto{:req, "~> 0.7"}, and the:ex_awsfloor rose from~> 2.4to~> 2.7. Swap the dependency and raise the floor in your hostmix.exs; a raised floor is not additive. - Operator-surface mounters — two new routes,
POST <path>/themeand<path>/rows/:table/:record_id, must pass any method allowlist, proxy rule, or Content-Security-Policy in front of your/auditmount. - Custom
Threadline.Storageadapters — theThreadline.Storage.put/2callback narrowed to binary content. This is visible to Dialyzer with no runtime change; update your adapter's typespec. - Callers of implementation modules — 25 implementation modules became
@moduledoc false. They remain callable for Threadline's own composition, but they are no longer a supported surface.
Per lane:
capture-onlyadopters can be touched by the S3-export and implementation-module items only;phoenix-surfaceadopters should also re-check the two new routes against whatever sits in front of their/auditmount. SeeCHANGELOG.md[0.10.0].- S3 export adopters — the export HTTP client moved from
0.10.x → 0.11.x: Threadline capture triggers now resolve and record a table's real primary key instead of always assuming a column named
id,Threadline.history/3andThreadline.as_of/4raiseArgumentErroron a bad key instead of silently returning nothing, andtrigger_coverage/1no longer counts a disabled or replica-only trigger as covered. Breaking changes: Yes — see the bullets above. Required migration: Yes — regenerate every audited table's trigger (mix threadline.gen.triggers) and add the row-history index (mix threadline.gen.row_history_index), then migrate. Config changes: Optional — aprimary_key:override underconfig :threadline, :trigger_captureis only needed for a table with no usable primary key.- Every adopter regenerates triggers and adds the row-history index; a table whose primary-key type is outside the supported set keeps its legacy trigger until you widen that support.
- Adopters with per-table capture settings (redaction or
store_changed_from) check whether two of their tables ever shared one capture function before regenerating — see the CHANGELOG Security note. - Callers matching
table_pkdirectly, such as{"id": null}, should match{}too after regenerating. - Optional: backfill the real key for rows captured before you regenerated, using the SQL in the upgrade guide.
See
CHANGELOG.md[0.11.0]. Full procedure: Upgrading to 0.11.0.11.x → 0.12.x: Export and retention telemetry, a
Threadline.history/3limit:option,--strictand--all-schemasflags formix threadline.health.coverage, and a legacy-key health warning. Breaking changes: Yes. Required migration: None. Config changes: Only if a capture table entry passes a bare atom toexclude:,mask:orexcept_columns:— wrap it in a list.- Operator-surface telemetry consumers whose handlers match on
actor_ref,session_actor_reforscope_actor_refin the[:threadline, :operator_surface, :authorize],[:threadline, :operator_surface, :export_authorize]or[:threadline, :operator_surface, :actor_ref_mismatch]events must remove those keys from their pattern matches and read the actor from their own session or scope instead. - Health-telemetry consumers whose handlers match the
[:threadline, :health, :checked, :error]event's%{error: message}metadata must match%{exception: mod}instead. - Capture config authors passing a non-list
exclude:/mask:/except_columns:(for exampleexclude: :ssn) on a:threadline, :trigger_capturetable entry must wrap the column name in a list (exclude: [:ssn]); this now raisesArgumentErrorat config load and at trigger generation instead of silently skipping the redaction.
See
CHANGELOG.md[0.12.0].- Operator-surface telemetry consumers whose handlers match on
0.3.x -> 0.4.x: the operator surface became an official optional dependency lane.capture-onlyadopters keep the no-optional-deps path.phoenix-surfaceadopters must align with the declaredphoenix,phoenix_live_view,phoenix_html, andphoenix_pubsubranges and re-check their router mount/auth setup after upgrade.sigra-referenceadopters should also re-check the current example app and Sigra guide before treating that path as unchanged.future minor upgrades: do not infer support from ecosystem norms or upstream release notes alone. Re-check this guide, the declared optional dependency ranges, the current example-app proof path, and the current changelog entry for the target Threadline minor.
Storage-schema migration expectation
storage_schema arrived in 0.10.0. It defaults to the host's public schema, and a dedicated schema (config :threadline, storage_schema: "threadline") is an explicit opt-in chosen before install.
The choice is frozen at generation time — set it before mix threadline.install. Changing it later is deliberate migration work (move or recreate the Threadline-owned tables, functions, and triggers in the new schema and re-run mix threadline.gen.triggers), not a runtime config edit. This is the one migration-shaped expectation in this guide, and it only affects you if you deliberately move schemas.
Existing installs need no action, and the 0.10.0 change is what makes that true — not anything settled in the 0.6.x → 0.9.x era, which had no storage_schema seam at all. 0.10.0 defaults to public precisely because Threadline cannot detect the schema an existing install generated into: a dedicated-schema default would have re-pointed every read path at tables that do not exist, while the already-deployed, unqualified triggers kept writing where they always did.
New installs should opt in. The default no longer provides schema isolation, so choose a dedicated schema before you run the installer if you want Threadline-owned objects out of public.
Threadline-owned storage_schema is separate from the host table_schema (--schema) of your audited application tables: host tables can stay in public, support, or another app schema while Threadline-owned objects live in public (the default) or a dedicated schema you choose. Existing adopters keep whatever schema they generated.
What breaks when Phoenix/LiveView floors move
If Threadline raises a Phoenix or LiveView floor for the optional surface, the break is surface-only unless the changelog says otherwise.
Typical symptoms:
mix deps.getor dependency resolution fails because your host app pins older Phoenix surface packages outside the declared rangesmix compilefails in a surface-mounted host because the mounted stack no longer satisfies the declared optional dependency ranges- docs/examples no longer match your older Phoenix router or LiveView APIs
capture-only adopters should not be affected by surface-only dependency floor changes as long as mix verify.compile_no_optional continues to pass for the Threadline release they adopt.
Packaging Boundary Scorecard
Threadline's 0.5.0 integration-breadth era closed with a clear package-boundary decision: stay in-tree for now. The optional Phoenix surface is already isolated behind optional dependencies, the repo still proves one coherent release story from the root package, and the current evidence does not justify the extra versioning and release overhead of a separate threadline_web package.
Future extraction is a scorecard decision, not a taste decision. The trigger is "yes, extract" only when one or more of these pressures is sustained and materially increases maintainer or adopter cost:
- Version Matrix Pressure: the root package must regularly prove multiple incompatible Phoenix or LiveView lines, or operator-surface dependency movement starts forcing unrelated core-package release coordination.
- Release Cadence Divergence: operator-surface changes want to ship on a meaningfully different cadence than the core capture/query APIs, so keeping one package either delays surface fixes or churns the core release line unnecessarily.
- Adopter Glue Burden: repeated real-world adopter feedback shows that the in-tree optional surface still leaves too much host-specific mounting or packaging glue, and a separate package would reduce that burden without weakening the core auth-agnostic contract.
Until those pressures are real, Threadline keeps the operator surface optional and in-tree. If a future split happens, Threadline will preserve the public threadline_operator_surface/2 router integration API so hosts do not have to rewrite their mount call shape as the cost of following the package boundary.
Surface-only deprecation and removal policy
Threadline treats the operator surface as a public surface-only contract. That contract includes the router macro and documented options, documented mount/auth pattern, documented operator-surface routes, required optional dependency ranges, parity Mix task names and flags, and stable machine-readable literals already locked by tests.
Surface-only deprecations require overlap:
- deprecate in docs and changelog first
- remove no earlier than the next Threadline minor after at least one released overlap window
- do not silently narrow the supported optional dependency ranges without updating this guide and the changelog together
Exceptions are allowed only for security issues, upstream hard incompatibility, or undocumented internals.
Release checklist for adopters
- Decide whether you are capture-only, phoenix-surface, sigra-reference, or phx-gen-auth-reference.
- Compare your host dependencies against the declared optional dependency ranges in
mix.exs. - If you are
capture-only, runmix verify.compile_no_optional. - If you are
phoenix-surface, runmix ci.alland verify your mounted routes and auth pipeline still matchguides/operator-surface.md. - If you are using the
sigra-referencelane, runmix verify.exampleand compare your host wiring againstguides/integrations/sigra.mdplusexamples/threadline_phoenix/README.md. - If you are on phx-gen-auth-reference, read guides/integrations/phx-gen-auth.md and compare your host actor_fn and authorize_fn wiring before deploy.
- Review
CHANGELOG.mdfor any surface-only deprecation notice before deploying.
Canonical references
guides/operator-surface.mdguides/integration-contracts.mdguides/integrations/sigra.mdguides/integrations/phx-gen-auth.mdexamples/threadline_phoenix/README.mdmix.exsmix.lock.github/workflows/ci.ymlCHANGELOG.md