Formal Verification
@txfence/verify provides bounded model checking for policy invariants. Given a policy and bounds, it either proves the property holds or produces a concrete counterexample showing how it can be violated. Use it in CI to catch policy misconfiguration before deployment.
Installation
npm install @txfence/verifyThree properties
absolute_cap_reachability
Can N agents × M transactions reach or exceed the absolute cap?
import { verify } from '@txfence/verify'
// verify() is synchronous — it returns a result immediately, no await needed
const result = verify({
kind: 'absolute_cap_reachability',
agentCount: 10,
transactionsPerAgent: 10,
capAmount: 50_000n * 10n ** 6n, // 50,000 USDC
token: 'USDC',
maxSpendPerTx: 1_000n * 10n ** 6n, // 1,000 USDC per tx
})
if (result.status === 'violated') {
console.error('Cap can be exceeded:', result.counterExample)
}Arithmetic check: agentCount × transactionsPerAgent × maxSpendPerTx vs capAmount. Generates the minimal counterexample — only the transactions needed to exceed the cap. Properties take cap/token/maxSpendPerTx directly rather than a full Policy so the checker can be exercised against hypothetical values without constructing a complete config.
rolling_window_saturation
Can N agents collectively exceed a rolling window cap through adversarial scheduling?
const result = verify({
kind: 'rolling_window_saturation',
agentCount: 5,
transactionsPerAgent: 20,
windowMs: 3_600_000, // 1 hour
capAmount: 25_000n * 10n ** 6n, // 25,000 USDC rolling cap
token: 'USDC',
maxSpendPerTx: 1_000n * 10n ** 6n,
})Tests four adversarial scenarios: simultaneous burst, staggered burst, double burst, worst-case single agent. Counterexample shows exact timestamps and amounts that cause the violation.
policy_containment
Is every action allowed by innerPolicy also allowed by outerPolicy?
const result = verify({
kind: 'policy_containment',
innerPolicy: stricterPolicy,
outerPolicy: broaderPolicy,
})Uses createTestActions + diffPolicies internally. Flags newly_rejected actions as containment violations. Use this to verify that tightening a policy doesn’t accidentally block actions that should still be allowed.
VerificationResult
type VerificationResult =
| { status: 'holds'; property: string; checkedBound: CheckBound; scenariosChecked: number; durationMs: number }
| { status: 'violated'; property: string; counterExample: CounterExample; durationMs: number }
| { status: 'unknown'; property: string; reason: string; durationMs: number }
type CheckBound = {
agentCount: number
transactionsPerAgent: number
windowMs?: number
}CounterExample
type CounterExample = {
description: string
transactions: TransactionScenario[]
violatedAt: number
violatedAmount: bigint
capLimit: bigint
checkedBound: CheckBound
}
type TransactionScenario = {
agentId: string
action: Action
timestamp: number
amount: bigint
}verifyAll
Run multiple properties in parallel. verifyAll is async (wraps each sync verify() call in a resolved Promise) so consumers get a single await instead of N synchronous checks.
import { verifyAll } from '@txfence/verify'
const results = await verifyAll([
{
kind: 'absolute_cap_reachability',
agentCount: 10,
transactionsPerAgent: 10,
capAmount: 50_000n * 10n ** 6n,
token: 'USDC',
maxSpendPerTx: 1_000n * 10n ** 6n,
},
{
kind: 'rolling_window_saturation',
agentCount: 5,
transactionsPerAgent: 20,
windowMs: 3_600_000,
capAmount: 25_000n * 10n ** 6n,
token: 'USDC',
maxSpendPerTx: 1_000n * 10n ** 6n,
},
])CLI
txfence verify absolute-cap --config ./txfence.config.ts
txfence verify rolling-window --config ./txfence.config.ts
txfence verify policy-contains --config ./txfence.config.ts
# JSON output
txfence verify absolute-cap --config ./txfence.config.ts --jsonAll commands exit 0 when the property holds, 1 when violated — CI-friendly.
Bounded verification only. A property that holds for N=10, M=10 may still be violated for larger values. Always document your bounds. A Z3 SMT backend for complete proofs is planned.