A flock of "vulture" agents that turn idle Claude / Codex subscription quota into continuous C/C++ vulnerability auditing.
VultureFlock is a multi-agent system that drives Claude Code / Codex CLI agents to read and reason about C/C++ codebases the way a human researcher would, and emits evidence-backed Markdown bug reports. Scheduling is quota-aware: it spends only the subscription capacity you are not using, ramping each backend up to a configurable utilization target and then backing off until the usage window resets.
It is built for recall, not precision — it surfaces many plausible candidates with a self-checked evidence chain, and leaves serious verification / PoC generation to downstream systems.
Status: alpha, but functional end-to-end. The full pipeline runs as a persistent, quota-gated service (Repo Analyzer → Auditor → optional Build/PoC) and writes bucketed Markdown bug reports. It has produced evidence-backed candidate reports on real targets (libpng, php-src). Layout and APIs may still change.
- Does: continuously enumerate unaudited scopes in target C/C++ repos, dispatch auditor agents under a token budget, and write Markdown bug reports.
- Does not: run fuzzing campaigns, ship a SAST rule engine, auto-fix code, or seriously validate bugs. PoC attempts (when a sanitizer build is available) are opportunistic and never gate report retention.
Vulture brain (LLM director) ── the SOLE work generator: reads a digest and decides
│ what to analyze / build / audit / PoC, balancing repos
│ and chasing productive areas (no deterministic work-gen)
▼
Controller (deterministic kernel)
├─ Job queue (SQLite) ── single source of truth for *work*
├─ Expander ── turns the brain's intents into deduplicated job rows
├─ Memory (Markdown tree) ── audit-coverage map; agents consult it before working
├─ Reports (Markdown tree) ── deliverable + source of truth for *findings*,
│ bucketed per repo: high / medium / low / abandoned
├─ Quota-aware scheduler ── per-backend, per-window (5h / 7d) usage caps
└─ Backends ── Claude Agent SDK / Codex CLI (per-backend model/effort/sandbox)
Agent roles == job types:
Repo Analyzer → (Build Agent) → Auditor → (PoC Attempter)
The brain is the only source of work; the controller below it makes no mining decisions — it just enforces safety (quota caps, concurrency, dependencies, budget) and dispatches each job to whichever backend sits furthest below its cap. The Auditor writes its report directly, so there is no separate reporter step. Disable the brain and the loop simply runs whatever is already queued.
- Python 3.12+
uv(recommended) orpip- At least one agent backend installed and logged in:
- Claude: the
claudeCLI (Claude Code) + a logged-in subscription - Codex (optional): the
codexCLI, logged in
- Claude: the
- For the optional Build/PoC stages only (
do_build/do_poc): a C/C++ toolchain with Clang/LLVM + AddressSanitizer/UBSan. Tested with Clang 14; any reasonably modern Clang works. On an old Clang 11 runtime whose ASan aborts at startup (SetAlternateSignalStackmmap failure), setasan_options = "use_sigaltstack=0"in your config. If your runtime sandboxes build/PoC commands with ptrace (e.g. Codex's sandbox) so LeakSanitizer can't run, setasan_options = "detect_leaks=0"and/or run Codex unsandboxed with[backends.codex] sandbox = "danger-full-access". Auditing and reporting do not need a compiler — only the sanitizer build + PoC attempts do.
# from a checkout
uv tool install .
# or, for development
uv venv
uv pip install -e ".[dev]"vultureflock init # write vultureflock.toml
$EDITOR vultureflock.toml # enable backends, set usage caps, add targets
vultureflock target add <repo-url> # (or edit the [[targets]] blocks directly)
vultureflock doctor # smoke-test each enabled backend
vultureflock run # start the persistent auditing loop (quota-gated)
vultureflock status # job queue + per-backend quota (5h/7d used vs cap)
vultureflock report list|index|show <id> # browse reports / rebuild the index
vultureflock audit <repo> <scope> # inject a high-priority audit into a running loopAll settings live in vultureflock.toml — run vultureflock init to write a
fully-commented starter (also browsable at
config.example.toml). It covers per-window
usage caps (global or per backend), concurrency, per-backend model / effort /
sandbox, per-job budgets and timeouts, the [brain] director, the optional
do_build / do_poc stages, and target projects. PoC attempts run in throwaway
scratch/<repo>__<id>/ dirs bounded by a size-based GC; only a confirmed PoC has its
whole attempt dir moved intact to poc/<repo>/<id>/ — the reproducible first-scene, kept
out of the GC's reach. Mutable fields hot-reload while the loop runs; credentials come
from each CLI's own login.
PoC/build agents compile and run arbitrary harnesses, and a malformed or oversized input can make one allocate without bound and OOM the whole machine (which usually has no swap, so the kernel kills processes abruptly). Three layers guard against this:
- ASan RSS self-limit —
asan_options = "hard_rss_limit_mb=N …"makes a sanitizer harness abort itself once its resident memory passesN, before it can exhaust the box. Inherited by both backends' harnesses. - Per-agent cgroup cap —
poc_memory_max_mbruns each PoC/build agent's whole process tree in a transientsystemd-run --scopewith a hardMemoryMax(OOMPolicy=kill), so a runaway harness OOM-kills only its own scope. Applies to Codex (Claude spawns inside its SDK and leans on layers 1 and 3). - Memory watchdog — if system
MemAvailablefalls belowmem_watchdog_min_mb, a background thread kills the heaviest agent subtree to keep the box alive.
A PoC that is killed for OOM (or whose harness is Killed/exits 137) is treated as
inconclusive, never a trigger, and the finding is abandoned, not retried — a
memory bomb only repeats. Set poc_memory_max_mb / mem_watchdog_min_mb to 0 to
disable a layer.
Every triggerable crash is not a reportable bug. A policies/<project>.md file (under
the work dir, operator-authored) lets you impose a project's real reporting bar — e.g.
a VRP's in-scope rules — on mining and trigger-evaluation. Two optional sections:
## Harness— pins which harness to build/use (it still decides how to compile it), with an optional**Build options:**line. E.g. force the project's own CLI rather than a fuzz target. The pin also governs trigger evaluation: the PoC stage may only establish a trigger through the pinned harness, so a crash reachable solely by some other entry point comes backtriggered=false. (Clear the project's sanitizer build to re-build onto a newly-pinned harness.)## Reporting constraints— free-text rules for what counts as reportable (e.g. "memory-safety only; assertion failures / API-misuse / OOM are out of scope"). The auditor avoids out-of-scope classes, and the PoC stage demotes a triggered bug that violates them to thelowbucket — kept as a record (withpoc_reportable: falseand a reason) but neither promoted nor reported. This keeps reportable output to bugs that actually clear the bar. No policy file ⇒ no constraints (default behaviour).
The pipeline above is built and runs end-to-end. Not yet done:
- Multi-account quota pooling — rotate across several Claude / Codex logins to widen
the idle-capacity envelope. Today VultureFlock uses whichever account each CLI is
logged into;
vultureflock accountonly reports current usage. - Async brain + richer steering — the director plans in one blocking step every
interval; making it non-blocking, with finer control over the fleet, would let it react and steer faster.