fix: deliver an accept token once, not on every message

send attached the token to every message addressed to an accepted
correspondent, so a secret rode in the body of ordinary mail indefinitely.
sec 5.8 says the owner delivers each token to its correspondent; it does not
say every message carries one.

Harmless between people, and fatal for any recipient that republishes what it
receives: a published record is verified by rebuilding the exact bytes the
author signed, and those cannot include a token without disclosing it. An
author who accepted such a service could never post to it again — and
accepting a correspondent is the behaviour the rest of the system teaches.

Schema 3 adds `delivered`, set only once the recipient's server has taken the
message, so a failed send leaves the token still owing. Not cleared by block:
the token is a pure function of the master and the identity frozen at
acceptance, so they keep the value either way and re-accepting hands back the
same one.

Existing rows migrate as still owing rather than delivered. One more message
carries the token, which is what every message did before, so nothing that
works today can break; assuming delivery would instead strand anyone accepted
but never written to.
This commit is contained in:
randogoth 2026-10-08 18:09:41 +03:00
parent 8872ec6c98
commit 16dafcd388
2 changed files with 121 additions and 4 deletions

View file

@ -529,8 +529,13 @@ pub fn send(
fields.push(("Reply-To".into(), home.uri(&me.pk())));
}
}
// sec 5.8: hand an accepted correspondent the token for our own mailbox.
if let Some(identity) = store.accepted_identity(&addr.short())? {
// sec 5.8: hand an accepted correspondent the token for our own mailbox —
// once. Re-attaching it to every later message put a secret in the body of
// ordinary mail, and a recipient that republishes what it receives cannot
// carry one: a published record is verified by rebuilding the exact signed
// bytes, and the token must not be among them.
let deliver_to = store.token_to_deliver(&addr.short())?;
if let Some(identity) = deliver_to {
fields.push(("Accept".into(), b32(&account.token_for(&identity))));
}
let borrowed: Vec<(&str, &str)> = fields
@ -567,6 +572,12 @@ pub fn send(
None
};
// Only now, with the message accepted by their server: a send that failed
// earlier leaves the token still owing, so the next attempt carries it.
if deliver_to.is_some() {
store.mark_delivered(&addr.short())?;
}
// sec 5.6: the ephemeral is gone, so keep a copy sealed to ourselves.
store.store_sent(&mid, &addr.short(), &self_copy, now())?;
Ok(Sent {