Cold-start latency, binary size, and the methodology behind every published number. The default --code scan adds a whitebox + dependency-scan phase (govulncheck / dep-CVE) on top of the secrets pass — broader coverage out of the box, at a flat ~40 ms cold-start cost that the v0.11 secrets-only path didn't pay. Use --fast to skip it.
Time from fendix scan exec to JSON-on-stdout exit. Includes process spawn, argv parse, scan setup, scan execution, JSON render, and exit. Cache wiped between every run so each invocation pays full cold-start cost.
| Configuration | p50 | p95 | mean | CI exit gate |
|---|---|---|---|---|
| v0.18.0— current default; adds a whitebox + dep-scan phase after secrets (~40 ms, mostly fixed) | 45 ms | 51 ms | 46 ms | ✅ 11× under 500 ms |
v0.18.0 + --python-engine— opt-in whitebox engine; now ~on par with default, since the default already pays the whitebox-phase cost | 43 ms | 52 ms | 45 ms | ✅ 11× under 500 ms |
| v0.11.0 default— what v0.11 ships | 6.1 ms | 7.0 ms | 6.1 ms | ✅ 82× under 500 ms |
v0.11.0 + --python-engine— opt-in legacy whitebox path (+ TASK-134 path-traversal) | 40.7 ms | 45.1 ms | 41.3 ms | ✅ 12× under 500 ms |
| v0.9.0 default— TASK-118 baseline | 5.6 ms | 6.3 ms | 5.6 ms | ✅ 89× under 500 ms |
| v0.8.0— pre-Phase-17b, embedded Python | 7.3 ms | 8.1 ms | 7.2 ms | ✅ 68× under 500 ms |
The extra milliseconds buy a lot more scanning.
v0.11 mostly ran secrets on --code. v0.18 adds a full whitebox + dependency-scan phase: an AST taint analyzer (interprocedural data-flow, Proven-Path route binding), transitive dependency-CVE (govulncheck + OSV), and an upgraded DAST module with new check categories (cookie flags, open-redirect, host-header, GraphQL, method-tampering, rate-limiting). On a real Flask app that meant +16 findings the old engine missed — so the jump from ~5 ms to ~45 ms is the cost of materially deeper coverage, and it still clears the CI gate 11× over. Need the old speed for a pre-commit hook? --fast drops the heavy phase.
v0.18 default vs opt-in
≈ on par
45 ms vs 43 ms p50 — the Python-engine startup cost that dominated earlier versions is now a rounding error next to the native scanners.
Where the time goes
whitebox phase
v0.11 default ran secrets only and finished in ~2 ms. v0.18 adds a whitebox + dependency-scan phase (the ~40 ms between 'secrets complete' and 'whitebox complete' in the trace). It's a flat cost, not proportional to repo size, and not semgrep (skipped if absent) — pass --fast to drop it for pre-commit speed.
CI exit gate
~11× under
Target is <500 ms p50 for code-only scans. v0.18 default = 45 ms p50 (min 41 ms), comfortably inside the gate.
We measure the user-perceived cost — wall-clock time from typing fendix scan to receiving the JSON. Go-internal benchmarks would only capture the middle of that path; the harness uses Python's time.monotonic() around subprocess.run so process spawn, argv parse, scan setup, scan execution, JSON render, and exit are all included.
python/tests/fixtures/secrets_target/ — 5 small files, 30 secrets findings. A tiny fixture isolates the fixed cost of cold start from the variable cost of scanning a large codebase. Bigger codebases will show bigger absolute numbers; the delta between PRE/POST stays the same (it's the cost of Python startup we removed).
N=30 runs per configuration. The ~/.fendix/engine directory is wiped before every run so each invocation pays the full cold-start cost (no warm cache).
Apple M-series, both binaries built with go build -ldflags="-s -w". Numbers will scale predictably on x86_64 — the relative deltas between PRE/POST and default/opt-in hold across architectures.
| Build | Bytes | Δ |
|---|---|---|
| PRE-TASK-118 (with embedded Python) | 19,002,482 | — |
| POST-TASK-118 (no embedded Python) | 18,903,282 | −99,200 (−0.5 %) |
The size delta is small because Go's binary build compresses embedded text aggressively. The real win is the dependency posture — fendix no longer carries a Python interpreter requirement at all in the default path; users can now run scans on machines without Python installed (CI runners, distroless containers, embedded systems).
Every number on this page is reproducible. Clone the engine repo and run the harness — same methodology, same fixture, same harness as the published numbers.
# Build the binary then run the cold-start benchmark
make build
python3 scripts/bench/coldstart.py
# Output (v0.18.0, Apple M-series; 30 runs, ~/.fendix/engine wiped per run):
#
# binary: ./bin/fendix
# fixture: python/tests/fixtures/secrets_target
# runs: 30
#
# [default (no Python whitebox)] N=30 min=41.2 p50=45.1 p95=50.9 mean=45.8 (ms)
# [--python-engine (opt-in)] N=30 min=41.0 p50=42.9 p95=51.7 mean=44.1 (ms)
#
# Note: v0.18 runs the full native check suite (secrets, semgrep, deps,
# textscan, + DAST checks) on --code, so cold-start is higher than the
# v0.11 secrets-only path. With --python-engine now ~on par with default,
# both stay an order of magnitude under the 500 ms CI gate.Want the legacy Python whitebox checks (auth, injection, AST analysis)? Add --python-engine — requires a local python/ tree or FENDIX_ENGINE pointing at one.
# Default — no Python required, fastest path
fendix scan --code ./src --format json
# Opt back into the Python whitebox engine
# (requires a local python/ tree or FENDIX_ENGINE pointing at one)
fendix scan --code ./src --python-engine --format json--python-engine and --no-native-deps.