Skip to content

editing chat messages#2336

Open
fiatjaf wants to merge 1 commit into
masterfrom
edit-9
Open

editing chat messages#2336
fiatjaf wants to merge 1 commit into
masterfrom
edit-9

Conversation

@fiatjaf
Copy link
Copy Markdown
Member

@fiatjaf fiatjaf commented May 9, 2026

I may be crazy, but I think this will work fine in NIP-29 groups.

Maybe this same approach also works in NIP-17 and other places.

The goal is to make it backwards-compatible such that it can still be read by people in clients that don't know about this change.

Because chat messages are delivered consistently by a single-source of truth this should work fine as a new message that performs a "state update" on the existing messages.

@vitorpamplona
Copy link
Copy Markdown
Collaborator

  1. Don't overload kinds. It's bad.

  2. Users don't like to see diff texts. This won't be good for those not implementing it. Devs that don't like edits can't easily opt out.

  3. Can I edit an Edit recursively?

  4. Nip 17 messages already have e-tags to mean different things. This will not work.

  5. You need to deal with indexing of Unicode points, new line chars, offset tracking,

  6. Require pubkey equality.

@vitorpamplona
Copy link
Copy Markdown
Collaborator

Almost no chat protocol diffs edits — they all just send the new full content:

  • Matrix — m.replace relation carrying the entire replacement event.
  • XMPP XEP-0308 ("Last Message Correction") — replacement message, no diff.
  • Slack / Discord / Telegram / WhatsApp / iMessage / Signal — full replace.

The reason is that chat messages are short, bandwidth is cheap, and diffs add a parser, an ambiguity surface, and an ordering dependency. The only argument for diffs in chat is "show what changed" UX, and you can compute that client-side from the old + new strings with any standard diff library.

@vitorpamplona
Copy link
Copy Markdown
Collaborator

Also, I asked Claude to review your proposed diff format and check if there are other standards we could use..

"Yes — several, and the proposal reinvents a worse version of all of them.".

😅

@pablof7z
Copy link
Copy Markdown
Member

pablof7z commented May 9, 2026

Yeah, agreed that a diff-based version is unnecessary complexity.

What happens if the edit of the event is a long time later than the original and the original cannot be found by the client? Should the client skip rendering the edit?

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 9, 2026

The reason for overloading the kind and using a diff is that clients don't have to implement it and can just render the thing as a normal message.

These other "protocols" are fully centralized with a single client and they don't care about interoperability or optionality, so for them that doesn't make any sense.

We have to try to keep the "minimum implementation surface" small for implementations to incentivize newcomers to show up and compete with incumbent, incentivize people to implement their own small client that is still usable, incentivize small implementations that are a small part of bigger clients, embedded in websites etc.

"Yes — several, and the proposal reinvents a worse version of all of them.".

Did it say which one was better and why? These answers from LLMs are useless as they are biased from your question and have no idea of the purpose of having this text diff format. I can easily ask an LLM to review this proposal and get it to say it is great.

By the way, I did look at a ton of other diff formats before coming up with this, none are readable or adequate for small messages, none of them is the canonical obvious best choice either, they are all more-or-less bespoke used by specific programs. And they are all very simple, like mine. But sure, if you think there is a bug here point to it.

Since you are the LLM lovers I'm surprised about you not liking the "parser dependency", since a parser for this is 5 lines of code and can be vibecoded in 10 seconds and an infinite amount of tests can be generated in a minute.

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 9, 2026

What happens if the edit of the event is a long time later than the original and the original cannot be found by the client? Should the client skip rendering the edit?

Yes, it should, editing old messages should probably be blocked anyway as is an evil act. Also having the full content doesn't help with deciding what to do about this.

Actually if you have the full content you don't even have the option of just throwing it into the chat as a normal message like with this, because it would look like a normal message but out of place.

Users don't like to see diff texts. This won't be good for those not implementing it. Devs that don't like edits can't easily opt out.

