Adds the PyQt6 Windows client (Homepage, Scan & results, About & updates, Settings), ClamAV scanning and quarantine, browser-download watch with an exclusive scan gate, Bolt loopback/PKCE sign-in with DPAPI key storage, Gitea release feed with checksum verification, opt-in diagnostics, and Windows/portable build tooling. Co-Authored-By: Claude Code <noreply@anthropic.com>
108 lines
5.0 KiB
Markdown
108 lines
5.0 KiB
Markdown
# BoltClam architecture
|
|
|
|
## Process layout
|
|
|
|
BoltClam is one PyQt6 process on the user's Windows desktop. It starts no
|
|
Windows service and installs no driver. ClamAV remains an external program.
|
|
|
|
```
|
|
BoltClam.exe (PyInstaller or portable launcher)
|
|
└─ boltclam.app
|
|
├─ MainWindow ── HomePage / ScanPage / AboutPage / SettingsPage
|
|
├─ RealtimeProtection ── DownloadWatcher + QThreadPool scan tasks
|
|
├─ ScanWorker / DefinitionWorker (QThread)
|
|
├─ Storage (SQLite, WAL)
|
|
├─ QuarantineService
|
|
├─ ClamAVService → clamscan.exe / freshclam.exe
|
|
└─ ApiClient → https://api.themajesticnetwork.com
|
|
```
|
|
|
|
Settings are JSON under `%APPDATA%\BoltClam`; history and quarantine live
|
|
under `%LOCALAPPDATA%\BoltClam`. `BOLTCLAM_CONFIG_DIR` and
|
|
`BOLTCLAM_DATA_DIR` override both for tests and portable/development use.
|
|
|
|
## Page responsibilities
|
|
|
|
- **HomePage** calls `system_info.collect()` on a worker. PowerShell supplies
|
|
Defender/firewall/network facts on Windows; missing data is "not reported".
|
|
The shell gauge and concern list are derived from ClamAV state, definition
|
|
age, real-time state, firewall, last scan, active detections and quarantine
|
|
count. It never labels an unknown state as protected.
|
|
- **ScanPage** persists scope/auto-response preferences immediately, runs
|
|
`ScanWorker`, records each detection in SQLite before remediation, and
|
|
exposes manual quarantine/delete/ignore after a scan. Quarantine management
|
|
restores to the original path or a chosen path and refuses overwrites.
|
|
- **AboutPage** calls `/boltclam/account` for user/plan data and
|
|
`/boltclam/updates` for the release feed. Downloads use the API asset proxy
|
|
and optionally verify `SHA256SUMS.txt`.
|
|
- **SettingsPage** validates ClamAV paths and extra options, applies logging
|
|
changes, updates the real-time watcher, and creates/uploads diagnostics
|
|
bundles only when the user has opted in.
|
|
|
|
## Sign-in sequence
|
|
|
|
```
|
|
BoltClam Browser Bolt desk API
|
|
|-- PKCE verifier/challenge --| |
|
|
|-- loopback listener :port/cb| |
|
|
|-- open /desk/auth/boltclam/start?... ------------------->|
|
|
| |<-- branded login page -----|
|
|
| |-- email/password or OAuth->|
|
|
|<-- 302 /cb?code=...&state=... ----------------------------|
|
|
|-- POST /desk/auth/oauth/exchange (code+verifier) -------->|
|
|
|<-- {api_key, user} --------------------------------------|
|
|
|-- DPAPI-store key, GET /boltclam/account ---------------->|
|
|
```
|
|
|
|
The one-time code is bound to the PKCE challenge, the exact loopback URI and
|
|
a short TTL. The app rejects a callback whose state does not match. The API
|
|
burns an exchange code on the first attempt, successful or not.
|
|
|
|
## Scan sequence
|
|
|
|
1. `ClamAVService.info()` locates `clamscan.exe`, runs `--version`, and
|
|
chooses the managed database if it contains signatures.
|
|
2. `scan_targets("homes")` reads the Windows `ProfileList` registry and
|
|
falls back to `C:\Users`; `"full"` enumerates fixed local drives.
|
|
3. `ScanWorker` starts clamscan with an argv list, no shell, and parses
|
|
detections/progress/summary. Each `path: Signature FOUND` becomes a
|
|
`Detection` row first, then quarantine/delete/notify is applied.
|
|
4. The scan row is finished even on cancellation or process failure. The UI
|
|
reloads history and the Homepage aggregates.
|
|
|
|
## Real-time sequence
|
|
|
|
`DownloadWatcher` polls each watched download folder at 1.5 s intervals (and
|
|
refreshes browser preferences every 30 s), ignores in-progress suffixes, and
|
|
waits for a stable size/mtime. `RealtimeScanTask` runs in a QThreadPool:
|
|
|
|
```
|
|
stable file → exclusive handle (share=0) → copy → ClamAV scans copy
|
|
→ clean: release → detection: release + quarantine/delete
|
|
```
|
|
|
|
The exclusive handle is held for the entire scan. A fallback copy is used
|
|
when the file is already open, with that fact recorded in the event detail.
|
|
|
|
## Update flow
|
|
|
|
- The Bolt API reads Gitea's `/api/v1/repos/TheMajesticNetwork/BoltClam/releases`,
|
|
caches for five minutes, filters draft/prerelease by channel, and returns
|
|
normalized release metadata.
|
|
- Asset downloads go through the API, so a future private repository needs
|
|
no desktop token. The API streams the Gitea asset with its own token.
|
|
- The client compares semantic versions, selects a Windows `.exe`/`.zip`
|
|
asset, downloads it to `%LOCALAPPDATA%\BoltClam\updates`, and verifies the
|
|
release checksum when published.
|
|
|
|
## Storage schema
|
|
|
|
- `scans` — scope, status, paths JSON, counts, engine/DB versions, duration.
|
|
- `detections` — scan id, path, signature, action, quarantine id, timestamp.
|
|
- `quarantine` — id, original/quarantine path, signature, SHA-256, status.
|
|
- `realtime_events` — download path, signature, action, detail, timestamp.
|
|
|
|
All writes open their own SQLite connection; WAL mode lets the UI read while
|
|
a scan thread writes. Quarantine sidecars are JSON, so the record survives a
|
|
database rebuild.
|