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
163 lines
7.2 KiB
Plaintext
163 lines
7.2 KiB
Plaintext
; 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;
|