A Rust-native security gateway that reviews your code before git does, entirely on-device. Sample findings.
The commit pipeline
Three layers with very different budgets, because a pre-commit hook that takes five seconds gets uninstalled.
| Layer | Runs on | Budget | Verdict |
|---|---|---|---|
| 1 — Rust regex | CPU | under 1ms | blocks |
| 2 — CoreML | Apple Neural Engine | ~200ms | blocks |
| 3 — Qwen2.5-Coder via MLX | local model | ~8-20s | advises |
| 3.5 — static analysis | local tools | parallel with 3 | advises |
- 1git commit
- 2staged diff, added lines only
- 3strip .localforgeignore paths
- 4layers 1 and 2 gate
- 5layer 3 writes a report
Layer 1 catches the obvious with no model at all. Layer 2 catches what a pattern cannot. Layer 3 is an opinion, not a gate — it never blocks a commit, it informs. Only added lines are scanned, so a commit that removes a secret is not punished for touching it.
A blocked commit
The key below is the one AWS publishes in its own documentation, so it is safe to print and it still trips layer 1.
Findings in the staged diff
- AWS access key in config.samplelayer 1 · blocked
AKIAIOSFODNN7EXAMPLE
- Hard-coded connection stringlayer 2 · blocked
Classifier score above threshold.
- SQL injection risk in fetch_records()layer 3 · advisory
User input concatenated into a query string.
- Error path swallows the exceptionlayer 3 · advisory
What the hook did
- 0msRegex scanlayer 1
1 finding · commit blocked
- ~200msCoreML classificationlayer 2
1 finding · commit blocked
- advisoryQwen reviewlayer 3
one consolidated report written, commit already stopped
- advisoryStatic analysislayer 3.5
same JSON schema, merged into the same report
Everything runs on the machine. Nothing in the diff leaves the device, which is the reason it can be pointed at client code at all.
Layer 1 — what it blocks
Deterministic patterns across thirteen providers, compiled once at startup. No subprocess, no model load.
| Provider | Patterns |
|---|---|
| AWS | Access Key ID · Secret Access Key |
| GCP | API key · service account JSON |
| Azure | Storage key · SAS token |
| Stripe | Live secret key · restricted key |
| GitHub | PAT · fine-grained PAT · Actions secret |
| Slack | Bot, user and app tokens · webhook URLs |
| Twilio · SendGrid | Account SID · API keys |
| npm · PyPI | Access tokens |
| HuggingFace · Anthropic · OpenAI | API tokens |
| Shopify | Access token · shared secret |
| Private keys | RSA · EC · DSA · OPENSSH · PuTTY |
| .env assignments | SECRET_KEY=bare_value shapes |
False positives are handled by a .localforgeignore in the repo root — matching diff hunks are stripped before scanning reaches any layer. Test fixtures with deliberately fake keys belong there, as does the pattern file itself.
The on-device classifier
A TF-IDF character n-gram vectorizer and a logistic regression, running on the Neural Engine. Small on purpose: it has 200ms.
Model card
- Version
- 2.1.0
- Training samples
- 297 (170 risky / 127 clean)
- Languages
- 11
- Features
- char 3-5gram, 1024
- Compute
- CPU and Neural Engine
Measured
- Train accuracy
- 89.56%
- CV F1 (5-fold)
- 0.754 ± 0.021
- Held-out
- 32 of 33 cases
- Previous version
- 0.496 ± 0.229
The jump from the previous version matters less for the mean than for the variance: ±0.229 across folds meant the old model's score depended on which fold you looked at. Rebuilt from 81 Python-only samples to 297 across eleven languages.
Covered languages
- Python, JavaScript, TypeScript, Java, Gotrained
- Rust, C#, PHP, Ruby, Swift, Kotlin, SQLtrained
Retrainable in under five seconds on M-series hardware, then redeployed with localforge --install.
Layer 3 — the written review
Report header
- Severity
- MEDIUM
- Summary
- SQL injection risk in fetch_records()
- Diff hash
- sample
- Model
- Qwen2.5-Coder-7B, 4-bit, local
- Written to
- ~/.localforge/reports/commit_<ts>.txt
Review categories
- Securityblocks nothing, flags plenty
Injection, insecure crypto, path traversal, unsafe deserialization, disabled TLS.
- Bug riskadvisory
Off-by-one errors, unhandled exceptions, null dereferences, race conditions.
- Code qualityadvisory
Dead and orphan functions, unused variables, overly complex logic.
Two guards keep the advisory usable: a clean-diff fast path skips the model entirely when no added line matches any risky keyword, so refactor commits cost nothing; and a false-positive filter drops known-safe shapes — parameterized queries, list-form subprocess calls — before anything is written. Large diffs are chunked at file boundaries and the findings merged into one report per commit.
Layer 3.5 — deterministic tools
For categories where a linter beats a language model, run the linter. Same schema, same report.
| Language | Tools | Looking for |
|---|---|---|
| Python | bandit · pylint | security · dead code, unused imports |
| JavaScript · TypeScript | eslint | no-unused-vars, no-eval |
| Go | go vet · staticcheck | correctness |
| Rust | cargo clippy | suspicious, correctness, perf, dead_code |
Installed automatically where the toolchain is present, and skipped quietly where it is not.
Repositories
The native app's two tabs are Monitor, which tails the live scan log, and Repos, below.
| Repository | Hook | Last run |
|---|---|---|
| sample-repo-a | Active | blocked 1 |
| sample-repo-b | Active | clean |
| sample-repo-c | Outdated | clean |
| sample-repo-d | Other hook | — |
| sample-repo-e | Missing | — |
What the tab does
- Scan Folderno terminal needed
Discovers every git repo under a folder you pick and offers to protect each one.
- Upgrade all hooksone click
Which is the only reason hook versions are tracked per repo at all.
- Reveal in Finderper repo
The Rust CLI does the work, so the app is optional — but a hook that fails silently is a hook nobody trusts, which is what the Monitor tab is for.
Command line, team install, audit export
Commands
- localforge --scanwhat the hook calls
- localforge --install <repo>hook, binary and model
Also writes PATH, installs the CoreML model and shims, and registers the repo with the app.
- localforge --list-repos · --upgrade-allfleet
- localforge --install-org <repo>team
Generates a shell script to paste into a dev setup doc. Each engineer runs it once; no admin rights needed.
- localforge --export-report json|csvcompliance
Commit id, timestamp, severity, summary, finding count and report path per row — for SOC 2, ISO 27001 and due-diligence questionnaires.
- localforge --mcp-port 7777IDE integration
MCP request
- method
- scan
- file_path
- src/api.py
- staged_diff_content
- +aws_token = '...'
MCP response
- blocked
- true
- blocked_by
- layer1
- layer2_score
- sample
- advisory
- null
JSON-RPC 2.0 over a socket, two methods: scan and ping. Enough for Cursor or VS Code to ask the same question the hook asks, without a second engine.