<feed xmlns='http://www.w3.org/2005/Atom'>
<title>npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/src/btftunnel.cpp, branch main</title>
<subtitle>[no description]</subtitle>
<id>https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/atom?h=main</id>
<link rel='self' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/'/>
<updated>2026-09-03T21:49:35+00:00</updated>
<entry>
<title>merge: !8 fix/tunnel-retry into deploy/all-2026-08-24</title>
<updated>2026-09-03T21:49:35+00:00</updated>
<author>
<name>bitflash-maintainer</name>
<email>noreply@bitflash.invalid</email>
</author>
<published>2026-09-03T21:49:35+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=ea8fe94658bf20d2b669b619231238f12e6bd8fc'/>
<id>urn:sha1:ea8fe94658bf20d2b669b619231238f12e6bd8fc</id>
<content type='text'>
</content>
</entry>
<entry>
<title>tunnel: give both pumps shared ownership of the sockets they close</title>
<updated>2026-09-02T17:39:50+00:00</updated>
<author>
<name>bitflash-maintainer</name>
<email>noreply@bitflash.invalid</email>
</author>
<published>2026-09-02T17:39:50+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=0a8c445742ce871c6b9352bf406d2232a6fd3134'/>
<id>urn:sha1:0a8c445742ce871c6b9352bf406d2232a6fd3134</id>
<content type='text'>
PumpIn's wrapper closed pumpEnd and the rendezvous socket the moment it
exited, without waiting for PumpOut, which then called shutdown() on both
at its own exit. Which of the two got there first decided whether that
shutdown addressed a live descriptor or a number the kernel had already
handed back out -- in which case it tears down an unrelated connection.
The inbound pump wins that race whenever the rendezvous socket dies before
the app socket, which is the ordinary shape of a relay restart or a far
side hanging up.

The failure path added in 0b566b4 has the same hole. Its catch shuts down
rv to wake an inbound pump it assumes is parked in RvReadN. PumpIn does
not park when the rendezvous connection is already dead: it returns on its
first length read and its wrapper closes both descriptors. A refused
pthread_create and a dead rv are not independent -- both are symptoms of a
loaded process -- so that is the likely case, not the exotic one.

Both descriptors now belong to a shared_ptr the two pumps hold for the
whole of their bodies, so the close happens after the last pump has
stopped touching them and no shutdown can precede it. appEnd is unchanged:
the caller gets it on success, StartTunnel closes it on every failure
path, no pump ever touches it.

0b566b4's message says of that catch that "closing those descriptors from
the failure path instead would race it". True as far as it goes, but it
reads as though what the code did instead was safe, and it was not -- the
shutdown raced the same close. There was no ordering there to prefer, only
a missing owner.

Measured against the real code with the relay in a separate process, every
close() and shutdown() traced and pthread_create refused on demand:

  normal teardown, 25 tunnels    29 shutdowns on a closed fd -&gt; 0
  refused outbound spawn, 25     25 shutdowns on a closed fd -&gt; 0

