OpenHack keeps a whitebox review in files, and the model calls come from your harness
Lightweight, file-based workspace for source-guided whitebox security review.
At a glance
- What is it?
- OpenHack is a Python workspace that turns whitebox security review into a chain of files: recon items, routing units, scenarios, candidates, triage decisions and findings. It ships no model client of its own, it expects a cloned checkout, and every phase stops for human approval before the next one starts.
- Who is it for?
- OpenHack suits a reviewer who already has a coding harness, a target they are allowed to test, and the patience to approve four phase gates one at a time. The durable chain is the strongest part of the design: nothing becomes a finding until an independent triage decision says so, and `validate-run` checks schemas, prompt hashes and coverage gates afterwards, which is more discipline than most prompt-driven review flows manage.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 126 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.
Editorial analysis
No model client ships with the package, only two dependencies
The dependency list is short enough to copy in full:
[project]
name = "openhack"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = [
"jsonschema>=4.18",
"PyYAML>=6.0",
]There is no HTTP client for a model provider in there, because the model calls do not come from this package. The division of labour is stated plainly: the harness, meaning Claude Code, Codex, Cursor or a custom runner, provides model execution, terminal access, repository access and human-in-the-loop approval, while openhack provides the durable workflow and the review artifacts. What it adds on top is the state machine. A command advances the run to the next durable state, an agent answers the exact prompt for that state, and the next recorder command validates the answer before materialising new work. Two libraries are enough for that because the state lives in JSON and YAML files rather than in a database.
The supported install is a clone with an editable install
The CLI is installed from the repository root:
python3 -m pip install -e .The reason it is an editable install of a checkout is stated next to it: this project is distributed workspace first, and the root level `agents/`, `config/`, `templates/` directories plus a writable `runs/` directory are runtime data. Invoke `openhack` from outside the checkout and it needs `OPENHACK_ROOT` pointed at the repository root. That is a real constraint on how you deploy it, because the state of a run is written beside the source tree rather than into a path you choose. One inconsistency is worth noticing next to it: the repository ships a `uv.lock`, so a uv based workflow is anticipated somewhere, while the only install command given is pip. Either path leaves the same constraint, an editable install of the checkout, because a wheel would not carry the runtime data directories.
pyproject.toml and setup.py declare the same metadata twice
Both build files are at the repository root and both are complete. pyproject.toml names the package openhack version 0.1.0, requires Python 3.9 or newer, registers the console script as `openhack = openhack.cli:main` and finds packages under `src`. setup.py repeats every one of those: the same name, the same version, the same description string, the same `packages=find_packages(where="src")`, the same `python_requires=">=3.9"`, the same two `install_requires`, the same dev extras and the same console_scripts entry point. The version is not read from the parsed table. setup.py opens pyproject.toml as text, walks the lines, stops at the next section header after `[project]`, and takes the first line that starts with `version`:
raise RuntimeError("Unable to read project.version from pyproject.toml")So the file that declares the version is parsed by string prefix in a second file, and the two declarations have to be edited together whenever either changes.
Every command stops at a checkpoint and prints the next one
The manual flow is one command at a time, and each one prints a checkpoint summary together with the next command to run:
# Create a run from a fresh git checkout
openhack init-run demo https://github.com/example/app.git --run-id demo-001
# Choose expert scope, then run reconnaissance
openhack run-recon demo demo-001 --all-agents
# Or scope the run to selected experts
openhack run-recon demo demo-001 \
--expert injection \
--expert broken-access-control
# Optional: enrich recon with bundled Semgrep rules
openhack run-recon demo demo-001 --all-agents --semgrep
# Generate the scenario backlog from routing units in one approved routing phase
openhack create-scenarios demo demo-001
# After the routerScope is a flag rather than a config file, either `--all-agents` or a repeated `--expert`. The optional `--semgrep` switch adds bundled Semgrep rules to recon, which is the one place another scanner enters the picture. To look at a run without changing it, `openhack summarize-run <target> <run-id>` prints counts and the next checkpoint command. The page also warns about cost: a full scope run over an entire codebase uses a lot of model tokens, cheaper models can still run the workflow, and the way to control spend is to review the generated scenarios and run only the ones you want to prioritise.
Human gates are phase gates, and approving a backlog is not a summary
Four approvals are named: the operator approves expert scope before recon, scenario routing after recon, the scenario backlog after the router answer, and the finding-triage backlog after candidate creation. The README is explicit about what approving a backlog means, namely process every unfinished item and not summarize the batch. That sentence is the difference between this and a report generator, because an approval at the backlog gate commits the run to working through a queue one item at a time. The long-running parts are described the same way, as item-by-item loops rather than bulk analysis. This section is also where the visible page stops: the heading Scenario review is a queue is followed by a line that begins Every `S#` and ends there.
Seven durable artifacts, and only two of them can create a finding
The chain is deliberately narrow:
recon item → routing unit → scenario → scenario result → finding candidate → triage decision → findingEach state has a file behind it. Initialized writes `run-config.yaml`, `sourcecode/` and `run-state.jsonl`, pinning the target to a source checkout, commit, branch and run id. Recon complete writes `recon-output/recon-items.jsonl` and `routing-units.jsonl`. The router prompt is `scenarios/scenario-router-prompt.md`, and once the router output passes coverage validation the backlog becomes `scenarios/index.jsonl` with `scenarios/backlog/S###.json` and `coverage-decisions.json`. Finished scenarios land in `scenarios/finished/S###.json` with a prompt hash, subagent id, reviewed files, status and evidence. A proposed issue becomes `finding-candidates/S###-F###.json`, which is explicitly not a final finding. Triage writes independent reportability, dedupe, confidence and severity reviews under `finding-triage/prompts/` and `finding-triage/decisions/`, and only accepted or downgraded decisions write `findings/*.md`. `validate-run` then checks schemas, prompt hashes, coverage gates, candidate and triage consistency, and whether the final findings were materialised.
Version 0.1.0, a badge for a release tag, and no published release
Versioning has one source of truth by rule and three places by practice. The rule is that pyproject.toml is the package source of truth, releases should be tagged with the same version in `vX.Y.Z` form such as `v0.1.0`, and the installed CLI reports the package version through `openhack --version`. In practice pyproject.toml holds `0.1.0`, setup.py reads that value back out of the same file by hand, and the README badge at the top points at a release page for tag v0.1.0. The repository lists no GitHub releases, so the badge leads to a release page that has nothing on it, and there is no tag to pin when you want to reproduce a review. The development defaults in the same file are strict for a project this young: mypy runs over `src` with untyped and partially typed definitions disallowed, and ruff targets Python 3.9. The last commit on the default branch is dated June 1, 2026.
Editorial conclusion
OpenHack suits a reviewer who already has a coding harness, a target they are allowed to test, and the patience to approve four phase gates one at a time. The durable chain is the strongest part of the design: nothing becomes a finding until an independent triage decision says so, and `validate-run` checks schemas, prompt hashes and coverage gates afterwards, which is more discipline than most prompt-driven review flows manage. It suits nobody who wants a one command scanner, because the run is a queue of items, and it does not carry its own model access, so the cost of a full scope recon lands on the harness you already pay for. Four things to check first: whether you have permission to review the target, since the tool clones the repository and hands an expert agent a scope; what you will do with `findings/*.md`, since only accepted or downgraded triage decisions write them; whether you can keep `runs/` writable in the checkout, because that is the supported install model rather than a workaround; and which version you are on, since the package reports `0.1.0`, the badge points at a v0.1.0 release tag, and no release is published on the repository. The last commit is dated June 1, 2026.
Frequently asked questions
What is Hadrian OpenHack?
A file-based workspace for source-guided whitebox security review: agents and tools that mimic how the Hadrian research team performs automated vulnerability research, keeping durable state in plain files. It is meant to run inside a model harness such as Claude Code, Codex or Cursor.
How do I install OpenHack?
From a clone of the repository root, with `python3 -m pip install -e .`. The project is distributed workspace first, so `OPENHACK_ROOT` has to point at the repository root if you invoke `openhack` from outside the checkout.
Does OpenHack call a language model itself?
Not through its own code. The declared dependencies are jsonschema and PyYAML, and the README says the harness provides model execution, terminal access, repository access and human-in-the-loop approval while openhack provides the durable workflow and review artifacts.
What does an OpenHack run cost in model tokens?
The README warns that running against an entire codebase with the full expert scope uses a lot of model tokens, that cheaper models can still run the workflow, and that the way to control spend is to review the generated testing scenarios and run only the ones you want to prioritize.
What files does an OpenHack run leave behind?
A narrow chain: recon item, routing unit, scenario, scenario result, finding candidate, triage decision, finding. Final reports are written to `findings/*.md`, and only accepted or downgraded triage decisions write them.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hadriansecurity-openhack)