Industry
The firewall that was not there
chovy Dev.to (EN Zone)
5 views
The firewall that was not there
A nixamp server on a box in Montreal would not answer. The symptoms were perfect: the port was open, the page never loaded, and every instinct pointed at ufw, the router, or whatever the hosting provider does to traffic it does not like.
It was none of those. It was the server holding its own event loop, and the way that failure presents is worth writing down, because from the outside it is indistinguishable from a packet filter.
What it looked like
Probing the host from somewhere else:
port 22 connects
port 80 no answer
port 443 no answer
port 4321 handshake completes
GET /api/health on 4321 times out, zero bytes, twice
A firewall that drops never completes a handshake. One that rejects says "connection refused" immediately. This one accepted the connection and then said nothing at all, for as long as you cared to wait. nmap -sV could not identify the service either: it accepted every probe and answered none.
The tempting conclusion is a middlebox. Plenty of providers run inline DDoS scrubbing that answers SYNs itself and only then tries the backend, which would produce exactly this. One probe rules it out. Ask for a port that is definitely closed:
port 45999 no answer
port 4321 handshake completes
If something upstream were answering handshakes on our behalf, the closed port would answer too. It did not. So TCP genuinely reached the box, the kernel genuinely accepted it, and the process on the other end genuinely never read it.
What it was
nixamp reads tags with one ffprobe per file. The library on that box is 5,712 media files across 322 GB. The tagging pass ran inside one async function with no yield in it:
const tracks = await loadSource(tools, root, true);
engine.retag(tracks, root);
That await is a lie about the shape of the work. probe is spawnSync. The whole pass is synchronous from the event loop's point of view, so nothing else runs until the last file is read.
Here is the part that makes it look like a firewall. The listening socket does not care that your process is busy. The kernel completes the TCP handshake from the accept backlog on its own, so a client connects successfully to a server that will not read a byte for the next several minutes. You get an open connection and silence, which is the one failure mode people reliably misattribute to the network.
Reproduced on 800 files:
$ curl -m 20 http://127.0.0.1:4399/api/health
curl: (28) Operation timed out after 20002 ms with 0 bytes received
The server had already printed its banner and announced itself as listening. By every internal measure it was running perfectly.
The fix
One turn of the event loop per file:
for (const path of paths) {
tracks.push(probe(tools, path));
await new Promise((done) => setImmediate(done));
}
A request now waits for one ffprobe instead of the whole library. Same 800 files, same request:
1st: HTTP 200 in 0.516737s
2nd: HTTP 200 in 0.538170s
The regression test races the tagging against a timer and asserts the timer wins. Reverting the loop to the blocking version makes it fail, which is worth checking rather than assuming, because a test that cannot fail is decoration.
The sequel, which was also us
With the server answering, the next report was choppy audio. That one is measurable too. Every frame of the event stream carried the entire track list, and the analyser pushes twelve times a second:
bytes in 10s: 9376450 9.4 MB
frames: 20 about 470 KB each
A megabyte a second of JSON, parsed on the same thread that is decoding the audio. The library is news only when it changes, so it now rides the first frame of a subscription and any frame where the list actually changed. After:
bytes in 8s: 46724
frames: 94
frames carrying a track list: 1
About 497 bytes a frame instead of 470 KB.
What else landed
The same day, in nixamp 0.5.x:
nixamp login grew OAuth 2.0 through the device authorization grant. The terminal shows a short code, you approve it in a browser on whatever device has a keyboard, and the terminal ends up holding the session. It never sees your password or the provider's token, which is why it works over ssh and on a television. There are CLI tokens for machines that cannot sign in at all, stored as hashes and revocable from anywhere, plus NIXAMP_TOKEN for CI.
Daemon mode got the two moves it was missing: d in the player hands the music to a daemon and gives you your terminal back, and nixamp attach puts the same player in front of it from anywhere.
Video plays with a picture now, and what ffprobe finds inside decides how much work that costs. Most Matroska holds H.264, where only the wrapper is wrong, so it is rewrapped for free. H.265 gets a real transcode, measured at 2.29x realtime for 1080p on a four core EPYC.
And nixamp serves https itself given a certificate from anywhere, because a browser refuses every request from an https page to an http one, including audio and video, which it upgrades and then gives up on. No amount of player cleverness gets around that.
curl -fsSL https://nixamp.com/install.sh | sh
Read original: https://dev.to/chovy/the-firewall-that-was-not-there-lni
← Previous
Ditched React for our school marketplace app 👇 Bundle: <50KB. Load time on a $80 Android: 320ms. Story + architecture breakdown here #DEVCommunity #webdev
Next →
Building a Pons Copy-Trading System on Robinhood Chain
Related
PWC 390 Weird Ways to Wrangle Words
Industry
0
DEV Community
Gemini 3.8 Flash is not a routine update
Industry
0
DEV Community
Shipt becomes the latest delivery app with an AI shopping assistant
Industry
0
TechCrunch
The Ancient Greek Water Clock That Kept the Most Accurate Time for 1,800 Years
Industry
0
Hacker News
Comments0
No comments yet — be the first