From ab633bc300792fbb520665ee251ddb16be1391d2 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Mon, 21 Sep 2026 17:31:00 +0300 Subject: [PATCH] chore(release): 0.14.2 Carries the control-byte escaping fix from #103. The id computed for content or tags containing a byte below 0x20 changes with this release, because the escaping now matches nostr-tools and go-nostr rather than the literal wording of NIP-01. Nothing else about the id moves, and no existing vector carried such a byte. Consumers pinning this library by tag pick the change up when they bump. Anything that signs content a person typed or pasted wants it: the wire form previously carried raw control bytes, which is not valid JSON, so a relay would have refused the event. --- CHANGELOG.md | 12 ++++++++++++ README.md | 4 ++-- build.zig.zon | 2 +- src/root.zig | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8f81dd..a6f4241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.14.2] - 2026-09-21 + +### Fixed + +- Control bytes are escaped as `\u00XX`, in the id serialization and in the wire JSON alike, matching nostr-tools and go-nostr. + + Two faults shared one line. `toJson` escaped content and tag fields with the id escaper, which handles the seven characters NIP-01 names and copies every other byte through untouched. RFC 8259 forbids a raw byte below 0x20 inside a JSON string, so an event whose content carried one was not JSON at all: `fromJson` refused this library's own `toJson` output, and a relay would have rejected the event on the wire. + + The id had the same cause and a wider blast radius. NIP-01 says the seven escapes are the only ones and that all other characters go in verbatim, and this library followed that sentence deliberately. The dominant implementations do not. nostr-tools builds the preimage with `JSON.stringify`, and go-nostr writes the same behaviour by hand in `escapeString`, so both escape every remaining control byte. Following the sentence produced an id nothing else reproduces: a correctly signed event from any JS or Go client read as a bad signature here, and an id computed here was unverifiable everywhere else. + + This changes the id computed for content or tags containing a control byte, and nothing else. Everything from 0x20 up, raw UTF-8 included, is still copied verbatim, so reaching for a general-purpose encoder is still wrong: escaping non-ASCII would change the id. + ## [0.14.1] - 2026-09-07 ### Fixed diff --git a/README.md b/README.md index dfedbfa..f79aa1c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ it like [Notary](https://github.com/zig-nostr/notary), a remote signer that keep your key off every client. Full docs, benchmarks, and the ecosystem overview live at [zignostr.com](https://zignostr.com). -> **Status: early (`v0.14.1`).** The library core, transport, local-first store +> **Status: early (`v0.14.2`).** The library core, transport, local-first store > and signer protocol have shipped and are covered by tests. Two native apps run > on it today. APIs may still change before 1.0. @@ -77,7 +77,7 @@ Methodology and the full write-up are on the Add the library to your `build.zig.zon`: ```sh -zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.1.tar.gz +zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.2.tar.gz ``` Wire the module in `build.zig`: diff --git a/build.zig.zon b/build.zig.zon index cf1d6db..2443a3e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .nostr, - .version = "0.14.1", + .version = "0.14.2", // Generated at project creation; never regenerate for this repo. .fingerprint = 0x208aa38fcd8fcc08, .minimum_zig_version = "0.16.0", diff --git a/src/root.zig b/src/root.zig index ee9d994..57d9028 100644 --- a/src/root.zig +++ b/src/root.zig @@ -4,7 +4,7 @@ const std = @import("std"); /// Kept in step with `build.zig.zon` by hand, and it had drifted three /// releases behind, so anything reading it was told the wrong number. -pub const version = "0.14.1"; +pub const version = "0.14.2"; pub const bech32 = @import("bech32.zig"); pub const nip19 = @import("nip19.zig");