Reader

Blog

Welcome to the Blog.

Why Constraints Matter

Give a programmer unlimited resources and they’ll build a mess. Give them 640 KB and they’ll build something elegant.

Constraints force creativity. The demoscene proved that artistic expression thrives under extreme limitations. The same principle applies to web design: this site uses no JavaScript, and the CSS-only approach has led to solutions I would never have considered otherwise.

I have seen this pattern in codebases, hardware, writing, and product work: when limits are explicit, quality decisions become visible. You stop saying “we can optimize later” and start choosing what must be fast, simple, and stable right now. Constraints are not a prison. They are a filter.

Types of useful constraints

Not all limits are equal. Bad constraints are random bureaucracy. Good constraints are deliberate boundaries with a clear purpose:

A tight budget often produces better architecture because you are forced to separate “core value” from “nice decoration.” In practice, this means fewer layers, stronger naming, and less accidental complexity.

Constraint-first design habit

... continue

Restoring an AT 286

PSU faults, battery damage, and XTIDE bring-up on a real machine

I found a Commodore PC 30-III (286 @ 12 MHz) at a flea market. The power supply was dead, the CMOS battery had leaked, and the hard drive made sounds like a coffee grinder.

After recapping the PSU, neutralizing the battery acid with vinegar, and replacing the MFM drive with a XTIDE + CF card adapter, the machine booted into DOS 3.31. The CGA output on a period-correct monitor is a shade of green that no modern display can reproduce.

The restoration looked simple from the outside, but each subsystem had to be proven independently. Old machines fail in clusters: power instability hides logic faults, corrosion causes intermittent behavior, and storage errors can masquerade as software problems.

Restoration sequence that worked

I treat this like incident response, not hobby magic. Predict expected output, test one hypothesis, compare reality, then decide the next step.

What surprised me

The most fragile part was not the CPU or RAM, but edge connectors and sockets. A careful reseat cycle fixed several “ghost bugs.” Also, DOS 3.31 felt faster than memory suggests once disk latency vanished behind solid-state storage. The machine became practical for retro workflows, not just shelf display. ... continue

RISC-V on a 10-Cent Chip

CH32V003 bring-up, UART echo, and 8 uA sleep

The WCH CH32V003 costs less than a stamp and runs a 32-bit RISC-V core at 48 MHz. It has 2 KB of RAM, 16 KB of flash, and a surprisingly complete peripheral set: USART, SPI, I²C, ADC, timers.

We set up the open-source MounRiver toolchain, flash a UART echo program over the single-wire debug interface, and measure current consumption in sleep mode: 8 µA. For battery-powered sensors, this chip is hard to beat.

The interesting part is not only the price. It is what this device teaches about writing firmware with hard limits. With 2 KB RAM, every buffer is a design decision. With 16 KB flash, libraries have to justify their existence. That pressure tends to produce cleaner code than “just add another package.”

Bring-up notes that save time

My shortest path to first success:

Most early failures are clock, pin mux, or toolchain path problems, not “mystical hardware bugs.” If serial output is dead, confirm GPIO mode and baud assumptions before rewriting half the project.

Why this chip is useful in practice

... continue

Nmap Beyond the Basics

NSE scripts and staged, auditable scan workflows

Everyone knows nmap -sV target. But Nmap’s scripting engine (NSE) turns a port scanner into a full reconnaissance framework.

We look at three scripts that changed how I approach engagements: http-enum for directory brute-forcing, ssl-heartbleed for quick Heartbleed checks, and smb-vuln-ms17-010 for EternalBlue detection. Combining these with --script-args and custom output formats (XML piped into xsltproc) creates repeatable, auditable scan reports.

The key upgrade is moving from “one clever command” to a staged workflow. I run discovery, service fingerprinting, and targeted scripts as separate passes with saved outputs. That keeps scans explainable and prevents noisy false conclusions from a single overloaded run.

A practical scan sequence

For real operations, reproducibility beats heroics. If results cannot be replayed or audited, they are weak evidence.

NSE discipline

NSE is powerful, but script selection should follow scope and authorization. Many scripts are intrusive. Treat them like controlled tests, not default checkboxes. I keep a small approved script set per engagement type, then expand only with explicit reason. ... continue

Format String Attacks

From %08x reads to %n arbitrary writes

Format string vulnerabilities happen when user-controlled input ends up as the first argument to printf(). Instead of printing text, the attacker reads or writes arbitrary memory.

We demonstrate reading the stack with %08x specifiers, then escalate to an arbitrary write using %n. The write-what-where primitive turns a seemingly harmless logging call into full code execution.

