fix(peer): gossip pool presents the node's advertised mTLS identity (#1532)#82
Merged
Merged
Conversation
…#1532) The node ran a SPLIT identity: the peer-RPC listener + DHT dials presented the persistent CA-signed NodeCert (the peer_id the relay stores + the auto-dialer pins), but the dig-gossip connected-pool listener loaded its cert/key from a separate `peer-net/node.cert` and, absent that file, minted its OWN throwaway ChiaCertificate with a DIFFERENT SPKI -> a DIFFERENT peer_id. Every relayed/direct dial to this node then failed closed with `peer_id mismatch: expected <advertised>, got <gossip-cert>` (#1062 Leg B). Leg A only "worked" because a dial-by-address accepted whatever cert the listener presented. Point the gossip pool's `cert_path`/`key_path` at the NodeCert files themselves (`gossip_identity_paths`; dig-gossip only READS them, so it can never clobber them) and set `cfg.peer_id` from the SAME SPKI via `dig_gossip::peer_id_from_tls_spki_der(identity.spki_der())`. One identity now spans every listener the node runs. Regression: peer::tests::gossip_listener_presents_the_advertised_peer_id asserts the leaf at the gossip cert path reconstructs the advertised peer_id. dig-node-only fix — no dig-gossip/dig-nat change, no release cascade. Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d
commented
Jul 22, 2026
MichaelTaylor3d
left a comment
Contributor
Author
There was a problem hiding this comment.
PASS (correctness gate). Verified against the #1532 Defect-1 fix:
gossip_identity_paths()(peer.rs:692) returnsnode_cert_dir/node.crt+node.key— byte-exact the filesdig_tls::NodeCertpersists (CERT_FILE="node.crt",KEY_FILE="node.key"in dig-tls 0.3.0 node_cert.rs:40-41). dig-gossip loads them read-only viaload_ssl_cert; never clobbers.cfg.peer_id = dig_gossip::peer_id_from_tls_spki_der(identity.spki_der())= SHA-256(SPKI DER) =identity.peer_id()= the advertised/registered/pinned peer_id (peer.rs:1393,1434). One NodeCert identity spans :9444 (peer-RPC) and :9445 (gossip pool).- No production throwaway path remains: NodeCert is written (line 1393) before GossipConfig (line 1420), so the files exist and dig-gossip loads rather than mints. The only self-minted cert left is a pool-stats unit test (lib.rs:4086) that does no peer_id pinning — not the identity path.
- Regression test
gossip_listener_presents_the_advertised_peer_idpasses locally and genuinely reconstructs the advertised peer_id from the exact filesgossip_identity_pathsreturns; it also implicitly guards dig-tls filename drift (a renamed CERT_FILE would fail the read). - SPEC §18 / ports section + DEVELOPMENT_LOG accurate. patch bump 0.54.0->0.54.1 (+ dig-node-core 0.16.0->0.16.1) correct for a behavior-preserving fix; both manifests agree.
Non-gating note (resolved): gossip_identity_paths re-hardcodes the literals "node.crt"/"node.key" that dig-tls owns as private consts. Mitigated by the regression test (drift would fail the read), but dig-tls could export these filenames for consumption. Tracked as a nicety, not a blocker.
The adversarial gate flagged the SPEC/DEVLOG as overclaiming. The fix unifies the CHIA-SSL listeners (:9444 peer-RPC + :9445 direct-gossip) onto the persistent NodeCert — verified correct — but dig-gossip's `nat_node_cert` (state.rs:643-654) still mints a random ephemeral cert for the dig-nat / DigPeer NAT-traversal transport (relayed + hole-punch, dialed via pex.rs:625/630). Narrow the SPEC §19 + DEVELOPMENT_LOG wording to the chia-ssl path only and point at #1541 for the separate NAT-traversal identity unification. No code change — the code fix is correct as-is. Co-Authored-By: Claude <noreply@anthropic.com>
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.
Root cause — Defect 1, NODE IDENTITY SPLIT (#1532)
The standalone node runs two in-process TLS listeners: the peer-RPC server (:9444) and the dig-gossip connected pool (:9445). The advertised / relay-registered / auto-dialer-pinned
peer_id = SHA-256(TLS SPKI DER)comes from the persistent CA-signedNodeCertundernode_cert_dir(). But the gossip pool loaded its listener cert/key from a SEPARATEpeer-net/node.cert, and — absent that file —dig_peer_protocol::load_ssl_certfell through to minting a throwawayChiaCertificatewith a DIFFERENT SPKI, hence a DIFFERENTpeer_id.Result: the gossip-pool listener presented an identity that did not match the advertised one, so every relayed/direct dial to this node failed closed with
peer_id mismatch: expected <advertised>, got <gossip-cert>— exactly the #1062 Leg B failure. Leg A (dial-by-address) only passed because it accepted whatever cert the listener presented.Fix (dig-node-only — no cascade)
gossip_identity_paths(node_cert_dir)returns the NodeCertnode.crt/node.keypaths; the poolcfg.cert_path/cfg.key_pathnow point there. dig-gossip only READS those files, so it can never clobber the canonical identity.cfg.peer_idis set from the same SPKI viadig_gossip::peer_id_from_tls_spki_der(identity.spki_der()), so the pool's self-dial guard + handshake agree on ONE identity.One mTLS identity now spans every listener the node runs (peer-RPC, DHT dials, gossip pool).
Blast radius (gitnexus disabled — via read + ripgrep, §2.0 override)
GossipConfigconstruction is local topeer::spawn/bring-up (one call site).gossip_identity_pathsis a new private helper. No public API changed; the gossip cert files move frompeer-net/node.certto the already-persistedpeer-net/identity/node.crt(read-only reuse). No dig-gossip / dig-nat change → no release-first cascade.Tests
peer::tests::gossip_listener_presents_the_advertised_peer_id— loads the identity from the exact files the pool loads and asserts the reconstructedpeer_idequals the advertised NodeCertpeer_id.peer::testsmodule green (44 passed), incl. the two-node loopback connect + distinct-port bind tests. fmt + clippy clean.Version
fix→ patch: workspace0.54.0 → 0.54.1,dig-node-core 0.16.0 → 0.16.1.Docs
SPEC §18 (env/ports) + DEVELOPMENT_LOG updated with the one-identity-across-listeners contract.
Acceptance
mainre-runs #1062 Leg B (relayed connect via the REAL relay.dig.net). The v0.54.x pointer stays HELD until Leg B is green. Defects 2 + 3 are separate (see the report); this PR closes Defect 1, the primary blocker.🤖 Generated with Claude Code