Skip to content

headlessripper/SentinelGuard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SentinelGuard

A Windows idle-guard / lock overlay built with PySide6. When the machine has been idle past a configurable timeout, SentinelGuard fades a fullscreen, always-on-top, frameless overlay across every attached monitor and requires re-authentication with a TOTP 2FA code, a master password, or (optionally) facial recognition before it disappears.

Alongside the auth panel it renders two informational "islands": a live weather widget, served by an embedded FastAPI server and displayed in a QWebEngineView, and a storage dashboard with animated usage gauges and a recycle-bin cleaner.

Part of the ZashironSentinel project.


Features

Authentication

  • First-run registration — name, email (regex-validated), date of birth, master password, and idle timeout.
  • Live password strength checklist — 8+ characters, 1 uppercase, 1 digit, 1 symbol, updated as you type.
  • TOTP 2FA via pyotp, with a QR code for Google Authenticator / Authy plus the raw base32 secret.
  • Recovery code — 25 characters from an unambiguous alphabet (no 0/O, 1/I/L), ~129 bits of entropy, grouped for transcription. Accepted with or without dashes and in any case.
  • Master-password sign-in as a fallback when the authenticator is unavailable.
  • Settings panel, gated behind the master password, for the idle timeout, password rotation, and face enrolment.
  • Brute-force throttling — 4 free attempts, then an exponential lockout (15s, 30s, 60s … capped at 15 minutes) with a live countdown. The counter is persisted, so restarting the app does not clear it.
  • Reveal toggles on every password field, and inline error messages rather than modal dialogs.

Credential storage

This is the part most worth knowing about, and it changed substantially:

Secret At rest Why
Master password PBKDF2-HMAC-SHA256, 600 000 iterations, 16-byte random salt Never needs reading back; compared with hmac.compare_digest
Recovery code Same, over the normalised code As above
TOTP secret Sealed with the Windows Data Protection API (DPAPI), bound to the logged-in user Must be reversible to generate codes, so hashing is not an option

DPAPI ciphertext is scoped to the Windows account and to this application via an entropy value, so another program running as the same user cannot unseal it, and a copied registry hive is useless on another machine. None of this requires a third-party crypto dependency.

Installs created by earlier versions are migrated automatically on first launch: plaintext values are hashed or sealed, then the plaintext keys are removed. Migration is idempotent and only deletes the old keys once the replacements are safely written. The work factor is also re-checked at each successful sign-in, so raising _ITERATIONS later transparently upgrades existing hashes.

Idle / lock behaviour

  • Idle time from the Win32 GetLastInputInfo / GetTickCount APIs, with correct handling of the 32-bit ~49.7-day counter rollover.
  • Audio-aware — if any process has an active audio render session (via pycaw), the machine counts as in use and the guard will not trigger. Watching a video does not lock the screen.
  • Spans all monitors using the virtual desktop geometry, rather than only the primary screen.
  • Click-through togglingWS_EX_TRANSPARENT is set while hidden so input reaches the desktop, and cleared when the guard appears.
  • Always returns to the sign-in prompt when raised; it never resumes on a settings page.
  • closeEvent and Esc are swallowed — the guard is dismissed by authenticating.
  • Single-instance enforcement via a named mutex, so two copies cannot fight over the fullscreen window.

Weather island

  • Local FastAPI + uvicorn server on a background daemon thread, bound to 127.0.0.1 on an automatically chosen free port.
  • GET /api/weather — geolocates by IP (ipinfo.io, with a fallback location), then queries OpenWeatherMap's current and 5-day forecast endpoints and returns a normalised payload.
  • Two-layer caching — weather for 10 minutes, geolocation for 6 hours, both behind a lock. A separate last-known-good payload is replayed with stale: true if upstream fails.
  • GET /weather — a self-contained animated card with day/night gradients, emoji conditions, and a live clock. Served same-origin, so no CORS middleware is needed.
  • GET /healthz — liveness and configuration check.

