Writing Criteria
Every guide in this repo is written to the same standard. If you're adding or editing a guide — human or AI — hold it to these.
- Plain words, real ideas. Simple language, never dumbed-down concepts. Explain jargon on first use; collect terms in the glossary.
- No hype. No marketing adjectives, no "revolutionary," no promises about what something will do for the reader.
- Honest about limits. Say what the thing can't do. State uncertainty instead of smoothing it over. Validation notes include what remains unverified.
- No invented numbers. Benchmarks, defaults, and specs are either measured or qualified with "depends on your setup." If it wasn't tested, say so.
- Checkable. Commands are meant to be run, with "Check:" blocks showing what success looks like. Sources are linked, not asserted.
- Respect the reader's setup. Call out Windows/Mac/Linux differences. Don't assume a GPU, a budget, or prior experience.
- Safety isn't skipped. Real risks get stated plainly, not buried.
- Scoped. Each guide says what it covers and points elsewhere for the rest. No guide pretends to be the whole subject.
- Structure over prose. Tables, before/after examples, symptom → fix. Scannable beats essay.
Verification notes for this guide
Standard 3 and 4 applied to this guide,
specifically. The build machine ran Ubuntu
24.04 as root, with OpenBSD netcat
(Debian patchlevel 1.226-1ubuntu2) already
installed (netcat-openbsd 1.226-1ubuntu2).
Every quoted output in the chapters is a
capture from that netcat, run against the
shipped lab on loopback. Working
transcripts are kept with the build
(netcat-lab/transcripts/, nineteen files).
The lab. examples/lab_server.py
stands up one fixture: a banner + echo
server on tcp/45002. Alongside it the
chapters use Python's built-in web
server on tcp/45003 serving
examples/index.html, netcat listening
to itself on tcp/45001, a stream
listener on udp/45004, a Unix socket in
the lab's scratch directory, and two
deliberate empty chairs: tcp/45999 and
udp/45998. The transfer payload is the
shipped examples/specimen.txt
concatenated 200 times (49,200 bytes,
sha256
4269a2a243bb386ef678ed333ab20b3db7c16f3ae16a776f2b5072879f23baf2).
Live-captured (every command run for real on the build box):
- The version/identity captures:
nc -h's first lines,dpkg-query -W netcat-openbsd, andcommand -v nc ncat— the latter returning only/usr/bin/nc, which is the evidence that ncat is absent here. - The fixture's
--checkoutput, its banner, and its echo of two typed lines undernc -v -N(thesucceeded!line, banner, both echoes, exit 0). - HTTP by hand:
GET / HTTP/1.0against the Python web server (full 200 response, headers, specimen body) andGET /no-such-page(full 404). The server's own log lines for those requests were also read back and match chapter 3's Check. - The two-terminal chat, both sides:
the listener's
Listening on/Connection received on localhost 37846lines and received text; the client'ssucceeded!line and received text; both exits 0. -kkeeping one listener alive across two sequential callers (both messages received, listener verified still running, then stopped by hand).- The backlog experiment: caller B connecting during caller A's call — B exit 0, B's line absent from the listener's total, listener exit 0 after A hung up.
- Port checks:
-zvopen (exit 0) and refused (exit 1, message verbatim); the-zloop over three ports, including its two reporting quirks (success prints without-v; failure prints nothing without-v), each re-verified in a direct follow-up probe before the chapters described it. - Manufactured silence: an iptables
DROP rule on a port with a live
listener produced the
timed out: Operation now in progressmessage and exit 1; the rule was removed immediately after the capture and the empty chain re-verified. - File transfers in both directions
(49200 bytes; identical sha256 at
both ends;
cmp→ IDENTICAL), and the nobody-listening pull (exit 1, silent, 0-byte file). - The piped report (
uname -a+df -h /) arriving whole at a listening netcat. - UDP on loopback: a datagram
delivered to a
nc -u -llistener;-zuvagainst a genuinely live UDP listener (succeeded!, exit 0) and against a closed port (silent exit 1, loopback's ICMP unreachable doing the work); a full UDP send to the closed port exiting 0 with no error. An earlier draft of this capture checked the port after its listener had already timed out and recorded a false "failure against a live listener"; the capture was re-run against a verified-live listener and the chapter follows the re-run. - Unix sockets:
-l -Ulistener, thesrwxrwx---socket file, a delivered message, the socket file's survival after the listener exits, and-zvU(silent exit 0 live; named error + exit 1 for a missing path). - Timeouts, measured with netcat timed
directly (a pipeline's clock measures
its slowest member — the lab's first
measurements were of producers, not
of netcat, and were discarded): the
unflagged hang (still connected when
timeoutkilled it at 3s);-Nexit 0 after 0.0s;-w 2exit 0 after 2.0s both after input ended and with input held open but silent. The-qflag is quoted from the local man page, not separately timed. - The resolver surprise: the build
sandbox's resolver answers
no-such-host.invalidwith 198.18.83.181 and a connect to it "succeeds" — captured whole, and taught in chapter 13 as an environment artefact, not netcat behaviour.
Documented-only (not run), and why:
- Traditional netcat and ncat
behaviour (all of chapter 12's
non-OpenBSD columns). Neither program
is installed on the build machine,
and the guide says so where it
matters. Their flags were checked
against the projects' own man pages
during the build — that check caught
one real error before publication:
ncat has no
-N; its spelling is--send-only— and the table says so. The OpenBSD column is captured; the other two are documentation, labelled as such. - The OpenBSD manual's
-c/-epassage quoted in chapter 11: the wording is the manual's, from the man page installed on the build machine. The fd-redirection execution it warns about was not performed. - Connecting to services beyond the lab (real SSH/SMTP banners and the like): described in prose in chapters 2–3 from the protocols' public behaviour. Every target this guide actually connected to is the build machine itself, on loopback.
- Non-loopback, cross-machine behaviour generally: the sandbox's only other address is its fabric's, which is not the guide's to probe. Consequences the chapters flag rather than demonstrate: UDP checks over filtered networks (the shrug is argued from the loopback captures' contrast), the firewall recipe run from a second machine, and transfer timings. Nothing depends on an invented remote.
- Windows and macOS netcat: named
(third-party
nc.exebuilds exist and vary in trustworthiness; macOS ships a close OpenBSD relative); no non-Linux machine was available to the build.
Specimen discipline, for the record:
every connection in this guide targets
127.0.0.1 or a Unix socket in the lab
directory, and every listener binds
127.0.0.1. The one iptables rule used
(DROP on the lab's own port) was added
for a single capture and removed
immediately after, with the removal
verified. No output was completed,
corrected, or invented: where a capture
surprised the draft — the backlog
caller's exit 0, -z's asymmetric
reporting, the resolver that answers
anything — the chapters were written
to the capture.