You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The MIP-03 legacy exporter-secret migration deadline and the MIP-04 legacy media key-derivation deadline both expired on 2026-05-15 00:00:00 UTC (see CHANGELOG entry for #222 which moved the deadline from June 4 to May 15). Both are encoded as the unix-epoch constant 1_778_803_200 in two parallel places in mdk-core.
From today onward, the runtime gates (allow_legacy_media_fallback_at / allow_legacy_exporter_secret_fallback_at) both return false for any caller using real time. That means every if allow_legacy { … } branch downstream of the gates is unreachable in production — but the legacy derivation code, storage trait methods, and threaded allow_legacy parameters are still in the source tree, still compile, and still get exercised by tests (which now must pin a pre-deadline timestamp via the _at variants introduced in #222).
#303 migrated the two real-time tests that started failing at midnight to the deterministic _at variants (2-line change in test bodies only) so CI would unblock; the production compat code was deliberately left untouched and is the subject of this issue.
crates/mdk-storage-traits/src/groups/mod.rs:122,139,323,330 — get_group_legacy_exporter_secret / save_group_legacy_exporter_secret on the storage trait + macro-generated proxies
The allow_legacy_exporter_secret: bool parameter threaded through several decryption helpers
3. Storage backends
Both mdk-memory-storage and mdk-sqlite-storage implement get_group_legacy_exporter_secret / save_group_legacy_exporter_secret. The sqlite backend has the legacy_exporter_secrets table (or equivalent rows) — decide whether to drop the table in a migration or leave the rows orphaned.
4. Tests / fixtures
fixed_pre_deadline_ts() / fixed_post_deadline_ts() helpers in encrypted_media/manager.rs and messages/decryption.rs
The boundary tests test_allow_legacy_media_fallback_at_boundary and equivalents
Concrete change checklist
Drop LEGACY_MEDIA_MIGRATION_DEADLINE + allow_legacy_media_fallback_at + the gate call site in manager.rs
Drop the allow_legacy: bool parameter from all media-path helpers; remove the if allow_legacy { … } branches
Drop derive_legacy_encryption_key_with_secret from encrypted_media/crypto.rs and its use imports
Drop LEGACY_EXPORTER_SECRET_MIGRATION_DEADLINE + allow_legacy_exporter_secret_fallback_at from messages/decryption.rs
Drop decrypt_message_with_legacy_exporter_secret from messages/crypto.rs
Drop MDK::legacy_exporter_secret from groups.rs and the allow_legacy_exporter_secret parameter threading
Remove get_group_legacy_exporter_secret / save_group_legacy_exporter_secret from the mdk-storage-traits trait + macros
Update mdk-memory-storage and mdk-sqlite-storage implementations
Decide migration story for existing sqlite rows in legacy_exporter_secrets (drop the table vs. leave orphaned)
Delete the *_legacy_media_compat tests, the fixed_pre_deadline_ts / fixed_post_deadline_ts helpers, and the boundary tests
CHANGELOG: breaking-change entry across mdk-core, mdk-storage-traits, mdk-memory-storage, mdk-sqlite-storage
Breaking changes
MdkStorageProvider (via GroupStorage) loses two trait methods — implementers downstream of mdk-storage-traits must update.
Existing groups with legacy_exporter_secret rows in storage are still readable (no decryption error) but those rows become inert; clients that still rely on legacy media/message decryption beyond today are already broken at runtime regardless of this PR — no callers can come back and fail "harder" than they already do.
Estimated diff size: ~300-600 LOC removed across the workspace.
Out of scope
The MIP-00 kind:443 → kind:30443 KeyPackage event-kind migration. That's a separate sunset with deadline May 31, 2026 (16 days from filing), and it's a manual/documented sunset (TODOs in key_packages.rs:434 and key_packages.rs:3338), not a runtime timestamp gate. File a separate issue for that one when its deadline approaches.
References
#222 — introduced both deadlines and the _at deterministic variants for tests
#303 — migrated the two real-time tests to the _at variants when they exploded on 2026-05-15 (commit 21c50b6); did NOT remove any legacy production code
Why now
The MIP-03 legacy exporter-secret migration deadline and the MIP-04 legacy media key-derivation deadline both expired on 2026-05-15 00:00:00 UTC (see CHANGELOG entry for #222 which moved the deadline from June 4 to May 15). Both are encoded as the unix-epoch constant
1_778_803_200in two parallel places inmdk-core.From today onward, the runtime gates (
allow_legacy_media_fallback_at/allow_legacy_exporter_secret_fallback_at) both returnfalsefor any caller using real time. That means everyif allow_legacy { … }branch downstream of the gates is unreachable in production — but the legacy derivation code, storage trait methods, and threadedallow_legacyparameters are still in the source tree, still compile, and still get exercised by tests (which now must pin a pre-deadline timestamp via the_atvariants introduced in #222).#303 migrated the two real-time tests that started failing at midnight to the deterministic
_atvariants (2-line change in test bodies only) so CI would unblock; the production compat code was deliberately left untouched and is the subject of this issue.Scope — two parallel surfaces
1. Media decryption (MIP-04)
crates/mdk-core/src/encrypted_media/manager.rs:28—const LEGACY_MEDIA_MIGRATION_DEADLINE: u64 = 1_778_803_200;crates/mdk-core/src/encrypted_media/manager.rs:43—fn allow_legacy_media_fallback_at— the runtime gatecrates/mdk-core/src/encrypted_media/manager.rs:53—try_decrypt_with_secret_compat(..., allow_legacy: bool, ...)— branches on the flag, calls the legacy HKDF derivationcrates/mdk-core/src/encrypted_media/manager.rs:84—try_decrypt_with_current_epoch_compat(..., allow_legacy: bool, ...)— same branching patterncrates/mdk-core/src/encrypted_media/manager.rs:242— the single call site that flipsallow_legacybased oncurrent_timecrates/mdk-core/src/encrypted_media/crypto.rs:154—pub(crate) fn derive_legacy_encryption_key_with_secret— the pre-0.7.1 HKDF extract+expand derivation itselfallow_legacy: boolparameter threaded through ~6 internal helpers (try_decrypt_with_epoch_hint,try_decrypt_with_epoch_secret_source, etc.)2. Message decryption (MIP-03)
crates/mdk-core/src/messages/decryption.rs:19—const LEGACY_EXPORTER_SECRET_MIGRATION_DEADLINE: u64 = 1_778_803_200;crates/mdk-core/src/messages/decryption.rs:85—fn allow_legacy_exporter_secret_fallback_at— the runtime gatecrates/mdk-core/src/messages/crypto.rs:83—pub(crate) fn decrypt_message_with_legacy_exporter_secret— the legacy MIP-03 decryption implementationcrates/mdk-core/src/groups.rs:431—MDK::legacy_exporter_secret(&group_id)accessorcrates/mdk-storage-traits/src/groups/mod.rs:122,139,323,330—get_group_legacy_exporter_secret/save_group_legacy_exporter_secreton the storage trait + macro-generated proxiesallow_legacy_exporter_secret: boolparameter threaded through several decryption helpers3. Storage backends
Both
mdk-memory-storageandmdk-sqlite-storageimplementget_group_legacy_exporter_secret/save_group_legacy_exporter_secret. The sqlite backend has thelegacy_exporter_secretstable (or equivalent rows) — decide whether to drop the table in a migration or leave the rows orphaned.4. Tests / fixtures
fixed_pre_deadline_ts()/fixed_post_deadline_ts()helpers inencrypted_media/manager.rsandmessages/decryption.rs*_legacy_media_compattests that feat(mdk-core): accept caller-supplied d-tag for KeyPackage events #303 migrated to the_atvariants (they're regression coverage for the soon-to-be-removed code — delete with it)test_allow_legacy_media_fallback_at_boundaryand equivalentsConcrete change checklist
LEGACY_MEDIA_MIGRATION_DEADLINE+allow_legacy_media_fallback_at+ the gate call site inmanager.rsallow_legacy: boolparameter from all media-path helpers; remove theif allow_legacy { … }branchesderive_legacy_encryption_key_with_secretfromencrypted_media/crypto.rsand itsuseimportsLEGACY_EXPORTER_SECRET_MIGRATION_DEADLINE+allow_legacy_exporter_secret_fallback_atfrommessages/decryption.rsdecrypt_message_with_legacy_exporter_secretfrommessages/crypto.rsMDK::legacy_exporter_secretfromgroups.rsand theallow_legacy_exporter_secretparameter threadingget_group_legacy_exporter_secret/save_group_legacy_exporter_secretfrom themdk-storage-traitstrait + macrosmdk-memory-storageandmdk-sqlite-storageimplementationslegacy_exporter_secrets(drop the table vs. leave orphaned)*_legacy_media_compattests, thefixed_pre_deadline_ts/fixed_post_deadline_tshelpers, and the boundary testsmdk-core,mdk-storage-traits,mdk-memory-storage,mdk-sqlite-storageBreaking changes
MdkStorageProvider(viaGroupStorage) loses two trait methods — implementers downstream ofmdk-storage-traitsmust update.legacy_exporter_secretrows in storage are still readable (no decryption error) but those rows become inert; clients that still rely on legacy media/message decryption beyond today are already broken at runtime regardless of this PR — no callers can come back and fail "harder" than they already do.Out of scope
key_packages.rs:434andkey_packages.rs:3338), not a runtime timestamp gate. File a separate issue for that one when its deadline approaches.References
_atdeterministic variants for tests_atvariants when they exploded on 2026-05-15 (commit21c50b6); did NOT remove any legacy production code