Skip to content

hherb/hhagent

Repository files navigation

hhagent

hhagent logo

A personal, always-on agentic system designed from the ground up for security and vendor neutrality.

What it is

A long-running personal AI agent that:

  • talks to you over secure messaging (Telegram, Signal) and email (its own IMAP/SMTP account)
  • remote-controls a web browser, performs web searches and page fetches
  • executes Python in a strict sandbox
  • maintains persistent memory in Postgres with hybrid retrieval (pgvector + BM25 + graph)
  • reviews its own plans through CASSANDRA, a semantic oversight layer with hard-coded constitutional constraints, before any tool runs
  • runs continuously, periodically resetting its context window from memories and a persistent task list

Design priorities (in order)

  1. Security boundary = the agent's own OS user account. Worst-case compromise (LLM, tool, dependency, or LLM-authored Python) does not escape that boundary.
  2. Vendor neutrality. Primary host is the NVIDIA DGX Spark, but no hard NVIDIA dependency. Linux and macOS are both first-class.
  3. License hygiene. Project is AGPL-3.0; every dependency is AGPL-compatible.
  4. Small core. The agent core is Rust (no eval, no metaprogramming, no dynamic import). Python lives only inside sandboxed workers.

Security architecture

hhagent security architecture

The mechanical layers along the bottom of the diagram — bwrap, Landlock, seccomp, the egress proxy, the dispatcher chokepoint — enforce boundaries: "this process cannot open that socket." The CASSANDRA layer running alongside the agent core enforces intent: "should the agent be doing this at all, given what the user actually asked for?" CASSANDRA reviews each plan (not each tool call) through a pipeline of deterministic and LLM stages, with five hard-coded constitutional constraints that no user, admin, or configuration change can override. See docs/cassandra_design_plan.md.

The diagram below traces a single user instruction through every gate — channel ingress, plan formulation, the CASSANDRA review pipeline (Stages −1 through 4), the dispatcher chokepoint, sandboxed worker execution, and the egress proxy — with the block / advisory / escalation branches drawn explicitly. Source: docs/security-request-flow.svg.

Request flow through hhagent's security layers

Why another one?

Several Rust personal-agent projects exist in the OpenClaw-derived family — notably IronClaw and ZeroClaw. They share a lot with hhagent: Rust core, local-first, OS sandboxing, MCP-compatible IPC. The reason for another one is posture, not feature count: security is the foundational property here, not a layer added later. Each rule below is a load-bearing invariant, not a default we relax under deadline pressure.

  • One OS process + one kernel sandbox per tool invocation. IronClaw runs tools as WASM modules inside the runtime; ZeroClaw runs them as in-process Rust traits with the OS sandbox wrapping the whole runtime. Both are software-only or coarse-grained boundaries. hhagent's boundary is the OS process boundary — bubblewrap on Linux, sandbox-exec on macOS — so a compromised tool reaches at most the endpoints in that tool's allowlist, never the next tool's, and never the core.

  • Double containment. The parent installs the OS sandbox at spawn; the worker then installs a second layer on itself (Landlock + seccomp-bpf on Linux) before serving any JSON-RPC request. A kernel bug in either layer alone does not breach the worker. See workers/prelude/.

  • seccomp is an allow-list, not a deny-list. Default action is KillProcess; ~110 base syscalls plus per-profile additions (e.g. the BSD-socket family for WorkerNetClient) are explicitly permitted. The kill-list-of-obviously-bad-calls posture common elsewhere lets new attack syscalls walk in unchallenged every time the kernel grows.

  • Dispatcher chokepoint. A single function authors every WorkerCommand, consults policy, and writes the audit-log entry. New channels (Telegram, Signal, IMAP) and scheduled routines call into it — they never spawn workers themselves. Borrowed from IronClaw's ToolDispatcher::dispatch() and made non-negotiable.

  • Semantic oversight on top of mechanical sandboxing (CASSANDRA). Kernel sandboxes catch "this process tried to open that socket"; they cannot catch "send this confidential pathology report to a recipient who happens to be permitted but contextually wrong." Every plan the agent formulates is reviewed by CASSANDRA before any tool runs — a chain of deterministic and LLM stages enforcing five constitutional constraints (no physical harm, no fraud / impersonation, no irreversible action without verified human-in-the-loop, no power concentration, no oversight suppression) that no user, admin, or configuration change can override. Runtime verification at the dispatcher then re-checks that what executes matches what was approved. The agent core never bypasses this gate. See docs/cassandra_design_plan.md.

  • AGPL with AGPL-compatible deps only. No CDDL, BUSL, SSPL, Elastic License, or "source-available" components. License hygiene is part of the security boundary: a permissive dep can re-enter the process under a corporate fork the user cannot audit.

  • Cross-platform parity by construction. The same SandboxPolicy struct drives both backends. Linux's stronger stack (bwrap + Landlock + seccomp) and macOS's weaker stack (Seatbelt) are both first-class with negative tests asserting that denials actually deny. The asymmetry between them is documented openly in docs/threat-model.md rather than papered over.

  • No vendor lock-in. Primary host is the NVIDIA DGX Spark, but nothing in the core requires NVIDIA, CUDA, or a specific cloud. Local LLMs run via vLLM/SGLang on Linux or llama.cpp/Ollama on macOS behind an OpenAI-compatible HTTP API.

The full set of invariants — including secret handling, single-point egress inspection, and the "no in-process untrusted code" rule — lives in docs/architecture.md. Reviewers are expected to refuse PRs that violate them.

Status

Early scaffold. See docs/architecture.md and docs/threat-model.md. Phased build plan is tracked in the design plan file outside this repo.

Layout

core/          Rust agent core (scheduler, memory, policy, LLM router, audit, IPC)
sandbox/       Cross-platform sandbox crate (bwrap+Landlock on Linux, Seatbelt on macOS)
supervisor/    Service-supervisor abstraction (systemd --user / launchd LaunchAgents)
workers/       Tool workers, each its own sandboxed process
adapters/      Channel adapters (Telegram, Signal)
db/migrations/ Postgres schema (pgvector, pg_search, Apache AGE)
config/        Runtime policy and per-worker sandbox profiles
docs/          Architecture & threat-model docs

Setup

Linux (Ubuntu 24.04+)

The kernel restricts unprivileged user namespaces by default (kernel.apparmor_restrict_unprivileged_userns=1), so bwrap cannot create its own jail without a per-binary AppArmor profile. Install one once:

sudo scripts/linux/install-bwrap-apparmor-profile.sh

This is the same pattern Flatpak uses (/etc/apparmor.d/flatpak). After installing, sandbox tests should pass:

cargo test -p hhagent-sandbox

If you skip this step, the agent will refuse to spawn workers and emit a clear error pointing back here. Other Linux distros without AppArmor user-ns restrictions don't need this script.

License

AGPL-3.0-only. See LICENSE.

About

A personal, always-on agentic system designed from the ground up for security and vendor neutrality.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages