CryptoContext + optionale context-Varianten (Default-Extension → bestehende Provider unverändert); ScopeVaultClient; ScopedCryptoProvider (Actor, scopeResolver, Sub-Key-Cache, Master-Fallback, shredded→wirft); createScopedKeyProviderFromVault; EventSyncConfig.scopeResolver → Engine baut Scoped-Provider, Context an emit/pull/reencrypt. AES-GCM-seal/open geteilt. 31 Tests grün, abwärtskompatibel. Parität zu @mana/event-sync 0.7.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.9 KiB
Changelog
Alle nennenswerten Änderungen an ManaEventSync. Semver strikt.
[Unreleased]
[0.6.0] — 2026-06-16
Added
- Per-Mandant-Crypto-Shredding (A3), Parität zu
@mana/event-sync0.7.0:CryptoContext+ optionale context-Varianten vonencryptPayload/decryptPayload(Default-Extension forwarded → bestehende Provider unverändert).ScopeVaultClient(getOrMint/get/shred gegen/encryption-vault/scopes/:appId/:scopeId/key, 404/410).ScopedCryptoProvider(Actor: scopeResolver, Sub-Key-Cache, Master-Fallback; geschreddert → wirft).createScopedKeyProviderFromVault.EventSyncConfig.scopeResolver→ Engine baut den ScopedCryptoProvider statt nur Master; Context an emit/pull/reencrypt durchgereicht. AES-GCM-seal/open in geteilte Funktionen extrahiert (Wire identischenc:1:). 31 Tests grün, abwärtskompatibel.
[0.5.0] — 2026-06-10
Erster getaggter Release (v0.5.0) — ab hier Semver-Tags pro Release,
kanonische Version in mana/docs/swift-libs.json.
Added
EventSyncEngine.modelContainer(public) — Zugriff auf den SwiftData- Container für Apps, die viaEventSyncConfig.additionalModelseigene@Model-Typen im selben Store ablegen (z.B. nutriphisLocalBMRProfile).- Decrypt-Failures sichtbar statt Silent-Skip (Parität zu
@mana/event-sync0.6.0): Ein nicht-entschlüsselbares Event aus dem Pull wurde bisher nur geloggt und verschwand — die App sah nie, dass ihr Daten fehlen (der memoro/nutriphi-enc:1-Footgun). Jetzt:- persistenter Zähler in Meta (
decrypt-failure-count,…-last-at,…-last-event), abfragbar viastats()→decryptFailures/decryptFailureLastAt, Reset viaclearDecryptFailures(); - neuer Hook
onDecryptFailure: (DecryptFailure) -> Voidpro Event, zusätzlich feuertonErrormitEventSyncError.decryptFailed(eventId:); decryptPulledPayload(_:eventId:crypto:)wirft auch, wenn der Provider den Ciphertext unangetastet durchreicht (NoOp ohne Key) — vorher wäre derenc:1:-String als „Plaintext" in der Projektion gelandet.
- persistenter Zähler in Meta (
Fixed
- Klartext-Leak-Schutz bei Crypto-Degradierung (
EventSyncEngine): Bei aktivierter Verschlüsselung (enableEncryption) und unerreichbarem Vault fiel die Engine still aufNoOpCryptoProviderzurück (nurLog.notice) — unddrainOutboxhätte eingeloggte Account-Daten unverschlüsselt gepusht, ohne dass die App es merkt. Zwei Ebenen Schutz, beide library-seitig (Apps brauchen keine Änderung):bootstrapCryptologgt jetzt.errorund feuertonError(error), damit die App reagieren kann (Banner o.Ä.).drainOutboxpausiert, solangeenableEncryption && crypto == NoOp(isCryptoDegraded): Events bleiben sicher in der Outbox statt als Klartext rauszugehen. Ein erneut erfolgreicherbootstrapCrypto(nächstersignIn/start) re-wrapped viareencryptOutboxund flusht dann. Apps mitenableEncryption == falsenutzen NoOp bewusst → kein Block. Lokaler Betrieb (Reducer/Projektion) läuft unverändert weiter (Local-First).
- WS-Reconnect-Jitter (
SyncWSClient.scheduleReconnect): Der exponentielle Backoff (1→30 s) feuerte ohne Jitter, sodass nach einem flächigen Server-Ausfall alle Clients synchron zur selben Sekunde reconnecten (Thundering Herd). Jetzt Equal Jitter (50–100 % des Backoffs); der Backoff-Verlauf bleibt deterministisch.
Added
-
E-4.0 — Paket-Skelett:
ManaEventSync-Product, Dependency aufManaCore, mana-swift-core-Konventionen (swiftformat/swiftlint). -
E-4.1 — Geteilte Plumbing aus pageta-native + nutriphi-native extrahiert + vereinheitlicht:
- Core:
EventEnvelope(+withPayload/attributed(to:), optionaleclientId/payloadHashfür Wire-Parität),JSONValue,ULID,JWTSubject,EventSyncError. EventStorage(SwiftData) mit parametrierbarem Store-Name, App-Group,additionalModels+ In-Memory-Option für Tests.- Transport:
SyncHTTPClient(mitX-Schema-Hash+422→schemaOutdated), optionalerSyncWSClient. - Crypto:
CryptoProvider-Protokoll,NoOpCryptoProvider,MasterKeyCryptoProvider(AES-GCM-256,enc:1:-Wire),VaultClient(authBaseURL parametriert) — alles opt-in.
- Core:
-
E-4.2 — Anonymous-Mode + Claim:
EventSyncEnginebesitzt die Identität:makeEnvelopestempeltattributedToUserId/actoraus dem Auth-Modus (anonanon:<id>ODER JWT-sub) — ersetzt dascurrentUserId()-Chokepoint der Apps.- Emit funktioniert offline ohne Login; Push/Pull/WS gated bis
signIn(realUserId:)re-taggt (idempotent) + drained. AnonymousRetention(default 10k Events / 90 Tage) inkl. Prune.
-
E-4.3 (teilweise) —
X-Schema-Hash-Header +422-Handling. Reducer-Golden-Parität gegenevent-kit/test-fixturesfolgt mit der ersten App-Migration (E-4.4). -
E-4.4 (Teil 1) — pageta-native ist Erst-Adopter: 9 Plumbing- Dateien (~900 LOC) gelöscht,
EventSyncCoordinatorbaut Events viaengine.makeEnvelope/emit+ liest viaengine.events(forAggregate:);initist jetzt syncthrows(Crypto-Bootstrap instart()). iOS-Sim-Build grün, 38/38 pageta-Tests grün. Store-Name + App-Group unverändert → lokale Daten bleiben erhalten.
Tests
- 18 Tests grün: Core (ULID/JWT/JSONValue/Envelope), Storage (append/ outbox/Claim-Reattribute/Retention/Meta), Crypto (AES-GCM-Round-Trip/ NoOp/Mixed-Tolerance/Fresh-IV).
Offen
- Re-Encrypt der geclaimten Outbox-Payloads mit dem Master-Key beim
signIn(heute Mixed-Tolerance: geclaimte Guest-Events liegen plaintext auf dem Server, neue verschlüsselt). - Wire-Contract-Test gegen den echten Server (
sequenceNumbernumber-vs-string,clientId/payloadHash). - Voller Engine-Integrationstest (braucht Keychain/AuthClient — heute über Storage- + Crypto-Unit-Tests + die App-Migration abgedeckt).