Small edge-to-control-plane setup: Pi runs an image inference service, laptop runs a control plane that stores telemetry + manages model deploy/rollback.
The Pi serves /infer with a bounded queue (so overload becomes a clear 503 instead of random timeouts). The control plane stores telemetry in Postgres, keeps model versions (sha + files), and can deploy a new model to the Pi over ssh + rollback if things look worse.
- TODO: add later
- Python (FastAPI)
- Postgres (docker compose)
- Raspberry Pi + systemd (edge service)
- bash + small python scripts for demo
- telemetry ingest + simple summaries (p50/p95, error/reject rate)
- model registry w/ sha256 + local artifact storage
- deploy model to Pi (scp/ssh) + verify + restart
- rollback on regression (latency/error/reject thresholds)
- copy
.env.exampleto.envif you want to change ports/tokens docker compose up -d --build- register the Pi once (see demo scripts below)
operator scripts -> control plane (FastAPI) -> Postgres + artifacts/
^
| telemetry batches (HTTP)
|
Pi edge service (Flask)
./demo/demo_up.sh./demo/demo_register_device.sh(needsSSH_HOST=...)./demo/demo_upload_models.sh./demo/demo_deploy.sh(setMODEL_VERSION=v1/v2)./demo/demo_deploy_and_rollback.sh(runs the full story)python demo/load_pi.py(load generator)
- token rotation + tighter auth
- artifact store to S3/MinIO
- do telemetry aggregates instead of per-event rows
- make deployments async (right now deploy waits inside the request)
- Overload behavior: the “nice” thing is fast responses, the correct thing is predictable failure (bounded queue + explicit reject).
- Deployment was mostly about safety checks and boring edges (checksum, restart, timeouts). That stuff is what breaks in real life.
- Telemetry design: if you store everything you drown; if you store nothing you can’t debug. Keeping a small schema helped a lot.
- Rollback logic is subtle because “numbers got worse” doesn’t always mean “bad model”, so I kept the trigger simple and obvious.
Solo project built by Zane Hensley