{
 "number": 32065,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/32065",
 "title": "i2p: make a time gap between creating transient sessions and using them",
 "author": "vasild",
 "author_association": "MEMBER",
 "created_at": "2025-03-14T08:29:59Z",
 "updated_at": "2026-09-17T07:39:48Z",
 "age_days": 552,
 "draft": false,
 "labels": [
  "P2P",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "1808139f52f1f0578352acce9d4761fd04519612",
 "head_ref": "i2p_early_create_session",
 "head_repo": "vasild/bitcoin",
 "head_history": [
  {
   "t": "2025-03-18T11:21:54Z",
   "sha": "a22a3b3c4c96e1c4090800fad621ba31216ddfa2"
  },
  {
   "t": "2025-03-20T11:56:12Z",
   "sha": "fccedfd710ee66a812d1f1b1a9503ce5b5de7cf7"
  },
  {
   "t": "2025-03-20T12:00:50Z",
   "sha": "ef525e8b7c1a78bf4e41053bd297f28a3e5772e8"
  },
  {
   "t": "2025-03-20T14:03:14Z",
   "sha": "859c25909200ecafe261df233bcd512b890f52bc"
  },
  {
   "t": "2025-05-21T11:45:30Z",
   "sha": "632eb1a3c9b1e793f770b447ad581728eab28201"
  },
  {
   "t": "2025-09-29T12:56:01Z",
   "sha": "a42449cb306c4b1310f9967b150f9526376143a4"
  },
  {
   "t": "2025-10-02T13:48:24Z",
   "sha": "fc1ba52a4d790a8ccdead48227f53260d34a659b"
  },
  {
   "t": "2025-10-07T09:15:31Z",
   "sha": "539d8b474edb2232c0a40001acbf3f29966ab430"
  },
  {
   "t": "2026-01-23T09:53:43Z",
   "sha": "6da9cf56a90e65491b1c891257383b6d09459068"
  },
  {
   "t": "2026-01-23T12:52:08Z",
   "sha": "8df10cfdd30115c18284ac83ddf3b619c84a2981"
  },
  {
   "t": "2026-04-30T10:32:52Z",
   "sha": "1808139f52f1f0578352acce9d4761fd04519612"
  }
 ],
 "additions": 94,
 "deletions": 47,
 "changed_files": 4,
 "commit_count": 3,
 "size_bucket": "M",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "zzzi2p",
      "url": "https://github.com/bitcoin/bitcoin/pull/32065#issuecomment-2725369358"
     },
     {
      "login": "eyedeekay",
      "url": "https://github.com/bitcoin/bitcoin/pull/32065#issuecomment-2726907987"
     },
     {
      "login": "1440000bytes",
      "url": "https://github.com/bitcoin/bitcoin/pull/32065#issuecomment-2726286747"
     }
    ]
   },
   "conflicts": [
    {
     "number": 34213,
     "title": "net: preserve anchors when network is disabled",
     "author": "brunoerg"
    },
    {
     "number": 32394,
     "title": "net: make m_nodes_mutex non-recursive",
     "author": "vasild"
    },
    {
     "number": 30951,
     "title": "net: option to disallow v1 connection on ipv4 and ipv6 peers",
     "author": "stratospher"
    }
   ]
  }
 },
 "acks_parsed": {
  "1440000bytes": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2025-03-15T06:44:07Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 1,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "1440000bytes",
   "eyedeekay",
   "jonatack",
   "mzumsande",
   "sedited",
   "zzzi2p"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-04-30T10:33:22Z",
  "last_reviewer_activity": "2026-09-17T07:39:44Z",
  "last_reviewer": "sedited",
  "author_silent_days": 140,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": [
   34213,
   32394,
   30951
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "Connecting to an I2P peer consists of creating a session (the\n`SESSION CREATE` command) and then connecting to the peer using that\nsession (`STREAM CONNECT ID=session_id ...`).\n\nThis change is only relevant for transient sessions because when a\npersistent session is used it is created once and used for all\nconnections.\n\nBefore this change Bitcoin Core would create the session and use it in\nquick succession. That is, the `SESSION CREATE` command would be\nimmediately followed by `STREAM CONNECT`. This could ease network\nactivity monitoring by an adversary.\n\nTo mitigate that, this change creates sessions upfront without an\nimmediate demand for new sessions and later uses them. This creates a\ntime gap between `SESSION CREATE` and `STREAM CONNECT`. Note that there\nis always some demand for new I2P connections due to disconnects.\n\n---\n\nSummary of the changes in the code:\n\n* Create the session from the `Session` constructor (send `SESSION CREATE`\n  to the I2P SAM proxy). This constructor was only called when transient\n  sessions were needed and was immediately followed by `Connect()` which\n  would have created the session. So this is a noop change if viewed\n  in isolation.\n\n* Every time we try to connect to any peer (not just I2P) create a new\n  I2P session, up to a limit (currently 10). This way a bunch of\n  sessions are created (`SESSION CREATE`) at times that are decoupled\n  from the time when sessions will be used (`STREAM CONNECT`).\n\n* Tweak the code in `CConnman::ConnectNode()` to avoid creating session\n  objects while holding `m_unused_i2p_sessions_mutex` because now\n  creating `i2p::sam::Session` involves network activity and could take\n  a few seconds.\n\n---\n\nBefore (note the timestamps):\n\n2025-03-13T09:23:40Z [opencon] [i2p:info] Transient SAM session cd7ea49a7e created\n2025-03-13T09:23:43Z [opencon] [i2p] -> STREAM CONNECT ID=cd7ea49a7e DESTINATION=...\n\n2025-03-13T09:23:51Z [opencon] [i2p:info] Transient SAM session cb0956207e created\n2025-03-13T09:23:52Z [opencon] [i2p] -> STREAM CONNECT ID=cb0956207e DESTINATION=...\n\n2025-03-13T09:24:04Z [opencon] [i2p:info] Transient SAM session 35ad67f343 created\n2025-03-13T09:24:04Z [opencon] [i2p] -> STREAM CONNECT ID=35ad67f343 DESTINATION=...\n\nAfter:\n\n2025-03-13T17:08:58Z [opencon] [i2p:info] Transient SAM session 16098cd4ea created\n2025-03-13T17:09:34Z [opencon] [i2p] -> STREAM CONNECT ID=16098cd4ea DESTINATION=...\n\n2025-03-13T17:09:38Z [opencon] [i2p:info] Transient SAM session 5d209bf1bc created\n2025-03-13T17:09:59Z [opencon] [i2p] -> STREAM CONNECT ID=5d209bf1bc DESTINATION=...\n\n2025-03-13T17:09:50Z [opencon] [i2p:info] Transient SAM session e78aa6d2e7 created\n2025-03-13T17:12:09Z [opencon] [i2p] -> STREAM CONNECT ID=e78aa6d2e7 DESTINATION=...\n\n---\n\nThis was suggested by @eyedeekay some time ago (in 2023 actually) and has been lingering in my TODO since then :eyes:. A further suggestion is to also add a time gap between the activity cease and the closing of the session. For this, when `CNode` is destroyed `CNode::m_i2p_sam_session` would have to be moved elsewhere and destroyed at a later time. cc @zzzi2p",
 "commits": [
  {
   "sha": "151e0c8ef69e748637975972bcba57686351bd02",
   "date": "2026-04-30T09:53:59Z",
   "message": "i2p: create the actual session from Session::Session()\n\nCreate the session with the I2P SAM proxy from the `Session` constructor\n(send `SESSION CREATE` to the proxy). This constructor is only called\nwhen transient sessions are needed and there it is immediately followed\nby `Connect()` which creates the session. So this is a noop change.\n\nWill be needed later to pre-create the session with the I2P SAM proxy in\nadvance, before it is needed."
  },
  {
   "sha": "659336fa9274b9630e6dae125999377028b0a03e",
   "date": "2026-04-30T09:54:03Z",
   "message": "net: change m_unused_i2p_sessions from queue to a single one\n\nChange `CConnman::m_unused_i2p_sessions` from `std::queue` to containing\njust one session (in `std::unique_ptr`). Given that the code that\nfills it is single threaded, the queue would have had either `0` or `1`\nentries.\n\nAlso rename it from\n`m_unused_i2p_sessions` to\n`m_unused_i2p_transient_session`\nto reflect that it contains a single transient session."
  },
  {
   "sha": "1808139f52f1f0578352acce9d4761fd04519612",
   "date": "2026-04-30T10:04:46Z",
   "message": "i2p: make a time gap between creating transient sessions and using them\n\nConnecting to an I2P peer consists of creating a session (the\n`SESSION CREATE` command) and then connecting to the peer using that\nsession (`STREAM CONNECT ID=session_id ...`).\n\nThis change is only relevant for transient sessions because when a\npersistent session is used it is created once and used for all\nconnections.\n\nBefore this change Bitcoin Core would create the session and use it in\nquick succession. That is, the `SESSION CREATE` command would be\nimmediately followed by `STREAM CONNECT`. This could ease network\nactivity monitoring by an adversary.\n\nTo mitigate that, this change creates a transient session upfront\nwithout an immediate demand for new sessions and later uses it. This\ncreates a time gap between `SESSION CREATE` and `STREAM CONNECT`.\nNote that there is always some demand for new I2P connections due to\ndisconnects."
  }
 ],
 "timeline": [
  {
   "t": "2025-03-14T17:45:02Z",
   "kind": "comment",
   "who": "zzzi2p",
   "assoc": "NONE",
   "text": "Thanks for CC'ing me. I also asked eyedeekay and orignal from i2pd to take a look.\n\n10 standby sessions is a lot, except maybe at startup. If your I2P connections limit is 10, then you're doubling your network resource usage. 2 or 3 is probably enough, depending on how fast your connections churn and how long is long enough for a unused session to be up to give you the anonymity you're looking for.\n\nA couple of alternatives:\n\n```c\nint max = MAX_UNUSED_I2P_SESSIONS_SIZE;\nif (uptime() > 15 minutes) max /= 3;\n```\n\nor\n\n```c\nint max = min(MAX_UNUSED_I2P_SESSIONS_SIZE,  3 + MAX_I2P_SESSIONS - current_i2p_sessions.size());\n```\n\nHowever, I'm also concerned about starting a whole bunch of sessions at once, especially at startup, and if the user started his router when he started bitcoin.  If this change will double or triple or more the session-creation rate at startup, that's probably too fast. The  Java router has some tunnel build rate-limiting that may kick in. I won't attempt any pseudocode for it, but some rate limiting in AddI2PSessionsIfNeeded() or some other special case for the first few minutes after startup is probably desirable.\n\nConcept ACK."
  },
  {
   "t": "2025-03-15T06:44:07Z",
   "kind": "comment",
   "who": "1440000bytes",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2025-03-15T18:16:46Z",
   "kind": "comment",
   "who": "eyedeekay",
   "assoc": "NONE",
   "text": "I agree with zzz, the concept ACK is fine but I'm very concerned about having the session bank be 10 sessions long. I think the best method of managing the session bank sort of depends on the rate at which you'll need them.\n\nIf you're creating sessions fast enough to need a bank of 10 sessions, then I think at some point you have no choice but to keep the sessions alive longer and re-use them more. On the other hand, if you aren't creating them fast enough to need a bank of 10 sessions, then keeping a bank of 10 of them is pretty wasteful. How you solve it is going to depend on the actual rates you expect people to need sessions. If the need for sessions is too unpredictable, then maybe the bank size limit needs to change size based on need from a very small number to a larger number and back.\n\nI'm going to take a closer look at the code here and try to make some more concrete suggestions."
  },
  {
   "t": "2025-03-18T11:21:54Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "a22a3b3c4c96e1c4090800fad621ba31216ddfa2"
  },
  {
   "t": "2025-03-18T11:27:54Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "A few notes:\n\n* Bitcoin Core would try to maintain up to 12 outbound connections to peers from all networks that are reachable (IPv4, IPv6, Tor, I2P, CJDNS). From these usually just a few are I2P. So, it is reasonable to assume that from 2,3,4 to 12 I2P connections will be needed. But the case with 12 is when I2P is the only reachable network which is not advisable because of the increased Sybil likelihood.\n\n* This PR is not going to create a storm of I2P sessions creation at startup because new session creation is not being done in a tight loop, deliberately. See the timings in the logs below.\n\n* The lifetime of an I2P connection varies a lot. I would like to avoid adding assumptions about that. How often I2P connections are needed depends on the lifetime + whether a randomly selected address from the address database for the new connection will be I2P.\n\n* Session reuse would degrade privacy because Bitcoin Core would connect to >1 peer from the same I2P source address. This is when transient sessions are used, affected by this PR. Note that there is another less-private mode if Bitcoin Core is listening and accepting new I2P connections. Then a single persistent I2P session is used - it accepts new connections to its permanent address and makes all outgoing connections from that address (= a single I2P session), this PR is not changing that.\n\n* The code before this PR would keep a session for later if it cannot use it for connecting to the given I2P peer (`NAMING LOOKUP` returns an error). I chose `10` for `MAX_UNUSED_I2P_SESSIONS_SIZE` arbitrarily to avoid a broken runaway code from filling all of the Bitcoin Core's memory with unused sessions. I think in practice the number of stored sessions is always `0` or `1` in `master`, before this PR.\n\nBelow is some data for when sessions are created, used and how long do they live. From startup to about 10 minutes after that, with this PR. It is just an example from my node. I guess timings will vary a lot between runs and in different environments. `t0` is the startup time.\n\n`MAX_UNUSED_I2P_SESSIONS_SIZE=10`:\n```\nt0 +  18 seconds: made new session upfront, used 93 seconds after being created, still active after 622 seconds\nt0 +  19 seconds: made new session upfront, used 177 seconds after being created, still active after 621 seconds\nt0 +  22 seconds: made new session upfront, used 297 seconds after being created, still active after 618 seconds\nt0 +  26 seconds: made new session upfront, used 397 seconds after being created, still active after 614 seconds\nt0 +  36 seconds: made new session upfront, not used in 604 seconds\nt0 +  44 seconds: made new session upfront, not used in 596 seconds\nt0 +  52 seconds: made new session upfront, not used in 588 seconds\nt0 +  60 seconds: made new session upfront, not used in 580 seconds\nt0 +  66 seconds: made new session upfront, not used in 574 seconds\nt0 +  69 seconds: made new session upfront, not used in 571 seconds\nt0 + 244 seconds: made new session upfront, not used in 396 seconds\n```\n\n`MAX_UNUSED_I2P_SESSIONS_SIZE=4`:\n```\nt0 +  20 seconds: made new session upfront, used 469 seconds after being created, still active after 699 seconds\nt0 +  25 seconds: made new session upfront, used 588 seconds after being created, still active after 694 seconds\nt0 +  30 seconds: made new session upfront, used 82 seconds after being created, lifetime: 125 seconds\nt0 +  43 seconds: made new session upfront, used 201 seconds after being created, lifetime: 121 seconds\nt0 + 244 seconds: made new session upfront, not used in 475 seconds\nt0 + 366 seconds: made new session upfront, not used in 353 seconds\n```\n\nGiven the above, I changed `MAX_UNUSED_I2P_SESSIONS_SIZE` from `10` to `4` in `42f98c9dfa...a22a3b3c4c`."
  },
  {
   "t": "2025-03-18T16:16:48Z",
   "kind": "review",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "a22a3b3c4c96e1c4090800fad621ba31216ddfa2",
   "text": "Two thoughts:\n- As far as I understand it (https://geti2p.net/en/about/performance/future), i2p tunnels last for 10 minutes by default. If we don't use these extra sessions before they expire, their creation would be a waste of ressources. I would image this to be particularly relevant in a scenario where i2p is used together with other networks. When we have no need for new regular outbounds because we are full and none of our peers disconnect, we make feeler connections every 2 minutes and extra block relay conns every 5 minutes - that's like 7 new connections every 10 minutes that would be divided over all reachable networks. Would be interesting to run with i2p and clearnet, and see how many of these tunnels expire before they are used.\n\n- When using `-onlynet=i2p`, this may not have much of an effect right after startup - `m_unused_i2p_sessions` will only grow if a connection attempt fails, for successful connections the newly formed connections will be used right away. Maybe it could make a sense to fill the `m_unused_i2p_sessions` buffer with 4 connections right after startup?"
  },
  {
   "t": "2025-03-19T17:14:08Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "I wonder what happens after the 10 minutes? For example, if a client does `SESSION CREATE` to acquire a session id and then waits for 12 minutes and does `STREAM CONNECT` with that session id. Is a new tunnel established by the I2P router immediately and transparently to the client at the 10th minute, or when the session is actually used at the 12th minute?\n\nThinking about this more, given that opening of new connections is done serially, one after another (not in parallel), and occasionally (except during startup) maybe it would be best to pre-create just one session?"
  },
  {
   "t": "2025-03-19T18:16:16Z",
   "kind": "comment",
   "who": "zzzi2p",
   "assoc": "NONE",
   "text": "Tunnels get built when the SAM session is opened, and are automatically rebuilt when they expire, the router does that for you as long as the SAM session stays open. A session is just a pool of inbound and outbound tunnels. I assume that's what you are trying to do here, have the session open and ready before it's needed."
  },
  {
   "t": "2025-03-20T11:56:12Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "fccedfd710ee66a812d1f1b1a9503ce5b5de7cf7"
  },
  {
   "t": "2025-03-20T12:00:50Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "ef525e8b7c1a78bf4e41053bd297f28a3e5772e8"
  },
  {
   "t": "2025-03-20T12:05:45Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`a22a3b3c4c...48a636cdde`: rebase due to conflicts and reduce the pre-created sessions to 1, given the above. Some logs from running this:\n\n```\n974be1f338 startup +129s: made new session\n974be1f338 startup +290s, 161s after creation: connect failed\n974be1f338 startup +372s, 243s after creation: connect failed\n974be1f338 startup +678s, 549s after creation: connect ok, connection duration: 1305s, destroyed after that\n\n55723ba1fc startup +1385s: made new session\n55723ba1fc startup +1441s, 56s after creation: connect ok, connection duration: 62s, destroyed after that\n\n1177c0761e startup +1469s: made new session\n1177c0761e startup +1853s, 384s after creation: connect ok, still active after 2786s\n\nf9a6456088 startup +1860s: made new session\nf9a6456088 startup +2282s, 422s after creation: connect ok, connection duration: 90s, destroyed after that\n\n832d073906 startup +2362s: made new session\n832d073906 startup +2367s, 5s after creation: connect failed\n832d073906 startup +2444s, 82s after creation: connect ok, connection duration: 1s, destroyed after that\n\nc7e68ca962 startup +2449s: made new session\nc7e68ca962 startup +2487s, 38s after creation: connect ok, connection duration: 2s, destroyed after that\n\nc936c53d2b startup +2453s: made new session\nc936c53d2b startup +2724s, 271s after creation: connect ok, still active after 1802s\n\n7f96d02e16 startup +2727s: made new session\n7f96d02e16 startup +2765s, 38s after creation: connect failed\n7f96d02e16 startup +2798s, 71s after creation: connect ok, still active after 1528s\n\n38d3bccdd4 startup +2884s: made new session\n38d3bccdd4 startup +3261s, 377s after creation: connect failed\n38d3bccdd4 startup +4253s, 1369s after creation: connect ok, connection duration: 2s, destroyed after that\n```"
  },
  {
   "t": "2025-03-20T14:03:14Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "859c25909200ecafe261df233bcd512b890f52bc"
  },
  {
   "t": "2025-03-20T14:03:49Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`48a636cdde...859c259092`: fix CI"
  },
  {
   "t": "2025-05-21T11:45:30Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "632eb1a3c9b1e793f770b447ad581728eab28201"
  },
  {
   "t": "2025-05-21T11:45:49Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`859c259092...632eb1a3c9`: rebase due to conflicts"
  },
  {
   "t": "2025-05-26T18:53:42Z",
   "kind": "comment",
   "who": "jonatack",
   "assoc": "MEMBER",
   "text": "Somehow I hadn't seen this PR yet.\n\nIf I understand correctly, this change is only for if a node is configured with `-i2pacceptincoming=0` to not accept inbound I2P connections?"
  },
  {
   "t": "2025-05-30T12:37:09Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "Yes. That is because if it is accepting incoming connections (has a permanent listening I2P address) then there is just one I2P session which is associated with that address and that session is used for all incoming and outgoing connections."
  },
  {
   "t": "2025-09-29T12:56:01Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "a42449cb306c4b1310f9967b150f9526376143a4"
  },
  {
   "t": "2025-09-29T12:56:19Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`632eb1a3c9...a42449cb30`: rebase due to conflicts"
  },
  {
   "t": "2025-10-02T13:48:24Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "fc1ba52a4d790a8ccdead48227f53260d34a659b"
  },
  {
   "t": "2025-10-02T13:48:41Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`a42449cb30...fc1ba52a4d`: rebase due to conflicts"
  },
  {
   "t": "2025-10-07T09:15:31Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "539d8b474edb2232c0a40001acbf3f29966ab430"
  },
  {
   "t": "2025-10-07T09:15:44Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`fc1ba52a4d...539d8b474e`: rebase due to conflicts"
  },
  {
   "t": "2026-01-23T09:53:43Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "6da9cf56a90e65491b1c891257383b6d09459068"
  },
  {
   "t": "2026-01-23T09:55:49Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`539d8b474edb2232c0a40001acbf3f29966ab430...6da9cf56a90e65491b1c891257383b6d09459068`: rebase due to conflicts and put the mechanical rename into its own commit so that it does not clutter the other changes."
  },
  {
   "t": "2026-01-23T12:52:08Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "8df10cfdd30115c18284ac83ddf3b619c84a2981"
  },
  {
   "t": "2026-01-23T12:59:12Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`6da9cf56a90e65491b1c891257383b6d09459068...8df10cfdd30115c18284ac83ddf3b619c84a2981`: fix includes in `test/util/net.h` - it uses `std::queue` but did not include `<queue>`. It was working before because `net.h` used to include `<queue>` and `test/util/net.h` includes `net.h`. But this PR removed the inclusion of `<queue>` from `net.h`."
  },
  {
   "t": "2026-04-30T10:32:52Z",
   "kind": "force_push",
   "who": "vasild",
   "commit": "1808139f52f1f0578352acce9d4761fd04519612"
  },
  {
   "t": "2026-04-30T10:33:22Z",
   "kind": "comment",
   "who": "vasild",
   "assoc": "MEMBER",
   "text": "`8df10cfdd30115c18284ac83ddf3b619c84a2981...1808139f52f1f0578352acce9d4761fd04519612`: rebase due to conflicts"
  },
  {
   "t": "2026-09-17T07:39:44Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "This has required rebase for many months now. Are you still interested in the change @vasild?"
  }
 ],
 "labels_log": [
  {
   "t": "2025-03-14T09:17:11Z",
   "action": "labeled",
   "label": "P2P",
   "who": "laanwj"
  },
  {
   "t": "2025-03-20T13:59:52Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-20T17:41:50Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-05-20T20:52:01Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-05-21T11:55:58Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-09-17T10:47:03Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-09-29T14:43:55Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-09-30T23:47:33Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-10-02T15:07:25Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-10-06T17:56:57Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-10-07T10:57:16Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-10-24T19:08:04Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-23T10:50:59Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-23T11:25:41Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-23T14:09:57Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-03-26T19:56:02Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-04-30T11:09:01Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-04T09:50:30Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2025-03-20T12:00:50Z",
   "kind": "closed",
   "who": "vasild"
  },
  {
   "t": "2025-03-20T12:06:10Z",
   "kind": "reopened",
   "who": "vasild"
  }
 ],
 "text_chars": 15961,
 "text_tokens_estimate": 3990,
 "changed_paths": [
  "src/i2p.cpp",
  "src/net.cpp",
  "src/net.h",
  "src/test/util/net.h"
 ],
 "files": [
  {
   "path": "src/i2p.cpp",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/net.cpp",
   "add": 50,
   "del": 23
  },
  {
   "path": "src/net.h",
   "add": 35,
   "del": 23
  },
  {
   "path": "src/test/util/net.h",
   "add": 2,
   "del": 1
  }
 ],
 "test_lines": 3,
 "git": {
  "head": "1808139f52f1f0578352acce9d4761fd04519612",
  "head_matches_backup": true,
  "base": "ef499680c8d426ac04f3a5d4ad67cbb3426e0e21",
  "commits": [
   {
    "sha": "151e0c8ef6",
    "subject": "i2p: create the actual session from Session::Session()",
    "files": 1,
    "add": 7,
    "del": 0
   },
   {
    "sha": "659336fa92",
    "subject": "net: change m_unused_i2p_sessions from queue to a single one",
    "files": 3,
    "add": 37,
    "del": 39
   },
   {
    "sha": "1808139f52",
    "subject": "i2p: make a time gap between creating transient sessions and using them",
    "files": 2,
    "add": 56,
    "del": 14
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "136de5717a8a13fd",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}