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
Local data — OAuth tokens and the calendar/task/calendar-list caches — is stored as plaintext JSON under %APPDATA%\Meridian. Not an active vulnerability, but it is exposed to "casual indexers": Windows Search, antivirus, cloud-sync agents, backup tools, and anyone glancing at the folder. Encrypt it at rest.
Decision (2026-05-29)
Mechanism: Windows DPAPI (System.Security.Cryptography.ProtectedData, DataProtectionScope.CurrentUser). Per-user, no key management on our side.
Keep JSON as the serialization format — DPAPI wraps the serialized bytes. No SQLite. SQLite was considered and rejected: it is not encryption (a .db is just as indexable/readable as JSON, so it wouldn't address the threat), and it brings no perf win at our data sizes plus NativeAOT-native-lib risk. Threat model is "casual indexers / backups," which DPAPI fully covers.
Add a small ProtectedFile.ReadBytes/WriteBytes (or ReadText/WriteText) helper wrapping ProtectedData.Protect/Unprotect. DPAPI output is binary, so switch the stores from File.WriteAllText/ReadAllText to byte-based I/O.
Migration: on read, try Unprotect; on failure (CryptographicException), fall back to reading legacy plaintext, then immediately rewrite protected. Otherwise existing users lose all cached state + tokens on upgrade.
account.txt holds the account email and is used to enumerate accounts before anything is decrypted (chicken-and-egg). Decide: either leave it as the one small plaintext index file, or protect it and derive enumeration differently. Email is low-sensitivity vs. event/task contents, so leaving it is defensible — document the choice.
NativeAOT
We publish with PublishAot=true. Confirm ProtectedData survives trimming (it P/Invokes crypt32; should be fine but verify the AOT publish + a round-trip on the published binary, not just the JIT debug build).
Payoff
Lets the privacy policy honestly state data is protected at rest using the OS's standard mechanisms (strengthens Google OAuth verification (exit test mode) #8 verification posture).
Closes the "plaintext tokens" note from the privacy-policy review.
Local data — OAuth tokens and the calendar/task/calendar-list caches — is stored as plaintext JSON under
%APPDATA%\Meridian. Not an active vulnerability, but it is exposed to "casual indexers": Windows Search, antivirus, cloud-sync agents, backup tools, and anyone glancing at the folder. Encrypt it at rest.Decision (2026-05-29)
System.Security.Cryptography.ProtectedData,DataProtectionScope.CurrentUser). Per-user, no key management on our side..dbis just as indexable/readable as JSON, so it wouldn't address the threat), and it brings no perf win at our data sizes plus NativeAOT-native-lib risk. Threat model is "casual indexers / backups," which DPAPI fully covers.Implementation sketch
ProtectedFile.ReadBytes/WriteBytes(orReadText/WriteText) helper wrappingProtectedData.Protect/Unprotect. DPAPI output is binary, so switch the stores fromFile.WriteAllText/ReadAllTextto byte-based I/O.Auth/GoogleTokenStore.cs—Load/Save(token.json).Services/JsonEventStore.cs,Services/JsonTaskStore.cs,Services/JsonCalendarListStore.cs— read/write paths.Unprotect; on failure (CryptographicException), fall back to reading legacy plaintext, then immediately rewrite protected. Otherwise existing users lose all cached state + tokens on upgrade.account.txtholds the account email and is used to enumerate accounts before anything is decrypted (chicken-and-egg). Decide: either leave it as the one small plaintext index file, or protect it and derive enumeration differently. Email is low-sensitivity vs. event/task contents, so leaving it is defensible — document the choice.NativeAOT
PublishAot=true. ConfirmProtectedDatasurvives trimming (it P/Invokescrypt32; should be fine but verify the AOT publish + a round-trip on the published binary, not just the JIT debug build).Payoff
Notes