The app on a laptop reported an engine that was never meant to be there
Signing in on a second Mac showed "engine not reachable at http://127.0.0.1:8010" and 0 of 0 cameras, on an account whose shops were running and recognising people the whole time. Nothing was broken: Live() and Cameras() read only the engine on loopback, so the app answered as though the person had never signed in - and camera sync goes through the engine, which is why the count was zero rather than stale. Having no engine is a normal state. A shop PC watches cameras; an owner's laptop, a manager's machine and a second till being set up do not, and all three are signed in to the same estate. Both methods now fall back to head office when loopback fails and somebody is signed in. Loopback is still tried first: a real shop PC must never be shown a minute-old summary when the engine two milliseconds away has the live one. Decisions worth keeping: - Viewing is on the snapshot, not inferred per screen. Three surfaces read it, and a screen that computed it separately is how the shops screen once came out labelled Working, in green, above "2 of 3 cameras not connecting". - fraction_below_gate takes the WORST shop, never an average. 0.10 against 0.73 averages to 0.42 and hides the only shop anyone needs to visit. - A remote camera is flagged, and Edit, Remove and Check placement are withheld. They talk to a camera on a LAN this computer cannot reach, and a button that cannot work is worse than one that is absent. - connected is three states. null is "no shop computer has reported yet" and reads as waiting; false is "Not connecting". A bare false sends somebody to check cabling on a camera nobody has tried to reach. - Snapshots are fetched in Go as data: URIs and cached by snapshot_at. A webview <img> resolves a relative src against wails:// and cannot send the bearer - the problem VisitorImage already solved - and this screen polls every 8 seconds at ~90 KB a camera. - With no engine AND no session, the engine error is still the answer. The person is most likely setting this PC up. The picture is the last snapshot and the banner says so: there is no live video from here, because the engine's MJPEG stream is on the shop PC's loopback behind a router with no inbound route. The LiveHub relay head office uses is the answer to that and is a further step for this client. Verified against production: five arrivals and two cameras parsed from the real API. viewing_test.go covers the fallback, the worst-shop rule, the withheld credentials and that an unchanged snapshot is fetched once across two polls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
52
CLAUDE.md
52
CLAUDE.md
@@ -3307,3 +3307,55 @@ Against real Postgres, on the demo tenant:
|
||||
- HTML, PDF, GIF and empty bodies are all refused as face images: the check is
|
||||
on the magic bytes, never the `Content-Type` header, because this endpoint
|
||||
stores what it is handed and serves it back to a browser.
|
||||
|
||||
## Viewer mode: the app on a computer that is not watching anything
|
||||
|
||||
Signing in on a second Mac showed `engine not reachable at
|
||||
http://127.0.0.1:8010` and **0 of 0 cameras**, on an account whose shops were
|
||||
running and recognising people the whole time. Nothing was broken. `App.Live()`
|
||||
and `App.Cameras()` read **only** `a.local`, so the app answered as though the
|
||||
person had never signed in — and camera sync goes *through* the engine, which
|
||||
is why the count was zero rather than merely stale.
|
||||
|
||||
That is the wrong model of what this application is. A shop PC watches
|
||||
cameras; an owner's laptop, a manager's machine, a second till being set up do
|
||||
not, and all three are signed in to the same estate. **Having no engine is a
|
||||
normal state, not a failure**, and the app now says what it can see from where
|
||||
it is standing instead of reporting the absence of something it does not need.
|
||||
|
||||
Both methods try loopback first and fall back to head office when it fails and
|
||||
somebody is signed in. The order matters: a real shop PC must never be shown
|
||||
head office's minute-old summary when the engine two milliseconds away has the
|
||||
live one.
|
||||
|
||||
- **`Viewing` is on the snapshot, not inferred in the browser.** Three surfaces
|
||||
read it — the banner, the camera tally, the getting-started panel — and a
|
||||
screen that computed it separately is how the shops screen once came out
|
||||
labelled **Working**, in green, directly above *"2 of 3 cameras not
|
||||
connecting"*. One fact, one place, the same rule as the tray being a client
|
||||
of `EngineStatus()`.
|
||||
- **`fraction_below_gate` is the WORST shop, never an average.** 0.10 against
|
||||
0.73 averages to 0.42 and hides the only shop anyone needs to visit. Same
|
||||
rule the heartbeat already follows with `worst_site`.
|
||||
- **A remote camera is flagged `remote: true`, and the screen withholds Edit,
|
||||
Remove and Check placement.** Those talk to a camera on a LAN this computer
|
||||
cannot reach, and an Edit button that cannot work is worse than one that is
|
||||
absent. The tenant response structurally cannot carry `host`, `username` or
|
||||
`has_password`, so nothing here can invent them either — a test asserts that.
|
||||
- **`connected` is three states.** `null` is "no shop computer has reported on
|
||||
this yet" and reads as *waiting*; `false` is *"Not connecting"*. A bare false
|
||||
sends somebody to check cabling on a camera nobody has tried to reach.
|
||||
- **The picture is the last snapshot, and it says so.** There is no live video
|
||||
here: the engine's MJPEG stream is on the shop PC's loopback behind a router
|
||||
with no inbound route. Head office's `LiveHub` relay is the answer to that
|
||||
and is a further step for this client; the banner does not imply otherwise.
|
||||
- **Snapshots are fetched in Go and passed as `data:` URIs, cached by
|
||||
`snapshot_at`.** A webview `<img>` resolves a relative src against `wails://`
|
||||
and cannot send the session's bearer — the same problem `VisitorImage`
|
||||
already solved — and this screen polls every 8 seconds at ~90 KB a camera, so
|
||||
re-fetching an unchanged frame is megabytes an hour to redraw the same
|
||||
picture. Keyed on the server's `snapshot_at`, because a new timestamp is the
|
||||
only thing that means a new photograph.
|
||||
- **With no engine AND nobody signed in, the engine error is still the answer.**
|
||||
There is nothing else to show and the person is most likely setting this PC
|
||||
up; naming head office there points them at a step they have not reached.
|
||||
|
||||
Reference in New Issue
Block a user