All commands run from the repository root with Go 1.25+ and TinyGo 0.43
(Go 1.27 from ~/.local/goroot where TinyGo needs it).
| Check | Result |
|---|---|
go test ./... | pass |
go vet ./... | pass |
go test -race ./pkg/tunnel ./pkg/neutrino ./internal/e2e | pass |
tinygo build -target wasm -o bin/nostrino.wasm ./cmd/nostrino-wasm | pass |
tinygo build ./cmd/nostrino-bridge ./cmd/nostrino-neutrino | pass |
| wasm sync through torly + bridge + test node | tip 50, 51 filters; ~2.7 s relay, ~20 ms WebSocket |
| IndexedDB reload (two wasm instances, fake-indexeddb) | second sync adds 0, same tip, local match still works |
| wasm cancel mid-sync | settles as cancelled, bridge closes the session |
| frozen reader / zero credit | 4 MiB write blocks, then unblocks on reset |
| NIP-42 on a torly with auth required | endpoint-to-endpoint sync passes |
| two relays, one dropping every event | stream continues on the other |
| WebSocket data plane (bridge=) | e2e sync over the WS stream; both wasm smokes assert transport=websocket |
| reorg to a different branch at height 6 | old filters replaced, new branch matches |
| checkpoint at height 10 | starts at 10, verifies 11..tip, no filter below |
gateway presence + nostrino-discover | finds the URI, chains and node tip |
| file store resume | second sync adds 0; tip and filters preserved |
| dropped relay events | 512 KiB echo survives 1-in-9 drops |
| profile JSON + difficulty/retarget/MTP | unit tests pass |
Reproduce:
make build wasm
./scripts/wasm-smoke.sh
(cd /tmp && npm install fake-indexeddb) # once
NODE_PATH=/tmp/node_modules ./scripts/idb-smoke.sh
bin/nostrino-discover -relay wss://relay.example -chain btc
The smoke scripts start torly, the deterministic test node, the bridge and the wasm bundle under Node. They are the acceptance tests for the browser target.
Ordered by what matters next.
parameters are not in the binary. -profile and -checkpoint-* accept
them; the operator must obtain them from a trusted node and keep them
current. A stale checkpoint only costs sync time; a wrong one is rejected
on the first filter header.
memory on open. That is fine for a regtest chain and a recent-range wallet,
but a full mainnet filter set (GB scale) needs a store that reads filters
on demand. The Store interface is the seam; the sync loop should not
change.
stored, so that one boundary is accepted from the peer. Every later boundary is verified. A checkpoint that also pins the window's start would close this.
is refused rather than silently accepted, which is the safe failure; a mainnet wallet also needs the height it scans from.
implemented, but the reference build listens in plain ws://. A public
wss:// endpoint needs a reverse proxy (or a later TLS listener) and the
operator supplies its advertised URL with -bridge-url.
advertisement; the tip is from a handshake every five minutes and can be
stale. There is no TTL enforcement beyond the relay's addressable-event
replacement, no per-publisher rate on announcements, and discovery trusts
the payload (the event signature and d tag are checked, but a gateway can
lie about its tip).
per-client session cap. There is no per-day quota, no per-IP limit, and no accounting to bill or block a heavy client.
auth result. A relay that requires auth for REQ is not specially handled; torly gates writes only.
UTXO selection, HTLC scripts, signing, the swap state machine, the settlement deadline recomputation on resume, and the server-side swap record. The registry and the tunnel deliberately do not carry swap state.
DATA/gateway.key (0600), the URI in DATA/gateway.uri.
45 s and the listener reconnects with backoff.
55 KiB of encrypted content.
adjust with -max-bytes-per-sec and -max-sessions-per-client.
nostrino-bridge -check -node ... reports whether the node advertises NODE_COMPACT_FILTERS; an operator must still confirm
blockfilterindex=basic and the filter index height.
bridge's idle bound and the WebSocket close/error events bound liveness. The P2P read deadline is advisory on that path.