In 2025 we did a lot of traveling as a family. When you're traveling with two boys, 10 and 19, the success of any extended trip comes down to one thing: whether the gaming console, the VR headset, and the Firesticks work the same way they do at home.

We spent time in Pachuca, Mexico and later in Spain. In both cases I needed US-based internet egress for family peace. A 10-year-old who can't connect to his game servers and a 19-year-old with his own connectivity requirements are problems I was not equipped to manage on top of everything else.

The catch: consumer devices, a gaming console, a VR headset, a handful of Firesticks, can't run a VPN client. You can't install Tailscale on a PlayStation. You can't configure WireGuard on a Fire TV. If you want those devices to route through a US exit node, the tunnel has to live in the network, not on the devices.

So I built it into the network. Traffic from Pachuca tunneled to California. Later, traffic from Spain tunneled to California. Every device on the local network got US egress automatically, with no client software, no configuration changes, nothing for the kids to manage or break.

That constraint, transparent tunneling for devices that can't run a VPN client, turned out to be one of the most useful design requirements I could have started with. It forced a network architecture that works for any device, not just ones you control.

The question wasn't whether I could build enterprise-grade security operations on my own. It was whether I could build it in a way I'd actually maintain, and that would survive two boys and an international move.

Start with the tunnel

My initial instinct was to use native WireGuard. Open source, no third-party dependencies, theoretically optimal performance. I built it out and benchmarked it. What I found was that the "native WireGuard" solution still relied on Tailscale to handle NAT traversal and routing anyway, so I was carrying all of Tailscale's complexity without any of its operational benefits. Eliminating the native WireGuard layer and letting Tailscale own the full stack decreased complexity dramatically while simultaneously increasing reliability and performance.

For context: I've built a lot of firewalls and site-to-site VPNs, first on Checkpoint, later on Netscreen. Those were serious pieces of enterprise gear. They were also genuinely complex to configure, especially without hands-on access to the far end. And they never could have negotiated the NAT traversal and routing automatically the way Tailscale does. What Tailscale handles seamlessly was, on that generation of hardware, a multi-day project requiring coordinated access on both sides. I prefer open source tools, but the job is to pick the best tool, and Tailscale earns that designation.

The right architecture for transparent tunneling is Tailscale running on the gateway, not on every device. Consumer devices connect to the local network normally and get tunneled egress automatically. The console doesn't know it's in Mexico. The streaming service doesn't know it's talking to a device in Spain.

The primary exit node was CALpi, a Raspberry Pi 4 on a consumer ISP connection in California. Traffic from Mexico tunneled through Tailscale to CALpi and exited there. For redundancy I built AWSpi, an EC2 instance managed with Terraform and Ansible, designed to spin up on demand rather than run continuously to keep costs down. Running in US-West-2, it kept the exit geographically close to CALpi. AWSpi covered planned CALpi maintenance and the occasional unplanned failure. The one exception was Hulu, which detected the EC2 exit node and blocked traffic flagged as coming from a VPN. Everything else worked cleanly through either node. I set the policy to fail-closed: if the tunnel drops, egress stops. Not "traffic routes around the VPN." It stops. That's an opinionated design choice and the right one when you care more about not leaking traffic uncontrolled than about uninterrupted connectivity.

The hardware problem

The original gateway in Mexico was MEXpi, a Raspberry Pi 5. It handled everything: DHCP server, wireless access point via a Realtek USB WiFi NIC, tunnel enforcement, and routing. The Realtek driver was the first real lesson in what happens when you ask one underpowered node to do too much.

Realtek USB WiFi drivers on Linux are historically unreliable. We hit driver failures multiple times. Each one took the wireless network down entirely until I could SSH in and reload the driver. When you're the only person in the house who can fix it, this becomes a quality-of-life problem quickly. On top of driver instability, throughput was constrained by the Pi's USB bus: 30 Mbps was about the best we saw, and performance in Spain degraded further because the Spain-to-California path added latency that the Pi couldn't compensate for with any additional headroom.

LEGO Paul in Spain with ESPpi — the Ryzen 7 upgrade that changed everything

Spain. The upgrade that fixed everything.

The fix was to stop compromising on hardware. I replaced MEXpi with ESPpi: a Ryzen 7 mini PC running Ubuntu, with dual 2.5G Ethernet interfaces for current capacity and future expansion. I also separated the wireless access point function entirely onto TP-Link WAPs connected via Ethernet, with ESPpi handling DHCP, DNS, routing, and tunnel enforcement. The WAPs became dumb radios. ESPpi became the brains.

The difference was immediate. Driver failures disappeared. ESPpi running Tailscale through to CALpi was able to exceed the throughput of the raw ISP links in both Mexico and Spain, with Ookla speed tests hitting as high as 265 Mbps on a 250 Mbps link, faster than going direct to the internet through the ISP router's built-in wireless! The dual 2.5G interfaces meant I could segment WAN links cleanly. And critically, ESPpi had enough compute headroom to run the monitoring stack I'd wanted from the beginning but couldn't implement on a resource-constrained Pi.

Back home, with a different network

When we returned home, the tunnel was no longer the point. We were back in California. The exit node was down the road. But I kept PiNet running as the home backbone, and the reason was everything I'd built on top of the connectivity layer.

ESPpi running Zeek on the home LAN gives me full network visibility: every DNS query, every TLS session, every external connection, logged and queryable. Importantly, this is metadata visibility — connection endpoints, session characteristics, certificate details, behavioral patterns — without needing to break or MitM encrypted HTTPS, SSH, or any other traffic to inspect it. You get meaningful signal without compromising the integrity of the encryption. That visibility doesn't stop being useful just because the tunnel is no longer needed for geographic bypass. If anything, it becomes more useful at home because the traffic volume is higher and the device mix is more complex.

I also added ISP redundancy on the LAN side. Not at the tunnel exit like before, but at the gateway itself. ESPpi's dual 2.5G interfaces cover the LAN and primary WAN links. For the second WAN uplink I added a USB-C to 2.5G Ethernet adapter, giving ESPpi a third interface without new hardware. Policy routing handles failover automatically: primary ISP for normal traffic, secondary ISP when the primary link drops. ESPpi detects the failure and reroutes without manual intervention. The kids never notice. That's the standard.

The observability problem

Secure and observable are different properties. Most home labs optimize for secure. They get a VPN, they add firewall rules, they check a box. What they don't get is any visibility into what's actually traversing the network.

This matters because threats don't announce themselves. Beaconing malware looks like normal HTTPS traffic until you analyze the connection patterns. Credential abuse looks like a normal login until you cross-reference it against behavior baselines. You can't see any of that without telemetry.

The fix was Zeek. Zeek is a network analysis framework, not a signature-based IDS. It doesn't match known-bad patterns. It produces structured logs of everything: DNS, HTTP, SSL, connections, file transfers. Every conversation on the network becomes a record you can query.

Adding Zeek to PiNet was the moment the project shifted from "secure network" to "monitored network." I could now answer the question I couldn't answer before: what is actually happening here?

From data to detection

Zeek gives you data. Data is not detection.

Detection requires knowing what normal looks like, identifying deviation from normal, and having enough context to distinguish malicious deviation from expected variance. Zeek logs give you the raw material. You need something else to do the analysis.

That something else is RITA: Real Intelligence Threat Analytics. RITA ingests Zeek logs and applies behavioral analytics, specifically looking for patterns that signature-based detection misses. Long connections. Beaconing cadences. Unusually high connection frequency to a single external host. These are the indicators of command-and-control traffic, data exfiltration, and persistent access. They don't trigger signatures. They show up in behavior.

