A typed-alert tower-defense for learning Python. Alerts of different kinds
(auth, ids, dns, firewall, email, cloudtrail, endpoint, waf,
vuln) flow a pipeline and queue at nodes. Turrets are typed consumers —
each drains a node's queue but only for the kinds its gun accepts. You hold
the line by composing guns, modules, gates (typed routers),
quelimiters (rate limiters), and parsers (decode raw alerts into typed
ones) across a topology you can design yourself.
It's a sandbox for the skills behind high-volume alert pipelines — typed routing, consumer specialization, coverage, queue/latency backpressure, and flood/burst handling — as a game.
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python -m chokepoint # play
make check # ruff + mypy + tests (no make on Windows? run the three directly)A guided, step-by-step tutorial runs on the first launch; press H any
time for the full controls + legend. See SETUP.md for details and optional
local-LLM help. Requires Python 3.11+ and pygame 2.6+.
There's a native-web version with a crisp HTML/canvas UI and a real code
editor (CodeMirror) — no install for players. The whole Python core + your
loadout.py run in the browser via Pyodide; player code
is sandboxed (safety.py) so it can't import arbitrary modules or reach the
network/DOM. The desktop build is unchanged (python -m chokepoint).
python serve_native.py # http://localhost:8001 (packages the core for Pyodide)The web/ app is pure static files with no backend — the Python runs in
the visitor's browser via Pyodide (loaded from a CDN, along with CodeMirror), and
player code is sandboxed. So it can be served by any static host, not just
GitHub:
- GitHub Pages — automated by
.github/workflows/pages.yml: pushing tomainpackages the core and deploysweb/. One-time setup: Settings → Pages → Build and deployment → Source: GitHub Actions. Publishes tohttps://<user>.github.io/<repo>/. - Netlify / Vercel / Cloudflare Pages / S3 / nginx / your own box — serve the
web/folder. Two caveats:- Include
web/chokepoint.zip(the packaged Python core). It's git-ignored, so build it first withpython -c "import serve_native; serve_native.build_package()"(or give the host that as a build command), then deployweb/. - Serve over http(s), not
file://, and visitors need internet on first load for the Pyodide/CodeMirror CDNs.
- Include
For a fully self-contained/offline build (no CDN dependencies), vendor Pyodide and
CodeMirror into web/ and point the <script> tags at the local copies.
- Loss (leaks): a kind no turret accepts flows to the exit, or a node's queue overflows.
- Latency (health): alerts that sit queued too long age out and bleed your health — backpressure made real.
A live COACH line tells you the most important thing to fix; the metrics
dashboard (M) shows queues, per-kind flow, a health trend, and the full
coaching list.
The board starts clean — you build the pipeline yourself (the tutorial and
coach walk you through it), or press F5 to load the example loadout.py.
- Editor (
E) — buy and place turrets/gates/limiters (drag-and-drop), equip modules; everything runs on a credit budget that grows as you clear waves. - Build mode (
T) — design the topology itself: add nodes, draw edges (cycle-checked), remove. Build a parallel branch and, when a turret is saturated, overload automatically spills down it to a backup consumer — the "else path" for a full worker. - Gates & parsers — gates route kinds down the branch that handles them
(Lambda/EventBridge-style pre-filter); parsers decode
rawalerts into their real kind so a consumer can take them (theingestdifficulty streams raw). - Code (
C) — editloadout.pyin-app (highlighted, undo, validated apply) or externally +F5.Ssaves your build and custom map to resume later. - Sandbox (
K) — free credits to experiment.
src/chokepoint/
packets.py # alert kinds + the wave curriculum + difficulty strategies
arsenal.py # drop-in guns, modules, turrets, synergies, unlocks, costs
economy.py # Bank: the credit budget
gates.py # Gate: typed router at a fork
limiter.py # Quelimiter: rate limiter / burst buffer
parsers.py # Parser: decode raw alerts into their real kind
maps.py # Graph topology (editable); built-in maps
simulation.py # World: queues, typed processing, spill, dual failure (NO pygame; tested)
metrics.py # Telemetry + failure debrief
hints.py # the in-game coach
editor.py # pure placement/economy state machine
tutorial.py # scripted, stepped onboarding (pure; render draws it)
codebuffer.py # text buffer for the in-app code editor
syntax.py # tiny tokenizer for editor highlighting
scores.py # high-score persistence
llm_assist.py # optional local-LLM diagnostics (stdlib, localhost-only)
loadout.py # YOU EDIT THIS: place + equip turrets (and gates/limiters/parsers) in Python
render.py # pygame UI (the only module with pygame)
web/ # native-web app (Pyodide + HTML/canvas); serve_native.py serves it
tests/ # headless tests for everything but render
Everything except render.py is pygame-free and tested headless; rendering only
reads simulation state.
A turret's gun accepts a set of kinds and drains the queue at the node it sits
on. Uncovered kinds leak; covered-but-overwhelmed nodes back up, age, and bleed
health. Gates pre-filter traffic to the right consumer (Lambda/EventBridge
style); quelimiters smooth bursts (but their buffer is finite, so sustained load
still needs throughput). Fire rate is static — you scale with modules, more
consumers, routing, and synergies. That's the alert-pipeline lesson, playable.
Contributions are welcome — see CONTRIBUTING.md for setup and the ground rules, and CODE_OF_CONDUCT.md for how we work together. Found a security issue? Please report it privately per SECURITY.md. Changes of note are tracked in CHANGELOG.md.
If the project helped you learn something, you can buy me a coffee ☕ — it keeps the work open-source and free. Thank you!
Chokepoint is released under the MIT License — you're free to use, copy, modify, and distribute it, including in your own projects, courses, or products, and at no cost. In plain terms, the license asks one thing in return:
Keep the attribution. Retain the copyright notice and the MIT license text (the
LICENSEfile) in any copy or substantial portion of the software.
That's the whole deal — credit stays, and the software is provided "as is," without warranty. If you fork it or build something on top, a link back to this repository is appreciated but not required; a CITATION.cff is included if you'd like to cite it formally (GitHub renders a "Cite this repository" button from it).
Cloning, forking, and starring are all welcome. If you're using it to teach or learn, I'd love to hear about it.