Repository navigation
c0mpute edge: verify every byte from an untrusted gateway against the owner's signature - #35
Merged
Merged
Conversation
… owner's signature Any c0mpute node can now announce a public gateway, and the edge already discovers and load-balances them from /api/nodes. It must not trust a stranger's bytes. Only our own gateways (C0MPUTE_GATEWAYS seeds and the new C0MPUTE_TRUSTED_GATEWAYS) are proxied as they answer; every discovered gateway gives verified reads only (C0MPUTE_VERIFY_ALL=1 verifies ours too). A verified read asks the gateway for /hosting/v1/sites/<site>/resolve?path= (c0mpute >= 0.3.5) and checks, mirroring hosting/site.rs: the ed25519 signature over the v1/v2 signing bytes, site id = blake3(owner, name), the manifest and every v2 part by blake3, the path resolution itself (first present candidate, 404.html as a 404, no skipped candidate, entry equal to the manifest's), and each chunk (from any gateway, /chunks/<hash>) by hash and length. A record below the highest seq already verified for the site is refused (rollback), persisted in $EDGE_CERT_DIR/site-seqs.json. Verified chunks are cached on disk by hash ($EDGE_CERT_DIR/chunks). A gateway without the routes (404) or that fails a check sits out verified reads (5 / 10 minutes); the edge never falls back to its unverified /s/ bytes. The first chunk is fetched before the status line so an unavailable file falls through to the next gateway and the stale copy; a later chunk that cannot be had cuts the response short rather than send anything unverified. blake3 is @noble/hashes 2.4.0, vendored unmodified (vendor/noble-hashes) so the dev2 checkout keeps running with no npm install. Cross-checked against records, manifests and chunks produced by the Rust node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Any c0mpute node can now be a public gateway (nodes announce
gatewayon/api/nodes, and the edge'sGatewayPoolalready discovers and load-balances them). This makes the edge refuse to trust a stranger's gateway.Trust
C0MPUTE_GATEWAYSseeds (our dev2 gateways) and the newC0MPUTE_TRUSTED_GATEWAYS./api/nodesis untrusted and gives verified reads only.C0MPUTE_VERIFY_ALL=1verifies ours too.Verified read (new
lib/c0mpute-verify.ts, mirrors c0mputehosting/site.rs)GET {gw}/hosting/v1/sites/{site}/resolve?path=(c0mpute >= 0.3.5) gives the record, the manifest (and v2 parts) and the gateway's resolution.site_id= first 20 hex of blake3(c0mpute-site\n{owner}\n{name}) and equal to the requested site; blake3 of the manifest and each part; manifest name; candidate resolution recomputed locally (first present candidate, then404.htmlas a 404; a gateway cannot skip a candidate that exists or leave out a part it needs); entry must equal the manifest's; chunk count, and each chunk's blake3 and length./hosting/v1/sites/{site}/chunks/{hex}), are cached on disk by hash ($EDGE_CERT_DIR/chunks, re-hashed on read).$EDGE_CERT_DIR/site-seqs.json; a lower seq from any gateway is refused.x-c0mpute-verified: 1, the gateway's ETag (304 on If-None-Match),cache-control: public, max-age=60,nosniff.blake3:
@noble/hashes2.4.0, vendored unmodified undervendor/noble-hashes(MIT) so the dev2 checkout keeps running with a plaingit pull, nonpm install(dev2 has no npm).Tests:
node --test tests/*.test.ts: 132 pass (18 new). Fixtures generated in TS in the Rust formats (ed25519 key, serde_json/BTreeMap manifests, v2 parts), plus golden records signed by the Rust node. Covers tampered chunk, wrong signature, forged record, wrong site id, rollback (pure and across gateways), v2 parted site (missing and tampered part), candidate skipping (foo.htmlclaimed whenfooexists, also across parts),404.html, an old gateway without routes (never sent a/s/request), andC0MPUTE_VERIFY_ALL. I also checked every record, manifest and chunk in a local Rust node's hosting catalog (4 v1 + 2 v2 records); all verify.Smoke against the live network: with
verifyAll, every current gateway (dev2 and the dev3/vienna nodes) answers 404 on the resolve route, so all of them are skipped and the edge answers 503 instead of serving unverified bytes. Without it, cloud.killer serves from the trusted dev2 gateway exactly as before.🤖 Generated with Claude Code