The compute for all of this runs on MacBuntu: a repurposed MacBook Pro running Ubuntu 26.04 LTS with 12 CPU cores and 16 GB RAM. MacBuntu is the analytics core of PiNet. All logs across the platform flow into a DuckDB-backed data lake for total visibility, not just Zeek but every telemetry source PiNet generates. I love this capability — it's conceptually similar to what AWS Security Lake offers at enterprise scale, without the vendor lock-in, and close to what I was designing at Couchbase before the acquisition and production freeze. RITA v5 runs in Docker on MacBuntu, pulling from that data lake and surfacing behavioral anomalies. Over 13 GB of historical data in the archive means RITA has real baseline history to work against, not just a rolling window.

MacBuntu also hosts the Streamlit dashboards that make the telemetry operationally useful. Rather than SSHing into nodes to query logs, operational visibility is a browser tab: connection trends, beacon scores, DNS anomalies, threat hunt findings. MacBuntu additionally serves as the Ansible control node, which means every other node in the network is managed from one place, with version-controlled playbooks and documented runbooks. The whole stack is coordinated from a single platform.

The runtime layer

Network visibility tells you what's talking to what. It doesn't tell you what's happening on the host. A compromised process can establish perfectly normal-looking connections while doing extremely abnormal things on the filesystem or in memory.

Falco handles host-level detection. It monitors system calls in real time and fires alerts when behavior deviates from defined policy: a web server spawning a shell, a configuration file being written by an unexpected process, a container attempting a privileged operation. These are indicators that network monitoring won't surface, because they happen before any traffic leaves the host.

The combination of network telemetry from Zeek, behavioral analytics from RITA, and runtime monitoring from Falco gives you three distinct visibility planes. An attacker who evades one will almost certainly surface in another.

▣ The stack at this point

ToolLayerWhat it answers
TailscaleNetworkWho can talk to whom, and is it authenticated?
CALpi + AWSpiExit nodesIs egress available and verified US-based?
Zeek (ESPpi)TrafficWhat conversations are actually happening?
RITA + DuckDB (MacBuntu)AnalyticsWhich patterns are anomalous?
FalcoHostWhat are processes doing at the syscall level?
Ansible (MacBuntu)OperationsIs every node in its defined good state?

The discipline problem

By late 2025 I had a functional security operations platform. What I didn't have was an operational discipline around it.

This is the part that separates home lab projects from production infrastructure. Anyone can build a stack. Fewer people build a stack they maintain consistently, document thoroughly, and can recover from failure states without tribal knowledge in their own head.

I made a deliberate decision to treat PiNet like production. That meant Ansible for all configuration management. No hand-edited config files on nodes. Every change version-controlled in GitHub — playbooks, architecture decisions, runbooks for common failure modes and network troubleshooting. Every deployment tested against a known-good baseline. If it isn't in the repo, it doesn't exist.

This wasn't perfectionism. It was operational necessity. When something breaks at 2am, you don't want to be reconstructing what you did six months ago from memory. You want to run a playbook and get back to baseline.

Applying production engineering practices to personal infrastructure taught me something I hadn't fully articulated before: the gap between "I understand how this works" and "I can operate this reliably" is a discipline gap, not a knowledge gap. Most organizations have the knowledge. They lack the discipline.

The moment it became something else

Somewhere in the process of hardening PiNet's operational practices, I realized I had built the thing I'd been advising clients to build for years. Not a collection of tools. An integrated security operations platform, running in production, maintained with engineering discipline, operated by one person who knew every layer of it.

That's the answer to the question I started with: how do you run enterprise-grade security operations on your own hardware, with open-source tooling, as a team of one?

You build it the way I built PiNet. And then you run it the way a production environment demands: documented, automated, testable, recoverable.

PiNet became KeserNet when I recognized that the platform and the operational model were both transferable. The architecture scales. The Ansible playbooks are parameterized. The monitoring stack can be pointed at any environment. The operational discipline is teachable.

KeserNet is PiNet, available as a managed service. The next articles in this series cover how the platform actually works in production: what telemetry it generates, what incidents it has caught, how automated recovery works, and where AI has been genuinely useful versus where it's been a liability.