Who knows what users want? Do users prefer to be completely out of the loop about an edit? And have everybody talking about an edit that they didn't see because their client didn't implement the kind:EDIT? No, probably not.

Can I edit an Edit recursively?

No. We can make that explicit, but if you're operating on a list of messages as your state then the edit just won't be there, so an attempt to edit an edit will be treated as just an edit over an event that doesn't exist by default.

Nip 17 messages already have e-tags to mean different things. This will not work.

What? No, it is a reply. This is adequate as a reply.

You need to deal with indexing of Unicode points, new line chars, offset tracking,

I already said, just index by unicode chars, not bytes. Newline is covered by the fact that we indicate the number of chars deleted and inserted.

@pablof7z
Copy link
Copy Markdown
Member

pablof7z commented May 9, 2026

Actually if you have the full content you don't even have the option of just throwing it into the chat as a normal message like with this, because it would look like a normal message but out of place.

You mean that the diff version is superior because if the client doesn't support edits it will just dump the diff in the chat and users will be like "wtf is this weird thing? this must be some bug" whereas the full version it will be a coherent message but out of place?

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 9, 2026

users will be like "wtf is this weird thing? this must be some bug" whereas the full version it will be a coherent message but out of place?

Well, users are not that dumb, they will see an EDIT with some words in it, and it may refer to a previous message. They will get it.

A coherent message out of place is much worse.

An example: Telegram has a bunch of custom message "kinds", and they keep adding them, but if you're running an older version of their client you'll see a message that says "this is not supported by your telegram version blablabla". Actually, it was like this for a while, but now when you get an unsupported message what you see is an emoji of a confused emotion, or something like that. Just an emoji, as if it was sent by the other user, indistinguishably. It's super confusing.

But on Nostr actually the default case (if we're using a different kind for edits) is to not get the edit at all, as most apps will subscribe only to kind:9, so some users will just not see anything.

@vitorpamplona
Copy link
Copy Markdown
Collaborator

users will be like "wtf is this weird thing? this must be some bug" whereas the full version it will be a coherent message but out of place?

Well, users are not that dumb, they will see an EDIT with some words in it, and it may refer to a previous message. They will get it.

No, they won't. And it's not because they are dumb. It's because it will look broken and/or an unfinished product.

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 9, 2026

Here's a stupid vibecoded encoder/decoder for the patches: https://claude.ai/public/artifacts/fb4718ff-ea13-45eb-8460-f55e9a7e225c

@staab
Copy link
Copy Markdown
Member

staab commented May 9, 2026

Just delete/republish the fixed message. This works without any special support with the worst case scenario being a duplicate similar message.

@Semisol
Copy link
Copy Markdown
Contributor

Semisol commented May 9, 2026

Just delete/republish the fixed message. This works without any special support with the worst case scenario being a duplicate similar message.

The way Matrix does this is a full replacement, for backwards compatibility the body does something like * <new content> to indicate it is a correction.

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 10, 2026

I think most relays shouldn't allow backdating messages or modifying the chat by inserting messages in the middle of a past conversation.

Even if your relay does that I think it's perfectly legitimate for most relays to not want to allow this and most users would probably be better off by not allowing people to completely change the meaning of past conversations from the future.

So this change should consider that scenario.

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 10, 2026

Actually, considering that, the "patch" approach described is even better, as it makes easy for relays to block large edits, which I think is a good feature.

They could also block large edits if the edit event is a full replacement, but that requires fetching the event being deleted and comparing, which is slightly more burdensome.

@staab
Copy link
Copy Markdown
Member

staab commented May 11, 2026

I think most relays shouldn't allow backdating messages or modifying the chat by inserting messages in the middle of a past conversation.

I agree, and I don't think people should do edits of old messages at all. Flotilla implements a 5 minute window and then removes the ability to even try. A matching relay policy seems perfectly fine.

@fiatjaf
Copy link
Copy Markdown
Member Author

fiatjaf commented May 11, 2026

Sounds reasonable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants