Files
Behavision/agent
Suriyakumarvijayanayagam 8c88aad06e Stopping the engine on Windows stops the whole engine
The installer runs the engine as <venv>\Scripts\python.exe, and since
Python 3.7.2 that file is a redirector that spawns the real interpreter
as a child. Stop() terminated the redirector and left the interpreter -
the process holding the cameras and the SQLite WAL - running with no
parent and nothing able to stop it. Seen on a Windows install: Quit from
the tray, and recognition still running.

The child is now started suspended, placed in a job object with
KILL_ON_JOB_CLOSE, and resumed. Cancel terminates the job, so the whole
tree goes; and the job dies with this process, so it goes even if the app
crashes. CREATE_NO_WINDOW while here: python.exe is a console program
and a GUI parent otherwise opens a black console on the shop counter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
2026-09-18 11:06:49 +05:30
..

Behavision agent (Go)

The half of the edge install that touches the network. The Python engine keeps the cameras and the models; this keeps the tray icon, the UI shell, the MQTT connection and the offline queue.

┌─ agent (Go) ───────────────────┐        ┌─ engine (Python) ────────┐
│ tray icon + WebView2 window    │        │ RTSP capture             │
│ supervises the engine process  │───────▶│ YuNet / ArcFace / FAISS  │
│ MQTT publish + offline spool   │◀───────│ SQLite (biometric)       │
│ S3 handoff, tenant config      │  local │ localhost API + events   │
└────────────────────────────────┘  HTTP  └──────────────────────────┘

Why the split

Go cannot run ONNX, OpenCV or FAISS, so the engine stays Python and ships frozen. Go is here for what it is actually better at: a durable queue that survives a store's internet dropping, a supervised child process, and one language shared with the server so the MQTT contract has a single definition.

Why not a Windows service

A service runs in session 0 and cannot draw a tray icon — that is Windows session isolation, not a library limitation. Since the product is "user starts and stops it from the tray", the agent is a normal user-session process that spawns the engine as a child. That also means it never needs elevation at runtime: starting a child process does not, controlling a service does.

internal/engine is written so a service wrapper can be added later without touching the supervision logic.

Layout

main.go              entry point, mode dispatch
internal/engine      start/stop/supervise the Python engine, health polling
internal/spool       durable event queue (survives restart and outage)
internal/mqtt        broker client, publishes from the spool
internal/config      tenant identity, broker settings, credentials
frontend/            React UI served into the WebView

Build

go build ./...            # agent alone
wails build               # agent + frontend, once the UI is added