NIP-44 drops the 65535-byte limit
Every NIP-44 payload has carried a 2-byte length prefix that caps plaintext at 65535 bytes. A change merged on 28 June 2026 adds a variable-length prefix that raises the ceiling to just under 4 GB, without a version bump and without breaking anything already deployed.
Nostr WoT Newsroom
Assembled by the Nostr WoT Newsroom from the cited primary sources, and published automatically without individual human review.
NIP-44 is the encryption scheme most of Nostr's private messaging is built on, including the gift-wrapped direct messages in NIP-17. Since it was introduced, it has had a hard ceiling on how much you can encrypt in a single payload: 65535 bytes. On 28 June 2026, a change from fiatjaf removed that ceiling, and it is worth being precise about what actually changed.
Where the old limit came from
The 65535-byte cap was never a deliberate policy decision. It was a direct consequence of how NIP-44 pads plaintext before encrypting it. The padding format stored the plaintext's length as the first two bytes of the padded blob, a big-endian unsigned 16-bit integer, u16. A u16 can only represent values from 0 to 65535, so anything larger simply could not be expressed in the length prefix, regardless of whether the underlying cipher could otherwise handle it. The spec's own constants section spelled this out directly: max_plaintext_size was 65535, "64kB minus 1".
That same 2-byte assumption propagated outward. NIP-44's validation rules on the base64-encoded payload and the decoded byte string were both range checks, not just minimum-length checks: base64 had to fall between 132 and 87472 characters, decoded bytes between 99 and 65603. Both upper bounds existed purely because a bigger message was structurally impossible under the 2-byte prefix.
What the new scheme does
The merged change keeps the 2-byte prefix for anything that still fits in it, and adds a second, larger format for anything that doesn't. The rule is a threshold at 65536 bytes:
- If the plaintext length is under 65536 bytes, the prefix is still 2 bytes, exactly the old
u16format:[plaintext_length: u16][plaintext][zero_bytes]. - If the plaintext length is 65536 bytes or more, the prefix becomes 6 bytes: two zero bytes followed by a big-endian
u32,[0x00, 0x00][plaintext_length: u32][plaintext][zero_bytes].
The two zero bytes at the start of the extended prefix are the signal, not filler. A valid plaintext is always at least 1 byte, so a u16 value of exactly zero could never occur in the old format. Decoding is unambiguous: read the first 2 bytes as a u16; if zero, read the next 4 bytes as a u32 for the real length; if not zero, that value is the length and the prefix was only ever 2 bytes.
max_plaintext_size moves from 65535 to 4,294,967,295, which is 2^32 minus 1, roughly 4 GB. The range checks on base64 and decoded payload length lose their upper bounds entirely and become minimum-only checks: at least 132 base64 characters, at least 99 decoded bytes, with no ceiling.
The spec also gained a new "Implementation considerations" section. It tells implementations to enforce their own maximum payload size for their platform, and to reject oversized payloads early, before base64 decoding, to avoid denial-of-service from decoding huge inputs. It notes that decryption can need several times the payload size in working memory, that JVM-based systems cap contiguous arrays at roughly 2 GB, well under the new theoretical 4 GB ceiling, and that padded-length calculations can now reach 2^32, so implementations need 64-bit arithmetic to compute padding correctly.
New test vectors were added at the boundary, checking a 65535-byte plaintext against a 65536-byte one, to confirm implementations switch prefix formats at exactly the right point.
Same version, no version bump
This is still NIP-44 version 2. The change did not touch the version byte or add a new version number to negotiate. It extends the existing v2 padding format with a second prefix length that only activates once the message crosses 65536 bytes. A version-2 decoder that has been updated to this NIP handles both old and new messages under the same version identifier.
Backward compatible, by the PR author's own account
The PR body states plainly: "This is backwards-compatible and no one has to change anything if they do not care about bigger messages." Nothing changes for the common case. Any plaintext under 65536 bytes is encoded with exactly the same 2-byte prefix as before, byte for byte. Implementations that never need to send anything larger do not need to update at all.
The PR body also names the concrete case that motivated the change: "The only reasonable case where this necessity has shown up so far was signing of big kind:3 lists via NIP-46." Large kind:3 contact lists, sent through NIP-46 remote signing, were the workload running into the old ceiling.
NIP-44 underlies encrypted direct messages on Nostr, so a limit on plaintext size is a limit on what a single encrypted message can carry. Raising it does not touch how the conversation key is derived, how ChaCha20 encryption works, or how the MAC is calculated. It changes what fits in one message.
Sources
Every claim in this piece links to a primary source.
- allow NIP-44 to encrypt more than 65535 bytes — nostr-protocol/nips (June 28, 2026)