Continuous, passive identity verification by how you type and move the mouse — not by a password.
SENTRY runs silently in the background. Every 10 seconds it evaluates a 12-dimensional behavioural fingerprint against a per-user Isolation Forest model. If the behaviour stops matching the enrolled user, the workstation locks automatically.
| Login | Data collection |
|---|---|
![]() |
![]() |
| Locked out | Dashboard |
|---|---|
![]() |
![]() |
A background thread (InputCapture, powered by pynput) hooks four event streams simultaneously:
| Stream | Events captured |
|---|---|
| Keyboard | key_press (down), key_release (up) — key identity + timestamp |
| Mouse move | on_move — (x, y, timestamp) |
| Mouse click | on_click — (x, y, button, pressed, timestamp) |
| Scroll wheel | on_scroll — (x, y, dx, dy, timestamp) |
Events are collected into a thread-safe ring buffer and flushed every 10-second micro-session into the cognitive mapping pipeline.
Raw events are converted into a 12-feature behavioural vector:
| Code | Feature | What it measures |
|---|---|---|
SE |
Saccade Entropy | Randomness of cursor scanning directions (Shannon entropy over 8 directional bins) |
KDV |
Key Dwell Variability | Std-dev of key hold durations |
DFT |
Digram Flight Time | Std-dev of key-release to key-press gaps across all keystrokes |
HHE |
Hick-Hyman Entropy Slope | How movement time scales with click-distance complexity (regression slope) |
PAR |
Pause-to-Action Ratio | Fraction of session time spent in gaps greater than 500 ms |
MJA |
Mouse Jerk/Acceleration | Mean derivative of cursor velocity (abrupt stop control, micro-tremor) |
SWV |
Scroll Wheel Variance | Std-dev of inter-scroll gaps × 100 (reading rhythm) |
ECR |
Error Correction Rate | Backspace+Delete events divided by max(total_keystrokes, 40) |
ARM |
Adaptation Reaction Margin | Latency to correct toward a displaced UI element (perturbation probe) |
MRC |
Motor Recovery Curve | Velocity normalisation slope after first contact with displaced target |
HRR |
Habituation Response Rate | Rate at which ARM shrinks across repeated probes (habituation) |
SOR |
Spatial Overshoot Ratio | Max overshoot divided by displacement when clicking a displaced target |
ARM / MRC / HRR / SOR are computed from periodic perturbation probes injected by the Electron frontend and smoothed with EMA (alpha = 0.3 fast update, alpha = 0.05 slow decay between probes).
A decoupled Isolation Forest ensemble is trained per user on StandardScaler-normalised features:
| Sub-model | Features | Contamination |
|---|---|---|
combined |
All 12 features | Dynamic (see below) |
kb |
KDV, DFT | 0.07 |
mouse |
SE, HHE, MJA | 0.05 |
bs |
ECR | 0.05 |
adapt |
ARM, MRC, HRR, SOR | 0.08 |
| per-metric (x12) | One feature each | Dynamic |
Hyperparameters common to all sub-models:
n_estimators = 100
random_state = 42
max_samples = "auto" (scikit-learn default — min(256, n_samples))
max_features = 1.0 (scikit-learn default)
Dynamic contamination (applied to combined and all per-metric models based on training-set size):
| Training sessions | contamination |
|---|---|
| fewer than 50 | 0.15 |
| 50 to 99 | 0.10 |
| 100 to 199 | 0.07 |
| 200 or more | 0.05 |
Scoring pipeline: each active sub-model emits a raw decision-function score; per-channel sensitivity sliders (mouse / typing / scroll / backspace, range 1.0–N) shift and amplify the score. The worst (most anomalous) score across all active sub-models drives the state machine. Per-metric scores are smoothed with EMA (alpha = 0.3).
| State | Enters when | Exits when |
|---|---|---|
AUTH_OK |
Default after model is trained | 1 ANOMALOUS session → LOCK; 1 SUSPICIOUS session → RISK |
RISK |
vote_count between 1 and lock_threshold | 3 consecutive SUSPICIOUS sessions → LOCK; 2 consecutive CLEAN sessions → AUTH_OK |
LOCK |
vote_count >= lock_threshold OR 3 consecutive SUSPICIOUS sessions | 2 consecutive CLEAN sessions OR manual unlock |
vote_count: number of per-metric Isolation Forests that vote anomalous for the current micro-session.
lock_threshold: default 3, configurable 1–8 via the dashboard sensitivity panel.
On entering LOCK, the Python backend calls ctypes.windll.user32.LockWorkStation() to lock the Windows session instantly.
- A minimum of 50 quality sessions (each 10-second micro-window with at least 3 non-zero features, de-duplicated by feature hash) must be collected before an Isolation Forest can be trained.
- Raw telemetry is persisted to
~/Documents/input data/<user>_<timestamp>.jsonso the model can always be retrained from scratch. - Offline replay:
python main.py --user <name> --replay <path>imports a saved telemetry JSON and retrains without live capture.
The desktop shell (frontend/) communicates with the Python process over newline-delimited stdio JSON:
| Screen | Purpose |
|---|---|
| Login | User selection and account creation (SQLite, bcrypt-hashed passwords) |
| Data collection | Enrolment UI with real-time session progress; injects perturbation probes (displaced click targets) to capture ARM/MRC/HRR/SOR adaptation metrics |
| Dashboard | Live 12-metric radar chart, Z-score sparkline, per-channel sensitivity sliders, auth-state indicator, session history |
| Lock screen | Shown when SENTRY raises an anomaly; requires password re-authentication to unlock |
daemon.py → InputCapture: background keystroke / mouse / scroll capture (pynput)
cognitive_mapper.py → raw events → 12D behavioural feature vectors
ml_backend.py → per-user decoupled Isolation Forest ensemble (scikit-learn)
auth_orchestrator.py → enrol / verify / lock finite state machine
main.py → stdio JSON bridge (Electron IPC ↔ Python backend)
frontend/ → Electron 40 + React 19 + Vite 7 desktop app
| Layer | Technology |
|---|---|
| Behavioural capture | Python · pynput · psutil |
| Feature engineering | NumPy · SciPy (entropy, linregress) |
| Anomaly detection | scikit-learn (IsolationForest, StandardScaler) |
| Data persistence | JSON (raw telemetry) · pickle (trained models) · SQLite (user accounts) |
| Desktop shell | Electron 40 · React 19 · Vite 7 · Recharts |
| IPC | stdio newline-delimited JSON |
pip install -r requirements.txt
python main.py --user your_usernameFirst run with no existing model enters unenrolled mode; the Electron frontend guides you through the data collection and training flow. Once 50+ quality sessions are captured, the Isolation Forest is trained and live verification begins.
# Offline replay from a previously saved telemetry file
python main.py --user your_username --replay path/to/telemetry.jsoncd frontend
npm install
npm run dev # starts Vite dev server + Electron concurrentlySENTRY was built as a final-year academic research project exploring passive continuous authentication through behavioural biometrics — as a complementary layer alongside (not a replacement for) traditional credentials. It is a research and demonstration system and is not hardened for production deployment.



