BoltClam 1.0.0: ClamAV desktop protection with Bolt sign-in

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>
This commit is contained in:
TheMajesticMagician
2026-10-25 16:21:14 -06:00
co-authored by Claude Code
commit addd00364d
76 changed files with 12503 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
"""Application bootstrap: login/restore, dependency wiring, and window lifecycle."""
from __future__ import annotations
import logging
import os
import sys
import time
from pathlib import Path
from PyQt6.QtCore import QEventLoop, QLockFile, QRectF, Qt, QTimer
from PyQt6.QtGui import QColor, QFont, QPainter, QPixmap
from PyQt6.QtWidgets import QApplication, QDialog, QMessageBox, QSplashScreen
from . import APP_ID, __build_date__, __version__, paths
from .api_client import ApiClient, ApiError, NetworkError
from .auth import AuthService, CredentialStore, Session, SessionExpired
from .clamav import ClamAVService
from .config import AccountCache, SettingsStore
from .context import AppContext
from .logging_setup import configure as configure_logging
from .quarantine import QuarantineService
from .storage import Storage
from .ui import brand, theme
from .ui.login_dialog import LoginDialog
from .ui.main_window import MainWindow
logger = logging.getLogger(__name__)
def _set_windows_app_id() -> None:
if os.name != "nt":
return
try:
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(APP_ID)
except Exception:
pass
def _splash_pixmap() -> QPixmap:
image = QPixmap(460, 250)
image.fill(Qt.GlobalColor.transparent)
painter = QPainter(image)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
painter.setBrush(QColor("#081820"))
painter.setPen(QColor("#1B3946"))
painter.drawRoundedRect(QRectF(1, 1, 458, 248), 22, 22)
brand.paint_mark(painter, QRectF(34, 34, 72, 72))
painter.setPen(QColor(theme.TEXT))
painter.setFont(theme.display_font(26, QFont.Weight.DemiBold))
painter.drawText(QRectF(124, 36, 300, 40), Qt.AlignmentFlag.AlignLeft,
Qt.AlignmentFlag.AlignVCenter, "BoltClam")
painter.setPen(QColor(theme.MUTED))
painter.setFont(theme.body_font(10.0))
painter.drawText(QRectF(126, 76, 300, 24), Qt.AlignmentFlag.AlignLeft,
Qt.AlignmentFlag.AlignVCenter, "ClamAV protection, without the noise.")
painter.setPen(QColor(theme.TEXT_SOFT))
painter.setFont(theme.body_font(10.5))
painter.drawText(QRectF(36, 154, 390, 26), Qt.AlignmentFlag.AlignLeft,
Qt.AlignmentFlag.AlignVCenter, "Starting protection…")
painter.setPen(QColor(theme.DIM))
painter.setFont(theme.mono_font(8.0))
painter.drawText(QRectF(36, 206, 390, 20), Qt.AlignmentFlag.AlignLeft,
Qt.AlignmentFlag.AlignVCenter, f"Version {__version__} · {__build_date__}")
painter.end()
return image
def _install_exception_hook() -> None:
def handle(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))
try:
QMessageBox.critical(
None, "BoltClam encountered an error",
f"{exc_type.__name__}: {exc_value}\n\nDetails were written to the BoltClam log.")
except Exception:
pass
sys.excepthook = handle
def _build_context(settings_store: SettingsStore, auth: AuthService,
settings, session: Session) -> AppContext:
storage = Storage()
quarantine = QuarantineService(storage)
clamav = ClamAVService(settings.clamav_path)
api = ApiClient(settings.api_base_url, session.api_key,
device_name=auth.device_name, install_id=settings.install_id)
return AppContext(
settings_store=settings_store,
settings=settings,
storage=storage,
quarantine=quarantine,
clamav=clamav,
auth=auth,
session_key=session.api_key,
account=session.account,
install_id=settings.install_id,
api=api,
offline=session.offline,
)
def _restore_session(auth: AuthService, stored: str | None) -> tuple[Session | None, str]:
if not stored:
return None, ""
try:
return auth.restore(stored), ""
except SessionExpired:
return None, "Your previous BoltClam session expired. Sign in again to continue."
except Exception as exc:
cached = auth.account_cache.load()
if cached:
logger.info("Starting offline with the last verified account: %s", exc)
return Session(stored, cached, offline=True), (
"Bolt is unreachable right now. BoltClam is using the last verified account "
"and local protection still works.")
logger.info("Could not restore the saved session: %s", exc)
return None, f"Could not restore your BoltClam session: {exc}"
def main() -> int:
_set_windows_app_id()
os.environ.setdefault("QT_ENABLE_HIGHDPI_SCALING", "1")
app = QApplication(sys.argv)
theme.setup_application(app)
app.setQuitOnLastWindowClosed(False)
app.setWindowIcon(brand.app_icon())
paths.ensure_dirs()
lock = QLockFile(str(paths.config_dir() / "boltclam.lock"))
if not lock.tryLock(150):
QMessageBox.information(
None, "BoltClam is already running",
"Only one BoltClam can watch downloads at a time. Open the copy in the "
"system tray, or quit it before starting another.")
return 0
settings_store = SettingsStore()
settings = settings_store.load()
env_api = os.environ.get("BOLTCLAM_API_URL", "").strip()
if env_api and env_api != settings.api_base_url:
settings = settings_store.update(api_base_url=env_api)
configure_logging(settings)
_install_exception_hook()
logger.info("BoltClam %s starting (%s)", __version__, paths.os_label())
splash = QSplashScreen(_splash_pixmap())
splash.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
splash.show()
app.processEvents()
credentials = CredentialStore()
account_cache = AccountCache()
auth = AuthService(
settings.api_base_url, install_id=settings.install_id,
credentials=credentials, account_cache=account_cache)
stored = auth.stored_key()
session, notice = _restore_session(auth, stored)
splash.close()
if session is None:
dialog = LoginDialog(auth, notice=notice)
if dialog.exec() != QDialog.DialogCode.Accepted or dialog.session is None:
logger.info("Sign-in cancelled at startup")
return 0
session = dialog.session
while True:
context = _build_context(settings_store, auth, settings, session)
window = MainWindow(context)
loop = QEventLoop()
window.finished.connect(loop.quit)
window.show()
loop.exec()
logged_out = window.logged_out()
window.deleteLater()
app.processEvents()
if not logged_out:
break
dialog = LoginDialog(auth, notice="You signed out. Sign in again to use BoltClam.")
if dialog.exec() != QDialog.DialogCode.Accepted or dialog.session is None:
break
session = dialog.session
settings = settings_store.load()
logger.info("BoltClam exiting")
return 0