Circuit constraints
The exact authorization predicates proven for documents.read, email.send, and payments.transfer, including current coverage gaps.
The current Compact circuit proves one common policy binding plus one tool-specific rule branch.
A useful way to read the contract is as a predicate:
AUTHORIZED(request, privatePolicy, ledgerState)that can evaluate to true only when all required assertions pass.
Common constraints
Every request must satisfy:
hashPolicy(privatePolicy) == ledger.policyCommitment
requestAgent == privatePolicy.allowedAgent
requestTool ∈ {
privatePolicy.documentsTool,
privatePolicy.emailTool,
privatePolicy.paymentsTool
}
nullifier(privatePolicy.secret, nonce) has not been usedThe first line binds the witness to the deployment policy. The second binds the action to the allowed principal. The third rejects unknown capabilities. The final check prevents replay.
documents.read
The document branch adds:
requestResource == privatePolicy.allowedResourceThe gateway derives requestResource from the tool's matterId argument.
Example:
private allowed resource: matter:thompson
requested resource: matter:thompson
→ authorizedprivate allowed resource: matter:thompson
requested resource: matter:unrelated-client
→ deniedCurrently not constrained
The demo tool also carries a documentId, but the current circuit does not constrain that field.
That means the current proof statement is:
the agent may read a document inside the authorized matter
not:
the agent may read this exact document identifier.
A stricter policy could include the document identifier, document class, path prefix, or a membership proof over an allowed document set.
email.send
The email branch adds:
approved == trueThe boolean is derived by the gateway from trusted approval metadata; it is not read from an ordinary approved tool argument.
Currently not constrained
The circuit does not currently constrain:
recipient
subject
message body
recipient domain
external/internal classificationSo the current proof means:
this email capability had a trusted approval signal
not:
the approval was cryptographically bound to this exact recipient and body.
Production approval should be scoped to an execution context so one approval cannot be reused for a materially different send.
payments.transfer
The payment branch proves two numeric rules:
requestAmount <= privatePolicy.maxPaymentAmountand:
requestAmount <= privatePolicy.paymentApprovalThreshold
OR approved == trueThis creates three regions:
0 ───────── approval threshold ───────── hard maximum ───────>
allow without approval approval required denyFor the demo policy:
approval threshold = £4,000
hard maximum = £5,000so:
| Request | Approval | Result |
|---|---|---|
| £2,750 | absent | authorized |
| £4,500 | absent | denied |
| £4,500 | verified | authorized |
| £8,000 | verified | denied |
Approval can satisfy the escalation rule but cannot override the hard maximum.
Currently not constrained
The payment branch does not currently bind:
recipient
currency
memo
bank/account class
matter/client contextTherefore a production payment normalizer/circuit needs more facts before the proof can be interpreted as authority over an exact financial transfer.
Formal shape
Ignoring implementation syntax, the current predicate is approximately:
policyMatches
∧ agentMatches
∧ knownTool
∧ freshNullifier
∧ (
documentsRead → resourceMatches
)
∧ (
emailSend → approved
)
∧ (
paymentTransfer → amount <= maxAmount
∧ (amount <= approvalThreshold ∨ approved)
)The branch implication matters. Payment fields can be zero/default for a document request because the document branch does not depend on them.
Execution commitment coverage
The execution commitment currently binds:
policy commitment
request agent
request tool
request resource
request amount
approved boolean
nonceIt does not bind arbitrary forwarded JSON fields omitted from the authorization envelope.
This is why normalizer design is a security-critical extension point: if a field matters to authority, it must be represented in the statement the circuit actually proves.
Adding a new policy primitive
A safe extension sequence is:
- identify the application property that controls authority;
- normalize it into a deterministic fixed representation;
- add it to the private policy and/or request inputs;
- include it in the relevant commitment so receipts bind to it;
- add the Compact constraint;
- add success/failure/replay tests;
- verify the value is not accidentally leaked in logs or public state.
The protocol should grow by explicit primitives, not by assuming arbitrary tool JSON is somehow covered automatically.