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

Workshop opening

Engagement Platform Labs

A two-day workshop that builds an engagement stack from its underlying primitives: OpenWrt firmware on a GL.iNet Mango, a Tailscale mesh, a Cloudflare Worker control plane, and a ChatOps decoder. You leave knowing each layer well enough to substitute it.

Pick your environment

This choice filters lab content (Codespace vs local Dev Container vs minimal local docker) across the whole course. You can change it any time from a lab page header.

Environment:

You will work with two computing contexts. A GL.iNet Mango (GL-MT300N-V2) sits on your bench: a $29 travel router with 16 MB of NOR flash and 128 MB of RAM, running OpenWrt 23.05.3. An operator console runs in your Dev Container or GitHub Codespace: a Debian shell with Tailscale, cloudflared, wrangler, and python pre-installed, plus a sibling OpenWrt rootfs container available for parity work. Both peers join the same Tailscale tailnet and report to the same Cloudflare Worker.

The Mango is the drop device. The operator console is where you do most of the work.

What you build

This is not a configuration tour. You build each component from its underlying primitives and learn the reason behind each decision.

By the end of Day 2 you will have:

  • A custom OpenWrt sysupgrade image for the Mango, assembled with ImageBuilder from upstream packages.
  • A Cloudflare Worker that handles device enrollment, command dispatch, and artifact delivery.
  • A two-node Tailscale mesh that gives your operator console and your Mango stable DNS names regardless of where either is physically connected.
  • A GitHub-issue-comment-driven workflow that decodes a food-emoji command in your Worker (the EmojiChef encoder), dispatches it over the tailnet to the Mango, runs a tcpdump there, uploads the capture to R2, and returns a signed download link as a comment reply.

The last item is the Lab 14 capstone.

Constrained and unconstrained Linux

The original workshop design used a different primary device that did not arrive in time. The Dev Container (and now Codespace) replacement turned out to teach a contrast worth keeping.

Deployment to a physical router is bounded by flash size, RAM, and physical security. Deployment to a container on a laptop, or to a Codespace VM, is bounded only by what you choose to install. The OpenWrt package manager, the UCI configuration system, and the firewall primitives apply on the Mango. The engineering tradeoffs differ.

On a Mango you have 16 MB of flash. Tailscale, cloudflared, and python3 do not fit in the firmware image. You learn why by trying, then you solve it with ExtRoot: a USB drive mounted as the filesystem overlay. The lesson is not that constraints are bad. It is that constraints force you to understand the system well enough to route around them.

On the operator console you have no flash constraint. The lesson there is reproducibility: two students who build the same Mango image from the same ImageBuilder version get bit-identical artifacts.

Why this matters

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.

Once you understand the primitives (firmware build, package selection, overlay filesystem, mesh networking, edge compute, signed-URL artifact delivery), vendor roadmaps lose most of their hold over your work. If a client network blocks Tailscale, you know which layer to substitute. If a vendor deprecates an API, you know where the equivalent primitive lives. If a new device ships, you know how to build firmware for it.

Day-by-day plan

Day 1: foundation. You start at the hardware. Identify the Mango’s UART pins, observe boot via serial console, and put the device into failsafe mode deliberately so recovery is familiar before you need it accidentally. Build a custom OpenWrt sysupgrade image for the Mango with ImageBuilder, and compare it against the unconstrained x86 OpenWrt rootfs sibling. Add ExtRoot so packages that did not fit in NOR flash install cleanly to USB. By the end of Day 1 you have a two-node Tailscale tailnet, a Cloudflare Tunnel fronting a service on your operator console, and a deployed Worker with a live health endpoint.

Day 2: control plane. The Worker grows into the system’s coordination point. Provision D1 for device enrollment and audit logging. Add KV for job queuing, and R2 for artifact storage behind signed URLs. Build the EmojiChef decoder that turns food emoji into ASCII commands. Bake enrollment secrets into the Mango firmware image so it self-registers on first boot. Add a malleable C2 relay on top of the Worker using Oblique-Relay patterns. Day 2 ends with the capstone: the food-emoji round-trip described above, end to end.

Pre-class checklist

Hardware. A Mango, a USB drive (16 GB or larger, ext4-formattable), an Ethernet cable, and a 3.3 V USB-to-UART adapter with dupont leads.

Operator console (pick one):

  • A Codespace on this repository. Zero local install.
  • Docker (or Podman) plus VS Code with the Dev Containers extension on your laptop. docker ps should return without error.
  • For Lab 01 only: Docker alone is enough. The full Dev Container is optional until Lab 04.

Accounts. Cloudflare with confirmed email, Tailscale, GitHub (free account; you need a repository, issue #1 open as your command queue, a fine-grained PAT, and webhook configuration). Labs 04 and 05 block without Cloudflare and Tailscale. Lab 11 blocks without GitHub.

Discord is optional. It is only needed for the Lab 11 take-home Discord variant; the in-class workshop uses GitHub issue comments throughout.

Operator console build. When you open the repository in VS Code and choose “Reopen in Container,” or click “Open in Codespaces” on a lab page, the console builds. First build takes a few minutes. Subsequent attaches are instant.

Outcomes

At the end of Day 2 you should have a working stack that:

  • Boots custom Mango firmware with your package selection and a persistent USB overlay.
  • Provides reachability between operator console and Mango without port forwarding or static IPs, via a Tailscale mesh.
  • Responds to commands sent through GitHub issue comments, decoded by a Worker, and dispatched to the Mango.
  • Records every command, validation, and artifact retrieval to D1 for later review.
  • Stores artifacts in R2 behind short-lived signed URLs.

You leave the course knowing how each component works and where its substitutes are. The Cloudflare primitives have open-source equivalents. Tailscale can be replaced with WireGuard. The GitHub webhook is a generic HTTP receiver. Free tiers keep the workshop cheap to run; they are not load-bearing for the techniques.

How to use this course

The labs build on each other. Do not skip ahead. If you get blocked, ask: the unblock for one lab usually teaches something about the underlying system, and falling silently behind by three labs is not recoverable in a day.

Keep your own notes on what failed and how you fixed it. The course materials cover troubleshooting, but a personal “I hit this error and solved it by doing X” log is more durable knowledge than any handout.


Open Lab 01. Connect your laptop’s Ethernet to the Mango’s LAN port (the black one) and a browser to http://192.168.8.1. Start with the hardware.

Ready to start

Lab 01 — Hardware Familiarization & Recovery

Mango on the bench, devcontainer (or Codespace) running, USB-UART attached. ~60 minutes.

Start Lab 01