Articles
Go, Rust, Linux, cryptography and UK privacy law. One properly researched article a day.
Page 3 of 6. 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.
-
What a Bulk Personal Dataset Warrant Lets the Agencies Keep
Part 7 of the Investigatory Powers Act lets MI5, MI6 and GCHQ hold datasets on mostly innocent people. Here is what the warrant covers and what limits it.
-
Yes, Your Security Logs Can Be Part of a SAR
Security logs can contain your personal data. Learn when a UK SAR may include them, what can be redacted, and how to ask precisely.
-
Does Your Server-Side Session Cookie Need a PECR Consent Banner?
Keeping session state on the server doesn't remove the cookie. Here is when PECR's strictly necessary exemption covers a session ID, and when it stops.
-
Why Your Daemon Keeps a Zombie Child Until You Call wait()
Defunct processes pile up when a parent never calls wait(). See what the kernel keeps, how to reproduce it in Go, and how to fix it, including in containers.
-
Your Browser Can Leak More Than Your IP Address
Your browser exposes clues about your device, network and habits. Learn what leaks, who can see it, and how to reduce the signal.
-
Why Your TOTP Codes Stop Working When the Server Clock Drifts
TOTP is only a hash of the time. See how a skewed server clock rejects correct codes, how to diagnose it, and how to log drift and widen the window safely.
-
Can Your Employer Read Your Work WhatsApp Under UK GDPR?
When an employer can lawfully read work WhatsApp messages in the UK: UK GDPR, the Investigatory Powers Act, Article 8 case law, and why the device matters.
-
epoll Said Readable, Then read() Blocked: Four Ways That Happens
epoll reports a hint, not a promise. Four ways a ready socket still blocks on read(), accept() or write(), and the one habit that fixes all of them.
-
Why a Go TCP Echo Server Stalls at 10,000 Clients, Not 10
A goroutine per connection scales fine in Go. What stalls at 10,000 clients is the accept loop, the listen backlog and file descriptors. Here is how to tell.
-
Serving 10,000 Static Pages from Go with Only net/http
Yes, net/http alone can serve 10,000 static pages. Here is the 30-line server, what the standard library does for free, and the traps to avoid.
-
Envelope Encryption in Go Without a Cloud KMS
Wrap per-record data keys with a master key using only Go's standard library, then rotate that master key without re-encrypting a single byte of data.
-
Why Your CLI's Exit Code Is Always 0 After a Pipe
A shell pipeline reports only its last command's status. See how pipefail and PIPESTATUS expose hidden failures, and where pipefail bites back.
-
Why an ESP8266 Forgets Its WiFi Channel Every Deep Sleep
An ESP8266 wakes from deep sleep as a fresh boot with no WiFi state. Cache the channel and BSSID in RTC memory to skip the scan and save battery.
-
Why Encrypting a File with AES-CBC Still Needs a MAC in Rust
AES-CBC hides your file's contents but not whether someone altered it. See a one-byte tamper in Rust, then fix it with encrypt-then-MAC using HMAC-SHA256.
-
Why Deleting a Running Binary Doesn't Crash It
rm on a running program works fine, but overwriting it fails with 'Text file busy'. Inodes, link counts and the kernel's references explain both.
-
Your Password Reset Link Is a Backdoor: Threat Model It
A reset link is a password-equivalent bearer token sent over email. Here is how to threat model it: guessing, leaks, scanners, host headers and MFA bypass.
-
Find the Deleted File That Is Filling Your Disk
Your disk is full, but du cannot find the missing gigabytes. Track open deleted files with lsof, /proc, and a safe service restart.
-
Can the UK Make You Hand Over Your Encryption Key?
RIPA Part III lets police demand keys or plaintext, with prison for refusing. Here is how section 49 notices work, and what the courts have said.
-
Why a Mutex-Protected Go Counter Still Races After a Struct Copy
A value receiver or a plain assignment copies the mutex along with the data. See how that races, deadlocks, and how go vet and -race catch it.
-
Why Your Password Hash Has a Pepper Problem Nobody Mentions
A pepper is easy to add and awkward to rotate. Here is why HMAC-ing your hash traps you, and a small Go scheme that lets you change the key offline.
-
What a Technical Capability Notice Can Require of a Telecoms Operator
A technical capability notice can only impose obligations listed in the 2018 regulations. Here is what is on that list for telecoms operators, and what is not.
-
delay() Doesn't Reset Your ESP8266, the Loop Without It Does
An ESP8266 that reboots in a WiFi loop is usually starved of yield time, not hurt by delay(). Here is how the watchdogs work and how to fix each case.
-
Why Your ESP32 ADC Readings Wander Once WiFi Is On
Why ESP32 analogue readings drift or vanish when WiFi starts: the ADC2 conflict, supply noise from radio bursts, and a median-of-samples fix in ESP-IDF C.
-
Why a Password Hash Takes 50ms Locally and 400ms in Production
Same Argon2 parameters, very different latency. How CPU quotas, lane counts, page faults and noisy neighbours skew hash timings, and how to measure each.
-
Why Rehashing Passwords at Login Beats a Bulk Migration
You cannot re-hash stored passwords without the plaintext. Here is how to upgrade hash parameters at login in Go, and what to do about dormant accounts.
-
A 30-Minute STRIDE Pass on a Weekend Project
How to threat-model a small side project with STRIDE in half an hour: draw one diagram, ask six questions per arrow, and end up with a short fix list.
-
Why Your Go Server Logs 127.0.0.1 for Every Client Behind a Proxy
r.RemoteAddr is the proxy, not the visitor. How to read X-Forwarded-For safely in Go without letting clients spoof their own IP address.
-
Why Your Rust Mutex Guard Lives Longer Than You Think
A MutexGuard created inside a match, if let or while let scrutinee stays locked for the whole block. Here is where it hides and how to shorten it.
-
Analytics Without a Cookie Banner: What PECR Now Allows
Since February 2026 PECR has a statistical purposes exception. Here is when analytics can skip the banner, and which conditions catch people out.
-
A Zero-Copy mmap Read Still Pays: Faults, Copies, Stalls
An mmap read skips the copy but pays in page faults. Here are the measured numbers, where copies sneak back in, and what changes under load.
-
Why Your ESP32 Brownouts Only Happen When WiFi Connects
ESP32 brownout resets that only fire on WiFi association are a power supply problem, not a software bug, caused by a current spike your regulator can't answer.
-
Why Rural Broadband Fails at the Worst Possible Moment
Rural broadband rarely fails as a clean outage. Learn to spot queues, radio fades, power faults and bad failover before they matter.
-
Zeroing Secrets in Rust Doesn't Survive the Optimiser
A Drop impl that zeroes a key's bytes can vanish entirely under release-mode optimisation. Here's why, and how to actually stop it.
-
Diffie-Hellman in Go Without Touching Raw Curve Points
Go's crypto/ecdh package wraps X25519 and NIST curve Diffie-Hellman in a type-safe API that rejects invalid points before your code ever sees them.
-
A Read Deadline Set Once Will Kill a Long-Lived Connection
A net.Conn deadline is a fixed point in time, not an idle timer. Here is why a one-off SetReadDeadline kills healthy connections, and where to reset it.
-
Read the Request Body Before WriteHeader on HTTP/1
Go's HTTP/1 server discards or closes the request body once response headers flush. Where that bites, and how EnableFullDuplex avoids it.
-
At Boot, crypto/rand Waits and /dev/urandom Doesn't
Go's crypto/rand can block early at boot while /dev/urandom returns at once. See what getrandom does, how Go falls back, and how to probe it safely.
-
Shell Injection in Rust Without Ever Calling a Shell
std::process::Command skips the shell, so metacharacters are inert. Option injection, ssh, sh -c and Windows batch files can still bite; here is how.
-
Dead SSE Clients Behind a Proxy Never Cancel the Context
Behind nginx or a load balancer, r.Context() may never fire for a vanished SSE client. Detect them with flush errors, write deadlines and leases.
-
Why One-Time Pad Code Still Leaks, Even with subtle.XORBytes
XORBytes is constant-time and correct, yet a one-time pad built on it can still leak through short keys, reused pads and bit flipping.
-
Routing TLS Handshake Noise from http.Server into slog
Route http.Server's ErrorLog into slog, parse the TLS handshake error lines, and demote scanner noise without hiding real certificate problems.
-
Decrypting Your Own Go TLS Traffic in Wireshark
Set KeyLogWriter on a Go tls.Config, capture with tcpdump, and read your own HTTPS and HTTP/2 traffic in Wireshark. Includes the pitfalls that bite.
-
Rejecting an Upload Before It Arrives with Expect: 100-continue
How Go's HTTP/1.1 server handles Expect: 100-continue, why the first Body.Read sends the 100, and how to reject an upload before it arrives.
-
Why SameSite=Lax Breaks Cross-Site POST Logins
Why an explicit SameSite=Lax in Go's http.Cookie drops your login state on a cross-site POST callback, and the fixes that actually work.
-
HTTP/2 Push Is Dead, but http.Pusher Still Compiles
Browsers dropped HTTP/2 push, but Go's http.Pusher still compiles. Use 103 Early Hints with WriteHeader instead; checked with curl over h2 and HTTP/1.1.
-
Where Did the Time Go? Timing DNS, Connect and TLS in Go
Attach an httptrace.ClientTrace to a Go request and see where the time goes: DNS, TCP connect, TLS handshake and first byte, plus the traps.
-
Per-Connection Values in Go with BaseContext and ConnContext
Use BaseContext and ConnContext to attach per-listener and per-connection values to every request, and learn where they run and what they block.
-
Your Handler Keeps Running After TimeoutHandler Sends a 503
http.TimeoutHandler sends a 503 but never stops your handler goroutine. Here is what actually keeps running, what gets swallowed, and how to cope.
-
Watching Every Connection Change State in a Go Server
How http.Server's ConnState hook reports new, active, idle, hijacked and closed connections, plus a small tracker that spots stalled clients.
-
BufWriter Swallows Errors Unless You Call flush() Yourself
BufWriter's Drop impl flushes the buffer but throws away any error. See how a failed write exits cleanly with code 0, and how flush() and into_inner() fix it.