Dev mode — no Cloudflare Access header present. Authenticated as dev@local.
EPL
dev@local

Complete Engagement Stack Workshop

Course Introduction

[Instructor holds up a GL.iNet Mango — roughly the size of a deck of cards — in one hand, and opens the VS Code devcontainer on the laptop in the other. Both are running OpenWrt 23.05.3. The OS is identical. The hardware could not be more different.]

This is the central tension of the next two days. One device has 16 megabytes of flash and costs $29. The other is your laptop — probably 512 gigabytes and a modern processor. Both run the same firmware. Both enroll into the same Tailscale tailnet. Both report to the same Cloudflare Worker.

The Mango gets dropped somewhere and forgets you were ever there. The devcontainer is where you do the work.

What We’re Actually Building

This is not a configuration workshop. You are not going to spend two days clicking through someone else’s interface. You are going to build every layer of this stack from source and understand why each decision was made.

By Sunday evening, every person in this room will have:

  • Built custom OpenWrt firmware from scratch — twice. Once for a container that lives on your laptop, once for a 16MB NOR flash chip that lives on a $29 router.
  • Deployed a Cloudflare Worker that acts as the front door to your engagement infrastructure. Device enrollment, command dispatch, artifact delivery — all through a Worker you wrote.
  • Set up a two-node Tailscale mesh so your devcontainer and your Mango share a private network with stable DNS names, regardless of where either of them is physically connected.
  • Sent a food emoji in Discord and watched it decoded by your Worker, dispatched through your Tailnet to your Mango, executed as a tcpdump, uploaded to R2, and returned as a cryptographically signed download link.

That last sentence is not an exaggeration. It is Lab 14.

Two Ways to Deploy OpenWrt

The original design for this workshop had a different primary device. That device did not arrive. What we gained from that failure is more interesting than what we lost.

The devcontainer constraint forces you to think about what “deployment” actually means. When you deploy to a physical router, you are constrained by flash size, RAM, and the physical security of the device. When you deploy to a container on a laptop, you are constrained by nothing except what you decide to install. The same OpenWrt package manager, the same UCI configuration system, the same firewall primitives — but the engineering tradeoffs are completely different.

That contrast is now the spine of the course.

On a Mango, you have 16MB. You cannot fit Tailscale, cloudflared, and python3 in the firmware image. You learn why, by trying, and then you solve the problem with ExtRoot — mount a USB drive as the filesystem overlay and suddenly the constraint disappears. That is the correct lesson: not that constraints are bad, but that constraints force you to understand the system deeply enough to route around them.

On the devcontainer, you have no flash constraint. You learn a different lesson: reproducibility. Two students who run make engagement-platform on the same day get bit-identical artifacts. That is harder to achieve than it sounds, and it matters in production.

Why This Matters Operationally

Raise your hand if you have ever needed custom functionality that a vendor’s platform would not support. Keep it up if you have hit a vendor deprecation that broke infrastructure you depended on. Higher if you have had to explain to a client why their deployment requirements are “incompatible with modern architecture.”

[Looks around room]

The industry trains practitioners to be sophisticated consumers. You learn to configure Cisco instead of understanding routing protocols. You learn to deploy cloud services instead of understanding the distributed systems underneath them. This workshop inverts that.

When you understand the primitives — firmware build, package selection, overlay filesystem, mesh networking, edge compute, signed-URL artifact delivery — vendor roadmaps become irrelevant. When the Beryl AX ships in six months, you already know how to build for it. When a client’s network blocks Tailscale, you know which layer to substitute. When a vendor deprecates an API, you know where the equivalent primitive lives.

The Technology Stack

Day 1 — Foundation

We start with the hardware because that is where understanding starts. You will open a Mango and look at the UART pins. You will put the device into failsafe mode deliberately so that recovery is not frightening when it happens accidentally. You will build two OpenWrt firmware artifacts from the same ImageBuilder image and measure the difference in size. You will mount a USB drive as your filesystem overlay and watch the package install that was previously impossible become trivial.

By end of Day 1 you have a working two-node tailnet, a Cloudflare Tunnel, and a deployed Worker with a live health endpoint.

Day 2 — Control Plane

The Worker becomes a real control surface. You provision D1 for device enrollment and audit logging. You add KV for job queuing and R2 for artifact storage with signed URLs. You build the EmojiChef decoder that turns food emoji into ASCII commands. You bake enrollment secrets into the Mango firmware image so it self-registers on first boot. You add a malleable C2 relay on top of the Worker using Oblique-Relay patterns. Then you run the capstone.

Your Pre-Flight Check

Before we dive in, verify the following:

Hardware: You should have a Mango, a USB drive, and an Ethernet cable. If anything is missing, raise your hand now.

Software: Docker must be working on your laptop. Run docker ps — if it returns without error, you are ready. VS Code with the Dev Containers extension installed. If you did the pre-class warmup (make engagement-platform), you are fully prepared.

Accounts: Cloudflare account with email confirmed. Tailscale account. Discord account. If any of these are missing, create them now — Labs 04 and 05 will block without them.

Devcontainer: When you open this repository in VS Code and choose “Reopen in Container,” VS Code will pull and start the engagement platform. If you pre-pulled the image during the warmup, this takes under 30 seconds. If not, it will take a few minutes the first time.

What Success Looks Like

By Sunday evening you will have working infrastructure that:

  • Boots custom firmware with your package selection and persistent overlays
  • Provides secure global access without port forwarding or static IPs
  • Responds to commands from chat using steganographic encoding
  • Logs every action with tamper-evident audit trails
  • Stores artifacts with cryptographically-signed access controls

More importantly, you will understand how each component works and how to replace it. The Cloudflare primitives have open-source equivalents. The Tailscale mesh can be replaced with WireGuard. The Discord webhook can be replaced with any webhook receiver. You are learning vendor-independent techniques that happen to use excellent free tiers for the workshop.

The Next 14 Hours

The labs build on each other sequentially. If you fall behind, raise your hand immediately — the instructor can often unblock a lab issue in under two minutes, and the solution usually teaches something important about the underlying system. Do not silently fall three labs behind; that is not recoverable in a day.

Take notes on what fails and how you fix it. The course materials include troubleshooting guidance, but your personal log of “I got this error and fixed it by doing X” is more durable knowledge than anything in a handout.


[Instructor puts the Mango on the table, leaves the devcontainer open on the screen]

Lab 1 starts now. Connect your laptop to the Mango’s WAN port via Ethernet and open a browser to 192.168.8.1. Let’s start with the hardware.