Trust
Security
ForkReason analyzes hostile input and lets a model participate in a decision. Both are treated as attack surfaces rather than features.
Repository content is never executed
Analyzed repositories are untrusted input. ForkReason materializes a snapshot with git archive <pinned-sha> into an isolated staging repository, which yields exactly the tracked blobs at one commit. There is no working tree, no .git directory, no hooks, and no submodule content.
- No
npm install, build, or package script ever runs. - Every
gitcall is an argument array withshell=false. - The git environment sets
GIT_CONFIG_NOSYSTEM,GIT_CONFIG_GLOBAL=/dev/nullandGIT_ASKPASS=/bin/false, so repository configuration cannot influence execution. - Symlinks, hard links, devices, absolute paths and traversal are rejected before and after path resolution.
Verified by apps/api/tests/test_safe_intake.py against archives that attempt traversal and symlink escape.
Prompt injection is a release gate
Consensus does not defend against prompt injection. If the leader and every validator read the same malicious instruction and obey it, they agree on the same wrong answer — and agreement is exactly what consensus is supposed to mean.
The defences, in the order they apply:
- Deterministic preprocessing. Fingerprinting strips comments and string literals before any structural comparison.
- Minimal excerpts. The consensus digest is capped at 12,000 characters and never contains whole files.
- Strong delimiters. Untrusted content is fenced inside
<forkreason_evidence>tags. - Explicit inert-data instruction, stated as an absolute rule above the evidence block.
- Strict typed parsing against an allowed enum set.
- Deterministic rule checks in code — chronology, contradiction between verdict and confidence, and the shared-upstream requirement.
- Independent validator evaluation of the substantive fields.
- Fail closed: unparseable or off-enum output is rejected.
Verified by contracts/tests/test_prompt_injection.py, which places the mandated attack phrases in a README, a code comment, a string constant, HTML, commit metadata and challenge evidence — and asserts that a verdict demanded by injected text is refused even when the leader has already obeyed it.
Verdicts are checked for substance
A leader is never accepted because its output parses. The validator independently derives its own decision from the same bounded evidence, applies the same deterministic guards, and compares the stable field tuple: verdict, confidence, direction, shared upstream, independent origin plausibility, and evidence classes. Rationale prose is never compared for equality.
A test drives the exact scenario that matters: a leader that returns a valid-looking but substantively incorrect verdict. The validator must reject it.
Your keys stay with you
ForkReason holds no server key that can act for a user. Every state-changing GenLayer action is signed by the browser wallet. The backend prepares payloads and indexes transaction hashes it observes; it never signs a write on a user's behalf.
The chain is the authority on revision and verdict state. The application database indexes that state for search and display, and never silently becomes an alternative source of truth.
Browser protections
- A nonce-based Content Security Policy restricts scripts to what this server emitted for the current request. Styles allow inline because Next injects critical CSS that way.
frame-ancestors 'none',X-Frame-Options: DENY,nosniff, strict referrer policy and a restrictive permissions policy are set on every response.- No remote images, so the image optimizer is not an open proxy.
- A decided transaction is never reported as successful unless the execution result is
FINISHED_WITH_RETURN.ACCEPTEDonly means the committee agreed on the receipt.
Accepted residual risk
'unsafe-eval' in the CSP script-srcacceptedWalletConnect's SDK evaluates a dynamically built bundle. Removing it breaks wallet connectivity. Scoped to script-src; no third-party script origins are permitted.Disclosure
If you believe you have found a vulnerability, open a private security advisory on the repository. Please do not open a public issue for an unfixed vulnerability.
Full detail lives in SECURITY.md, docs/THREAT-MODEL.md and docs/SECURITY-FINDINGS.md.