Specification · v1
µLang v1
µLang v1 is the open contract layer of µLang. A v1 contract names one kind of request (an entity with typed fields), the actions it governs, and the rules each action must satisfy. Given an action and the facts of one request, a contract returns a decision: a verdict, PERMIT or DENY, and the id of the clause that decided it.
// Expense approvals: who may approve, and up to how much.
CONTRACT ExpenseApproval {
ENFORCEMENT_MODE: enforced
CONTEXT {
ENTITIES Expense {
amount: float
approver_is_submitter: bool
approver_limit: float
receipt_attached: bool
category: string
}
CONSTANTS {
receipt_threshold: float == 75.0 [SOURCE:FIN-POLICY-4.2]
hard_cap: float == 10000.0 [SOURCE:FIN-POLICY-4.1]
}
}
TRIGGER {
approve_expense()
}
GUARANTEE {
ENSURE approve_expense() WHEN Expense.receipt_attached == true OR Expense.amount < receipt_threshold [SOURCE:receipt_required]
}
PROHIBITION {
FORBID approve_expense WHEN Expense.approver_is_submitter == true [SOURCE:no_self_approval]
FORBID approve_expense WHEN Expense.amount > hard_cap [SOURCE:over_hard_cap]
FORBID approve_expense WHEN Expense.amount > Expense.approver_limit [SOURCE:over_approver_limit]
FORBID approve_expense WHEN Expense.category == "gift_card" [SOURCE:no_gift_cards]
}
OBSERVABILITY {
LOG "expense approval decided" ON decision
}
}
valid ExpenseApproval 5 clauses · sha256 5a6326d24470e02c54a4f175994a1b0c2c4a6efa08baec2c025b0af1fc78e7fd
Facts of the first request; each other request changes only the fields shown.
{
"Expense": {
"amount": 120,
"approver_is_submitter": false,
"approver_limit": 500,
"receipt_attached": true,
"category": "travel"
}
}
| Request | Action | Changed | Verdict | Clause |
|---|---|---|---|---|
| within limits, receipt attached | approve_expense | as above | PERMIT | ExpenseApproval.approve_expense.permit |
| approver submitted it | approve_expense | approver_is_submitter: true | DENY | ExpenseApproval.no_self_approval |
| above the approver’s limit | approve_expense | amount: 800 | DENY | ExpenseApproval.over_approver_limit |
| no receipt above the threshold | approve_expense | receipt_attached: false | DENY | ExpenseApproval.receipt_required |
| no receipt, under the threshold | approve_expense | amount: 40 receipt_attached: false | PERMIT | ExpenseApproval.approve_expense.permit |
| self-approval and no receipt: prohibitions are checked first | approve_expense | approver_is_submitter: true receipt_attached: false | DENY | ExpenseApproval.no_self_approval |
Nothing else comes back: no score and no explanation text. A decision is the verdict plus the clause that produced it, and the contract is identified by the SHA-256 of its canonical text.
What v1 is for
v1 governs actions that must be decided before they happen: an agent claiming files, a push to a protected branch, a tool call, a token being minted. The µLang Git forge decides every governed effect with v1 contracts; its twelve production contracts are on the examples page.
Properties a v1 decision always has:
- Deterministic. The same contract, action and facts always give the same decision. There is no clock, no randomness and no model in the evaluation path.
- Attributable. Every
DENYnames one clause. EveryPERMITnames the action that was permitted. - Total. A contract that passes validation decides every action it lists, for every well-typed set of facts. It never throws, loops or reads anything outside the facts.
- Bounded. Every input is size-, nesting- and depth-limited, and checked in one linear pass before any recursive parsing.
- Sourced. Every executable clause and every constant carries a
[SOURCE:id]tag, so each rule points at where it came from.
The reference implementation
ulang-lite is the reference implementation of v1. It is TypeScript with zero runtime dependencies, MIT licensed, and runs unchanged in Cloudflare Workers, Node.js 22.18 and later, and browsers. It is the same engine the playground runs in your browser.
This specification is written from ulang-lite as released at commit
61af4281ed. Where the two disagree, ulang-lite is
authoritative, and the disagreement is a bug in this document. Every contract, diagnostic, decision and digest on this site is produced by that release when the site is built, not typed by hand.
Two layers: what parses, and what executes
v1 has a grammar and an executable subset.
- The grammar is deliberately wider than what executes. It accepts things such as calls, guarded triggers and soft constants, so that they can be rejected with a precise diagnostic instead of a generic syntax error.
- The executable subset is what a valid contract may contain. Validation enforces it in a fixed order, and the first problem found is the one reported.
A source is accepted only when it parses, stays inside the subset and validates. Then, and only then, it can be evaluated and hashed.
Chapters
| Chapter | Covers |
|---|---|
| Syntax | characters, comments, names, literals, the full grammar, and the sharp edges |
| Executable subset | what a valid contract may contain |
| Validation and diagnostics | the check order, and every diagnostic kind and code |
| Evaluation | decide, facts, and how comparisons work |
| Canonical forms | canonical text, canonical JSON, and the contract digest |
| Limits | the bounds scan and the resource limits |
| API and CLI | using ulang-lite from code and from the command line |
| Test vectors | every case on this site, with its expected result, as JSON |
| Changelog | versions of v1 and of this document |