Core concepts
Policies
A policy is a bundle of roles, actions, parties, delegations, and rules. It is written in a small typed language, compiled to a canonical AST, and identified by the content hash of that AST.
A policy, whole
The smallest useful policy declares a vocabulary and one rule over it. Here a clerk may post, and the authority to do so is delegated from a human principal.
policy {
role clerk
action post()
party clerk_bot agent roles(clerk)
party overseer principal
delegation clerk_grant {
principal overseer
agent clerk_bot
scope { actions(post) roles(clerk) window(0, 1000000) }
}
rule permit_post { permitted any_in_role(clerk) do post priority 1 }
}
Read it downward. role and action declare the vocabulary: what kinds of actors exist and what they can be said to do. party binds concrete actors to it — clerk_bot is an agent holding the clerk role, overseer is a principal, which is to say a human who can be held responsible. delegation is the chain that makes the agent's authority real, and it is scoped: clerk_bot may perform post, in the role clerk, within a bounded window of ticks. rule states the norm.
Every agent's authority terminates at a principal. There is no rule that grants an agent power on its own account, because there is no such power.
Prohibitions and priority
Add one rule and the same vocabulary refuses instead.
policy {
role clerk
action post()
party clerk_bot agent roles(clerk)
party overseer principal
delegation clerk_grant {
principal overseer
agent clerk_bot
scope { actions(post) roles(clerk) window(0, 1000000) }
}
rule permit_post { permitted any_in_role(clerk) do post priority 1 }
rule forbid_post { forbidden any_in_role(clerk) do post priority 10 }
}
Both rules apply. The prohibition has the higher priority, so it wins, and a post from clerk_bot now comes back ForbiddenRejected with forbid_post named as the winning rule. The permission is not deleted; it is defeated, and the provenance record says so. An auditor can see that the clerk would have been allowed to post, and what stopped them.
This is defeasibility: rules override other rules by priority, then by specificity, then by a stable order. Conflicts resolve deterministically, and the resolution is part of the record.
Deny by default
If no rule applies to a proposed action, the verdict is refusal. Silence is not permission.
The practical consequence is that a policy has to say what may happen, not merely what may not. This is more work to author and it is the only defensible default for a system that spends money on an organization's behalf.
Conditions and duties
Rules are conditional and typed. A rule can guard on the action's bound parameters, on aggregate state such as cumulative spend, and on membership in replicated reference data — the approved-counterparty list, the chart of accounts, the fiscal calendar — so that "the counterparty must be approved" and "the period must be open" are enforceable norms rather than after-the-fact checks.
A rule can also obligate. The action that triggers a duty and the action that discharges it are distinct: "if a purchase exceeds five hundred dollars, obtain manager approval within two business days" declares one action as the trigger and another as the duty, with a deadline anchored at the trigger. This distinction is why an allow/deny list cannot express organizational policy. See obligations.
Identity and immutability
A policy is content-addressed. Its id is the hash of its canonical AST, so a policy version is immutable by construction: change a character and it is a different policy with a different id.
Old transactions therefore stay auditable under the exact policy that governed them, forever, with no versioning discipline required of anyone. The provenance record on a transaction from last March names the policy id in force last March, and that bundle still means exactly what it meant.
Authoring
The language is designed to be written by a model and verified by a person.
Its syntax is optimized for formal cleanliness rather than for typing comfort: keyword-led, explicitly typed, no operator precedence to remember. The parser returns structured diagnostics — stable codes, spans, expected-sets, corrective hints — and recovers to report all errors at once, because the consumer of a parse failure is an authoring loop, not a human reading the first message.
The trust bridge is the renderer. Any policy AST can be rendered back into faithful natural language, under a contract that every semantic element appears and nothing is overclaimed. The reviewer approves the rendering, never the raw source. A human who cannot read the DSL can still be accountable for what the policy says, which is the only way this is workable in an organization that has to sign off on controls.
The canonical artifact is the AST
The DSL text is an input. What is hashed, replicated, evaluated, and cited in provenance is the typed AST it compiles to. Two textually different sources that compile to the same canonical AST are the same policy.