The language
How the language works
A .esk file compiles to definition records (IR-D) and nothing else. No construct has semantics of its own: every line becomes one or more of the ten kernel records, every record renders as an English sentence, and the whole thing is checkable without running anything. This page walks through Pebble, the smallest organization that has been run.
Module header and vocabulary
module pebble.core @ 1.0.0
uses esk.kernel @ 1
import lib.org @ 2.* as org
import lib.governance @ 1.* as gov
import lib.finance @ 1.* as fin
import lib.evidence @ 1.* as ev
import lib.realization @ 1.* as rz
import lib.agents @ 1.* as ag
import lib.econ @ 1.* as econ
vocabulary ws @ 1.0.0 {
availability(r : Capability) : claim "availability of realization r over the observed window, in percent"
price(c : Capability) : claim "per-instance monthly price of an offered compute capability"
price-change(c : Capability) : claim "an offered capability's price changed"
ScaleRequest(n : Number, projected : Quantity<currency>) : entity "a request to run n instances"
MigrationProposal(candidate : Capability) : entity "an Architect proposal to rebind WebService"
Scale(n : Number) : event external "change the instance count of a compute realization"
LoadTest(n : Number, rate : Number) : event external "drive synthetic load against a sandbox realization"
}uses esk.kernel @ 1 pins the kernel. Imports bind a version range; the lockfile pins the exact content hash each range resolved to. A vocabulary declares the organization's terms and what sort each one is: a claim can be T, F, N or B; an entity is a kind of thing; an event external is something done to the world, which will always need authorization.
Scope, persona, positions
scope Pebble : org.Organization {
persona PebbleCo
use gov.Constitution(founders = { Owner }, amendBy = Owner, quorum = 1) as Const
position Owner : org.Owner { cardinality 1 eligible when self is org.Person }
position Operations : org.Officer { cardinality 1 eligible when self is ag.Autonomous }
position Architect : org.Officer { cardinality 1 eligible when self is ag.Autonomous
incompatible with Operations }
position Engine : rz.Engine { cardinality 1 }The scope acts through its persona, PebbleCo. Positions carry cardinality, eligibility (by party kind: a person, an autonomous agent, a program, a model-based party) and incompatibilities. The engine that runs the organization is itself a position, so its authority is granted and checked like anyone else's.
Intents and required capabilities
intent Available : org.SLO { maintain holds ws.availability(WebService) >= 99.5 % hard }
intent Responsive : org.SLO { maintain holds ws.p95-latency(WebService) <= 300 ms hard }
intent Frugal { avoid holds ws.cost(WebService) > 500 USD per month soft weight 0.5 }
requires capability WebService {
operation serve(request : Entity) -> response : Entity
operation scale(n : Number) -> outcome : Claim
guarantee eventually holds ws.Served(request)
property availability >= 99.5 % per day
property p95-latency <= 300 ms
property cost <= 500 USD per month
for Available, Responsive, Frugal
}Intents say what the scope wants. hard intents must be covered by a responsible position or a required capability; soft ones carry weights or priorities for comparisons. A required capability is a contract: operations, what it may assume, what it must guarantee, and properties with thresholds. Quantities carry dimensions; comparing ms to USD is a compile error.
Norms and patterns
use rz.RealizationEnvelope(holder = Operations, requirement = WebService, operation = ws.Scale,
admissible = a.n <= 5 and holds ws.price(bound(WebService)) * a.n <= 500 USD per month) as OpsEnvelope
use org.Approval(approver = Owner, subject = ws.ScaleRequest,
threshold = s.n > 5 or s.projected > 500 USD per month,
deadline = 1 business-days, escalateTo = Owner) as ScaleApproval
norm OwnerBinds { power of Owner to bind | unbind _ about WebService level collective }
norm DailyCheck {
obligation of Operations to persona
when occurred tick(t) and t.boundary = "day"
aim within 2 h: occurred derive(ws.daily-status(t.day)) by Operations
not tradeable
}A norm has a bearer, a deontic line, a condition (when), an aim, and optionally an or-else, a level, a source, a priority and tradeability. use instantiates a library pattern: rz.RealizationEnvelope expands to one power norm, org.Approval to four (the power to decide, the duty to decide by a deadline, the escalation, and a gate that prohibits executing before approval). Every expanded record keeps expandedFrom: the pattern, package, version and use site.
Note holds ws.price(…) inside the envelope. The price claim comes from the provider, whose statements start at N under Pebble's trust policy until two daily bills agree. Until then the envelope admits nothing and Operations has to ask.
Information rules
use ev.TrustPolicy(source-kind = ev.Monitor, base = T) as MonitorTrust
use ev.TrustPolicy(source-kind = ws.Provider, base = N,
promote-when = ev.ConsecutiveDailyObservations(2), vocab = { ws.price, ws.price-change }) as ProviderTrust
}Trust policies decide what status a claim gets from its source. Your own monitors are trusted; a provider's price page is not, until your bills corroborate it. A model-based party's output gets N by default in every reference organization.
What the compiler produces
Verbalization
Every record carries its rendering. A reviewer's sign-off is a decide event over the rendered text, so what people approved and what the engine runs are the same object.
IR record
{"@type":"Norm","@id":"esk:norm/eu-residency","scope":"esk:scope/acme",
"deontic":"F","bearer":"esk:pos/member",
"aim":["exists",["var","x","Entity"],
["and",["has-kind",["var","x"],"fin:PersonalData"],
["not",["in",["custody-jurisdiction",["var","x"]],["set","EU"]]]]],
"condition":["true"],"source":{"issuer":"esk:party/eu","ref":"GDPR art. 44–49"},
"level":"constitutional","tradeable":false,"defeasible":false,
"verbal":"No member may cause personal data to be held outside EU jurisdiction. Source: GDPR. Not tradeable."}Check report
summary 241 records · 0 E · 11 W · 77 U · 214 I
W-SINGLE-POINT esk:pos/Meridian.Owner has cardinality 1, no Deputy pattern, and bears … exclusive powers
W-OVERLAPPING-POWER Meridian.OwnerSpend and Meridian.FinanceSpend overlap on decide without declared priority
E-FREEZE-COVERAGE OpexBudget.Freeze: exceptions cover all non-tradeable obligations in scope
E-ARCH-PRODUCTION Architect holds no bind/unbind/authorize power over any production requirement
U esk:cap/Meridian.Database.rpo: satisfaction depends on observed evidence — UNKNOWN until runtimeThe four severities: E the spec violates the kernel and can't be enacted; W enactable but suspicious; U undecidable until the organization runs (listed so that "we don't know yet" is an output of compilation); I information, including every chain of authority.
Conditions are the whole constraint language
There's no separate policy language. Conditions combine claim tests (holds, unknown, contradicted, false), log tests (occurred, count … over, since), normative tests (violated commitment of, breached, in-force, gap) and bounded temporal operators (within, before, until). A condition that tests any claim is four-valued, and the checker warns (W-UNKNOWN-BRANCH) when no norm in scope says what happens if it evaluates to unknown.
The language has no loops, functions or string manipulation. Computation is done by capabilities, whatever realizes them, and its results come back as claims. That keeps the specification a description of what is required and what is in force, and stops it becoming a place for implementation to hide.