@hisoka-io/anon-rpc-worker 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +120 -15
- package/dist/anon-rpc-worker.js +5955 -3361
- package/dist/anon-rpc-worker.js.keccak256 +1 -1
- package/dist/anon-rpc-worker.provenance.json +19 -8
- package/package.json +2 -2
- package/snapshot/capabilities.json +7 -4
- package/snapshot/nox-snapshot.json +6 -3
- package/snapshot/nox-snapshot.json.keccak256 +1 -1
- package/src/config.ts +77 -0
- package/src/core.ts +96 -4
- package/src/embed-modules.d.ts +16 -0
- package/src/errors.ts +7 -1
- package/src/fetch-map.ts +109 -13
- package/src/local-answers.ts +238 -0
- package/src/tls/channel.ts +183 -0
- package/src/tls/module.ts +98 -0
- package/src/tls/pool.ts +358 -0
- package/src/tls/transport.ts +257 -0
- package/src/tls/tunnel.ts +360 -0
- package/src/worker.ts +12 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@hisoka-io/anon-rpc-worker`.
|
|
4
4
|
|
|
5
|
+
## 0.4.0 (2026-10-10)
|
|
6
|
+
|
|
7
|
+
Built on `@hisoka-io/nox-client` 0.8.0. Every 0.3.0 config stays valid; with the new defaults, https calls go
|
|
8
|
+
through TLS tunnels and need exits on nox rc.9 or later with `[tunnel]` enabled.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- End-to-end TLS (README "End-to-end TLS"): the worker runs TLS 1.3 and 1.2 itself (rustls with ring in a second
|
|
13
|
+
embedded WebAssembly module, `packages/nox-tls`, compiled at boot) and exits relay TLS records over one TCP
|
|
14
|
+
connection per tunnel. Requests and responses stay end-to-end encrypted between the worker and the RPC provider,
|
|
15
|
+
and the mixnet keeps the sender anonymous; the exit sees the provider's host name, timing and sizes.
|
|
16
|
+
- Config `tls` (`"required"` default, `"preferred"`, `"off"`), `tlsSession` (`"per-call"` default, `"keep-alive"`),
|
|
17
|
+
`tlsSpares`, `tlsSpareTtlMs`, `tlsKeepAliveMs`, `tlsMaxCallsPerSession`, `tlsOpenTimeoutMs`, `tlsCopyAfterMs`,
|
|
18
|
+
`tlsGapMs`, `tlsMaxCopies`.
|
|
19
|
+
- Per-call sessions with handshaken spares per recently used host, opened in the background after the first wallet
|
|
20
|
+
call settled; keep-alive sessions as an opt-in. Resumption and 0-RTT stay off; ALPN is `http/1.1`.
|
|
21
|
+
- HTTP/1.1 in the worker: caller headers keep their order and duplicates; framing, hop-by-hop and `User-Agent`
|
|
22
|
+
headers are set by the encoder alone; CR, LF and NUL are refused; a whole JSON request is padded to a size bucket;
|
|
23
|
+
responses are complete only by HTTP framing or TLS close_notify.
|
|
24
|
+
- Call code `permission-denied` (the tunnel exit refuses the destination). Log events `boot.tls`, `tls.open`,
|
|
25
|
+
`tls.fallback`, `tls.reject`, `tls.exit.skipped`, `tls.retry`.
|
|
26
|
+
- Build: `nox-tls` built with the pinned toolchain and a pinned clang (`DEBIAN_SNAPSHOT`, `CLANG_PACKAGE_VERSION`,
|
|
27
|
+
`CLANG_VERSION` in `scripts/toolchain.env`); provenance records its digest, clang, the webpki-roots version and
|
|
28
|
+
release date, and `extraRoots`. `build.mjs --extra-root` and `build-test-worker.mjs --extra-root` for test beds;
|
|
29
|
+
`build-worker.sh --release` refuses a bundle with extra roots.
|
|
30
|
+
- Release gate: `verify-snapshot.mjs --release` requires an exit whose capability hints carry `tunnel_v1`.
|
|
31
|
+
|
|
32
|
+
## 0.3.0 (2026-10-06)
|
|
33
|
+
|
|
34
|
+
Every 0.2.0 config stays valid. Works against entries on nox 0.4.0-rc.6 and on nodes with claim protocol v2.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Config `hedgeAfterMs` (default 3,000, 0 turns it off): resendable reads send a second copy on another route at
|
|
39
|
+
about the observed p95 reply time; the first reply wins.
|
|
40
|
+
- The client runs with the low-latency settings: binary, retaining, long-polling claims where the entry supports
|
|
41
|
+
them; concurrent claims with data blocks first; immediate resend on a lost reply; two transport-failure resends;
|
|
42
|
+
same-entry resends for a single bridge; a standby entry kept connected (claims share the primary connection).
|
|
43
|
+
- Wallet calls first: the first registry check waits for the first call to settle (at most 15 s).
|
|
44
|
+
- Compressed replies: upstream requests carry `accept-encoding: gzip` where the runtime has `DecompressionStream`,
|
|
45
|
+
and the worker inflates gzip bodies itself, capped at `maxResponseBytes`. A 414 KB `eth_getLogs` reply crosses the
|
|
46
|
+
mixnet as about 41 KB (2 reply packets instead of 14), a full block of 490 KB as about 81 KB (3 instead of 16).
|
|
47
|
+
- Worker-local answers: `eth_chainId` and `net_version` per upstream URL once a second, independently routed
|
|
48
|
+
request agrees. Reads addressed by block hash (`eth_getBlockByHash`, `eth_getBlockReceipts`, `eth_getLogs`
|
|
49
|
+
with `blockHash`, EIP-1898 `{blockHash}` state reads, kept once two calls returned the same result) are behind
|
|
50
|
+
the `LocalAnswers` option `blockHashReads`, off in the worker until answers are checked against the hash. A hit answers at
|
|
51
|
+
once with the caller's JSON-RPC `id` and sends nothing. `Cache-Control: no-cache` or `no-store` skips it. An
|
|
52
|
+
empty log or receipt list is always fetched again, since an upstream behind the chain head answers `[]`.
|
|
53
|
+
- The client's send-path settings: chunked packet writes, a send-window warm-up after each entry connection
|
|
54
|
+
opens, and concurrent calls spread over the pinned and standby entries.
|
|
55
|
+
|
|
5
56
|
## 0.2.0 (2026-10-06)
|
|
6
57
|
|
|
7
58
|
Built on `@hisoka-io/nox-client` 0.6.0 (`kps.discovery`). Every 0.1.0 config stays valid.
|
package/README.md
CHANGED
|
@@ -10,15 +10,16 @@ ready the worker reads the registry through the mixnet itself. Where the nodes a
|
|
|
10
10
|
looked up at run time, so operators change IPs whenever they like and new nodes join without a new bundle. A cold
|
|
11
11
|
boot needs no seed server and no public RPC; the bundle reaches the network only through the harness's KPS dialer.
|
|
12
12
|
|
|
13
|
-
What each party sees: the entry node sees the wallet's IP address and encrypted packets
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
What each party sees: the entry node sees the wallet's IP address and encrypted packets. With end-to-end TLS (the
|
|
14
|
+
default, `tls: "required"`), requests and responses stay end-to-end encrypted between the worker and the RPC
|
|
15
|
+
provider, and the mixnet keeps the sender anonymous: the worker runs TLS itself and the exit relays TLS ciphertext
|
|
16
|
+
over one TCP connection, seeing the provider's host name, timing and sizes (see [End-to-end TLS](#end-to-end-tls)).
|
|
16
17
|
|
|
17
18
|
## Layout
|
|
18
19
|
|
|
19
20
|
| Path | Contents |
|
|
20
21
|
|---|---|
|
|
21
|
-
| `src/` | The worker: `worker.ts` (bundle entry), `core.ts` (boot, readiness, accept loop, deadlines), `config.ts`, `fetch-map.ts` (`fetch` to Nox `HttpRequest`), `jsonrpc.ts`, `errors.ts`, `log.ts`, `storage.ts`, `spec-types.ts` (anon-rpc SPEC 0.3.2 worker types) |
|
|
22
|
+
| `src/` | The worker: `worker.ts` (bundle entry), `core.ts` (boot, readiness, accept loop, deadlines), `config.ts`, `fetch-map.ts` (`fetch` to Nox `HttpRequest`, redirects, gzip), `tls/` (end-to-end TLS: `tunnel.ts` one exit tunnel, `channel.ts` TLS and HTTP/1.1 over it, `pool.ts` sessions, spares and exit choice, `transport.ts` the per-hop transport and error mapping, `module.ts` the TLS module bindings), `jsonrpc.ts`, `errors.ts`, `log.ts`, `storage.ts`, `spec-types.ts` (anon-rpc SPEC 0.3.2 worker types) |
|
|
22
23
|
| `snapshot/` | The pinned registry snapshot, its keccak-256, JSON Schema and reviewed capability hints, and the discovery bootstrap (`nox-bootstrap.json`) |
|
|
23
24
|
| `scripts/` | Snapshot generator and verifier, reproducible WASM and bundle build, hashing, provenance |
|
|
24
25
|
| `specifier/` | Worker specifier contracts (Foundry) and a read-only deploy planner and inspector |
|
|
@@ -41,6 +42,7 @@ optional, and every config valid for 0.1.0 stays valid:
|
|
|
41
42
|
| `checkpoint` | | | reserved; any value fails with `bad-config` |
|
|
42
43
|
| `logLevel` | `"debug"`, `"info"`, `"warn"`, `"error"` | `"info"` | |
|
|
43
44
|
| `attemptTimeoutMs` | integer | 12,000 | 3,000-60,000 |
|
|
45
|
+
| `hedgeAfterMs` | integer | 3,000 | 0-60,000. Resendable reads send a second copy on another route when no reply arrived after this long, raised to the observed p95 reply time and kept below `attemptTimeoutMs`; the first reply wins. 0 turns hedging off |
|
|
44
46
|
| `callDeadlineMs` | integer | 25,000 | at least `attemptTimeoutMs`, at most 120,000 |
|
|
45
47
|
| `maxConcurrentCalls` | integer | 16 | 1-64 |
|
|
46
48
|
| `maxRequestBytes` | integer | 1,048,576 | 1,024-4,194,304 |
|
|
@@ -50,6 +52,16 @@ optional, and every config valid for 0.1.0 stays valid:
|
|
|
50
52
|
| `bootRetryMaxMs` | integer | 60,000 | 5,000-300,000 |
|
|
51
53
|
| `topologySources` | integer | 2 | 1-4 |
|
|
52
54
|
| `warmup` | boolean | false | one echo through a full route before ready |
|
|
55
|
+
| `tls` | `"required"`, `"preferred"`, `"off"` | `"required"` | end-to-end TLS, see [End-to-end TLS](#end-to-end-tls) |
|
|
56
|
+
| `tlsSession` | `"per-call"`, `"keep-alive"` | `"per-call"` | one TLS session per call, or sessions shared by the calls to one host |
|
|
57
|
+
| `tlsSpares` | integer | 1 | 0-4 handshaken spare sessions per recently used host (per-call mode) |
|
|
58
|
+
| `tlsSpareTtlMs` | integer | 20,000 | 1,000-50,000; an unused spare is closed after this long |
|
|
59
|
+
| `tlsKeepAliveMs` | integer | 30,000 | 1,000-50,000; how long a keep-alive session serves calls after it opened |
|
|
60
|
+
| `tlsMaxCallsPerSession` | integer | 100 | 1-1,000 calls per keep-alive session |
|
|
61
|
+
| `tlsOpenTimeoutMs` | integer | 5,000 | 1,000-30,000; an exit that does not answer a tunnel open in this long is skipped for 10 minutes |
|
|
62
|
+
| `tlsCopyAfterMs` | integer | 2,500 | 500-30,000; another copy of the current tunnel exchange when nothing arrived for this long (raised to the observed p95) |
|
|
63
|
+
| `tlsGapMs` | integer | 800 | 100-10,000; another copy when a gap in the reply stream stays open this long |
|
|
64
|
+
| `tlsMaxCopies` | integer | 3 | 1-5 copies per exchange for silence or a gap |
|
|
53
65
|
|
|
54
66
|
An unknown key, a wrong type or an out-of-range value fails the boot with `bad-config`, naming the field.
|
|
55
67
|
|
|
@@ -63,6 +75,70 @@ Examples:
|
|
|
63
75
|
{ gateways: ["100.56.0.72:15005:uEiBVDwIs40bsslDkM-BYb2AOHw3PHe70_bj5U_09r7vdIQ"] }
|
|
64
76
|
```
|
|
65
77
|
|
|
78
|
+
## End-to-end TLS
|
|
79
|
+
|
|
80
|
+
The worker carries a TLS client (rustls with ring, compiled to WebAssembly, `packages/nox-tls`) and runs TLS with
|
|
81
|
+
the RPC provider itself. A Nox exit that advertises `tunnel_v1` holds one TCP connection to the provider per tunnel
|
|
82
|
+
and relays TLS records both ways (`ServiceRequest::TunnelV1`, nox `docs/tunnel.md`). Requests and responses stay
|
|
83
|
+
end-to-end encrypted between the worker and the provider, and the mixnet keeps the sender anonymous; the exit relays
|
|
84
|
+
ciphertext.
|
|
85
|
+
|
|
86
|
+
What the exit learns: the provider's host name (from SNI, its own DNS lookup and the IP) and port 443; when a tunnel
|
|
87
|
+
opens and closes; the number, size and timing of TLS records in both directions (a whole JSON request is padded to
|
|
88
|
+
512 B, 1 KiB, 4 KiB, 16 KiB, then 16 KiB steps, so calls of similar size share one record size; response sizes stay
|
|
89
|
+
visible, and larger buckets are a later milestone); the TLS client fingerprint, shared by every worker of one
|
|
90
|
+
version; in keep-alive mode, which calls share a session. The URL path and query (API keys included), headers,
|
|
91
|
+
JSON-RPC methods and parameters, signed transactions, response content and the sender stay hidden from it, and the
|
|
92
|
+
worker detects any change or truncation of a response.
|
|
93
|
+
|
|
94
|
+
`tls`:
|
|
95
|
+
|
|
96
|
+
| `tls` | https on port 443, host by name | http, another port, or an IP-literal host | no known exit advertises `tunnel_v1` |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `"required"` (default) | tunnel | rejects `unsupported` | rejects `network-error` |
|
|
99
|
+
| `"preferred"` | tunnel | exit `HttpRequest` (logged `tls.fallback`) | exit `HttpRequest` (logged `tls.fallback`) |
|
|
100
|
+
| `"off"` | exit `HttpRequest` | exit `HttpRequest` | exit `HttpRequest` |
|
|
101
|
+
|
|
102
|
+
The transport is chosen before any byte of a call leaves the worker, from the URL and the capability data, and a
|
|
103
|
+
call that started on a tunnel never continues on `HttpRequest`, redirects included. The capabilities of pinned
|
|
104
|
+
members are the hints in the bundle's snapshot (`snapshot/capabilities.json`), so tunnel exits change, and an exit
|
|
105
|
+
stops counting as one, only with a new bundle; with discovery, other members' capabilities come from the seed's
|
|
106
|
+
liveness data. `"preferred"` uses plaintext whenever no known exit advertises `tunnel_v1`; `"required"` keeps every
|
|
107
|
+
call end-to-end encrypted. An exit that answers `Disabled`, or no tunnel open within `tlsOpenTimeoutMs`, is skipped
|
|
108
|
+
for 10 minutes and the open moves to another tunnel exit; skipped exits still count as tunnel exits, so with every one
|
|
109
|
+
skipped a call fails with `network-error` under both settings.
|
|
110
|
+
|
|
111
|
+
Session modes:
|
|
112
|
+
|
|
113
|
+
- `"per-call"` (default): every call has its own TLS session and `Connection: close`; no resumption, no 0-RTT, a
|
|
114
|
+
fresh key share each time. `tlsSpares` keeps handshaken, unused sessions for hosts the wallet called, opened in
|
|
115
|
+
the background (only after the first wallet call settled) on a random tunnel exit after an exponential delay
|
|
116
|
+
(mean 2 s), each serving one call. Each call travels in its own session; at low traffic the timing of a spare's
|
|
117
|
+
open can still chain sequential calls of one wallet to one host, which weakens as traffic grows.
|
|
118
|
+
- `"keep-alive"`: calls to one host share a session on one exit for `tlsKeepAliveMs`, up to 4 sessions per host.
|
|
119
|
+
The exit links the calls of a session, and the provider sees them on one TLS connection.
|
|
120
|
+
|
|
121
|
+
Latency: a call on a ready session (a spare, or a warm keep-alive session) costs what the exit `HttpRequest` path
|
|
122
|
+
costs. The first call to a host, and calls beyond the ready spares, add one mixnet round trip for the handshake.
|
|
123
|
+
|
|
124
|
+
Certificates are checked against the Mozilla root store compiled into the bundle (webpki-roots; the version and its
|
|
125
|
+
release date are in the provenance, refreshed with every worker release), with the host name and the device clock;
|
|
126
|
+
a wrong clock fails with a message that names it. Each worker release ships its root store, so wallets keep their
|
|
127
|
+
roots current by moving to new worker releases. Resends of a tunnel exchange go to the same exit through another
|
|
128
|
+
entry and mix and carry identical bytes, which the exit writes once; after a lost tunnel only reads that may be resent
|
|
129
|
+
run again on a new tunnel.
|
|
130
|
+
|
|
131
|
+
`tls` governs the wallet's calls. The chain check's registry reads (discovery) use the exit `HttpRequest` path: public
|
|
132
|
+
chain data, accepted only when a quorum of exit and provider pairs agrees byte for byte.
|
|
133
|
+
|
|
134
|
+
Next milestones: registry reads through tunnels as well, Encrypted Client Hello for providers that publish it, and
|
|
135
|
+
a hybrid post-quantum key exchange (X25519MLKEM768).
|
|
136
|
+
|
|
137
|
+
Errors: `permission-denied` (the exit refuses the destination), `network-error` (no tunnel exit, a certificate
|
|
138
|
+
failure, the exit cannot reach the provider), `protocol-error` (TLS or HTTP checks failed, a truncated response),
|
|
139
|
+
`too-large`, `timeout`. Log events: `boot.tls`, `tls.open` (debug: handshake time and CPU), `tls.fallback{reason}`,
|
|
140
|
+
`tls.reject{code}`, `tls.exit.skipped`, `tls.retry`; none carries bytes or a host name.
|
|
141
|
+
|
|
66
142
|
## Discovery (identity from chain, location at run time)
|
|
67
143
|
|
|
68
144
|
The bundle pins `snapshot/nox-bootstrap.json` (`nox-anon-rpc-bootstrap/1`) next to the snapshot, both covered by
|
|
@@ -124,19 +200,42 @@ were read at, and when each member outside the snapshot was first seen. At most
|
|
|
124
200
|
days. A cache that fails any check (another registry, too many records, one malformed record) is ignored whole, and
|
|
125
201
|
a learned address is used only after the node's `/metadata.json` names the same member.
|
|
126
202
|
|
|
203
|
+
## Release 0.3.0
|
|
204
|
+
|
|
205
|
+
| | |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `workerHash` (keccak-256 of `dist/anon-rpc-worker.js`) | `0x24604525d220bcc7e39f2dbc22966a814600a63ada51baafad5dd0bcc1d28549` |
|
|
208
|
+
| Size | 961,799 bytes; 279,515 bytes with `gzip -9 -n` |
|
|
209
|
+
| sha256 | `0458b19258dffcfb9ce23d584096f3df06532080390d34ee71b201b6b78dbbac` |
|
|
210
|
+
| Specifier | Ethereum Sepolia `0x29B51ca9Ad80E9c0B0D111C8748E6a7908b82eDB` (`ImmutableWorkerSpecifier`, resolvers in [specifier/README.md](./specifier/README.md#deployments)) |
|
|
211
|
+
| Snapshot | NoxRegistry at Arbitrum Sepolia block 316,207,920, keccak-256 `0xa351960e…94fbb2` |
|
|
212
|
+
| nox-wasm module | 188,708 bytes, sha256 `5171416972ea4499fd4b4ea8bcadf002f4c10f10ed42cafb0d5a426121da9c4f` |
|
|
213
|
+
| Built from | `nox-sdk` commit `6bddd851c5b1f4e5508f4718e2ea9b4db626b17f`, rustc 1.95.0, wasm-pack 0.13.1, wasm-bindgen 0.2.114, binaryen 117, esbuild 0.27.3 |
|
|
214
|
+
|
|
215
|
+
The npm package carries the bundle and provenance file of the first pinned container build.
|
|
216
|
+
|
|
217
|
+
Release 0.2.0: `workerHash` `0x0a58f9915f686950072a4786249d396ecbf2194a39ac835effe8ea1a7c76f324` (924,847 bytes, commit
|
|
218
|
+
`ef019a7`), specifier `0x29b4a6A8Cc11769531854d87f9F33EC63Efe8fe6`.
|
|
219
|
+
|
|
220
|
+
Two builds in fresh containers of the pinned images (at two different paths) give these exact bytes
|
|
221
|
+
(`scripts/verify-reproducible.sh --ref 6bddd85`). Run it from the repository root as
|
|
222
|
+
`packages/anon-rpc-worker/scripts/verify-reproducible.sh` (or `pnpm verify:reproducible` inside the package); host builds (`--native`,
|
|
223
|
+
`--local`) use rustc 1.95.0, so set `RUSTUP_TOOLCHAIN=1.95.0` when the host default differs.
|
|
224
|
+
|
|
127
225
|
## What the bundle contains
|
|
128
226
|
|
|
129
227
|
- `dist/anon-rpc-worker.js`: a single IIFE built by esbuild (target es2022, not minified, so it can be read
|
|
130
228
|
and audited). Every module is inlined: the worker source, the `@hisoka-io/nox-client` sources, the
|
|
131
|
-
`nox-wasm` WebAssembly
|
|
132
|
-
byte for byte as one string.
|
|
229
|
+
`nox-wasm` and `nox-tls` WebAssembly modules (base64), the pinned registry snapshot and the discovery bootstrap,
|
|
230
|
+
each embedded byte for byte as one string. The TLS module is compiled at boot, off the first call's path.
|
|
133
231
|
The bundle has no imports and never loads code or WebAssembly by URL. The SDK's ambient `fetch` and
|
|
134
232
|
`WebSocket` defaults are replaced at build time by a stand-in that fails closed, and the build checks the
|
|
135
233
|
output for any other network API.
|
|
136
234
|
- `dist/anon-rpc-worker.js.keccak256`: the Ethereum keccak-256 of those exact bytes, the value a worker
|
|
137
235
|
specifier pins as `workerHash()`.
|
|
138
236
|
- `dist/anon-rpc-worker.provenance.json`: source commit, toolchain versions, input digests (snapshot, bootstrap,
|
|
139
|
-
WASM, lockfile) and
|
|
237
|
+
both WASM modules, lockfile), the compiled-in root store (webpki-roots version and release date, and
|
|
238
|
+
`extraRoots`, 0 in every release) and output digests.
|
|
140
239
|
|
|
141
240
|
## Pinned registry snapshot
|
|
142
241
|
|
|
@@ -155,13 +254,16 @@ The committed release snapshot is of block 316207920 (finalized, read and re-ver
|
|
|
155
254
|
providers of different organisations). It carries the KPS addresses nox-1, nox-2 and nox-8 publish, so the worker
|
|
156
255
|
finds its entries from the snapshot alone, and the default anchors and the chain check add any later moves.
|
|
157
256
|
The release gate names the seven mix and exit nodes, which serve the mixnet without a KPS listener, with
|
|
158
|
-
`--allow-missing-kps
|
|
257
|
+
`--allow-missing-kps`, and requires an exit whose capability hints carry `tunnel_v1`: the hints in
|
|
258
|
+
`snapshot/capabilities.json` come from the seed topology of 2026-10-10 (fleet on nox 0.4.0-rc.9), where the three
|
|
259
|
+
exits advertise `tunnel_v1`. The registry state at that block is the same as in 0.3.0 (fingerprint
|
|
260
|
+
`0x14ca3b69…7c75`); with the new hints the snapshot's keccak-256 is `0x1090f071…014681`.
|
|
159
261
|
|
|
160
262
|
```bash
|
|
161
263
|
pnpm --filter @hisoka-io/nox-client build
|
|
162
264
|
node scripts/make-snapshot.mjs --rpc <arbitrum-sepolia-rpc> --rpc <second-rpc> [--block <n>]
|
|
163
265
|
node scripts/verify-snapshot.mjs --offline # schema, hashes, SDK-derived fields
|
|
164
|
-
node scripts/verify-snapshot.mjs --rpc <rpc> --rpc <rpc> --release # chain re-read
|
|
266
|
+
node scripts/verify-snapshot.mjs --rpc <rpc> --rpc <rpc> --release # chain re-read, KPS addresses, a tunnel exit
|
|
165
267
|
```
|
|
166
268
|
|
|
167
269
|
## Building and checking the hash
|
|
@@ -173,10 +275,11 @@ node packages/anon-rpc-worker/scripts/hash.mjs packages/anon-rpc-worker/dist/ano
|
|
|
173
275
|
```
|
|
174
276
|
|
|
175
277
|
`scripts/toolchain.env` pins every tool that shapes the bytes: the Node and Rust container images by digest,
|
|
176
|
-
rustc, wasm-pack, wasm-bindgen and binaryen (with sha256 digests of their release archives)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
278
|
+
rustc, wasm-pack, wasm-bindgen and binaryen (with sha256 digests of their release archives), and clang with llvm-ar
|
|
279
|
+
for ring's C sources in `nox-tls` (Debian bookworm packages at a snapshot.debian.org timestamp, the same on x86_64
|
|
280
|
+
and aarch64); pnpm comes from the repository's `packageManager` field and esbuild from the lockfile.
|
|
281
|
+
`scripts/build-wasm.sh` follows the tor-js recipe: path remapping, a `RUSTC_WRAPPER` that makes cargo's metadata
|
|
282
|
+
host-independent, `--locked`, and a pinned `wasm-opt` on `PATH`.
|
|
180
283
|
|
|
181
284
|
`scripts/verify-reproducible.sh` builds a commit twice from clean `git archive` exports in fresh containers
|
|
182
285
|
of the pinned images, at two different paths, and compares the WASM, bundle and provenance digests
|
|
@@ -185,12 +288,14 @@ container builds and requires all three to match; `--local --bwrap` runs the two
|
|
|
185
288
|
namespaces at the container paths, for hosts without a container runtime.
|
|
186
289
|
|
|
187
290
|
For a test bed, `node scripts/build.mjs --snapshot <file>` embeds another canonical snapshot (for example a
|
|
188
|
-
local mesh); the provenance records which snapshot a
|
|
291
|
+
local mesh) and `--extra-root <ca.der>` a test CA next to the Mozilla roots; the provenance records which snapshot a
|
|
292
|
+
bundle carries and how many extra roots. The release build (`build-worker.sh --release`) refuses a bundle with an
|
|
293
|
+
extra root (`provenance.mjs --release`).
|
|
189
294
|
|
|
190
295
|
## Tests
|
|
191
296
|
|
|
192
297
|
```bash
|
|
193
|
-
pnpm --filter @hisoka-io/anon-rpc-worker test # worker, SDK integration over an in-memory KPS network, build
|
|
298
|
+
pnpm --filter @hisoka-io/anon-rpc-worker test # worker, SDK integration over an in-memory KPS network, build, e2e bed unit tests
|
|
194
299
|
pnpm --filter @hisoka-io/anon-rpc-specifier test # Foundry and harness-path tests (forge, anvil)
|
|
195
300
|
```
|
|
196
301
|
|