A universal software-app template built on the Jaeger app format. Copy this directory into a new repo and you have a complete, runnable app — supervised nodes on a message bus under a single-window PySide6 shell — plus a landing page and CI, ready to make your own.
app/ the chassis copy — you OWN this (the format's copy model)
core/
messages.py your /act + /sense vocabulary (the per-app registry)
+ shared services: memory, safety, models (optional)
nodes/
software/worker.py a thread node: publishes a periodic reading
hardware/device_sim.py a SUBPROCESS node: crash-isolated, auto-restarted
surfaces/
main_window.py the shell: supervisor table + telemetry + controls
tray.py menu-bar icon; quit tears everything down together
agent/ (optional) an agent loop, if your app has one
middleware/ (optional) hardware / integration glue
docs/ your app's documentation (architecture, guides, notes)
site/ the landing page (GitHub Pages, deployed by Actions)
tests/
test_app_format.py conformance — the format's contract, executable
test_app_smoke.py boots the real app headless, crashes + restarts a node
_worker_node.py subprocess fixture for the conformance suite
.github/workflows/ ci.yml (lint + tests) · pages.yml (deploy the site)
jaeger.toml structure: nodes, surfaces, bus, event loop
config.yaml behavior knobs (per-node)
main.py entry point (GUI, or `--headless N` for CI/smoke)
pyproject.toml your deps (empty wheel on purpose — see its header)
README.md CHANGELOG.md LICENSE
python3 -m venv .venv
.venv/bin/pip install -e ".[dev,zmq,gui]" # dev+zmq+gui = everything
.venv/bin/python main.py # the GUIHeadless smoke (no display — what CI runs):
.venv/bin/python main.py --headless 5
.venv/bin/python -m pytest tests/ -q # conformance + smoke must passWhat to try in the GUI: watch the nodes boot in the supervisor table;
hit 💥 CRASH the device subprocess and watch on_failure restart it
(the restarts column ticks, the pid changes) while everything else
keeps running; Stop/Start/Restart/Diagnose a selected node; drive the
device with the level slider; open extra Monitor windows; quit from the
tray — no orphaned processes, ever.
- Rename
my-app→ your app's name injaeger.toml([app] name),pyproject.toml([project] name),main.py, and the registry path intests/test_app_smoke.py. - Replace the example nodes under
nodes/<category>/with your own (keepmake_node/modulewiring), and updatecore/messages.pywith your/act+/sensedataclasses (each needs atopic). - Rewire the surfaces in
surfaces/to your telemetry and controls; keep the supervisor table — it works for any app. - Edit
site/index.html— fill in thePROJECT/your-org/your-repoplaceholders (the<style>block is the shared design system; leave it). - Keep the conformance suite green:
pytest tests/ -q.
- No GUI? Set
ui = "none"andevent_loop = "none"injaeger.toml, deletesurfaces/, and dropguifrompyproject.toml. - No subprocess node? Delete
nodes/hardware/device_sim.pyand its[[node]]entry, switch[bus] backend = "inproc", and dropzmq. - No landing page? Delete
site/and.github/workflows/pages.yml.
Push to your repo, then set Settings → Pages → Source → GitHub Actions
once. pages.yml redeploys site/ on every push that touches it; the
live URL shows in the workflow run summary. See site/README.md.