Off-the-record conversations: the mechanism
A thread held in memory and written to disk by neither side. Close the app and it is gone: no file to seize, no backup to leak. It is a proposal, not a switch. The device turning it on sends the control envelope and keeps writing until the peer echoes it back. A build that does not know the kind never echoes, so the thread stays on the record on both sides rather than on one of them, which is worse than not having the feature because both people would believe the first thing. Every message reaches disk through one call, so one gate is enough, and a test pins that count so a second write path cannot quietly walk around it. Ratchet state is still persisted: messages stop decrypting without it, and it is not message content. The control for it comes next. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
740e8411cb
commit
ce7f50230b
@@ -939,6 +939,23 @@ class Conversations(
|
||||
sendPlaintext(toUserId, Content.disappearing(seconds))
|
||||
}
|
||||
|
||||
/**
|
||||
* Propose that this conversation goes off the record, or comes back on.
|
||||
*
|
||||
* Nothing is saved here: the proposer waits for the peer to echo it. A
|
||||
* build that does not understand the kind never echoes, so the thread stays
|
||||
* on the record on both sides rather than on one of them.
|
||||
*/
|
||||
fun proposeOffTheRecord(
|
||||
toUserId: Long,
|
||||
on: Boolean,
|
||||
) {
|
||||
sendPlaintext(toUserId, Content.offTheRecord(on))
|
||||
}
|
||||
|
||||
/** Whether this conversation is off the record on this device. */
|
||||
fun offTheRecord(toUserId: Long): Boolean = vault.offTheRecord(Vault.threadForUser(toUserId))
|
||||
|
||||
/**
|
||||
* Offer a contact the bridge this phone uses (§6.19).
|
||||
*/
|
||||
@@ -1455,6 +1472,16 @@ class Conversations(
|
||||
vault.saveDisappearSeconds(Vault.threadForUser(fromUser), seconds)
|
||||
}
|
||||
|
||||
Content.Kind.OFF_RECORD -> {
|
||||
val on = Content.offTheRecord(content) ?: return
|
||||
vault.saveOffTheRecord(Vault.threadForUser(fromUser), on)
|
||||
// Answer a proposal so the other side learns this build kept
|
||||
// its side of it. An echo is not echoed back.
|
||||
if (!Content.offTheRecordIsEcho(content)) {
|
||||
runCatching { sendPlaintext(fromUser, Content.offTheRecord(on, echo = true)) }
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Unit
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user