Platform / how it works
From one file to generated code.
One example file, quoted verbatim, followed through every stage the toolchain has. Nothing below is illustrative: the shell lines are executed by CI against the released binary, and the artifact fields are read from the artifact this repository commits.
SOURCE → CHECK → CORE IR → BACKENDS → OUTPUT
01 / The source
A domain, declared once
A .mox file declares a package and what lives in it. Every other surface reads this file rather than restating it.
This is examples/support/support.mox, lines 3–32, unchanged:
enum TicketState {
Open as "open" = 0
InProgress as "in_progress" = 1
Resolved as "resolved" = 2
}
class User {
id readonly String userId
String name
boolean active
refers Ticket[] assignedTickets opposite assignee
}
class Ticket {
id readonly String ticketNo
String title
boolean internal
int refundCents
TicketState state
refers User[0..1] assignee opposite assignedTickets
derived boolean needsRefund {
expr { refundCents > 0 }
}
op boolean raiseRefund(int cents) {
expr { refundCents + cents <= 5000 }
}
op boolean approveRefund(int cents) {
expr { refundCents + cents <= 100000 }
}
}Four lines carry the design decisions:
refers User[0..1] assignee opposite assignedTicketsdeclares both ends of one relation. The compiler checks that the two ends agree, so a relation cannot be half described.derived boolean needsRefundis computed, not stored: it has a body and no stored value, and the generators know the difference.op boolean raiseRefund(int cents)is an operation whose body is written in the shared expression language and type-checked againstintarithmetic and the class's own features.TicketStateis a closed enum. A state machine declared in a design over this class has to use its literals — which is the next surface's problem, not this file's.
02 / Check
The compiler refuses to be surprised
Parsing, resolution, validation and type-checking all happen before a byte of artifact is written. A file that fails any of them produces no model at all.
pherolang check examples/support/support.moxIt prints OK examples/support/support.mox and nothing else. An error blocks the model outright: an unresolved type, a duplicated declaration, an opposite that does not pair, or a guard that does not type as boolean all end the run with no artifact written.
Each diagnostic carries a stable code allocated in one central registry, so a code in an issue stays the same code in the editor:
pherolang explain E0104prints Unknown action reference — the meaning of the code, not a mangled message number. The full registry is generated from the compiler's own table.
03 / Compile
The artifact is the product
Lowering happens once. What leaves the compiler is a versioned JSON document that describes the model completely enough to be read without the model.
pherolang ir examples/support/support.mox -o support.phero.jsonThe first command writes the artifact and prints nothing. The second reads it back with no compiler in the loop — no model opened, no import resolved, no grammar involved:
pherolang artifact check support.phero.jsonIt prints OK support.phero.json.
{
"formatVersion": 2,
"pheroVersion": "0.3.0",
"packages": [ … ]
}Two fields, two jobs. formatVersion is the compatibility gate: a reader accepts that number or refuses the file, and it is the only thing a reader may gate on.pheroVersion is provenance — which build ofpherolang wrote these bytes — and nothing more.
The package body follows (nz.example.support here). Evolution is additive: a new optional field can appear without changing the bytes of a model that does not use it, because the goldens are compared byte for byte. From 1.0 that becomes a promise — no golden changes without a formatVersion bump — and it is what lets a reader pinned to formatVersion 2 keep reading later artifacts while the number stays where it is.
Every artifact this site talks about is committed undersite/data/artifacts/, one per surface, and regenerated by CI from the released binary. Versioning and release policy.
04 / Backends
Pure functions from artifact to output
Each backend is handed a compiled value. None of them sees a syntax tree, and none of them can: the compiler does not hand one out.
| Backend | Reads | Emits |
|---|---|---|
| phero-backend-cedar | Core IR + ActorModel | Cedar policies and an entity schema |
| phero-backend-cmmn | CmmnModel + domain | a cmmn-rs CaseDefinition |
| phero-backend-dmn | DmnModel | the model the dsntk engine evaluates |
| phero-backend-jsonschema | Core IR | JSON Schema — wire and api profiles |
| phero-backend-rust | Core IR | arena-based Rust model code |
05 / Output
Two targets, one shape
The same source, generated twice. Neither run reads the other's output, and neither one can invent a field the model does not have.
pherolang gen rust examples/support/support.mox -o support-genIt prints generated Rust model code into support-gen: arena-based Rust model code, generated from the compiled model rather than from the syntax tree. An expr operation body is parsed, typed and lowered at generation time; a rust body is emitted verbatim, by definition trusted code.
pherolang gen json-schema examples/support/support.mox --profile wire -o support-schemaIt prints generated JSON Schema into support-schema. The wire profile is the JSON contract the canonical instance documents are validated against; the api profile is a flattened DTO projection for OpenAPI-style use, over the same model.
Cedar policy is the third target from this same domain, and it needs one more file: an.actor surface that imports the domain and declares who may do what under which condition. Policy is a separate artifact and a separate dimension — it never appears in the domain model or in its schemas.
The two artifacts are then checked against each other — which is the point of writing policy in the same language as the model:the authorisation surface has the shape of that file, andthe platform overview explains the cross-surface pass.
Continue
Run it yourself
The install page has the archive for your platform; the walkthrough runs these same commands end to end from a clean checkout, with the exact output of each one.