Behavision: face recognition for retail, edge to head office
Five components that ship as one product:
- behavision/ the recognition engine. RTSP ingest, YuNet detection, IoU
tracking, ArcFace embeddings, a FAISS/SQLite gallery, and a
FastAPI dashboard. Identity is decided once per TRACK from an
average of at least three embeddings, never per frame.
- agent/ the Go edge agent: supervises the engine, holds a durable
spool, and drains it to MQTT. Nothing is acked before the
broker confirms.
- desktop/ the shop PC application (Wails + React + tray).
- server/ the cloud API, MQTT consumer, reports and assistant.
- web/ platform.loyaly.ai, the head-office app, embedded in the
server binary.
The gallery stores 512-float embeddings and timestamps - no images unless
`app.store_faces` is switched on. Those embeddings are biometric personal
data under GDPR and India's DPDP: template inversion reconstructs a
recognisable face from an ArcFace vector, so data/behavision.db is treated
as a biometric database and DELETE /api/visitors/{id} is a real erasure.
CLAUDE.md carries the reasoning behind every non-obvious decision here,
including the ones that were measured and the ones that were wrong first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HViLj9gYNRtSr7YVZmW5sn
This commit is contained in:
162
installer/behavision.iss
Normal file
162
installer/behavision.iss
Normal file
@@ -0,0 +1,162 @@
|
||||
; Behavision installer (Inno Setup 6).
|
||||
;
|
||||
; Built by installer\build.ps1, which stages dist\Behavision first. Compile it
|
||||
; by hand with:
|
||||
; ISCC.exe /DMyAppVersion=0.1.0 installer\behavision.iss
|
||||
;
|
||||
; Three decisions worth reading before changing anything here:
|
||||
;
|
||||
; 1. ADMIN AT INSTALL TIME, NEVER AT RUN TIME. The files go under Program
|
||||
; Files, so installing needs elevation. Running does not: the app is a
|
||||
; normal user-session process that spawns the engine as a child, which is
|
||||
; also why it can draw a tray icon at all - a Windows service runs in
|
||||
; session 0 and cannot.
|
||||
;
|
||||
; 2. NO MODELS IN THE PACKAGE. They are ~200 MB and `setup-models` downloads
|
||||
; them resumably into the state root on first run. Bundling them would
|
||||
; quadruple this file and force a re-sign for a model change.
|
||||
;
|
||||
; 3. NOTHING WRITABLE UNDER Program Files. The database, logs, camera list,
|
||||
; downloaded models and agent config all live in %PROGRAMDATA%\Behavision,
|
||||
; which is what makes an upgrade a file copy rather than a migration. That
|
||||
; split is behavision/paths.py and agent/pkg/paths, and this file must not
|
||||
; contradict it.
|
||||
|
||||
#define MyAppName "Behavision"
|
||||
#ifndef MyAppVersion
|
||||
#define MyAppVersion "0.1.0"
|
||||
#endif
|
||||
#define MyAppPublisher "Loyaly"
|
||||
#define MyAppURL "https://platform.loyaly.ai"
|
||||
#define MyAppExeName "Behavision.exe"
|
||||
|
||||
[Setup]
|
||||
AppId={{7C4B9E2A-3F51-4C86-9D0A-B1E7A2F65D11}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=..\dist
|
||||
OutputBaseFilename=Behavision-Setup-{#MyAppVersion}
|
||||
Compression=lzma2/max
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
; Admin, because Program Files is. See decision 1 above.
|
||||
PrivilegesRequired=admin
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
; The engine folder alone is ~400 MB unpacked; saying so up front beats a
|
||||
; wizard that stops halfway on a small shop PC.
|
||||
ExtraDiskSpaceRequired=450000000
|
||||
; Ask Windows' Restart Manager to close a running copy instead of writing DLLs
|
||||
; underneath it. The engine holds the SQLite WAL and the camera, so a half
|
||||
; replaced install fails on the NEXT start - long after anyone would connect
|
||||
; the two events. RestartApplications=no because the [Run] section starts the
|
||||
; app again itself, and twice is one process too many for one webcam.
|
||||
CloseApplications=yes
|
||||
RestartApplications=no
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Create a &desktop shortcut"; GroupDescription: "Shortcuts:"
|
||||
; Ticked by default: the product is meaningless if it is not watching. A shop
|
||||
; PC reboots after a power cut at 3am with nobody there to open anything.
|
||||
Name: "startup"; Description: "Start Behavision when this PC starts"; GroupDescription: "Startup:"
|
||||
|
||||
[Files]
|
||||
Source: "..\dist\Behavision\Behavision.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\dist\Behavision\behavision-agent.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\dist\Behavision\engine\*"; DestDir: "{app}\engine"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; ~2 MB bootstrapper, downloaded at build time by build.ps1. The app is a
|
||||
; WebView2 window; without the runtime it opens BLANK - not an error, just an
|
||||
; empty white rectangle, which is the single worst failure mode to hand a shop.
|
||||
; Present on Windows 11 and recent Windows 10, absent on plenty of older
|
||||
; machines, and a shop PC is exactly where an older machine lives.
|
||||
Source: "vendor\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}"; \
|
||||
Flags: deleteafterinstall; Check: WebView2Missing
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
; Per-user, not HKLM\Run: the app draws a tray icon and a window, so it has to
|
||||
; start in an interactive session. A machine-wide entry would try before anyone
|
||||
; has logged in.
|
||||
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startup
|
||||
; Named in the installer's own text when somebody skips the download, so it has
|
||||
; to exist. Console window on purpose: it is a 200 MB download with a progress
|
||||
; line, and a silent one looks like nothing happened.
|
||||
Name: "{group}\Download models"; Filename: "{app}\engine\behavision.exe"; \
|
||||
Parameters: "setup-models"; Comment: "Download the face recognition models"
|
||||
Name: "{group}\Where is my data"; Filename: "{app}\engine\behavision.exe"; \
|
||||
Parameters: "paths"; Comment: "Print the installed layout"
|
||||
|
||||
[Dirs]
|
||||
; Created here so a first run does not have to, and ACLed to Administrators
|
||||
; plus the installing user rather than Everyone: it holds face templates, which
|
||||
; are biometric personal data, and the engine's generated API password.
|
||||
Name: "{commonappdata}\Behavision"; Permissions: admins-full users-modify
|
||||
|
||||
[Run]
|
||||
Filename: "{tmp}\MicrosoftEdgeWebview2Setup.exe"; Parameters: "/silent /install"; \
|
||||
StatusMsg: "Installing Microsoft Edge WebView2 (required to show the app)..."; \
|
||||
Flags: waituntilterminated; Check: WebView2Missing
|
||||
|
||||
; ~200 MB over the network, so it is offered rather than forced, and it is
|
||||
; resumable: a killed download leaves a .part file and the next run continues.
|
||||
Filename: "{app}\engine\behavision.exe"; Parameters: "setup-models"; \
|
||||
StatusMsg: "Downloading face recognition models (about 200 MB)..."; \
|
||||
Flags: runhidden waituntilterminated; Check: WantModels
|
||||
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "Start {#MyAppName} now"; \
|
||||
Flags: nowait postinstall skipifsilent
|
||||
|
||||
[UninstallDelete]
|
||||
; The unpacked engine writes nothing here, but PyInstaller leaves stray
|
||||
; __pycache__ directories that would keep {app} alive after an uninstall.
|
||||
Type: filesandordirs; Name: "{app}\engine"
|
||||
|
||||
[Code]
|
||||
var
|
||||
ModelsPage: TInputOptionWizardPage;
|
||||
|
||||
procedure InitializeWizard;
|
||||
begin
|
||||
ModelsPage := CreateInputOptionPage(wpSelectTasks,
|
||||
'Face recognition models',
|
||||
'Behavision needs about 200 MB of model files to recognise faces.',
|
||||
'These are downloaded once. If this PC has no internet connection now, ' +
|
||||
'skip this and run "Download models" from the Start menu later - the app ' +
|
||||
'will not recognise anyone until they are present.',
|
||||
True, False);
|
||||
ModelsPage.Add('Download the models now (recommended)');
|
||||
ModelsPage.Values[0] := True;
|
||||
end;
|
||||
|
||||
function WantModels: Boolean;
|
||||
begin
|
||||
Result := ModelsPage.Values[0];
|
||||
end;
|
||||
|
||||
// The WebView2 runtime registers itself under EdgeUpdate with a non-empty
|
||||
// version. Checked in three places because the runtime can be installed
|
||||
// per-machine (both registry views on 64-bit) or per-user.
|
||||
function WebView2Missing: Boolean;
|
||||
const
|
||||
CLIENT = '{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
|
||||
var
|
||||
pv: string;
|
||||
begin
|
||||
Result := True;
|
||||
if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then
|
||||
Result := False
|
||||
else if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then
|
||||
Result := False
|
||||
else if RegQueryStringValue(HKCU, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then
|
||||
Result := False;
|
||||
end;
|
||||
Reference in New Issue
Block a user