Close counts are identical before and after (8 and 5 per tunnel, every one
accounted for) and live descriptors return to their starting count, so the
defect was only ever the ordering, never a leak or a double close.
</content>
</entry>
<entry>
<title>tunnel: report which stage failed instead of guessing the cause</title>
<updated>2026-09-02T09:03:01+00:00</updated>
<author>
<name>bitflash-maintainer</name>
<email>noreply@bitflash.invalid</email>
</author>
<published>2026-09-02T09:03:01+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=7c70eb4498e88725241493033edfe89554ff1b18'/>
<id>urn:sha1:7c70eb4498e88725241493033edfe89554ff1b18</id>
<content type='text'>
</content>
</entry>
<entry>
<title>net: a refused thread spawn drops one peer, not the node</title>
<updated>2026-09-02T07:09:45+00:00</updated>
<author>
<name>bitflash-maintainer</name>
<email>noreply@bitflash.invalid</email>
</author>
<published>2026-09-02T07:09:45+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=0b566b4869b9e86c90162befd87f9882b6dd143c'/>
<id>urn:sha1:0b566b4869b9e86c90162befd87f9882b6dd143c</id>
<content type='text'>
std::thread throws std::system_error when pthread_create is refused
(RLIMIT_NPROC, a container's pids cgroup, memory pressure), and nothing
caught it, so the node died by std::terminate. Threads are spawned per
peer connection, so the pressure that triggers this scales with peer
count, which is attacker-influenced. Observed killing a healthy
participant worker mid-mining.

Every spawn site in the node build, and what a failure now does:

- btftunnel.cpp: both tunnel pumps are guarded. The inbound pump starts
  first because it owns cleanup of the pump end and rendezvous socket;
  if the second spawn is refused, shutdown() wakes the first out of its
  read and it cleans up itself -- closing those descriptors from the
  failure path instead would race it onto reused descriptors. Both
  paths return INVALID_SOCKET, which every caller already treats as
  "this connection failed".
- btfrv.cpp: the relay's accept loop drops the connection it cannot
  serve and stops accepting for a moment -- longer after consecutive
  failures, capped at 5s -- instead of dying at an arbitrary one. The
  per-pair second pump now runs on the handling thread itself, leaving
  exactly one spawn to guard and nothing running yet to race.
- main.cpp: a participant that cannot spawn its hash workers stops the
  ones that did start, drops the pool connection, and returns to the
  reconnect loop, the same way an ordinary disconnect does.
- randomx_pow.cpp: dataset init joins the workers it got, releases the
  dataset, and stays in light mode -- the same trade the allocation
  failure path above it already makes.
- rpc.cpp, net.cpp: the remaining unchecked _beginthread calls (which
  report failure by value, they cannot throw) now say precisely what
  did not start. The pool's accept and payout threads failing silently
  left the pool running deaf -- nobody accepts, nobody gets paid, and
  nothing in the log says why.

Left alone deliberately: the per-relay accept spawns (rpc.cpp) already
check, log and retry on the next poll; startup-critical spawns in
net.cpp keep failing hard; the selftest-only std::thread uses may abort
a selftest run, which is acceptable where dying over one peer is not.

Verified: full node rebuilds clean and links; pool-stratum selftest
passes; a scratch harness under RLIMIT_NPROC=1 confirms _beginthread
reports -1 and std::thread throws system_error, the premises of every
path above.
</content>
</entry>
<entry>
<title>diag: count sockets by where they were created, and whether the close took (#86)</title>
<updated>2026-08-01T16:10:23+00:00</updated>
<author>
<name>Bitflash</name>
<email>btf01@proton.me</email>
</author>
<published>2026-08-01T16:10:23+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=21c8bb63cb85f3001ddee9237b1d7e689675d293'/>
<id>urn:sha1:21c8bb63cb85f3001ddee9237b1d7e689675d293</id>
<content type='text'>
A node was found holding 1502 sockets in Windows bound state, one ephemeral port
each, 9% of the machine range in nineteen hours. Reading the code did not find
it: every connect() failure path here closes its socket.

So count. Every socket is tagged at creation and untagged at close, across all
ten sites, with a counter for closes that fail. Already rules out three
explanations: nothing is created or closed behind the counter (untagged closes
are zero), no close is failing, and the leaked sockets are not in the live set --
tracked live matches what the OS reports as established, while the bound pile is
something the program already closed successfully.</content>
</entry>
<entry>
<title>Bitflash: fair-launch release</title>
<updated>2026-07-24T14:18:11+00:00</updated>
<author>
<name>Bitflash-sh</name>
<email>btf01@proton.me</email>
</author>
<published>2026-07-24T14:18:11+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1q9geyrch3feyczz242sxjaa05yczv50dlpug082sjln2kl0wrnxst0nrfa/bitflash/commit/?id=51d617e70f90a972185917ea0096c1ecef2bfa8b'/>
<id>urn:sha1:51d617e70f90a972185917ea0096c1ecef2bfa8b</id>
<content type='text'>
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf
addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a
headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.

</content>
</entry>
</feed>
