{"id":50574737,"url":"https://github.com/branover/hexgraph","last_synced_at":"2026-06-04T21:00:50.942Z","repository":{"id":362536394,"uuid":"1254568808","full_name":"branover/hexgraph","owner":"branover","description":"Self-hosted, agentic vulnerability research for binaries \u0026 firmware: an AI agent decompiles, fuzzes, and verifies exploits inside a sandbox, recording every finding to a typed graph. BYOK, fully local.","archived":false,"fork":false,"pushed_at":"2026-06-04T19:07:00.000Z","size":26501,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T19:07:39.152Z","etag":null,"topics":["agentic-ai","ai-agents","binary-analysis","claude","claude-code","cybersecurity","exploit-development","firmware-security","fuzzing","ghidra","iot-security","llm","mcp","pentesting","reverse-engineering","security","security-tools","vulnerability-research"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/branover.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T18:24:53.000Z","updated_at":"2026-06-04T18:36:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/branover/hexgraph","commit_stats":null,"previous_names":["branover/hexgraph"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/branover/hexgraph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/branover%2Fhexgraph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/branover%2Fhexgraph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/branover%2Fhexgraph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/branover%2Fhexgraph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/branover","download_url":"https://codeload.github.com/branover/hexgraph/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/branover%2Fhexgraph/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33917202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agentic-ai","ai-agents","binary-analysis","claude","claude-code","cybersecurity","exploit-development","firmware-security","fuzzing","ghidra","iot-security","llm","mcp","pentesting","reverse-engineering","security","security-tools","vulnerability-research"],"created_at":"2026-06-04T21:00:39.240Z","updated_at":"2026-06-04T21:00:50.912Z","avatar_url":"https://github.com/branover.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ⬡ HexGraph\n\nHexGraph is a self-hosted workbench for AI-assisted vulnerability research that runs entirely on\nyour own machine. You point it at a binary or a firmware image, and it does the unglamorous parts for\nyou: it ingests the target, pulls firmware apart into its component binaries, runs analysis tasks\ndriven by whatever model access you already have, and writes every result down as a structured\n**finding** in a typed graph backed by SQLite. The graph ties everything together: targets,\nfunctions, sockets, hypotheses, and findings, joined by typed and attributed edges. You browse all of\nit, launch new tasks, and triage findings from a web UI that only ever listens on localhost, and the\nsame operations are available to a coding agent over MCP.\n\n![The typed knowledge graph of a firmware engagement](docs/images/graph.png)\n\nThree principles are non-negotiable, and they shape everything else:\n\n- **It stays local.** The API and UI bind to `127.0.0.1` and nothing else. HexGraph never phones a\n  server we operate; there is no telemetry and no auto-update ping.\n- **You bring the key, or you bring nothing.** Model access comes from your own Anthropic API key, a\n  local Claude Code session, or the built-in **mock** backend. The mock is the default, it needs no\n  key and no network, and it lets you run the entire loop for $0.\n- **Every target is treated as hostile.** All parsing, unpacking, and analysis of target bytes\n  happens inside a disposable Docker container with no network and tight resource limits. HexGraph is\n  static-only by default. Executing the target, reaching the network, and rehosting firmware are each\n  a separate capability you opt into deliberately, and even then they run inside that same locked-down\n  sandbox. The model never sees raw target bytes, only the output of the tools HexGraph runs for it\n  (decompilation, strings, imports, and so on).\n\n\u003e **Status: pre-1.0.** The core loop works end to end today, from ingest through recon, AI analysis,\n\u003e a structured finding, the graph, and on to the next task it suggests. That includes extracting real\n\u003e vendor firmware, linking the same bug across binaries, coverage-guided fuzzing, and proofs of\n\u003e concept that actually execute and are verified (including foreign-arch MIPS and ARM targets under\n\u003e qemu). Expect rough edges. Release-by-release history lives in [`CHANGELOG.md`](CHANGELOG.md).\n\n---\n\n## Install\n\nYou will need Python 3.11 or newer, a Docker daemon your user can talk to (check with `docker run\n--rm hello-world`), Node.js 18+ with npm (the setup builds the web UI), and Linux or macOS. No API key\nis required, since the default mock backend runs fully offline.\n\nThere are two equivalent ways to bootstrap, so you are **not** required to install the\n[`just`](https://just.systems) task runner if you'd rather not. Both build the virtualenv, install the\ndependencies and the web UI, and then drop you into the very same interactive setup wizard — pick\nwhichever you prefer:\n\n```bash\ngit clone https://github.com/branover/hexgraph.git hexgraph \u0026\u0026 cd hexgraph\n\n# Option A — with the `just` task runner (recommended):\njust setup                 # venv + deps + web UI, then the interactive setup wizard\njust serve                 # → http://127.0.0.1:8765\n\n# Option B — without `just`, a plain shell script:\n./setup.sh                 # the same venv + deps + web UI, then the same wizard\n.venv/bin/hexgraph serve   # → http://127.0.0.1:8765\n```\n\n(`just setup` is in fact a thin wrapper that just calls `./setup.sh`, so the two paths can't drift.)\nEither one hands off to an interactive setup wizard. The wizard walks you through the optional\nfeatures, and for each one that relaxes the security posture it shows you the implication and asks you\nto confirm before turning it on. It then writes your settings and builds the images you picked, and it\ncan optionally register HexGraph's MCP server with a coding agent and install the VR skill for you\n(both local-only, no secret). If you accept the defaults you stay in the static-only posture;\neverything beyond that is something you turn on yourself, with eyes open. To skip the prompts and take\nthe static-only defaults, pass `--yes` (`just setup --yes` or `./setup.sh --yes`). For the wizard, the\nmanual step-by-step, the non-interactive CI mode, and Ghidra, see **[docs/setup.md](docs/setup.md)**.\n\n\u003e To install `just` without sudo:\n\u003e `curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin`\n\u003e (make sure `~/.local/bin` is on your `PATH`), or `snap install just`. Run `just` on its own for the\n\u003e full, grouped recipe menu.\n\n### Run it with Docker instead\n\nIf you would rather not install anything on the host, you can run the whole workbench in a container.\nFrom a checkout, one command brings it up:\n\n```bash\ndocker compose up --build      # or: just up   →  http://127.0.0.1:8765\n```\n\nThat builds an app image bundling the web UI and the Python backend, runs the database migrations on\nstartup, and serves the workbench. The service is published only on the host loopback\n(`127.0.0.1:8765:8765`), so it stays local to your machine exactly the way the pip install does. Your\n`ANTHROPIC_API_KEY` is passed through from the environment if you have one set, no key is ever baked\ninto the image, and with nothing set the container falls back to the offline mock backend.\n\nThere is one thing worth understanding before you take this path. The compose file mounts the host's\nDocker socket into the app container, because HexGraph needs to talk to a Docker daemon to launch its\ndisposable sandbox, build, fuzz, and rehosting containers. In this deployment those sibling containers\nrun on your host daemon, isolated the same way they are under the pip install. Mounting the daemon\nsocket effectively gives the app container root-equivalent control over your host's Docker. That is a\ndeliberate trade-off for a single-user, local, self-hosted tool you run on your own machine, and it is\nnot a hardened or multi-tenant posture. Do not expose this stack to untrusted users or networks. If\nthat trade-off is not one you want to make, the host pip path above is still the primary and\nrecommended way to run HexGraph for development.\n\nStop the stack with `docker compose down` (or `just down`); your project database and findings\npersist in a named Docker volume.\n\nBy using HexGraph you agree to use it only against targets you are authorized to analyze. Please read\n[DISCLAIMER.md](DISCLAIMER.md).\n\n---\n\n## The core loop\n\nEverything in HexGraph exists to prove one loop: **target → task → structured finding → graph → spawn\nthe next task.** The fastest way to watch it happen is the demo, which runs the whole thing on the\nbundled targets, offline, for free, and exits 0:\n\n```bash\njust demo\n```\n\nYou can also drive it yourself. Ingest a target (recon runs automatically and unpacks firmware into\nchild targets), then launch tasks from the UI and triage the findings they produce:\n\n```bash\n.venv/bin/hexgraph ingest tests/fixtures/synthetic_fw.bin --name demo\n.venv/bin/hexgraph serve          # → http://127.0.0.1:8765\n```\n\nThere are two ways to drive the loop. Both write into the same graph, and both keep target bytes\ninside the sandbox.\n\nThe first is the **web UI**: pick a target, choose a task (recon, static analysis, RE, a pattern\nsweep, harness generation, fuzzing, or a PoC), and run it. Behind your chosen backend, HexGraph runs\nan agent loop. The model asks for sandboxed tools (decompile, strings, imports, xrefs, fuzz), HexGraph\nexecutes them, and the loop continues until the model emits findings. You triage the results and can\none-click whatever follow-up it suggests.\n\nThe second is a **coding agent over MCP**. Running `hexgraph mcp install` registers HexGraph as an MCP\nserver, and Claude Code, Codex, or gemini-cli can then inspect targets and populate the graph on their\nown through the same sandboxed tools. The details are in **[docs/mcp.md](docs/mcp.md)**.\n\nIn both cases the model only ever directs the work; HexGraph runs the tools. A plain API key is enough\non its own, and no external coding agent is required.\n\n| Backend | Select with | Notes |\n|---|---|---|\n| `mock` (default) | — | Deterministic, schema-valid findings from fixtures. No key, no network. Powers dev, CI, and `just demo`. |\n| `anthropic` | `--backend anthropic` / `HEXGRAPH_LLM_BACKEND=anthropic` | Your own key via `ANTHROPIC_API_KEY` (env or `config.toml`). Spends real tokens. Needs `pip install -e \".[byok]\"`. |\n| `claude_code` | `--backend claude_code` | Uses your local `claude` CLI, headless. |\n\nHexGraph never logs or stores your API key.\n\n---\n\n## Features\n\nEverything above the static-only baseline is off by default and toggled in **Settings** (or with\n`hexgraph config set \u003ckey\u003e \u003cvalue\u003e`). Each feature that relaxes the security posture is its own\nseparate, explicit opt-in.\n\n| Feature | What it adds | Doc |\n|---|---|---|\n| **Typed graph + findings** | Targets, functions, sockets, endpoints, hypotheses, and findings as typed nodes joined by typed, attributed edges, all browsed, launched, and triaged in a three-pane UI. | [graph-ui.md](docs/graph-ui.md) |\n| **Verification \u0026 the assurance ladder** | Every finding carries an assurance level (`code_present`/`input_reachable` × `static`/`dynamic`), and opt-in **PoC verification** executes the target against an unforgeable `{{NONCE}}` oracle (foreign-arch via qemu-user). | [verification-assurance.md](docs/verification-assurance.md) |\n| **Fuzzing** | Coverage-guided, surface-aware, campaign-driven fuzzing (AFL++, libFuzzer, qemu-mode, boofuzz, desock), detached and crash-safe, with live triage, dedup, minimization, and one-click re-verification. Campaigns can run on a beefier host you own. | [fuzzing.md](docs/fuzzing.md) |\n| **Build from source** | Compile a managed source tree into an instrumented, reproducible artifact through a recorded recipe HexGraph runs in the sandbox, with the build-to-fuzz handoff wired up automatically. Includes an in-browser **Source / IDE tab** with coverage shading. | [build-from-source.md](docs/build-from-source.md) |\n| **Dynamic surfaces, rehosting \u0026 remote** | Model a running web service or a raw-TCP daemon as a first-class **surface**, **rehost** a whole firmware image under full-system emulation, or assess a physical **remote** device over SSH/telnet, all with bounded and audited egress. | [dynamic-surfaces-rehosting-remote.md](docs/dynamic-surfaces-rehosting-remote.md) |\n| **Coding-agent integration (MCP)** | Drive HexGraph from Claude Code, Codex, or gemini-cli, or have HexGraph drive a headless agent in delegate mode. Either way the agent is restricted to HexGraph's sandboxed tools. | [mcp.md](docs/mcp.md) |\n\n### The opt-in policy tiers, briefly\n\nThe enforced default is static-only with `--network none`. From there, each higher tier sits behind\nits own gate and nothing relaxes anywhere except the single [policy\nseam](docs/verification-assurance.md). `features.poc` and `features.fuzzing` allow sandboxed\nexecution; `features.build` compiles a source tree; `features.build_fetch` adds a separate, audited,\nallowlisted dependency fetch; `features.network` permits bounded loopback and private-network egress;\n`features.rehost` boots full-system emulation; `features.remote` reaches one authorized live device;\nand `features.fuzz_remote` runs a campaign on a compute host you own. You turn on only what a given\nengagement needs.\n\n---\n\n## CLI\n\nRun `.venv/bin/hexgraph \u003ccommand\u003e` (or just `hexgraph` with the venv active):\n\n```text\nhexgraph ingest \u003cpath\u003e [--name N] [--project ID] [--no-recon] [--backend B]\nhexgraph targets \u003cproject\u003e\nhexgraph run \u003ctarget\u003e --type T [--objective TEXT] [--function F] [--backend B] [--mock-scenario S]\nhexgraph rehost \u003ctarget\u003e [--brand HINT]      # boot firmware under emulation (needs features.rehost)\nhexgraph findings \u003cproject\u003e [--status S] [--export FILE]\nhexgraph graph \u003cproject\u003e --export FILE\nhexgraph config list | get K | set K V       # managed settings + optional-feature toggles\nhexgraph mcp [--tools read,write,run] | mcp install [--agent A] | mcp --check\nhexgraph serve [--host H] [--port P]          # loopback-only API/UI (default 127.0.0.1:8765)\n```\n\nThe task types are `recon`, `static_analysis`, `reverse_engineering`, `pattern_sweep`, and\n`harness_generation`, plus `fuzzing`, `poc`, and `agent_delegate` once you enable them. Web surfaces\nadd `surface_recon` and `web_recon`. The full configuration story (environment variables,\n`config.toml`, and how the layers override each other) is in **[docs/setup.md](docs/setup.md)**.\n\n---\n\n## Security model\n\n- **Loopback only.** The server refuses to bind a non-loopback address unless you set\n  `HEXGRAPH_I_KNOW_WHAT_IM_DOING=1`.\n- **Hostile-target isolation.** Every operation on target bytes runs in a fresh container with\n  `--network none`, a read-only root filesystem, a tmpfs scratch space, memory, CPU, and PID limits,\n  and a wall-clock timeout. Only HexGraph's own probe scripts ever run in there.\n- **Static by default, with capability that is opt-in and graduated.** Each tier is a separate,\n  explicit opt-in that flips the single policy seam, and nothing relaxes anywhere else. The same\n  sandbox hardening holds for every tier, with foreign-arch work running under qemu-user rather than\n  on the host. The full ladder is in [docs/verification-assurance.md](docs/verification-assurance.md).\n- **The model never sees raw target bytes**, only tool output.\n- **Secrets are never persisted or logged.** Your API key, along with any SSH or remote-Docker\n  credentials, lives only in your environment or `config.toml`. HexGraph reads it on demand and\n  reports it as present or absent, never by value.\n\n---\n\n## How it works\n\nHexGraph is built around a handful of clean seams. You change behavior by swapping an implementation\nbehind a seam, never by branching on the backend, the tier, or the executor.\n\n- **`LLMBackend`** makes `mock`, `anthropic`, and `claude_code` interchangeable; task code never knows\n  which one is in play.\n- **Executor** is the single container boundary for all target-byte handling, whether that container\n  runs on local or remote Docker.\n- **Decompiler** is radare2 by default, with Ghidra available behind the same seam.\n- **Rehoster** drives full-system firmware emulation, using FirmAE for vendor blobs and qemu+KVM for\n  disk images.\n- **Policy** is the one place, and the only place, the static-only invariant is relaxed.\n\nThe Finding is the heart of the product. Every task and every backend, the mock included, emits the\nsame frozen schema (`src/hexgraph/schemas/finding.schema.json`), and a `finding_type` field (kept in a\nDB envelope, not the schema) classifies it for triage.\n\nThe data model is SQLite through SQLAlchemy, with UUID ids and WAL mode so the UI and an agent's MCP\nserver can share the database at the same time. It holds a `project`, a `target` (a self-referential\ntree of reachable surfaces), `node` rows (typed sub-file and conceptual entities), a polymorphic\ntyped, attributed `edge`, a `task`, and a `finding`. The graph is relational, and Neo4j is\ndeliberately out of scope. More detail lives in [docs/graph-ui.md](docs/graph-ui.md).\n\nA few test targets ship under `tests/fixtures/` (regenerate them with `just fixtures`): `vuln_httpd`\nwith its unbounded `strcpy`, `libupnp.so` as a pattern-sweep sibling, and `synthetic_fw.bin`, a\nsquashfs firmware that unpacks into both. A set of escalating, CVE-class challenge targets lives under\n`tests/fixtures/challenges/`.\n\n---\n\n## Development\n\n```bash\njust                 # list every recipe, grouped\njust test            # the full suite (mock backend; sandbox/Docker tests auto-skip without the image)\njust demo            # the full offline loop, exits 0 — doubles as a smoke test\njust ui              # rebuild the SPA (after any frontend/ change)\njust showcase --reset \u0026\u0026 just capture   # regenerate the doc screenshots (see docs/images/README.md)\n```\n\nThe source lives under `src/hexgraph/` (`models/`, `llm/`, `db/`, `sandbox/`, `engine/`, `api/`,\n`cli.py`, `mcp_server.py`). For the working agreement, the seam rule, and the worktree-and-PR\ndiscipline, read [`CLAUDE.md`](CLAUDE.md). Release history is in [`CHANGELOG.md`](CHANGELOG.md).\n\n---\n\n## Out of scope, by design\n\nHexGraph does not do accounts or multi-user, cloud or hosted compute, exploit *generation*, Neo4j, or\nKubernetes. Dynamic execution exists only as the opt-in, policy-gated, sandboxed path described above.\nIt never runs unsandboxed, and it never runs on the host.\n\n---\n\n## License\n\nHexGraph is released under [**AGPL-3.0**](LICENSE). It is free and open: use it, run it, study it, and\nmodify it as you like. The copyleft terms mean that any modified version you distribute, or offer to\nothers over a network, has to be released under the AGPL-3.0 too, which is what keeps the project open\nand prevents a closed, proprietary fork. There are no license gates and no paid tiers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbranover%2Fhexgraph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbranover%2Fhexgraph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbranover%2Fhexgraph/lists"}