Commit graph

3 commits

Author SHA1 Message Date
Hound
6016e1b4ea packaging: .deb, AppImage, rpm spec, PKGBUILD, hardened unit, app icon
Phase 2. "Distro-agnostic" was a claim with no packaging behind it.

.deb and AppImage are both built and tested here; the rpm spec and
PKGBUILD are written but not yet built, since neither rpmbuild nor
makepkg exists on this machine.

The AppImage is deliberately not a packaged daemon. An AppImage is
unprivileged by design — no install, no root, no systemd — so the
execution gate cannot exist in one, and pretending otherwise would be
worse than saying so. What it is instead is the try-before-you-install
build: on-demand scanning, quarantine under the user's own data dir,
rootkit and supply-chain checks, all with nothing left behind. Asking
it for the gate prints why it cannot and how to get it. Verified with
an isolated HOME: status, scan, EICAR caught, gate refusal.

Two packaging bugs caught by looking rather than assuming:

* mktemp -d creates 0700 and dpkg applies the staging root's mode to
  "/". Installing that package would have chmodded the root directory
  to 0700 and broken the machine.
* AppRun wrote its log before creating the directory, and built a
  socket path that can exceed sun_path (108 bytes) when
  XDG_RUNTIME_DIR is long. Both fixed; the socket falls back rather
  than failing with an error nobody can act on.

The systemd unit is hardened as far as this daemon can be. Notably
ProtectSystem=strict is WRONG here and is left off on purpose: it
remounts everything read-only, and quarantine has to remove a threat
from wherever it landed. ProtectHome is off for the same reason. The
CapabilityBoundingSet mirrors what caps.rs drops to, so the machine is
protected even if the binary is replaced by one that does not reduce
itself.

App icon: the mark in white on a periwinkle tile, per Joe. Small sizes
are not the same artwork scaled down — the mark is line-weight, so at
16px a 62% inset leaves about a pixel and a half of stroke and the head
turns to mush. Each size is authored with its own inset and corner
radius, and the ground goes flat below 32px because a gradient across
16 pixels is just noise. The tray ladder is untouched: those glyphs stay
transparent and re-tint per state so they can sit on any panel colour.

Package installs are NOT enabled by default beyond the daemon itself —
exec_gate stays off until the operator turns it on, in every packaging
format.

99 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:37:17 -05:00
6ef296caa1 Full feature set: realtime monitor, quarantine vault, rootkit scan, settings, events
Daemon (houndd)
- realtime.rs: inotify monitor over watched dirs (default ~/Downloads,
  ~/Documents, ~/Desktop), ClamAV scan on touch, on_detect action
  (quarantine/rename/remove), ransomware heuristic (writes/renames per
  minute above threshold -> 'watching'/'alarm' + critical event)
- quarantine.rs: SHA-256-keyed vault under ~/.local/share/hound/quarantine,
  add/list/restore/remove with original-path metadata
- rootkit.rs: setuid anomaly detection (allowlisted stock binaries),
  deleted-but-executing inodes, world-writable /usr /bin; 3 severity levels
- settings.rs: persisted ~/.config/hound/settings.json, hot-reload on set
- events.rs: ring buffer of severity-tagged events, query + clear

API (hound-api): Settings, Event, QuarantineEntry, RootkitScan/
RootkitFinding, RealtimeStatus types + 10 client methods; Status gains
engine field (engine-agnostic seam)

CLI (hound): events, quarantine list|add|restore|remove, settings
[show|paused|auto-update|notify|realtime on|off|watch|on-detect|
max-size|exclude], rootkit, realtime [status|on|off] — color human
output, --json everywhere

GUI (Tauri 2):
- 16 backend commands bridging every client method
- tray watcher: 1s poll loop, 4-state icon ladder (green/amber/red/gray),
  desktop notification on fresh critical events
- 6-tab frontend: Protection (hero + scan + update), Quarantine (vault
  manager + manual add), Realtime (stats + watch list + toggle), Rootkit
  (on-demand scan), Alerts (event log + clear), Settings (full editor)
- capabilities/default.json for dialog/notification/event permissions

Verified: 27/27 workspace tests, live E2E — EICAR dropped in ~/Downloads
auto-quarantined by the running daemon (critical event logged, file
removed from origin).
2026-08-20 20:33:44 -05:00
a7871b5e27 Engine seam (ScanEngine trait) + Tauri GUI
ClamAV is a temporary dependency. Everything ClamAV-specific moves
behind a 4-method trait in crates/houndd/src/engine.rs:

    trait ScanEngine { name; probe; scan; update }
    struct ClamAvEngine   // today
    const ENGINE          // one-line flip when the native engine lands

- parse_clamscan() is now a pure fn with unit tests (OK/FOUND/INCOMPLETE,
  dedup, malformed lines)
- main.rs drops all clamscan/clamd/freshclam knowledge; Status reports
  engine='clamav' via the trait
- README documents the seam and the wire contract that stays stable

GUI (gui/, Tauri 2, standalone workspace, vanilla JS premium-dark shell):
- system-tray sentinel on the 4-state dog-head ladder (green/amber/red/gray)
  with tooltips; clicks open the window / scan / update
- window: protection hero, path + recursive scan, progress bar, results
  table, signature update log; state mirrored to the tray
- compiles clean (cargo build, 0 warnings) on Mint 22.3 + webkit2gtk-4.1

All 11 workspace tests pass, incl. the E2E EICAR scan over the real
Unix-socket daemon (clamscan finds the planted EICAR).
2026-08-20 17:55:40 -05:00