{
 "number": 35578,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35578",
 "title": "net: don\u2019t self advertise tor exit node ip addresses in outbound connections",
 "author": "stratospher",
 "author_association": "MEMBER",
 "created_at": "2026-06-21T18:37:45Z",
 "updated_at": "2026-09-17T06:47:04Z",
 "age_days": 87,
 "draft": false,
 "labels": [
  "P2P"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
 "head_ref": "2026-05-self-adv-tor-exit",
 "head_repo": "stratospher/bitcoin",
 "head_history": [
  {
   "t": "2026-09-14T21:59:07Z",
   "sha": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289"
  }
 ],
 "additions": 53,
 "deletions": 4,
 "changed_files": 7,
 "commit_count": 5,
 "size_bucket": "S",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "0xB10C",
      "url": "https://github.com/bitcoin/bitcoin/pull/35578#issuecomment-4807919033"
     },
     {
      "login": "naiyoma",
      "url": "https://github.com/bitcoin/bitcoin/pull/35578#issuecomment-4938015789"
     },
     {
      "login": "mzumsande",
      "url": "https://github.com/bitcoin/bitcoin/pull/35578#pullrequestreview-4789341836"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36177,
     "title": "net: fix startup failures from stale Tor examples",
     "author": "l0rinc"
    }
   ]
  }
 },
 "acks_parsed": {
  "0xB10C": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-26T08:37:48Z",
   "stale": false
  },
  "naiyoma": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-07-10T17:46:58Z",
   "stale": false
  },
  "mzumsande": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-07-27T16:38:16Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 3,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "0xB10C",
   "mzumsande",
   "naiyoma",
   "sedited"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-15T01:22:17Z",
  "last_reviewer_activity": "2026-09-08T21:12:53Z",
  "last_reviewer": "sedited",
  "author_silent_days": 2,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [],
  "depends_on": [
   34031
  ],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 34031,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2025-12-10",
    "title": "net: Remove \\\"tor\\\" as a network specification"
   }
  ],
  "conflicts": [
   36177
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/net.cpp"
 ],
 "body": "- During self advertisement in[ `GetLocalAddrFromPeer`](https://github.com/bitcoin/bitcoin/blob/ea626c268add060c207809c5b22c0e11f284f232/src/net.cpp#L241) - we advertise the \"IP address we think we are\" and sometimes replace it with what \"IP address the peer thinks we are\".\n- when an outbound connections is made to a peer via a proxy, the \"IP address the peer thinks we are\" is the proxy's ip address.\n- so the proxy's ip address can get relayed as our self advertisement\n- in normal cases the proxy's ip address is not a reachable inbound address and shouldn't be relayed on the network.\n\nExample: when a node running both Tor and clearnet via a SOCKS5 proxy connects to a clearnet peer (`proxy=127.0.0.1:9050, listen=1, discover=1` (or) `onion=127.0.0.1:9050`), the \"IP address the peer thinks we are\" is the Tor exit node's IP address and doesn't make sense to relay it. 55-76% of all tor exit nodes were present in various addrman snapshots I checked (my node and the ones from b10c\u2019s peer.observer) from now and few years ago. would be nice to not unnecessarily relay these tor exit IPs since they end up being stored as fresh and gossiped from multiple sources entries in our addrman.\n\nan advanced node runner might have a proxy whose ip address is also a reachable inbound address. I've personally not used such a proxy but 1 difference for them is that they'd need to explicitly set `-externalip=<addr>` to self-advertise their proxy address. i feel it\u2019s an acceptable tradeoff and would imagine they are already using` -externalip=<addr>` config flag as they are explicitly managing their node's public address.",
 "commits": [
  {
   "sha": "b08f497c9f17b78d186c7b9d34ee679719336c52",
   "date": "2026-09-14T21:48:06Z",
   "message": "net: don't self-advertise proxy address in outbound connections\n\nWhen outbound connections to clearnet peers are made via a proxy,\n\n- During self advertisement - we advertise\n  \"IP address we think we are\" and sometimes replace it with what\n  \"IP address the peer thinks we are\".\n- the \"IP address the peer thinks we are\" is the proxy's ip address.\n  and can get relayed as our self advertisement\n- in normal cases the proxy's ip address is not a reachable inbound\n  address and shouldn't be relayed on the network.\n\nthere is a soft-set init parameter interaction proxy \u2192 discover=0\nto prevent this situation but don't think it works in practice\nbecause people might end up overriding it with discover=1\nin real life, 55-76% of all tor exit nodes were present in\nvarious addrman snapshots from now and few years ago. so would be\nnice to check when behind a proxy before self advertising."
  },
  {
   "sha": "b40393792735af2dd61ef990089fe252123db6ee",
   "date": "2026-09-14T21:48:21Z",
   "message": "doc: use proxy=127.0.0.1:9050=onion in tor.md\n\nupdate tor.md after PR 34031\n```\n[error] Unrecognized network in -proxy='127.0.0.1:9050=tor': 'tor'\nError: Unrecognized network in -proxy='127.0.0.1:9050=tor': 'tor'\n```"
  },
  {
   "sha": "115b5aafb800d012cb346ae857f0200e96627c12",
   "date": "2026-09-14T21:51:50Z",
   "message": "netbase: add ResetProxy() for tests\n\na unit test that sets a proxy would leak it into later cases\nin the same binary without a ResetProxy function."
  },
  {
   "sha": "100fd2f3954722bc2807991af16a8898c59f8538",
   "date": "2026-09-14T21:51:56Z",
   "message": "test: check we don't self-advertise a proxy's address\n\nWith a proxy set, check that when we make an:\n- outbound: the peer's view of us is the proxy's address, so it is no\n  longer used and nothing is advertised\n- inbound: unaffected, no proxy is involved and the peer does observe\n  our own address\n- -externalip: still advertised"
  },
  {
   "sha": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "date": "2026-09-14T21:51:56Z",
   "message": "doc: add release note for proxy self-advertisement change\n\nadd a note for advanced operators who want to run reachable proxies\n(ex: tor exits and bitcoin nodes)"
  }
 ],
 "timeline": [
  {
   "t": "2026-06-26T08:37:48Z",
   "kind": "comment",
   "who": "0xB10C",
   "assoc": "MEMBER",
   "text": "Concept ACK, it makes sense to me to not advertise an address we know we can't be reached on. I think this is the case when using `-proxy`, as we specifically say \"make outbound connections through this SOCKS5\" but don't expect inbound connections via this socket. There seems to be something called [`BIND`-request](https://www.rfc-editor.org/info/rfc1928/#section-6) for SOCKS5 proxies, but I don't think that's widely supported (and not implemented in Bitcoin Core).\n\nI've looked at the number of connections my nodes make to Tor exit node IPs in this [post](https://bnoc.xyz/t/outbound-connection-success-rates-of-a-bitcoin-node/142/10?u=b10c): In mid 2026, my nodes each make between 7.5 to 12.5 connection attempts per day to Tor exit node IPs.\n\nWhile nearly all of these connection attempts fail (as expected), there's about 20 IPs where I've seen some connections to Tor exit node IPs succeed over the last years. I assume these are hosts that run (or ran at some point) a Tor exit node and a Bitcoin node, which isn't too unexpected."
  },
  {
   "t": "2026-06-26T09:12:42Z",
   "kind": "comment",
   "who": "0xB10C",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nHm are we sure this is being used like this? With just `-proxy` set, we wouldn't be listening and not self-announcing.\n\nhttps://github.com/bitcoin/bitcoin/blob/93012d7ff918eef41adf453bf10ddb4da21f3df4/src/init.cpp#L812-L825\n\nhttps://github.com/bitcoin/bitcoin/blob/93012d7ff918eef41adf453bf10ddb4da21f3df4/src/net_processing.cpp#L5475-L5476"
  },
  {
   "t": "2026-06-30T09:21:10Z",
   "kind": "comment",
   "who": "stratospher",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\n[quoted text omitted]\ngood point. it's either this or people using `onion=127.0.0.1:9050`.\n\neven though there is the soft-set init parameter interactions which does  `proxy=127.0.0.1:9050` -> `listen=0` and `discover=0` - I wouldn't be surprised if people end up overriding it.\n\n-  `listen=1` is overriden to allow inbound connections without having to deal with the clearnet firewall rules. I've seen/done this a lot.\n- I think `discover=1` and its interaction with other parameters can be confusing to users. It makes a node's own IP discoverable to peers (the node advertises it). That IP is obtained from the [node's own machine](https://github.com/bitcoin/bitcoin/blob/3c76bd435681ee2ebaa1f407ac23736d9c3b331b/src/net.cpp#L3422), and (less obviously) also from what peers [report the node's address](https://github.com/bitcoin/bitcoin/blob/3c76bd435681ee2ebaa1f407ac23736d9c3b331b/src/net_processing.cpp#L3701) to be.\n    - If the node wants control over what exactly it advertises, `-externalip=<ip>` can be used (keeping the soft-set `discover=0`) - the node then advertises only the address we specified in `externalip`, and not the peer-reported view of a node's IP.\n    - if `-externalip=<ip>` is used with explicit `discover=1` override in the bitcoin.conf then again the peer-report view of a node's ip can be advertised.\n\nquick GitHub search shows `proxy=127.0.0.1:9050, listen=1, discover=1` use [here](https://github.com/bitcoin/bitcoin/issues/24536#issue-1166639434) and [here](https://github.com/fajremvp/homelab/blob/main/configuration/orangeshadow/bitcoin/bitcoin.conf). unsure if it's common/rare/how many people use it like this though."
  },
  {
   "t": "2026-07-10T17:46:58Z",
   "kind": "comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "text": "Concept ACK\nIt makes sense not to advertise these addresses, as they are not useful.\ni used this list  https://check.torproject.org/torbulkexitlist  to check for tor exit nodes.\n\nOn master, I noticed that sometimes clearnet outbound peers would send me their own addresses as my address, and I would occasionally self-advertise those same addresses back to them. I think this PR fixes that  only for proxy, but maybe we should also add  check so that a peer does not send us their own address.\n\ntested 412aa3d1fde685bb6b0da7c34ae943383bd02189 with `listen=1 proxy=127.0.0.1:9050 discover=1 `\n\nMy logs show that even when the score flip favoured the peers reported address for an outbound clearnet (tor_proxied ) peer , we do not advertise it , instead we now always fallback to advertising our own adress that we know.\n\nLogs\n\n```\n2026-07-10T13:12:40Z New outbound-full-relay peer connected: transport: v2, version: 70016, peer=4, peeraddr=91.67.73.147:8333\n2026-07-10T13:12:40Z [net] sending sendcmpct (9 bytes) peer=4\n2026-07-10T13:12:40Z [net] sending ping (8 bytes) peer=4\n2026-07-10T13:12:40Z [net] Maybe we should self announce to this address relay peer 4, relay_enabled=1, fListen=1, next_local_addr_send=0, current_time=1783689160359904\n...\n2026-07-10T13:12:40Z [net] GetLocalAddrForPeer peer=4: ours=[2605:59c1:4782:7c08:4936:c6a5:fdee:50d2]:8333 (routable=1) peer_reported=185.220.101.24:43126 (routable=1) | peer_good=1 own_unroutable=0 own_score=1 LOCAL_MANUAL=4 flip_bits=1 (1-in-2) won_flip=1 -> using peer's addrLocal\n2026-07-10T13:12:40Z [net] Advertising address [2605:xxx:xx:xxx:xx:xx:xx]:8333 to peer=4\n```"
  },
  {
   "t": "2026-07-10T17:48:11Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "src/net.cpp",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "in_reply_to": null,
   "text": "Maybe add a comment? to explain a bit on why there's a proxy check here"
  },
  {
   "t": "2026-07-16T11:28:22Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "src/net.cpp",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "in_reply_to": null,
   "text": "An alternative approach would be to check whether an outbound connection is proxied before we even check if `IsPeerAddrLocalGood()`, since the result isn't useful if we won't use the address anyway.\n\n```diff\n\nstd::optional<CService> GetLocalAddrForPeer(CNode& node)\n {\n     CService addrLocal{GetLocalAddress(node)};\n+    // For outbound connections through a proxy, the address the peer reports\n+    // seeing us as is the proxy's exit IP, not ours, so it must never be\n+    // relayed as our self-advertised address. Skip the peer's view\n+    // entirely in that case and keep what we know ourselves to be.\n+    const bool outbound_via_proxy{!node.IsInboundConn() &&\n+                                  GetProxy(node.addr.GetNetwork()).has_value()};\n     // If discovery is enabled, sometimes give our peer the address it\n     // tells us that it sees us as in case it has a better idea of our\n     // address than we do.\n     FastRandomContext rng;\n-    if (IsPeerAddrLocalGood(&node) && (!addrLocal.IsRoutable() ||\n+    if (!outbound_via_proxy && IsPeerAddrLocalGood(&node) && (!addrLocal.IsRoutable() ||\n          rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))\n     {\n         if (node.IsInboundConn()) {\n             // For inbound connections, assume both the address and the port\n             // as seen from the peer.\n             addrLocal = CService{node.GetAddrLocal()};\n-        } else if (!GetProxy(node.addr.GetNetwork()).has_value()) {\n+        } else {\n\n```"
  },
  {
   "t": "2026-07-16T13:17:56Z",
   "kind": "review_comment",
   "who": "naiyoma",
   "assoc": "MEMBER",
   "path": "src/net.cpp",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "in_reply_to": 3560920468,
   "text": "I think its possible to test this in `net_tests.cpp` similar to `get_local_addr_for_peer_port`\n but  using `SetProxy()` and verifying that we dont relay what the peer told us.\n\nI tried this locally\n\n``` diff\n\n+BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_proxy)\n+{\n+    // when proxy is set for an outbound connection, the address\n+    // the peer sends us is the proxy's view of us, not our actual address\n+    // we ignore this this and instead announce what we think we are\n+    // With no local address know,nothing is advertised\n+    // with a routable local address known, that is advertised\n+\n+    in_addr peer_us_addr;\n+    peer_us_addr.s_addr = htonl(0x02030405);\n+\n+    const CService peer_us{peer_us_addr, 20002};\n+    std::cout << \"peer_us: \" << peer_us.ToStringAddr() << std::endl;\n+\n+    in_addr peer_out_in_addr;\n+    peer_out_in_addr.s_addr = htonl(0x01020304);\n+\n+    std::cout << \"peer_out_in_addr: \" << CService{peer_out_in_addr, 8333}.ToStringAddr() << std::endl;\n+    CNode peer_out{/*id=*/0, /*sock=*/nullptr,\n+                     /*addrIn=*/CAddress{CService{peer_out_in_addr, 8333}, NODE_NETWORK},\n+                     /*nKeyedNetGroupIn=*/0,\n+                     /*nLocalHostNonceIn=*/0,\n+                     /*addrBindIn=*/CService{},\n+                     /*addrNameIn=*/std::string{},\n+                     /*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,\n+                     /*inbound_onion=*/false,\n+                     /*network_key=*/0};\n+\n+    peer_out.fSuccessfullyConnected = true;\n+    peer_out.SetAddrLocal(peer_us);\n+\n+    std::cout << \"peer_out: \" << peer_out.addr.ToStringAddr() << std::endl;\n+    BOOST_REQUIRE(SetProxy(NET_IPV4, Proxy{CService{peer_out_in_addr, 9050}}));\n+\n+    BOOST_REQUIRE(!GetLocalAddrForPeer(peer_out));\n+\n+    const CService our_addr{Lookup(\"8.8.8.8\", 8333, false).value()};\n+    BOOST_REQUIRE(AddLocal(our_addr, LOCAL_MANUAL));\n+\n+    auto chosen = GetLocalAddrForPeer(peer_out);\n+    std::cout << \"our_addr: \" << our_addr.ToStringAddr() << std::endl;\n+    std::cout << \"chosen: \" << (chosen ? chosen->ToStringAddr() : \"none\") << std::endl;\n+\n+    BOOST_REQUIRE(chosen);\n+    BOOST_CHECK(*chosen == our_addr);\n+    ResetProxy(NET_IPV4);\n+    RemoveLocal(our_addr);\n+}"
  },
  {
   "t": "2026-07-27T16:38:16Z",
   "kind": "review",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "b5d58ee1496c554ceeb7e37d0a8256a696112fa0",
   "text": "Concept ACK\n\nI wonder if it is really all explained by misconfigurations, overriding defaults, and if everyone would be running this branch, the exit nodes would stop being relayed.\n\nMaybe it could also due to transparent torification where users do not change any defaults but the traffic is rerouted via tor externally, e.g. by an OS such as Whonix? In that case, the fix might not solve the problem completely."
  },
  {
   "t": "2026-09-08T21:12:53Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "@stratospher what is the status here? There are a few unaddressed comments."
  },
  {
   "t": "2026-09-11T20:32:19Z",
   "kind": "review_comment",
   "who": "stratospher",
   "assoc": "MEMBER",
   "path": "src/net.cpp",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "in_reply_to": 3595004116,
   "text": "[quoted text omitted]\n\nI don't think this use case would work with this patch. ex: tor exits who are also running bitcoin nodes.\nI guess we should mention in the release notes that they now need to explicitly set `externalip=<addr>` ."
  },
  {
   "t": "2026-09-11T20:37:26Z",
   "kind": "comment",
   "who": "stratospher",
   "assoc": "MEMBER",
   "text": "@mzumsande good point! I don't think we have a way to know unless we see it in action in the P2P network and this patch obviously won't work for systems running transparent torrification. but we can try to reduce it from core's end when people override defaults.\n\neven though it's not a problem currently, ~95% of the tor exit IPs don't have ports in `p2p-bad-ports.md`. some examples: 1031 tor exit IPs on port 443 (I vaguely remember some discussion on 443 being a good option for core as well), 705 tor exit IPs on port 9001, 276 tor exit IPs on port 9002.\n\nif we ever switch to non default ports - it might get problematic for the tor exits. so I think it's still good to reduce unnecessary connection attempts to tor exits from our side (this is not an issue currently)."
  },
  {
   "t": "2026-09-14T21:59:07Z",
   "kind": "force_push",
   "who": "stratospher",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289"
  },
  {
   "t": "2026-09-14T22:00:33Z",
   "kind": "review_comment",
   "who": "stratospher",
   "assoc": "MEMBER",
   "path": "src/net.cpp",
   "commit": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
   "in_reply_to": 3560920468,
   "text": "done both in the [latest push](https://github.com/bitcoin/bitcoin/compare/b5d58ee1496c554ceeb7e37d0a8256a696112fa0..d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289). thanks!"
  },
  {
   "t": "2026-09-15T01:22:17Z",
   "kind": "comment",
   "who": "stratospher",
   "assoc": "MEMBER",
   "text": "CI failure is unrelated. could someone start the failed CI job again? thanks!\nthe [latest push](https://github.com/bitcoin/bitcoin/compare/b5d58ee1496c554ceeb7e37d0a8256a696112fa0..d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289) adds tests, a comment and a release note."
  }
 ],
 "labels_log": [
  {
   "t": "2026-06-21T18:37:48Z",
   "action": "labeled",
   "label": "P2P",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-14T23:03:17Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-17T06:47:04Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-09-17T05:50:13Z",
   "kind": "closed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-17T05:50:29Z",
   "kind": "reopened",
   "who": "DrahtBot"
  }
 ],
 "text_chars": 13903,
 "text_tokens_estimate": 3475,
 "changed_paths": [
  "doc/release-notes-35578.md",
  "doc/tor.md",
  "src/init.cpp",
  "src/net.cpp",
  "src/netbase.cpp",
  "src/netbase.h",
  "src/test/net_tests.cpp"
 ],
 "files": [
  {
   "path": "doc/release-notes-35578.md",
   "add": 10,
   "del": 0
  },
  {
   "path": "doc/tor.md",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/init.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/net.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/netbase.cpp",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/netbase.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/net_tests.cpp",
   "add": 29,
   "del": 0
  }
 ],
 "test_lines": 29,
 "git": {
  "head": "d7e3ec6d0a4e34867b92bc7efc06b16c1df7f289",
  "head_matches_backup": true,
  "base": "9f7b08c61ca00c2a0c154ac1d69455f8f925a38b",
  "commits": [
   {
    "sha": "b08f497c9f",
    "subject": "net: don't self-advertise proxy address in outbound connections",
    "files": 1,
    "add": 2,
    "del": 1
   },
   {
    "sha": "b403937927",
    "subject": "doc: use proxy=127.0.0.1:9050=onion in tor.md",
    "files": 2,
    "add": 3,
    "del": 3
   },
   {
    "sha": "115b5aafb8",
    "subject": "netbase: add ResetProxy() for tests",
    "files": 2,
    "add": 9,
    "del": 0
   },
   {
    "sha": "100fd2f395",
    "subject": "test: check we don't self-advertise a proxy's address",
    "files": 1,
    "add": 29,
    "del": 0
   },
   {
    "sha": "d7e3ec6d0a",
    "subject": "doc: add release note for proxy self-advertisement change",
    "files": 1,
    "add": 10,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "1935e56eb141fc10",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}