Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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`:
Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading