Targgen
An agentic libFuzzer harnessing tool.
Targgen pulls in coding agents with different harnessing strategies to generate fuzzing harnesses for C/C++ libraries.
Docs: strategies · coordinator loop · config-fuzzing · honesty gates · CLI & arena spec · architecture · security & trust boundary
What a run does
- Bootstrap. Get the source, build the library with coverage and ASan instrumentation, and fetch seed corpora. For an OSS-Fuzz project it also fetches the committed harnesses and the Fuzz-Introspector coverage snapshot.
- Pull in a strategy. A strategy answers "what is worth harnessing?" — for example the functions production fuzzing never covers, or the functions that changed most recently. An agent reads the strategy's candidate pool and names the targets.
- Generate. One agent per target writes a harness and ensures it reaches the desired functionality.
- Validity Checks. A deterministic check replays fixed inputs against the harness. If a fixed input reproduces most of the harness's coverage, the harness is not effectively using fuzzer-generated data and is subsequently rejected.
- Fuzz. Admitted harnesses go into a shared pool of fuzz slots. A harness whose branches another harness already covers is pruned.
- Stop and report. The run ends on the first budget dimension that trips (time or token usage).
Harnessing strategies
Strategies are the core driver behind how harnesses are generated.
List a set of them in the arena's strategies:; the coordinator runs them
concurrently and pulls each in again as coverage plateaus.
| Strategy | Selects from | What it harnesses | Needs |
|---|---|---|---|
uncovered |
functions | Uncovered, high-yield buried functions from the Fuzz-Introspector snapshot. | An OSS-Fuzz project. On a raw repo the coordinator substitutes uncovered-from-run, the same selector over this run's own zero-coverage functions. |
high-value |
functions | High-value entry points reasoned straight from the source. | — |
git-churn |
functions | Functions with the largest recent, recency-weighted churn in Git history. | Readable Git history. |
api-sequencing |
sequences | A fixed producer→consumer call sequence around one state-carrying handle (the fuzzer supplies data, not order). | The CodeQL CLI. |
harness-variant |
harnesses | Config variants of committed + kept harnesses — vary one axis to unlock new coverage. Always runs in the loop, listed or not. | — |
config |
functions | Identify impactful build configurations for the target. | A config: block. |
user |
functions | Exactly the functions you name in functions:. |
— |
Config is a strategy aligned around pulling in an agent to identify and build non-traditional configurations for the target. config works in tandem with other harnessing strategies
to actually produce the fuzzing harness after the build. See config-fuzzing.
Requirements
Install these before the first run.
- Python ≥ 3.13 and
uv. - LLVM toolchain on
PATH:clang,llvm-cov,llvm-profdata. - The
claudeCLI, logged in, onPATH. Every agent stage is aclaudeinvocation, so runclaudeonce by hand first and complete the login. - CodeQL — only for the
api-sequencingstrategy. Targgen finds it through$TARGGEN_CODEQL,PATH, or./codeql/codeql. Without it, a run that listsapi-sequencingstops at the analyze step.
Disclaimers
Isolation — run Targgen in a container or a disposable VM. It compiles and executes
LLM-written C on the host, and its agents hold Bash. There is no sandbox. See
SECURITY.md for the trust boundary, what Targgen writes outside its workspace,
and what leaves the machine.
Cost — you are responsible for your own API usage. Every stage of a run invokes the claude
CLI, and Targgen applies no spending limit unless you ask for one. Cap a run with
budget.token_ceiling_usd, keep budget.wall_clock_seconds short on a first run, and monitor
your own usage.
Quickstart
1. Set up a source checkout (optional)
git clone https://github.com/trailofbits/targgen.git
cd targgen && uv sync
2. Write an arena.yaml
Three things are required: a source, a strategy set, and a wall-clock budget.
oss_fuzz_project: cjson # source: an OSS-Fuzz project name...
strategies: [high-value, harness-variant] # ...what to harness
budget: { wall_clock_seconds: 3600 } # ...and when to stop
That file is a complete campaign. Everything else has a default. The next section covers the other keys you may want to add.
3. Run it
If you cloned the repository, run:
uv run targgen arena -a arena.yaml --out ./run-bundle
To run the published package without cloning, use uvx from the directory containing
arena.yaml:
uvx targgen arena -a arena.yaml --out ./run-bundle
4. Read the results
--out gets report.json, report.md, and a portable bundle/. The report holds per-function
coverage, the run's duration and cost, and — for an OSS-Fuzz project — the coverage delta against the committed harnesses.
Writing an arena.yaml
Required keys
| Key | Value | Rule |
|---|---|---|
oss_fuzz_project: or repo_url: |
string | Set at least one. oss_fuzz_project gives you the OSS-Fuzz integration, the FI snapshot, and a coverage baseline to compare against. repo_url clones any repo directly. Set both to build a raw repo with the OSS-Fuzz integration as a build aid. |
strategies: |
list of strings | A non-empty list from the table above. An unknown name is an error that lists the valid ones. |
budget: |
mapping, with wall_clock_seconds |
Discovery stops after its rounds, but the fuzz slots keep taking bonus time on kept harnesses, so only a clock ends a run. |
The keys you are most likely to add
The Value column is what the YAML value must be. Targgen rejects the wrong shape at load time —
strategies: uncovered fails with "must be a list of strings, got a string" rather than starting a
run that harnesses nothing.
| Key | Value | Default |
|---|---|---|---|
| model | string | sonnet |
| effort | low|medium|high|xhigh|max | CLI default |
| run | string | arena |
| budget.token_ceiling_usd | number | none — the run has no spending ceiling until you set one |
| budget.follow_up_pull_ins | integer | 6 |
| concurrency | mapping of 3 integers | {max_strategies: 1, max_gen_agents: 2, max_fuzz_slots: 5} |
| target_count | integer | 5 |
| config | integer or mapping | off |
| functions | list of strings | [] |
| judge | boolean | false |
| configure_args | list of strings | [] |
Every key, its value type, and the sub-keys of budget, concurrency, and config are in the
arena spec.
Where the output goes
Two places:
--out DIR— the report and the portable bundle. This is what you read and share.- The workspace — everything else: the source checkout, the instrumented build, agent logs,
corpora, and coordinator state. It defaults to
~/.targgenand follows$TARGGEN_WORKSPACEor-w DIR. One project's artifacts live underprojects/<project>/, and each run forks its own outputs underruns/<run>/, so two runs never clobber each other.
Resuming
If a run is interrupted, pick it up from the state on disk:
uv run targgen arena -a arena.yaml --resume # reload state + continue the campaign
uv run targgen arena -a arena.yaml --resume-fuzz-only # reload + only fuzz existing harnesses
Both re-fuzz every non-discarded harness from its saved corpus; --resume also keeps pulling in new
strategy work. See resuming.
Bootstrap primitives (debugging)
The steps the coordinator runs internally are also callable standalone:
uv run targgen build -a arena.yaml # acquire source + build the instrumented library
uv run targgen analyze -a arena.yaml # run the strategies' selection-data providers
uv run targgen bootstrap -a arena.yaml # build + config discovery + analyze
uv run targgen seeds -a arena.yaml # collect the seed corpus on its own
Contributing
Issues and pull requests are welcome.
make dev # sync dev deps + install the prek hooks
make lint # ruff format --check, ruff check, ty check
make test # unit tests under test/
make format # ruff format + ruff check --fix
Work on a branch and keep a PR to one logical change. Three checks run on every PR: make lint,
make test, and make integration. That last one crosses the subprocess boundary the unit suite
stops at — it compiles and fuzzes a tiny C library with the real toolchain and installs a built
wheel — so it needs clang, llvm-cov, and llvm-profdata to run locally.
Two things worth knowing before you start:
- Tuning agent behaviour is usually a prompt edit, not a code change. The skills under
src/targgen/skills/are meant to be read and edited. - Adding a strategy or a data provider has a checklist: extending Targgen. Internals: architecture.
Please report security issues through SECURITY.md rather than a public issue.
License
Targgen is licensed under Apache-2.0.
Metadata
Release files for targgen 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| targgen-0.0.1.tar.gz | 274.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| targgen-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 600.9 kB
Release files / targgen-0.0.1.tar.gz
| Download URL | targgen-0.0.1.tar.gz |
|---|---|
| Size | 274.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
38a90c14237444e739094610e9d96284a58ad2e1b1f779880ea12729fbc5491c
|
|
BLAKE2b-256 checksum How to use checksums |
11b2acece16cabd1940afeed6ba377a7bd24db7ed09777142b81e6eb48ab72c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / targgen-0.0.1-py3-none-any.whl
| Download URL | targgen-0.0.1-py3-none-any.whl |
|---|---|
| Size | 326.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5431d3e0660879459b8fd3ba3b3a516da13ba882d5480591328ce89416a49996
|
|
BLAKE2b-256 checksum How to use checksums |
a4299045027580fa1a4e54b8994a4ad553fd45513bca7d5bacc2970c5d84626f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log