Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3e406d1
feat(core): port the wire codec, NIP-42, NIP-43 and validation
chappie-daemon Oct 23, 2026
09afda6
feat(relay): port the handlers, transport, limiters and live dispatch
chappie-daemon Oct 23, 2026
4a9ebfe
feat(nostreamd): serve the relay through the acceptor
chappie-daemon Oct 23, 2026
7785e06
refactor(relay): route every outbound frame through the transport
chappie-daemon Oct 23, 2026
15c6525
test(relay): add the case census and the wire-session oracle
chappie-daemon Oct 23, 2026
4d267fc
docs(relay): state the strategy stub rule without the literal
chappie-daemon Oct 23, 2026
b42324e
fix(relay): reclaim expired rate-limit keys and cap the stores
chappie-daemon Oct 23, 2026
ba0b88d
fix(relay): drain a connection whose drain began before its loop
chappie-daemon Oct 23, 2026
498a074
test(relay): make the oracle deterministic and bound its exceptions
chappie-daemon Oct 23, 2026
7c3901d
test(relay): require a mapping to name a live test
chappie-daemon Oct 23, 2026
5d4c4c3
fix(relay): answer a store error with the generic failure, not a refusal
chappie-daemon Oct 23, 2026
11f8e05
refactor(relay): one construction path, one strategy shape, shared ev…
chappie-daemon Oct 23, 2026
80b82c2
test(relay): compare a step through one function, skipped session inc…
chappie-daemon Oct 23, 2026
9daafbf
fix(relay): refuse a new rate-limit key instead of freeing a live one
chappie-daemon Oct 23, 2026
3aefb05
fix(relay): delete the leftover read-guard probe
chappie-daemon Oct 24, 2026
b4e0a04
docs(relay): state the limiter's cost and closure per level
chappie-daemon Oct 24, 2026
d73abfb
docs(relay): the event level's cost is set by the kinds a flood picks
chappie-daemon Oct 24, 2026
4f8ac67
docs(relay): name the whole uncovered kind set at the event level
chappie-daemon Oct 24, 2026
27a86fe
chore(relay): add an empty changeset for the port
chappie-daemon Oct 24, 2026
74ea36e
chore(changeset): keep the prose entry, drop the duplicate empty one
chappie-daemon Oct 24, 2026
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
19 changes: 19 additions & 0 deletions .changeset/relay-wire-handlers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
---

feat(relay): answer a real client over the wire, with the capture's rate limits

`nostream-relay` now serves the wire surface job 008 specified: connection control,
`EVENT` ingestion through the strategy and limiter pipeline, `REQ`/`CLOSE` with
streaming `EVENT` and `EOSE`, NIP-42 `AUTH` with restricted reads, `COUNT`, and the
`OK`/`NOTICE`/`CLOSED` replies the capture sends. The limiter keeps four per-level
stores with lapse-preferring eviction and a store-owned sweep, so a refused level
reopens within one window plus one sweep interval.

Parity is measured rather than asserted. `ts_case_parity` runs 462 cases of the
TypeScript census against the Rust handlers; `wire_parity` replays 16 captured
sessions, 78 steps, and compares frames byte for byte with one declared tolerance.

The TypeScript tree is untouched: no `src/` or `test/` file changes, so the published
package has no user-visible change. Decisions are recorded in
`docs/rewrite/decisions/0004-wire-transport.md`.
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ resolver 3:
| `nostream-core` | domain vocabulary: events, filters, kinds, wire messages, NIP type surfaces, `ShutdownState` |
| `nostream-config` | typed settings, loader, deep merge, validator, dot-path editor, redaction, guided catalogue |
| `nostream-storage` | event-store backends (LMDB via heed, Postgres), cache |
| `nostream-relay` | acceptor, connections, wire protocol, NIP-77 engine |
| `nostream-relay` | connections, wire protocol, handlers, rate limiters, live dispatch, NIP-77 engine (the acceptor lives in `nostreamd`) |
| `nostream-services` | NIP-66 monitors, Tor, schedulers, outbound work |
| `nostream-admin` | admin API and auth |
| `nostream-cli` | operator command line (`nostream-cli config validate`) |
Expand Down Expand Up @@ -139,11 +139,12 @@ resolver 3:
recorded per case.** When a message or an exemption must change, change the fixture in
the same commit and say why; never loosen the comparison to make a case pass.

## Porting discipline (this branch, phase 1)
## Porting discipline (this branch, phase 2)

- **Stubs are labelled, not hidden.** Every unimplemented item carries
`/// Phase 1 stub — job 005; <what it will do>; see <TS path>` on the item, takes typed
arguments, and returns `NotImplemented`. `grep -rn 'Phase 1 stub' crates` must count
`/// Phase 2 stub — job NNN; <what it will do>; see <TS path>` on the item (a stub left
from phase 1 keeps its `Phase 1 stub` label until a job touches it), takes typed
arguments, and returns `NotImplemented`. `grep -rn 'Phase [12] stub' crates` must count
the stubs.
- **No silent `Ok(())`, no plausible fake data, no partial port.** A function that would
need real behaviour returns the error. A `Result`-returning stub whose body is
Expand Down
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ chacha20 = "0.10.2"
chrono = { version = "0.4", default-features = false, features = ["std", "clock", "serde"] }
clap = { version = "4", features = ["derive"] }
futures-core = "0.3"
futures-util = "0.3"
getrandom = "0.3"
heed = { version = "0.20", features = ["read-txn-no-tls"] }
hex = "0.4.3"
hkdf = "0.13.0"
Expand All @@ -40,7 +42,8 @@ sha2 = "0.11.1"
sqlx = { version = "0.9", default-features = false, features = ["runtime-tokio", "postgres", "migrate", "macros", "chrono", "json", "tls-rustls-ring-webpki"] }
tempfile = "3"
thiserror = "2"
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "signal", "sync", "time"] }
tokio = { version = "1", features = ["io-util", "macros", "net", "rt-multi-thread", "signal", "sync", "time"] }
tokio-tungstenite = { version = "0.30", features = ["connect", "handshake"] }
tracing = "0.1"
tracing-opentelemetry = "0.34"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
Expand Down
2 changes: 2 additions & 0 deletions crates/nostream-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ description = "Nostr domain types, wire messages and NIP surfaces for the nostre
[dependencies]
base64.workspace = true
chacha20.workspace = true
getrandom.workspace = true
hex.workspace = true
hkdf.workspace = true
hmac.workspace = true
Expand All @@ -20,6 +21,7 @@ serde_json.workspace = true
sha2.workspace = true
thiserror.workspace = true
tokio.workspace = true
url.workspace = true

[lints]
workspace = true
Loading
Loading