The fix is trivial: always pass a format string literal. printf("%s", buf) instead of printf(buf). Yet this class of bug resurfaces in embedded firmware to this day.

Why does this still happen? Because logging code is often treated as harmless, copied fast, and reviewed late. In small C projects, developers optimize for speed of implementation and forget that formatting functions are tiny parsers with side effects.

Exploitation ladder

Typical progression in a lab binary: ... continue

Turbo Pascal in 2025

DOSBox-X, the classic IDE, and a 3 KB RTC utility

Turbo Pascal 7.0 still compiles in under a second on a 486. On DOSBox-X running on modern hardware, it’s instantaneous. The IDE — blue background, yellow text, pull-down menus — is the direct ancestor of the Turbo Vision library that inspired this site’s theme.

I wrote a small unit that reads the RTC via INT 1Ah and formats it as ISO 8601. The entire program, compiled, is 3,248 bytes. Try getting that from a modern toolchain.

What surprised me was not just speed, but focus. Turbo Pascal’s workflow is so tight that experimentation becomes natural: edit, compile, run, inspect, repeat. No dependency resolver, no plugin lifecycle, no hidden build graph. You can reason about the whole stack while staying in flow.

Why it is still worth touching

Turbo Pascal remains one of the best environments for learning low-level software discipline without drowning in tooling:

If you want to sharpen systems instincts, this is still high-return practice.

Practical 2025 setup that stays reproducible

... continue

Linux Networking 7: nftables in Production

Ten years on: migration scars, defaults, and operator truth

Ten years after nftables entered the Linux landscape, we can finally evaluate it as operators, not just early adopters.

In 2024, nftables has enough production mileage for operator-grade evaluation: distributions default toward nft-based stacks, migration projects have real scar tissue, and incident history is deep enough to separate marketing claims from operational truth.

By 2024, in many production environments, nftables has effectively displaced direct iptables administration. Compatibility layers still exist, legacy scripts still survive, but the center of gravity changed.

The important question now is not “is nftables new?”
The important question is “did the move improve real operations?”

What changed in daily practice

For teams that completed migration well, the practical improvements are clear: ... continue

Linux Networking 6: BPF and eBPF

Programmable networking and observability at the 2015 horizon

A decade of Linux networking work with ipchains, iptables, and iproute2 teaches a useful discipline: express policy explicitly, validate behavior with packets, and automate what humans consistently get wrong at 02:00.

By 2015, another shift is clearly visible at the horizon: BPF lineage maturing into eBPF capabilities that promise more programmable networking, richer observability, and tighter integration between policy and runtime behavior.

This article is not a final verdict. It is an in-time outlook from the moment where the tools are just mature enough to be taken seriously in production pilots, while broad operational experience is still being collected.

Why old firewall/routing skills still matter

Before discussing eBPF, an important reminder:

New programmability does not erase fundamentals. It amplifies consequences. ... continue

Storage on Budget Linux

IDE, SATA, backups, and what actually survived production in the 2000s

If there is one topic that separates “it works in the lab” from “it survives in production,” it is storage reliability.

In the 2000s, many of us ran important services on hardware that was affordable, not luxurious. IDE disks, then SATA, mixed controller quality, inconsistent cooling, tight budgets, and growth curves that never respected procurement cycles. The internet was becoming mandatory for daily work, but infrastructure budgets often still assumed occasional downtime was acceptable.

Reality did not agree.

This article is the field manual I wish I had taped to every rack in 2006: what actually made budget Linux storage reliable, what failed repeatedly, and how to build recovery confidence without enterprise magic.

The first uncomfortable truth: storage failure is normal

We lose time when we treat disk failure as exceptional. In practice, component failure is normal; surprise is the failure mode. ... continue

Mailboxes to Internet 4: Perimeter and Proxies

Firewalls, monitoring, and when internet access becomes mandatory

The final phase of the migration story starts when internet access stops being “useful” and becomes “required for normal business.”

That is the moment architecture changes character. You are no longer adding online capabilities to an offline-first world. You are operating an internet-dependent environment where outages hurt immediately, security posture matters daily, and latency becomes political.

If Part 1 taught us gateways, Part 2 taught policy discipline, and Part 3 taught identity realism, Part 4 teaches operational maturity: perimeter control, proxy strategy, and observability that is good enough to act on.

The perimeter timeline everyone lived

In the late 90s and early 2000s, many of us moved through the same progression:

Tool names changed over time. The operating truth stayed constant: ... continue