/metadata: the size it keeps is exact, not approximate (relay#59) #151

Merged
paulhitt merged 1 commits from docs/metadata-says-length-is-exact into main 2026-09-08 11:54:30 +00:00
2 changed files with 16 additions and 1 deletions
+1 -1
View File
@@ -29,7 +29,7 @@
<thead><tr><th>Kept</th><th>Why it has to be</th><th>Gone after</th></tr></thead>
<tbody>
<tr><td>Recipient account and device</td><td>An envelope cannot be delivered without an address</td><td>#if(retention.deleteOnAck):the moment your app confirms receipt#else:#(retention.deliveredDays) day(s) once delivered#endif</td></tr>
<tr><td>Arrival time and approximate size</td><td>Ordering, and the size is the ciphertext itself</td><td>#if(retention.deleteOnAck):the moment your app confirms receipt#else:#(retention.deliveredDays) day(s) once delivered#endif</td></tr>
<tr><td>Arrival time and <strong>exact size</strong></td><td>Ordering, and the size is the ciphertext itself &mdash; so the length of a message is <strong>not</strong> hidden, to the byte. Sizes group by kind: a read receipt is not the size of a photo, and a one-word reply is not the size of a paragraph. Nothing has to be decrypted to see that, and it is a weak but real guess about what an envelope carries. Hiding it means padding messages on the sending device before they are encrypted &mdash; a change to the apps rather than to this server, because the server cannot pad what it must not read. It has not been made yet.</td><td>#if(retention.deleteOnAck):the moment your app confirms receipt#else:#(retention.deliveredDays) day(s) once delivered#endif</td></tr>
<tr><td>Sender account and device</td><td>Identified sends only — a <strong>sealed</strong> send stores neither</td><td>#if(retention.deleteOnAck):the moment your app confirms receipt#else:#(retention.deliveredDays) day(s) once delivered#endif</td></tr>
<tr><td>The ciphertext</td><td>It is the message; the server holds no key that opens it</td><td>#(retention.undeliveredDays) days if never collected</td></tr>
<tr><td>Attachment blobs (photos, voice notes, stories — all ciphertext)</td><td>Devices fetch them separately from the envelope that references them</td><td>#(retention.attachmentDays) days</td></tr>
@@ -213,6 +213,21 @@ final class PublicPagesTests: XCTestCase {
XCTAssertEqual(offenders, [], "\n" + offenders.joined(separator: "\n"))
}
/// ⚠️ **The transparency page has to be accurate in the unflattering
/// direction too.** `/metadata` called what it stores the "approximate
/// size" of a message. It is not approximate: the ciphertext length is
/// exact, and lengths group by kind, so a byte count is a weak guess about
/// what an envelope carries (relay#59). A page that undersells what it
/// keeps is worse than one that keeps more and says so.
func testMetadataDoesNotUndersellWhatItKeeps() throws {
let page = try String(contentsOf: views.appendingPathComponent("metadata.leaf"),
encoding: .utf8)
XCTAssertFalse(page.localizedCaseInsensitiveContains("approximate size"),
"the stored ciphertext length is exact, not approximate")
XCTAssertTrue(page.localizedCaseInsensitiveContains("exact size"),
"the page no longer states that the size it keeps is exact")
}
func testTheSafetyPageCarriesAReachableContact() throws {
let page = try String(contentsOf: views.appendingPathComponent("safety.leaf"),
encoding: .utf8)