Storage island

  • Exact drive totals from psutil, plus a measured recycle-bin size.
  • Two scan modes. A quick scan gives instant totals with an estimated User/Apps/System split; pressing Analyze runs a real background walk of Users, Program Files*, ProgramData and Windows to measure the split, deriving "Other" by subtraction. The UI states which is on screen, and a completed measurement is never overwritten by a later estimate.
  • The analyze pass skips junctions and symlinks, so the WinSxS hardlink farm and C:\Users\All Users are not counted repeatedly. It is cancellable and pauses the auto-refresh while running.
  • Drive metadata (label, model, media type) via wmi; the import is optional and degrades to generic labels.
  • Empty recycle bin via SHEmptyRecycleBinW, scoped to the configured drive, behind a confirmation dialog that names the size being deleted.

Project structure

SentinelGuard/
├── SentinelGuard.py              # Entry point: SentinelWall main window and auth panels
├── requirements.txt              # Core runtime dependencies
├── requirements-face.txt         # Optional facial-recognition extra
├── .env.example                  # Template for OWM_API_KEY
└── Guard/
    ├── Head.py                   # Shared imports, resource/idle/audio helpers, face workers
    ├── SentinelCrypto.py         # PBKDF2 hashing + Windows DPAPI sealing
    ├── SentinelVault.py          # Persisted state, schema migration, lockout tracking
    ├── SentinelWeather.py        # FastAPI app, OpenWeatherMap client, weather HTML
    ├── SentinelStorage.py        # StoragePage widget, gauges, scan/clean workers
    ├── SentinelFacialRec.py      # ArcFace ONNX + MediaPipe face enrol/auth (optional)
    ├── port_handler.py           # Module-level PORT constant
    ├── find_free_port.py         # Free-port helper
    ├── Style_Qss/
    │   ├── qss.py                # Qt stylesheet for the storage page
    │   └── AnimatedDot.py        # Animated indicator widget
    ├── Build/
    │   └── SentinelGuard_Build.json  # auto-py-to-exe / PyInstaller configuration
    └── background/               # Icons and assets

Requirements

  • Windows only. Depends on ctypes.windll (user32, kernel32, crypt32, shell32), pycaw, and optionally wmi. main() exits early with a clear message on other platforms.
  • Python 3.10+ (3.11 recommended; verified on 3.11.0 with PySide6 6.11).
python -m pip install -r requirements.txt

Running

From the repository root, so the Guard package resolves:

python SentinelGuard.py

Add --verbose for debug logging. Logs go to stderr and to %LOCALAPPDATA%\SentinelGuard\sentinelguard.log.

On first launch you are taken through profile creation → authenticator linking → the 2FA prompt. Save the recovery code — it is shown once, and without it (or the master password) a reset means clearing the registry keys by hand.

Configuration

Setting How to set it
OWM_API_KEY Environment variable, or copy .env.example to .env. Without it the weather card shows a configuration message and everything else works normally.
Idle timeout Set at registration; changeable in More options → Settings (1–1440 minutes).
Master password Set at registration; rotatable in the settings panel.

State is persisted through QSettings("ZashironSentinel", "SentinelGuard") — on Windows, HKEY_CURRENT_USER\Software\ZashironSentinel\SentinelGuard.

Getting a key: OpenWeatherMap's free tier is sufficient. Sign up at https://openweathermap.org/api. Note that a new key can take a couple of hours to activate.


Building a Windows executable

Guard/Build/SentinelGuard_Build.json is an auto-py-to-exe configuration: one-file, windowed, custom icon, with Guard/background bundled as data. Paths are now repository-relative, so load it and build from the repository root.

The equivalent PyInstaller invocation:

pyinstaller --noconfirm --onefile --windowed --name SentinelGuard --icon "Guard/background/Icon/Icon-100.ico" --add-data "Guard/background;Guard/background/" SentinelGuard.py

