From d9798a18f1b1586c9e791ce7ae2ef1817d7b6c72 Mon Sep 17 00:00:00 2001 From: randogoth Date: Wed, 14 Oct 2026 22:08:55 +0300 Subject: [PATCH] fix: trim the body before signing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compose buffer was passed through verbatim, so whatever the author last pressed ended up in the payload — usually a trailing newline. That is not content, but it becomes content the moment it is signed, and nothing downstream can remove it without invalidating the signature. smolboid refuses any post whose text carries a line break, because a published record is one line of a feed, so every post sent from carrier was rejected with "contains TAB, LF, CR or U+2028". Trimming has to happen here, before the seal, rather than at the receiver: stripping is not invertible, so a receiver that trimmed could no longer rebuild the bytes the author signed. --- src/ops.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ops.rs b/src/ops.rs index 8d3444f..103bce0 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -716,7 +716,14 @@ pub fn send( .collect(); let draft = client::SendDraft { address: &addr, - text: body.to_string(), + // Trimmed at the send site, before the payload is signed, so the + // signature covers exactly the text the author meant. A compose buffer + // ends with whatever the author last pressed, and a trailing newline + // is not content — but it is content once it has been signed, and + // nothing downstream can remove it without invalidating the + // signature. smolboid, for one, refuses a post whose text carries a + // line break, because a published record is one line of a feed. + text: body.trim().to_string(), subject, reply_to, headers: &owned,