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).
164 lines
5.9 KiB
Markdown
164 lines
5.9 KiB
Markdown
# Hound Antivirus
|
|
|
|
A premium, freemium antivirus for Linux. One engine, three faces:
|
|
a Rust daemon (`houndd`), a CLI (`hound`), and a Tauri GUI with a
|
|
system-tray sentinel that changes color with your security state.
|
|
|
|
Built for the distros people actually run: **Ubuntu, Debian, Linux Mint**,
|
|
and anything else that ships ClamAV.
|
|
|
|
## Repository layout
|
|
|
|
```
|
|
antivirus/
|
|
├── Cargo.toml # Rust workspace
|
|
├── crates/
|
|
│ ├── hound-api/ # shared wire types + socket client (daemon/CLI/GUI all use it)
|
|
│ ├── houndd/ # the daemon: Unix-socket API over a pluggable engine
|
|
│ └── hound/ # CLI client
|
|
├── assets/icons/ # dog-head brand mark + 4-state tray ladder
|
|
└── gui/ # Tauri 2 desktop app (system tray + scan UI)
|
|
```
|
|
|
|
## Architecture
|
|
|
|
```
|
|
houndd (Rust daemon — the engine)
|
|
┌──────────────────────────────────┐
|
|
│ ScanEngine trait │
|
|
│ ├─ L1 ClamAV signatures (now) │
|
|
│ ├─ L2 Curated threat packs(Pro)│
|
|
│ ├─ L3 Behavioral monitor (Pro) │
|
|
│ └─ L4 Supply-chain checks(Pro) │
|
|
└──────────────┬───────────────────┘
|
|
Unix socket (JSON-RPC, line-delimited)
|
|
┌───────────┼───────────┐
|
|
hound CLI GUI (Tauri) future modules
|
|
```
|
|
|
|
The daemon is the only process that touches a scanning engine. CLI and GUI
|
|
are thin clients — so future suite tools (firewall, updater, …) plug into
|
|
the same socket.
|
|
|
|
### Swapping the engine (the ClamAV seam)
|
|
|
|
ClamAV is a **temporary** dependency. Everything ClamAV-specific — version
|
|
probe, signature freshness, the `clamscan` subprocess + output parsing,
|
|
`freshclam` — lives in one file behind a four-method trait:
|
|
|
|
```
|
|
crates/houndd/src/engine.rs
|
|
trait ScanEngine { name; probe; scan; update }
|
|
struct ClamAvEngine // today
|
|
const ENGINE: ClamAvEngine // ← flip this line when the native
|
|
// engine lands; nothing else in the
|
|
// daemon, CLI, GUI, or wire API moves
|
|
```
|
|
|
|
The wire stays engine-agnostic: `Status.engine` names the implementation
|
|
(`"clamav"` today) and `Status.db` carries what any signature store has —
|
|
a file name and a timestamp. When our own Rust engine ships, it's a new
|
|
`ScanEngine` implementation, a one-const flip, and the tray/CLI/GUI simply
|
|
start reporting the new engine name.
|
|
|
|
## Icon system
|
|
|
|
The brand mark is a solid dog head (`assets/icons/hound.svg`), a single
|
|
flat fill. It ships in two treatments:
|
|
|
|
- **Brand ladder** `hound-{16,22,24,32,48,256}.png` — native periwinkle
|
|
`#9896E0`, for the window icon, About box, and marketing.
|
|
- **Tray-state ladder** `state-<name>-{16,22,24,32,48}.png` — the same
|
|
path re-tinted per security state, for the system-tray sentinel:
|
|
|
|
| State | Fill | Meaning |
|
|
|---|---|---|
|
|
| `protected` | `#22C55E` green | up-to-date / protected / clean |
|
|
| `scanning` | `#F59E0B` amber | scan in progress / signatures need update |
|
|
| `threat` | `#EF4444` red | infection found |
|
|
| `paused` | `#6B7280` gray | real-time monitor off |
|
|
|
|
Green is the *good* state; amber is *work in progress*, never a failure.
|
|
|
|
## Quickstart (development)
|
|
|
|
Prereqs: Rust (see `rust-toolchain.toml`), Node 20+, ClamAV, and the
|
|
Tauri system libs (`libwebkit2gtk-4.1-dev`, `libgtk-3-dev`, `libayatana-appindicator3-dev`).
|
|
|
|
```sh
|
|
# 1. Signatures (needs the clamav freshclam DB)
|
|
sudo freshclam
|
|
|
|
# 2. Daemon (terminal 1)
|
|
cargo run -p houndd
|
|
|
|
# 3. Scan (terminal 2)
|
|
cargo run -p hound -- status
|
|
cargo run -p hound -- scan ~/Downloads
|
|
|
|
# 4. GUI
|
|
cd gui && npm install && npm run tauri dev
|
|
```
|
|
|
|
### Verifying the engine with the EICAR test file
|
|
|
|
EICAR is the industry-standard 68-byte test signature — every AV that
|
|
works will flag it. Generate it and scan it:
|
|
|
|
```sh
|
|
printf 'X5O!P%%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > /tmp/eicar.com
|
|
cargo run -p hound -- scan /tmp/eicar.com
|
|
# expect: exit code 1, "Eicar-Test-Signature FOUND"
|
|
```
|
|
|
|
### Updating signatures
|
|
|
|
`hound update` wraps `freshclam` (trying `sudo freshclam` first, since
|
|
plain-user runs can't write `/var/lib/clamav` and `/var/log/clamav`). The
|
|
GUI's "Update Signatures" button drives the same RPC and shows the log.
|
|
```sh
|
|
cargo run -p hound -- update # or: hound update --json
|
|
```
|
|
|
|
## The GUI (`gui/`)
|
|
|
|
A Tauri 2 desktop app — a thin view over the *same* `houndd` socket the
|
|
CLI uses (via the shared `hound-api` client), so the window and the
|
|
command line never disagree about your security state.
|
|
|
|
```
|
|
gui/
|
|
├── dist/ # the front-end (vanilla HTML/CSS/JS, premium dark shell)
|
|
└── src-tauri/ # Tauri 2 shell + system-tray sentinel
|
|
```
|
|
|
|
The tray sentinel swaps the 4-state icons (green/amber/red/gray) as your
|
|
state changes; the window shows a live protection hero, a scan progress
|
|
bar, a results table, and the signature-update log.
|
|
|
|
Build it:
|
|
```sh
|
|
cd gui
|
|
npm install
|
|
npm run tauri dev # dev with hot reload
|
|
npm run tauri build # → .deb in src-tauri/target/release/bundle/
|
|
```
|
|
|
|
## Git conventions
|
|
|
|
- Branch `main` is deployable; small, focused commits.
|
|
- **No hardcoded secrets.** For pushes, the bot token lives in a repo-local
|
|
credential file (never tracked):
|
|
|
|
```sh
|
|
git config credential.helper 'store --file=.git/.git-credentials'
|
|
chmod 600 .git/.git-credentials
|
|
echo 'https://<user>:<token>@git.joelovestech.com' >> .git/.git-credentials
|
|
```
|
|
|
|
- Commits: imperative subject, ≤ 72 chars. Example: `houndd: add line-delimited JSON-RPC socket API`
|
|
|
|
## License
|
|
|
|
TBD — core daemon likely proprietary (freemium), shared CLI possibly OSS.
|
|
Decision pending; `workspace.package.license = MIT` is a placeholder.
|