ADR-005: Decouple React Presentation from FastAPI Processing
ID: ADR-005 Status: Accepted Date: 2026-07-09
Context
The previous Streamlit UI reran server-side presentation code during frame processing and coupled page state to inference. FaceGuard needs a responsive SPA, an explicit authenticated API, and the ability to use either a client-attached camera or a camera attached to a remote Raspberry Pi.
Decision
Use a React/Vite SPA for presentation and FastAPI for REST, WebSocket, recognition, persistence, and GPIO orchestration.
- REST requests carry a Bearer JWT.
- Recognition/enrollment WebSockets carry
faceguard.jwtplusbearer.<JWT>subprotocols; tokens are not URL query parameters. - In
browsermode the SPA captures JPEGs and permits only one outstanding frame. - In
backendmode FastAPI owns the Pi V4L2 camera and returns the processed JPEG. - Both modes return bbox, dimensions, and sequence for the exact processed frame.
- Service URL and camera mode
VITE_*settings are compiled at frontend build time.
Considered alternatives
- Continue with Streamlit: rejected because it couples UI reruns and inference state.
- Browser-only capture: rejected because a remote administration browser cannot access the camera physically attached to the Pi.
- Put JWTs in WebSocket query strings: rejected because URLs are commonly logged.
- A single HTTP request per frame: rejected due to repeated request setup and poorer stream lifecycle semantics.
Consequences
- UI, data APIs, and ML logic have clear boundaries and independent tests.
- Two technology stacks and compatible runtime URL configuration must be maintained.
- A reverse proxy must forward WebSocket upgrades and subprotocol headers.
- This decision removes the old UI bottleneck but does not itself prove the real-hardware 3-second QR-001 target; no unsupported 1.1-second claim is made.