Resource lookup understands sys._MEIPASS, so bundled icons resolve correctly from a one-file build.


Facial recognition (optional)

Guard/SentinelFacialRec.py implements headless face enrolment and authentication using MediaPipe Face Landmarker for detection and an ArcFace ONNX model for 512-d embeddings, compared by cosine similarity. It also has a CLI:

python Guard/SentinelFacialRec.py enroll

The feature is wired into the UI and detects its own availability at runtime: facial_recognition_status() checks for the optional packages and both model files, and the "Sign in with Face" option is hidden when they are absent. Enrolment and authentication run on a QThread so the GUI never blocks.

To enable it:

python -m pip install -r requirements-face.txt

then supply the two models, which are not distributed here:

File Source
Guard/Face_Model/face_landmarker.task MediaPipe Face Landmarker model bundle
Guard/model/arcface.onnx Any ArcFace ONNX export with a 112×112 input, e.g. from insightface

Enable it in Settings, capture a face, and the option appears under More options. The embedding is written to %LOCALAPPDATA%\SentinelGuard\ rather than next to the script, so it survives a one-file build.

Consider face sign-in a convenience, not a security boundary. The default similarity threshold is low (FACE_SIM_THRESHOLD = 0.09) and single-frame 2-D recognition is spoofable with a photograph. TOTP remains the strong factor.


What changed in this revision

Fixes to real defects found in the previous version:

  • TOTP sign-in was broken. The shared success path called self.mp_login_input.clear() unconditionally, but that widget is None unless you signed in by master password, so every successful 2FA login raised AttributeError inside the slot.
  • Emptying the recycle bin never asked. _confirm_and_clean went straight to deletion despite its name, and passed None as the drive root — which empties the recycle bin on every drive, not just C:.
  • Four methods were defined twice (update_last_input, check_idle, animate_hide, animate_show); the earlier wall-clock implementations were silently dead.
  • NVMe SSDs were reported as HDDs. MSFT_PhysicalDisk returns numeric MediaType/BusType codes, so substring-matching them against "ssd"/"nvme" could never match, and the query did not filter to the disk backing the volume.
  • The password checklist shipped mojibake (✗) from a mismatched encoding. Now written as \uXXXX escapes so the source is pure ASCII and cannot regress.
  • The face module could never importHead.py referenced Guard.SentinelFacialRec while the file lived in Guard/Style_Qss/.
  • The idle calculation could go negative across the 32-bit tick rollover.
  • The face embedding was written next to the script, which is a read-only temp directory under a one-file build.

Security and robustness:

  • Credentials are hashed/sealed as described above, with automatic migration.
  • The OpenWeatherMap key is no longer committed; it comes from the environment.
  • Added brute-force lockout, single-instance enforcement, multi-monitor coverage, and Esc suppression.
  • The storage breakdown is measured rather than fabricated, and the UI says which mode it is showing.
  • Weather requests are cached, pooled, and lock-guarded; CORS allow_origins=["*"] was dropped in favour of same-origin fetches.

Known limitations

  • Face sign-in is a convenience factor, not a strong one — see the note above.
  • QSettings on Windows means the registry; DPAPI protects the TOTP secret's confidentiality, but a process running as you can still call the same API. This defends against offline/registry-copy attacks, not against malware already running in your session.
  • The storage page analyses one drive at a time. The drive is persisted (storage_drive) but there is no UI to change it yet.
  • DeepCleanWorker (junk/duplicate detection) is implemented and cancellable but still not surfaced in the UI.
  • There are no automated tests. The modules were verified by hand this revision: DPAPI round-trip, migration from a simulated v1 install, directory measurement against an independent os.walk, and a full panel-navigation and auth-flow sweep.

About

SentinelGuard A Windows idle-guard lock overlay built with PySide6. When the machine has been idle past a configurable timeout, SentinelGuard fades a fullscreen, always-on-top, frameless overlay across every attached monitor and requires re-authentication with a TOTP 2FA code, a master password

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages