Scope-Forge
A formally verified policy language and compiler that turns access rules into proven, native machine code.
Policy mistakes are caught at build time, not in production.
01Thesis
Proven, not just tested.
Scope-Forge treats a policy as mathematics: every question about it is answered exactly, at build time, by a compiler whose own code is machine-checked with Verus. A proof covers every possible input, so the logic it covers has no untested case, which is exactly the case an attacker accelerated by AI searches for.
Why now. In 2026 Google's Threat Intelligence Group reported, for the first time, a threat actor using a zero-day exploit developed with AI, with "high confidence that the actor leveraged an AI model to support the discovery and weaponization of this vulnerability": a script that "enables the user to bypass two-factor authentication (2FA) on a popular open-source, web-based system administration tool" (Google Threat Intelligence Group, May 2026).
In November 2025 Anthropic reported a campaign in which "the threat actor was able to use AI to perform 80-90% of the campaign, with human intervention required only sporadically", at "an attack speed that would have been, for human hackers, simply impossible to match" (Anthropic, November 2025). In August 2026 the NSA, CISA, FBI, Department of Energy and EPA jointly advised that AI-generated exploitation scripts are "dramatically reducing the technical expertise and time required to develop working ICS exploitation scripts and malicious tools" (joint advisory, August 2026).
"31% of breaches now start with software vulnerabilities, beating stolen passwords as the top way attackers get in" (Verizon 2026 DBIR), and "the time between disclosure and exploitation has shrunk to days" (UK NCSC, May 2025). Testing checks the cases someone thought of; a proof has none.
- 001A policy describes exactly which requests are allowed; granting access is checking whether a request belongs to that set.In plain terms: Every access decision is one membership check.
- 002A policy that contradicts itself, or that no request could ever satisfy, does not build.In plain terms: Caught before anything is deployed.
- 003A security rule written into the policy is checked against every possible record; a change that breaks it stops the build with a counterexample.In plain terms: The rule cannot quietly erode over years of edits.
- 004Every policy question is decided by Scope-Forge's own verified set arithmetic; no solver runs when a policy is checked.In plain terms: The identical result on any machine.
- 005Policies compile ahead of time, through a verified back end, to native machine code.In plain terms: The policy that was proved is the policy that runs.
- 006Compiled policies integrate with existing applications and specialized systems through a dedicated Scope-Forge code generator.In plain terms: It fits the systems you already run.
If you run OPA or Cedar today: they evaluate each request against the policy you deployed, and Cedar's analysis can check a policy set when you run it. Scope-Forge makes the check the build: a contradiction does not compile, every security rule is held against every possible record, and the code that decides each request is itself machine-checked.
02Assertions
Security rules the build cannot skip.
Write the policy, not the proof. Write "contractors can never hold a clearance that permits restricted reads" into the policy once, and no later change can break it without the build failing, showing your reason, and producing the exact record that would have slipped through.
Below, that sentence as an assertion.
Principal requires {
employment: str where in "employee" || "contractor"
clearance: str where in "none" || "confidential" || "secret"
}
Contractor requires Principal && {
employment: str where == "contractor"
clearance: str where == "none"
}
RestrictedRead requires Principal && {
clearance: str where in "confidential" || "secret"
}
assert Contractor never in RestrictedRead
because "contractors never hold a clearance that permits restricted reads"
accepted: 3 declarations, 1 assertion, 0 contradictions
Months later, an edit grants contractors a confidential clearance.
Contractor requires Principal && {
employment: str where == "contractor"
clearance: str where in "none" || "confidential"
}
rejected: 1 assertion failed
contractor-clearance.sf
because "contractors never hold a clearance that permits restricted reads"
counterexample, a record in both sets
{ employment: "contractor", clearance: "confidential" }
The failed build shows the author's reason and a record that is in both sets.
An assertion relates two sets: in (a subset), not in, never in (no record in both), equal and not equal.
03Comparison
Against Cedar, OPA and XACML.
Cedar, behind Amazon Verified Permissions, is modeled and proved in Lean; its Rust engine is checked against that model by differential random testing, not proved. OPA evaluates Rego policies when an application queries it, and a XACML Policy Decision Point evaluates XML policies per request; OPA ships a test framework. Sources: How We Built Cedar; cedar-spec; cedar-policy-symcc; Cedar syntax; OPA; OPA testing; XACML 3.0.
| Criterion | Scope-Forge | Cedar |
|---|---|---|
| The engine itself | Machine-checked with Verus, an SMT-backed verifier for Rust, for every possible input. | A Lean model with proofs; the Rust engine is checked against it by differential testing, not proved. |
| A contradiction | A compile error: why, where and a suggested fix. | A validator warning, not an error; cedar symcc can prove an always-denying policy set when run, and at request time a deny from any forbid policy overrides an allow. |
| The author's security rule | Written into the policy with a reason and checked against every possible record at every build; a breaking change stops the build with a counterexample. | A separate analysis, cedar symcc, decides subsumption and disjointness with the cvc5 SMT solver and returns a counterexample when run. |
| Outside solver | None at policy-check time: its own verified set arithmetic, no time-outs. The compiler's own proofs are discharged by Verus when the compiler is built. | The analysis above compiles policies to SMT-LIB and runs cvc5, and is itself verified in Lean; evaluation uses none. |
| Execution | Compiled ahead of time, through a verified back end, to native machine code with no garbage collector. | Evaluated per request by the Cedar engine or Amazon Verified Permissions. |
- Scope-Forge
- Machine-checked with Verus, an SMT-backed verifier for Rust, for every possible input.
- Cedar
- A Lean model with proofs; the Rust engine is checked against it by differential testing, not proved.
- Scope-Forge
- A compile error: why, where and a suggested fix.
- Cedar
- A validator warning, not an error; cedar symcc can prove an always-denying policy set when run, and at request time a deny from any forbid policy overrides an allow.
- Scope-Forge
- Written into the policy with a reason and checked against every possible record at every build; a breaking change stops the build with a counterexample.
- Cedar
- A separate analysis, cedar symcc, decides subsumption and disjointness with the cvc5 SMT solver and returns a counterexample when run.
- Scope-Forge
- None at policy-check time: its own verified set arithmetic, no time-outs. The compiler's own proofs are discharged by Verus when the compiler is built.
- Cedar
- The analysis above compiles policies to SMT-LIB and runs cvc5, and is itself verified in Lean; evaluation uses none.
- Scope-Forge
- Compiled ahead of time, through a verified back end, to native machine code with no garbage collector.
- Cedar
- Evaluated per request by the Cedar engine or Amazon Verified Permissions.
The engine itself
A contradiction
The author's security rule
Outside solver
Execution
04Contradiction
A contradiction does not build.
The compiler says exactly why and where, with a suggested fix. Two rules whose requirements cannot both be met leave an empty set, and an empty set is a compile error.
Cleared requires {
clearance: num where in 4..5
}
Probationary requires {
clearance: num where in 1..2
}
ProbationaryRelease requires Cleared && Probationary
rejected: 3 declarations, 1 contradiction
contradictory-policy.sf
Cleared.clearance
admits 4..5
Probationary.clearance
admits 1..2
the intersection is empty
help: widen one range until 4..5 and 1..2 overlap, or do not compose them
05No solver
No third-party solver in the decision.
The same answer on every machine, with no time-outs and no machine-dependent limits. When a policy is checked, Scope-Forge's own verified set arithmetic decides every question and no solver runs. The compiler's own proofs are discharged by Verus, an SMT-backed verifier for Rust, when IronVelo builds the compiler. What is proved: the compiler and the core libraries are machine-checked with Verus for every possible input, so the set arithmetic that decides each policy question is proved, not tested; the checked policy is then lowered into Pancake and compiled by the formally verified CakeML back end. Every verified system trusts its toolchain. Scope-Forge's trusted base is small, and every item on it is listed.
06Roles
Attributes, roles, or both.
Groups are described by what their members are and combined freely, so a person in several groups is natural. A role builds on any number of parent roles, and the whole hierarchy is checked at compile time.
DayShift requires {
hour: num where in 6..18
clearance: num where in 1..4
}
Mission requires {
hour: num where in 10..22
clearance: num where in 2..5
}
MissionDay requires DayShift && Mission
accepted: 3 declarations, 0 contradictions
MissionDay: hour 10..18, clearance 2..4
A rule can also relate one field to another field of the same record: the approver must not be the requester; the request time must be before the credential's expiry. The relation is a comparison (before, after, at most, at least, equal, not equal) against the other field's resolved value, across nested records. Below, the expiry rule.
PositiveTime: num where > 0
Request requires {
expires: PositiveTime
now: PositiveTime
}
Live requires Request && {
now: PositiveTime where < resolved This.expires
}
Stale requires Request && {
now: PositiveTime where >= resolved This.expires
}
assert Stale never in Live
because "a request made at or after its credential's expiry is never live"
accepted: 4 declarations, 1 assertion, 0 contradictions
Go deeper: Numeric Sets for rules on numbers, and Regular Languages for rules on text.
07Review
Built for review.
Definitions are private unless explicitly exported, and every import is explicit, so every permission can be traced to its origin. A reviewer audits a changed module through its imports alone: audits scale with the size of the change, not of the whole policy base.
use ops::Mission
Escort requires {
clearance: Mission.clearance
sector: Mission.sector
speed_kts: num where in 120..450
}
pub relay_clearance: num where in Mission.clearance && != 5
accepted: 1 import, 2 declarations, 0 contradictions
08Runtime
Native machine code, no collector.
The exact form verified at compile time is the form that runs: native machine code for ARMv7, ARMv8, x86-64, RISC-V, MIPS-64 and Silver, with no garbage collector, from data center servers to low-power field equipment. Scope-Forge lowers a policy into Pancake, the low-level language of the formally verified CakeML toolchain, which compiles it to native code. A compiled policy is native code that an existing application calls, generated for it by the Scope-Forge code generator, and runs beside the systems already in place. Everything decided at compile time is removed, so the running system executes only the checks that remain.
09Cross-domain
Classification labels and cross-domain rules.
An application of the approach, not a cross-domain product: the same set-based policies and compile-time checks apply to the policy layer of a cross-domain rule set, classification labels and the rules for moving data between security domains, as sets checked at build time.
Label requires {
level: num where in 0..4
marking: str where in "NOFORN" || "REL" ++ (" " ++ ("A".."Z")+)+
}
CrossDomainTransfer requires Label && {
level: num where in 1..3
marking: str where not in "NOFORN"
}
accepted: 2 declarations, 0 contradictions