No description
Find a file
dev 51b8fcd573 0.1.11: one restart mechanism, and a launcher that raises the window
Three tray bugs today, each caused by fixing the previous one without
looking at what else the fix touched. Worth writing down as one thing
rather than three.

**Two dogs, again.** 0.1.4 taught the app to notice its own package
being replaced and reopen. 0.1.5 added the same behaviour to `hound
update`, which is the better mechanism because it works no matter which
version was running — and I did not remove the first. Both fire during
an update: the running app re-execs under a new pid while apt is
working, so the updater kills a pid that has already moved and starts a
second instance beside it.

The in-app restart now waits eight seconds before acting. If the
updater is doing its job this process is terminated during the pause
and never restarts itself; what survives the wait is the only case the
updater cannot cover, which is somebody running `apt upgrade` directly.
Two mechanisms, one arbiter.

**The launcher stopped working.** The single-instance lock added in
0.1.5 to prevent the duplicate icon made a second launch exit quietly,
so clicking the desktop shortcut while Hound was open did nothing at
all — no window, no error, no feedback. It now hands the request to the
running instance, which shows, unminimises and focuses. The same
channel the right-click scan already used: a launch with paths means
"scan these", a launch without means "show yourself".

Also in this release, and the reason the version is what it says it is:
the workspace version did not get bumped. Adding the hound-watch crate
pushed `version =` from line 6 to line 7 of Cargo.toml, and the bump
was a line-numbered sed. The three GUI files went to 0.1.10 and the
workspace stayed at 0.1.9, so the package built as 0.1.9 while the
manifest would have advertised 0.1.10 — every installed agent would
have offered an update forever, installed it, and still seen one
available.

`the_declared_versions_all_agree` caught it before the build finished.
tools/bump-version.sh now does this by matching content and section
rather than line numbers, asserts every substitution actually happened,
and refuses to write invalid JSON.

Adds crates/hound-watch: the registry firehose. npm publishes a CouchDB
_changes stream and PyPI an RSS feed of every new project, both public,
so this needs no user data at all — which is the answer to how a
product with no telemetry grows its detection. Each new package is
handed to the detectors Hound already ships, so there is one definition
of "this install script is hostile" and a rule that fires in CI fires
identically on the firehose. Verified against the live registries: 240
releases in one poll, 60 triaged, parsers tested against captured
responses rather than the network.

476 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:53:18 -05:00
action 0.1.8: a GitHub Action, and a light theme that is actually light 2026-08-21 13:40:35 -05:00
assets/icons 0.1.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -05:00
crates 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
dist 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
gui 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
packaging 0.1.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -05:00
site rootkit: stop reporting every process on the machine as hidden 2026-08-21 11:21:06 -05:00
tools 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
.editorconfig Rust engine + CLI over ClamAV Unix socket 2026-08-20 16:59:14 -05:00
.env.example Rust engine + CLI over ClamAV Unix socket 2026-08-20 16:59:14 -05:00
.gitignore Full feature set: realtime monitor, quarantine vault, rootkit scan, settings, events 2026-08-20 20:33:44 -05:00
Cargo.lock 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
Cargo.toml 0.1.11: one restart mechanism, and a launcher that raises the window 2026-08-21 14:53:18 -05:00
README.md Engine seam (ScanEngine trait) + Tauri GUI 2026-08-20 17:55:40 -05:00
rust-toolchain.toml Rust engine + CLI over ClamAV Unix socket 2026-08-20 16:59:14 -05:00

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).

# 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:

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.

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:

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):

    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.