Changelog
All notable changes to Tayra are recorded here, newest first. Tayra follows Semantic Versioning: major versions may break the public API, minor versions add backward-compatible features, and patch versions are bug fixes only.
The framework major lives in the package name (Tayra.Marten8 / Tayra.Marten9, Tayra.Wolverine5 / Tayra.Wolverine6); the version below is Tayra's own suite version, shared across every package. See Installation.
2.3.0 - 2026-06-10
Core hardening: encryption now fails closed, tampering is surfaced, and crypto-shredding stays complete after key rotation. Annotations now work on public instance fields as well as properties.
Changed (breaking)
- Encryption fails closed.
EncryptAsyncnow throws anInvalidOperationExceptionwhen a[DataSubjectId]value isnull, or when no encryption key can be resolved for a field's group. Previously the affected fields were silently persisted as plaintext with only a warning log. Decryption tolerance is unchanged: a missing (shredded) key still follows the replacement-value path. - The metadata API no longer exposes a
PropertyInfo. The reflection model now describes annotated members through aPersonalDataMemberaccessor (withName,MemberType,CanWrite,GetValue,SetValue, andUnderlying) that backs both properties and fields.PersonalDataFieldInfo.PropertybecomesPersonalDataFieldInfo.Member,DataSubjectInfoexposesMember, and the blind-index metadata exposesSourceMember/IndexMember. Code that read.Propertyoff the public metadata types must move to the newMemberaccessor. - Non-writable annotated members now fail with a clear error at first use instead of being silently skipped. A member Tayra writes back to (
TextandSerializedkinds, plus their companions) must be writable: a settable property (init-only is fine) or a non-readonly, non-constfield. A get-only property or areadonly/constfield throws anInvalidOperationExceptionat first encrypt. Similarly, read-only string collection instances (e.g.ImmutableList<string>) now throw instead of being silently skipped. - The AAD decrypt path rejects legacy v1 ciphertexts unconditionally. Version
0x01payloads predate associated-data binding, so they cannot be context-verified - a database-level attacker could splice a v1 ciphertext into a different field of the same subject.AesGcmEncryptor.DecryptWithAssociatedData/DecryptStringWithAssociatedDatanow throw aCryptographicExceptionon v1 payloads; at the field-encrypter level a v1 value is left untouched and surfaces as anIntegrityCheckFailedaudit event. The v1 format remains valid only for the no-AADAesGcmEncryptor.Encrypt/Decryptprimitive pairing.
Added
- All seven attributes now annotate properties or public instance fields.
[PersonalData],[DeepPersonalData],[SerializedPersonalData],[DataSubjectId],[BlindIndex],[CompoundBlindIndex], and[ArrayBlindIndex]are recognized on public instance fields in addition to properties; field usage compiles and works. Only public instance members are scanned (private and static members are ignored), mirroring the existing property scan rule. The fluent API expression selectors now accept field expressions (x => x.SomeField) as well as property expressions. - Tamper detection is surfaced. A
CryptographicExceptionduring decryption (tampered ciphertext or context mismatch) is now logged atWarningand emits a newTayraAuditEventType.IntegrityCheckFailedaudit event; the field value is left untouched. Legacy-plaintext tolerance (FormatException) stays quiet atDebug. HashSet<string>and other mutableICollection<string>properties are now encrypted (previously onlyIList<string>/string[]worked; other collection types were silently skipped). A new fluent overloadEntityTypeBuilder<T>.PersonalData(Expression<Func<T, IEnumerable<string>>>)configures string-collection properties directly.
Fixed
ShredAsync(subjectId)now deletes the subject's base key and every{subjectId}:-prefixed key - rotated versions (:vN) and group keys - so crypto-shredding remains complete after key rotation. Previously only the exact base key was deleted, leaving data encrypted under rotated keys readable. Neighboring subjects that merely share an ID prefix are unaffected.[DeepPersonalData]object graphs with cycles no longer cause aStackOverflowException, and shared (diamond) references are processed exactly once instead of being double-encrypted.DeepPersonalDataconfigured on a collection property via the fluent API now encrypts the elements (previously silently skipped).- Eliminated a key-creation race that could encrypt data with a never-persisted key:
IKeyStore.StoreAsyncis now documented as first-writer-wins, and the crypto engine and blind-index key provider read the key back after storing and serialize per-key-ID creation in-process. - Blind-index HMAC key rotation now actually replaces the stored key (delete, store, then read-back verify); previously the first-writer-wins store made rotation a silent no-op. The HMAC key cache also gained a TTL (default 5 minutes) so rotations and deletions performed elsewhere are observed.
2.2.0 - 2026-06-10
Changed
- Updated the integration targets to Marten 9.7.1 and Wolverine 6.6.0. The dependency ranges still cap the next framework major, so a consumer that pulls Marten 10 or Wolverine 7 fails fast at restore instead of at runtime.
- Wolverine 6 dead-letter exception redaction now uses a supported hook. When
RedactExceptionMessagesis enabled,Tayra.Wolverine6scrubs the exception text on Wolverine'sIDeadLetterInterceptor(added in WolverineFx 6.6.0) instead of reaching into private exception internals. The redacted dead letter now preserves the original exception type, so you can still filter and triage dead letters by type while the message is replaced withException details redacted by Tayra.The behavior of the option is otherwise unchanged.Tayra.Wolverine5keeps its existing redaction mechanism.
No public API changes.
2.1.0 - 2026-06-09
Added
- Per-major integration packages. Pick the package that matches your framework version:
Tayra.Marten8/Tayra.Marten9andTayra.Wolverine5/Tayra.Wolverine6. Both lines ship from a single trunk at the same suite version, so you are never forced onto a new framework major to stay current with Tayra. See Installation.
Fixed
- HashiCorp Vault key store: keys are now read back correctly from Vault KV v2.
- Wolverine 5: the integration's source generator is kept enabled so its handlers (such as the built-in GDPR erasure handler) are discovered at runtime.
2.0.0 - 2026-06-07
Tayra 2.0 moves the default integrations onto the current framework majors.
Changed (breaking)
- The Marten integration now targets Marten 9 and the Wolverine integration targets Wolverine 6. Marten 8 / Wolverine 5 support continues through the per-major packages introduced in 2.1.0.
- The Wolverine integration now encrypts at the message serializer rather than via handler middleware. PII is therefore ciphertext at rest in the durable outbox/inbox and in dead-letter storage (the durable outbox could previously hold cleartext).
UseTayraMiddleware()is now an[Obsolete]alias forUseTayra(), and theRedactDeadLetterQueuesoption was removed because dead-letter bodies are encrypted automatically.
Added
- Searchable encrypted collections via
[ArrayBlindIndex], with fluent configuration. - New Roslyn analyzers: TAYRA007 (PII on Marten binary events), TAYRA008 (flat-table PII guard), TAYRA009 (duplicated-field PII guard), and TAYRA010 (PII on a Wolverine saga).
- Marten: fail fast at startup if a custom serializer drops the Tayra wrapper.
Performance
- Core encrypt/decrypt dispatch is cached as compiled delegates.
1.4.0 - 2026-05-05
Added
- Envelope encryption: two-tier encryption with pluggable master and data key stores and pluggable master-key id resolution.
- AWS Aurora (PostgreSQL with IAM authentication) key store.
- Persistent, hash-chained audit trail (Marten-backed), plus signed (ECDSA P-256) and scheduled compliance reports with a pluggable archive.
- Date-only perpetual-fallback licensing model (the major-version gate was dropped).
Changed
[DeepPersonalData]now recurses transitively through nested annotations.
Removed
- The
Tayra.MediatR,Tayra.MassTransit, andTayra.NServiceBusintegrations were dropped because their upstreams moved to commercial or copyleft licensing. Tayra ships no copyleft or commercial-by-default dependencies.
1.3.0 - 2026-05-01
Changed
- Compliance features were extracted into a dedicated
Tayra.Compliancepackage.
1.2.0 and earlier - 2026-03 to 2026-04
Initial public releases established the core library and ecosystem:
- AES-256-GCM field-level encryption with a versioned wire format and embedded key version for rotation, plus GDPR crypto-shredding.
- The attribute model (
[PersonalData],[DataSubjectId],[DeepPersonalData],[SerializedPersonalData]) and Roslyn analyzers TAYRA001 to TAYRA006. - Key stores: In-Memory, SQLite, PostgreSQL, HashiCorp Vault, Azure Key Vault, AWS Parameter Store, and AWS Secrets Manager.
- Integrations for Entity Framework Core, Marten, MongoDB, Serilog, System.Text.Json, and ASP.NET Core, plus the
dotnet tayraCLI.
