PARITY: history at rest was already built — the third stale row #84
@@ -113,7 +113,7 @@ From `E2E/E2EInbox.swift`, which is the real feature list.
|
||||
| Trust state, verification, identity-change acceptance | ✅ pinned in the vault, surfaced on the thread |
|
||||
| Key-log enforcement and head gossip | ✅ opt-in enforcement, gossip detects a fork |
|
||||
| Clear thread | ✅ keeps the session, so the chat still works |
|
||||
| History at rest (encrypted, device-only key) | ⚪ |
|
||||
| History at rest (encrypted, device-only key) | ✅ **this row was stale** — sealed per thread under a Keystore key, read straight from disk; see below |
|
||||
| Presence (read and set) | ✅ shown from the relay, never inferred |
|
||||
| Buddy list, add by username | ✅ |
|
||||
| Remove, block, unblock | ✅ |
|
||||
@@ -121,7 +121,35 @@ From `E2E/E2EInbox.swift`, which is the real feature list.
|
||||
| Buddy commands | — retired; not a feature any more (owner, 2026-08-25) |
|
||||
| Voice calls (WebRTC, DTLS-SRTP, TURN) | ✅ P2P-first; ⚠️ no TURN deployed |
|
||||
| Notifications list, read state | ✅ |
|
||||
| Services (AIM/ICQ via `flamenet-oscar`) | ⚪ |
|
||||
| Services (AIM/ICQ via `flamenet-oscar`) | — retired; not a goal for this app (owner, 2026-09-04) |
|
||||
|
||||
🔴 **History at rest was marked ⚪ and it has been built the whole time — the
|
||||
third stale row in this file.** Every read goes to disk: `Session.history` calls
|
||||
`Vault.history`, which decrypts the record. There is no in-memory copy to be
|
||||
fooled by.
|
||||
|
||||
What Android does, against what iOS's `E2EHistoryStore` does:
|
||||
|
||||
| | iOS | Android |
|
||||
|---|---|---|
|
||||
| Cipher | AES-256-GCM | AES-256-GCM |
|
||||
| Key | Keychain, `ThisDeviceOnly` | Android Keystore — never leaves the process at all |
|
||||
| Bound to its slot | one file, nothing to confuse | 🟢 **the record NAME is the AAD**, so a blob cannot be moved between slots |
|
||||
| Shape | one snapshot for everything | one record per thread |
|
||||
| Off backups | excluded from iCloud | `allowBackup="false"`, and a Keystore key is unbackupable anyway |
|
||||
| At rest, locked | `.completeFileProtection` | Credential-Encrypted storage: `filesDir` is unreadable until the first unlock after boot |
|
||||
| Plain sign-out | keeps history | keeps history |
|
||||
| Erase this device | `eraseLocalData()` | `forgetEverything()` **and** the Keystore key is deleted |
|
||||
|
||||
⚠️ Two honest differences rather than a claim of equivalence. Android's
|
||||
per-record AAD is a binding iOS's single file does not need; iOS's
|
||||
`.completeFileProtection` is a per-file assertion, while Android's equivalent is
|
||||
a property of the storage volume rather than of this app's request. Neither gap
|
||||
favours one platform overall.
|
||||
|
||||
⚠️ `Vault.history` prunes expired messages on the way out, so disappearing
|
||||
messages are enforced on READ rather than by a timer nothing runs. It rewrites
|
||||
the record only when something actually expired — a read is not a write.
|
||||
|
||||
✅ Calls are built, on `io.github.webrtc-sdk:android` — the one third-party
|
||||
library in this app, and unavoidable: the alternative is hand-rolling ICE,
|
||||
|
||||
Reference in New Issue
Block a user