AshReplicant 0.3.0
The Ash Replicant.Sink adapter for CDC mirror targets — the "ash_postgres of replicant."
This release adds SCD2 history mirroring and hardens multitenancy to fail-closed at
compile time across five tenant-isolation vectors — every one caught by tests proven to go
red on the defect, and by cross-vendor review.
✨ Added
- SCD2 history mirroring (
history_strategy :scd2) — opt a resource into validity-windowed
history: instead of overwriting current state, mirror a source table into a host-defined
version table (close-current + insert-version). Effect-once, fail-closed multitenancy,
value-free boundaries, and route-through-Ash are all preserved. Adds theValidateHistory
compile verifier andon_truncate :close. (Audit-log needs remain served by AshPaperTrail on
an SCD1 mirror.)
🔒 Security — multitenancy fail-closed
A multitenant mirror must never write rows unscoped. Five gates now enforce that — four at
compile time, one at runtime. See
ADR-0001
for the full model.
- A declared tenant source now requires an Ash
multitenancyblock — for both
tenant_attributeandtenant_mfa. Without one, Ash silently ignores thetenant:the sink
passes and mirrors every tenant into one unscoped table.ValidateMultitenancyfails the build
closed (any strategy, incl. aglobal?block, satisfies it). - A
false-resolved tenant now fails closed. Ash treats a falsy tenant as no scoping, so a
tenant_mfareturningfalsepreviously wrote unscoped. It now returns:tenant_required,
likenil. - Sink-selected actions can no longer bypass tenancy. The new
ValidateActionMultitenancy
rejectsmultitenancy :bypass/:bypass_allon the host's primary read, create, destroy, and
the SCD2 close action — a:bypassread would otherwise let abulk_update/bulk_destroy
match and mutate another tenant's rows. - The multitenancy discriminator column is now shape-checked. Under
strategy :attribute,
ValidateMultitenancyrejects asensitive-classified or binary-storage-typedattribute(it
would store/compare a mismatched value and silently mis-scope). AshCloak-encrypted attributes
are already rejected by Ash's own verifier.
📦 Install
{:ash_replicant, "~> 0.3.0"}Full changelog:
https://github.com/baselabs/ash_replicant/blob/main/CHANGELOG.md