Repository navigation
Conversation
…n; a relay setting never stops the node
Key proof (R3-3, the root cause, replacing the retry): relay-auth carries keyProof
"mmp-relay-auth-v1" when the node holds its identity key; on a relay-challenge (sym-relay 0.8.0)
the node sends relay-auth again with publicKey and an Ed25519 proof over
lp("mmp-relay-auth-v1") || lp(nonce) || lp(room) || lp(nodeId) || lp(origin dialed)
(lib/core/relay-auth-proof.js, vector tests/relay-auth-proof-v1.json shared with sym-relay). A relay
that never challenges takes the first frame as before: relayVerifiesKeys false, said once.
Removed from the 0.14.3 transport fix: the 4004/4006 long-backoff retry, the ping-pong detector,
identityRetry/relayIdentityRetry(Ms), pingPong/retryInMs and their tests. 4004 and 4006 are hard
stops again (MMP 4.4.7), said with their cause: another process proved this identity's key, or the
relay does not verify keys. identity-collision is { nodeId, name, code, kind, reason } plus
relayVerifiesKeys; the metric is relay-hard-stop again.
Slots: roster and presence entries with key/proven; the bound key's slot is the peer, another key
is never a candidate and its departure ends nothing; directed frames name toKey (unicast and
fan-out) when the relay lists key-proof and a slot proven for the bound key.
B1: http(s) relay URLs are ws(s) everywhere a relay URL enters (SymNode, RelayConnection,
parseInvite, the daemon); a setting that is no relay URL disables the relay (status phase
disabled, one log line, a relay-disabled event at start) and never throws or fails start().
…and the relay setting, replacing the identity-close retry
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 341376b48f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!slots.has(slotKey) && slots.size >= MAX_SLOTS_PER_NODE) { | ||
| this._slotsIgnored++; | ||
| if (this._slotsIgnored === 1) this._log(`Relay: ${MAX_SLOTS_PER_NODE} keys already listed for one nodeId; announcements of more are ignored (counted in relay state)`); | ||
| return; |
There was a problem hiding this comment.
Preserve the bound slot when enforcing the per-node cap
When a token holder registers 16 distinct proven keys under a known member's nodeId before that member appears, this unconditional cap rejects the later slot whose key matches _keyOf(peerId). _preferredSlot() then finds neither the bound key nor a legacy slot, so the legitimate member is never announced as present and cannot be targeted with toKey; the impostor slots have therefore recreated a denial of service against the identity this change is intended to protect. Always admit the bound key, evicting an unbound slot if necessary.
Useful? React with 👍 / 👎.
…fter, and the cross-version runs
…nd hello budgets by (nodeId, key); several proven keys for an unbound nodeId are an ambiguity, not TOFU - A relay frame carries fromKey (the key the sender's slot proved, sym-relay 0.8.0). The node drops a frame whose fromKey is not the key it binds for that nodeId, before any session or bucket, and never charges the bound peer. Relay server sessions and the per-sender hello bucket are laned by (nodeId, fromKey), so an impostor proving its own key under a member's nodeId cannot supersede the member's handshake or spend its hello budget. Re-measured: an impostor flooding client-hellos at 25/s no longer keeps a restarted member (whose peer binds it) from re-handshaking. - A nodeId the relay presents under several proven keys, none bound here, is an identity ambiguity: the node starts no session for it and binds none (no trust-on-first-use by who answered first), reports it (identity-ambiguity event, relay-identity-ambiguous metric, status coreSecure identityAmbiguities and relayState.identityAmbiguous, a log line), and resolves it only via a pin, a certification, or when one proven key remains. A single proven key is first proven use as before.
…eral proven keys for an unbound nodeId are an ambiguity, not TOFU
…ble auth refusal, never a downgrade A key proof refused with relay-error kind key-proof (origin-mismatch, invalid, no-challenge) and 4002 is held for the close and treated like 4003: phase refused (refused.kind key-proof, refused.origin the origin this node signed for), one loud FATAL naming it, the slow cadence, and never a fall-back to authenticating without a proof on a relay that challenged. reason rate comes as 4008 and is retried with the usual backoff.
… present while its preferred slot does
Into
release/0.14.3. Pairs with sym-bot/sym-relayfeat/0.8.0-relay-key-proof(draft PR, relay 0.8.0). The MMP spec text follows only after both are tested end to end.Why
The relay keyed this node's slot by a nodeId the node only claimed. Any holder of the channel token could therefore close a member (4004) or refuse it (4006). The 0.14.3 transport fix answered with a retry on a long backoff and a ping-pong detector. That was a patch over an identity nobody verified. It also let two live processes that hold one key flap forever. This branch removes the patch and fixes the cause: the node proves its key.
Key proof (client)
keyProof: "mmp-relay-auth-v1"when the node holds its identity key.relay-challenge(32-byte nonce), the node sends relay-auth again withpublicKeyandproof. The proof is Ed25519 overlp("mmp-relay-auth-v1") ‖ lp(nonce) ‖ lp(room) ‖ lp(nodeId) ‖ lp(origin), whereoriginis the origin of the URL the node dialed. Seelib/core/relay-auth-proof.js.tests/relay-auth-proof-v1.jsonholds the vector shared with sym-relay.relayVerifiesKeys: false, and says so once.identity-collisionis{ nodeId, name, code, kind, reason, relayVerifiesKeys }. The metric isrelay-hard-stop.relayIdentityRetry(Ms),pingPongandretryInMs, and their tests. Every other transport fix stays: lifecycle, held records, size bounds, the backoff reset after auth, and the line readers.keyandproven: trueare tracked per nodeId.toKey(unicast and fan-out) when the relay listskey-proofand a slot proven for the bound key.relayState.relayVerifiesKeys, andrelayStatussays whether the relay verified the key.B1 (re-review): a relay setting never stops the node
disabledwith the reason;RELAY DISABLED:line in the log;relay-disabledevent at start().Results
tests/relay-key-proof.test.jshas 13 tests andtests/relay-url-setting.test.jshas 7.toKeyto an old relay" pin.relay-statehard-stop cases (3) and the rewritten R3-11 lifecycle case fail on 9f6847c.--test-concurrency=2): 1,667 tests, 1,663 pass, 0 fail, 4 skipped.npm audit: 0.node:22-slim,node:20-slim): the new and changed test files run 68/68.scripts/key-proof-matrix, real processes on 127.0.0.1): 46/46. The table is in sym-bot/sym-relay#10.toKeyreaches only the proven slot. With two processes on one key, the older stops for good with no flap.relayVerifiesKeys: false.server.jswithSYM_RELAY_URLset to https://, http:// orrelay.example.comexits at once on 9f6847c (EBADRELAY) and stays up on this branch.tests/relay-url-setting.test.js).Known, not fixed here
fromKeyon delivered frames, plus per-(nodeId, key) receiver state, would end it. That is a wire addition for review.Round 2 (coordinator's calls)
fromKey. The relay stamps the proven key on every frame it delivers from a proven slot. A client-writtenfromKeyis refused.fromKeyisn't the key it binds for that nodeId, before any session or bucket, without charging the bound peer.fromKey).fromKey, the member was kept out for 40 s or more.identity-ambiguity, a metric, a status entry and a log line. It resolves only through a pin, a certification, or one key remaining. A single proven key is first proven use.render.yamlsetsSYM_RELAY_ORIGINS=wss://sym-relay.onrender.com. An origin mismatch is refused askind: key-proof,reason: origin-mismatch, naming both origins. It is not retried, and the client never downgrades to unproven auth.node:22-slimandnode:20-slim): sym 70/70, relay 75/75.npm audit: 0 in both repos.