Blog
Go, Rust, Linux, cryptography and UK privacy law. One properly researched article a day.
Working notes from a software engineer who spends the day in Go, Rust and Linux and the evening reading legislation. Each post takes one specific question and answers it properly: why a worker pool deadlocks, what fsync really promises, how to enrol a TPM into LUKS without locking yourself out, what a technical capability notice can actually compel. Step-by-step how-tos sit alongside the deep dives, everything is checked against source code or primary legislation, and nothing is padded to hit a word count.
Subscribe with the Atom feed to get each post as it lands.
-
Metadata Privacy in Signal: What Sealed Sender Actually Hides
How Signal's Sealed Sender strips the sender's identity from the delivery envelope, and why recipient, timing and IP metadata still aren't covered.
-
Go's Nil Channel Trick: Disabling a select Case at Runtime
A nil channel blocks forever, so setting a channel variable to nil is how Go programs switch select cases on and off at runtime.
-
Go's ReadHeaderTimeout: The http.Server Setting Everyone Skips
Go's http.Server.ReadHeaderTimeout stops slow-header attacks, but setting it alone still leaves the request body completely unbounded.
-
eBPF for the Curious: Tracing TCP Connections From Go Without tcpdump
How to hook a kernel tracepoint with eBPF and stream live TCP connection state changes into a Go program, no tcpdump required.
-
Go's http.Server Shutdown Ignores Hijacked and WebSocket Connections
Server.Shutdown returns while hijacked and WebSocket connections are still open, then main exits and kills them. Here is why, and a small tracker that fixes it.
-
Go's ConstantTimeCompare Returns Early on Length Mismatch
subtle.ConstantTimeCompare is constant time only for equal-length inputs. What the length shortcut leaks, when it matters, and a hash-first fix.
-
Go's database/sql Pool: SetConnMaxLifetime Behind a Firewall
Why the first query after a quiet spell hangs or fails when a firewall forgets your connection, and how lifetime and idle limits in database/sql fix it.
-
Go's crypto/rand.Text and rand.Read: Why Seeded Tokens Get Guessed
Why session tokens built from Go's non-cryptographic rand package get guessed, and how crypto/rand.Text and rand.Read replace them in a few lines.
-
Go's context.WithoutCancel: Audit Logs That Outlive the Request
A client hangs up mid-request and your audit row silently never gets written. How context.WithoutCancel fixes it, and the timeout and shutdown traps it creates.
-
Go's http.Client: When Redirects Strip Authorization
Learn when Go forwards or strips Authorization on redirects, why subdomains and scheme changes surprise, and how to enforce a safer policy.
-
Go's http.Transport Idle Pool: Why MaxIdleConnsPerHost Is 2
Why Go keeps only two idle connections per host, how bursty traffic to one API burns handshakes, and how to measure and fix it with httptrace.
-
Go's http.Request.Clone: Which Fields Get Copied, Which Are Shared
Request.Clone deep-copies headers but shares the Body, while WithContext shares almost everything. Here is what that means for RoundTrippers and retry loops.
-
Go's ServeMux Method Patterns: Why You Get 405, Not 404
Since Go 1.22, ServeMux returns 405 with an Allow header when a path matches a method pattern but the verb does not. Here is why, and what PathValue sees.
-
Go's http.ResponseController: Resetting Streaming Deadlines
Give each streamed chunk its own write deadline, surface flush errors promptly, and keep healthy long-lived HTTP responses alive in Go.
-
Go's net.Dialer and Happy Eyeballs: Why Dual-Stack Connects Lag
How net.Dialer races IPv6 against IPv4, what FallbackDelay and Timeout really do, and why a black-holed AAAA record costs you 300ms per new connection.
-
Go's http.Hijacker: Where Buffered Bytes Go After an Upgrade
Learn why reading the raw connection after http.Hijacker can hide protocol bytes, and how to handle upgrades without stalls or reordering.
-
Go's io.Pipe Has No Buffer: How a Stalled Reader Freezes Writes
See why io.Pipe writes block, reproduce the freeze, and structure Go streaming pipelines so errors and cancellation unblock both ends.
-
Go's http.MaxBytesReader: Limiting Uploads Without Breaking Keep-Alive
Use http.MaxBytesReader to cap Go uploads, return 413 reliably, and understand when HTTP/1.1 connections can safely stay alive.
-
Go's os.Root: Traversal-Resistant File Access Without openat2
Go 1.24's os.Root confines file access to a directory, symlinks and .. included. See how it works, a zip-extraction example, and what it does not cover.
-
Go's sync.Pool: Why Pooled Buffers Leak Data Between Requests
How sync.Pool buffers end up showing one request's bytes to another: missing resets, aliasing after Put, over-long slices, and stale secrets, with fixes.
-
Go's net/netip vs net.IP: Why == Finally Works on Addresses
net.IP is a byte slice, so == won't compile and map keys need string hacks. netip.Addr is a comparable value type; here are the sharp edges that remain.
-
Go's exec.ErrDot: Why exec.Command Won't Run a Binary From PATH's Dot
Since Go 1.19, os/exec refuses to run a program found via a relative PATH entry. What ErrDot means, why it exists, and the correct fixes.
-
Signal's Double Ratchet in Go: A Toy Forward-Secret Messenger
Build a working Double Ratchet in Go from crypto/ecdh, HMAC and AES-GCM alone, and see exactly where forward secrecy and post-compromise security come from.
-
Go's bufio.Scanner Stops at 64 KiB: Reading Long Lines Safely
Learn why Go's bufio.Scanner rejects long lines, when to raise its buffer limit, and how to read bounded records without risking runaway memory use.
-
Right to Repair vs Firmware Locks: What UK Ecodesign Rules Require
The UK's 2021 right to repair rules cover washing machines and TVs, not phones: the ban on firmware-locked spare parts is still years away in Great Britain.
-
ESP8266 vs ESP32 on Battery: Why the Older Chip Wins at Idle
Deep sleep numbers suggest the ESP32 should be the more efficient chip, but RTC power domains and default configuration often leave it drawing more current.
-
Go's singleflight: Stop Cache Misses Stampeding the Database
Use singleflight to collapse concurrent cache misses, handle cancellation safely, and avoid subtle duplicate database reads in Go services.
-
Go's log/slog: Structured Logging Without a Third-Party Library
How Go's log/slog package gives you structured, leveled logging with context propagation, without adding zap or zerolog to go.mod.
-
Go's GOMEMLIMIT: Keeping the OOM Killer Away From a Container
Why a Go service can be OOM-killed inside a memory-limited container despite having a garbage collector, and how GOMEMLIMIT fixes the pacing.
-
Digital Forensics 101: Why dd and a Hash Beat cp When Imaging a Drive
Why bit-for-bit disk imaging with dd, a write blocker and a SHA-256 checksum beats a filesystem copy when acquiring a drive for forensic analysis.
-
Go's io.Copy Can Write Gigabytes Before Reporting an Error
Learn why io.Copy can partially mutate a destination before failing, how its byte count helps, and when to stage writes for an atomic result.
-
TOTP From Scratch in Go: Implementing RFC 6238 Without a Library
How the six digits in your authenticator app are actually produced: HMAC-SHA1, dynamic truncation and time steps, built in plain Go.
-
OAuth Device Authorization Grant: Logging In From a CLI
RFC 8628 explained: how tools like gh, docker and kubectl authenticate on headless machines by polling a token endpoint instead of a browser redirect.
-
Go's errgroup: Cancelling Goroutines Without Hand-Rolled WaitGroups
How golang.org/x/sync/errgroup collects errors, cancels sibling goroutines on first failure, and bounds concurrency without a semaphore channel.
-
Go's errors.Join: Combining Errors Without Losing errors.Is
How errors.Join builds a tree of errors that still works with errors.Is and errors.As, and where the abstraction leaks.
-
DKIM, SPF and DMARC for Self-Hosted Mail: A Practical Setup
A step-by-step guide to configuring SPF, DKIM and DMARC for a self-hosted mail server, and why the DNS records alone will not stop spam folders.
-
Shamir's Secret Sharing in Go: Split a Key So Nobody Holds It
How Shamir's Secret Sharing splits a key into shares using polynomial interpolation over GF(256), with a working Go implementation.
-
Online Safety Act Age Checks: What 'Highly Effective' Means
Ofcom's 'highly effective' age assurance rests on four criteria, not a fixed toolset, and 2026 enforcement shows what that means for real services.
-
Encrypted Client Hello: Hiding Which Site You Visit Over TLS
TLS 1.3 still leaks the hostname you're connecting to in plaintext. Here's how Encrypted Client Hello fixes that, and where it still falls short.
-
seccomp-bpf in Go: Restricting Syscalls Without a Container
How to build and install a seccomp-bpf syscall filter from a plain Go binary with golang.org/x/net/bpf, and why goroutines need the TSYNC flag.
-
The UK-US Data Bridge: Where GDPR Adequacy Actually Stands in 2026
A status check on the UK-US Data Bridge: PCLOB's quorum collapse, the Latombe appeal at the CJEU, and the UK's own EU adequacy renewal.
-
Go's testing/synctest: Testing Concurrent Code Without Sleeps
How Go 1.25's testing/synctest package lets you test timers, retries and debounce logic deterministically, without real time.Sleep calls.
-
Go's rate.Limiter: Token Buckets Explained by Reading the Source
A walk through golang.org/x/time/rate's actual source to see how Go's token bucket limiter tracks tokens without a background goroutine.
-
Linux pidfds: Closing the PID Reuse Race in Supervisors
Learn how Linux pidfds provide a stable handle for polling, signalling and waiting without accidentally targeting a reused PID.
-
Go's comparable Constraint: Why Structs with Slices Can't Satisfy It
Why Go's generics compiler rejects structs containing slices as comparable, the reasoning behind it, and how to work around it.
-
Right to Erasure vs Backups: What UK GDPR Actually Requires
Why UK GDPR's right to erasure doesn't mean scrubbing every backup tape immediately, and what the ICO actually expects organisations to do instead.
-
What 'Authorisation' Actually Means Under the Computer Misuse Act
The Computer Misuse Act has no good-faith defence, so a pentest's legality rests entirely on paperwork. Here's what that paperwork actually needs to say.
-
Go's sync.Map: When It Actually Beats a Mutex-Protected Map
Why sync.Map only wins for two specific access patterns, how Go 1.24's trie-based rewrite changed the numbers, and when a plain mutex wins instead.
-
ESP32 Deep Sleep: Why 'Low Power' Mode Still Drains a Battery
Deep sleep current specs look great on paper, but wake-up current spikes and a board's regulator usually decide the real battery life.
-
Postgres Advisory Locks in Go: Cheaper Than a Distributed Lock Service
How to use Postgres advisory locks from Go for cron dedup and leader election, and the connection-pooling pitfall that breaks them silently.