No description
Find a file
dev aae41a9371 0.1.6: security hygiene, and right-click scanning
Malware scanning asks whether a file is hostile. The check that
actually loses people their accounts is a different one: what has
already been exposed, and what is about to be? crates/hound-supply/src/
hygiene.rs answers it, and runs as part of every project sweep.

  - A secret file tracked by git. The emergency case: it is in the
    history, in every clone, and in every fork. The advice says rotate
    BEFORE `git rm --cached`, because removing a pushed secret does not
    un-share it, and a test asserts that ordering.
  - Credentials hardcoded in source, recognised by issuer format —
    AWS, GitHub, Anthropic, OpenAI, Stripe, Slack, GitLab, npm, PyPI,
    Google, and the PEM private-key headers.
  - Secret files readable by every account on the machine.
  - Secret files with nothing in .gitignore covering them: the near
    miss that the next `git add -A` turns into the emergency above.
  - GitHub Actions: pull_request_target with a checkout of the pull
    request (a stranger's code, your secrets, your write token), a
    secret echoed into the build log, a downloaded script piped into a
    shell, and third-party actions on a moving tag.

Two rules govern all of it. **Findings are actionable**: no entropy
heuristics, because "high entropy string" is a coin flip a human then
has to adjudicate, and people stop reading after the second false
alarm. Every detector recognises a documented credential format or
reports a structural fact that is true or false. **Nothing secret is
copied into a finding** — a report naming the key it found has moved
the key into a log, a CI artefact, or an assistant's context window,
which is the thing being prevented. There is a test for that.

Tracked-file status comes from parsing .git/index rather than running
git: the sweep is pointed at repositories precisely because they are
not trusted, and starting a subprocess inside one is what a hostile
repository wants.

Against a deliberately bad test repository: four critical, five
warnings, and correctly silent on .env.example and actions/checkout@v4
— flagging those is how a scanner gets ignored.

Also in this release:

  - Right-click "Scan for Threats with Hound" in Nemo, Caja and
    Dolphin, whose menu entries are system files. GNOME Files and
    Thunar keep theirs per-user, so `hound context-menu install`
    handles those. The icon is symbolic, so the file manager recolours
    it to the menu's own theme instead of dropping a violet dog into a
    monochrome menu.
  - A right-click while the app is already open hands the request to
    the running instance rather than refusing. A menu item that
    silently does nothing because the app happens to be open is
    indefensible.
  - Two fixes for the duplicate tray icon. The updater slept a fixed
    600ms after SIGTERM and then started the replacement; if the old
    process outlived that, the panel kept its item and the result was
    two dogs, the older of which could not be clicked or closed because
    nothing was behind it. It now waits for the process to actually
    leave /proc, escalating to SIGKILL after five seconds. And the app
    itself now holds an advisory lock for its lifetime, so a second
    instance cannot exist — the kernel releases the lock however the
    process dies, so a stale one is not a state that can happen.

402 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:15: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.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -05:00
dist 0.1.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -05:00
gui 0.1.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -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 defs: rebuild and publish the feed daily instead of by hand 2026-08-21 12:31:54 -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.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -05:00
Cargo.toml 0.1.6: security hygiene, and right-click scanning 2026-08-21 13:15:35 -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.