Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Local

Understanding your app

osy model [path] [--json]

Prints your app's RESOLVED model — field types bound to real types, relations wired to the entity they target, each entity's actual security posture, what every function reads, writes, calls out to and raises, and the REST surface the app publishes to the outside.

stablelocalcliauthoring

Summary#

Prints what your app is — not what its source says, but what the compiler makes of it. Types are bound, relations are wired to the entity they actually point at, every entity reports the access posture it really has, and every function reports what it does when you call it. It needs no platform and no database: it parses and resolves, nothing else.

Signature#

osy model [path] [--json]

Description#

Reading source tells you what a field is called and what its type is spelled. osy model tells you what those resolve to — which is where the surprises live:

  • Relations are wired. A LineItem[] Lines field reports its target: a collection of LineItem. A Order Order field on the child reports a reference back. You never have to infer a relation from a name.

  • Enum-backed fields are named by their enum, not by the string or int they store as — because the enum is what you have to write.

  • The rules a field is held to are listed. [Unique], [Min], [Max], [Pattern], [MaxLength], [Immutable] and friends are reported per field under constraints, each named as you write it, with its argument. This is how you tell a rule about the data from a check in one screen: a constraint holds wherever the row is written — a function, an import, another tab — while an if in a page holds only in that page. A [Unique(A, B)] written on the entity is a rule about the combination and belongs to no single field, so it is reported on the entity instead. ([Required] is not repeated in that list — it has its own required flag.)

  • Security is the DERIVED posture, not the block. An entity with no security { } block is denied to everyone (see secure by default (deny-all)) — not open, and there is no setting that could make it open. That is the fact people most often read backwards, so the model reports the derived posture (deny-all, allow, deny) rather than leaving you to work it out from whether a block is present.

  • Functions report their effects. What each one reads, creates, modifies, deletes, calls out to, raises, and throws — derived from the body, not from the name. Each function is reported twice: its own effects, and its transitive ones (what happens once its callees are included). A function that writes nothing itself but calls one that does will tell you so.

  • A workflow reports the machine, not just its parts. States and events on their own describe a workflow the way a cast list describes a play. The model also reports what moves between the states, and — because this is where the surprises live — the things that quietly decide an outcome: which states accrue SLA time (a state left out of Accrues pauses every clock while a run sits in it), the schedule those clocks run on, each slot's Candidates and Requires gates, and each milestone's promise, reminders and retry policy.

    One link is worth knowing about. You raise an event, but a state subscribes it under a slot alias, and the alias is what handlers and transitions use — so Respond and FirstReply can be the same act under two names. Each event reports where it can be delivered, and each transition reports the event that drives it, so you never have to reconcile two lists of identifiers by hand.

  • The REST surface you publish is reported, with the columns it hands out. Every RestApi in app.Apis appears under apis: the address it is really served on (/api/rest/v{major}/{route} — with the default version applied, so it is where the API is, not what the source spelled), the credential kinds it accepts, each function you mapped to a URL, and each entity you exposed. This is the one part of your app that faces strangers, so it is reported at the level of detail that decides whether something leaks:

    • No declared Auth means anonymous, and the model says so in words rather than by an empty list. An unauthenticated API is the whole story about that API, and it is the easiest thing in the document to read past.
    • A Crud<T> selects no subset. It publishes T's whole row — your fields and the ones the platform adds — so the model lists them by name under fields. A cost column or a private note is on the wire, and the declaration that put it there never mentions it.
    • A field-scoped deny read is the one thing that takes a column back off, so those are listed separately under maskedFields. A field that appears in fields and not in maskedFields is handed to every caller the API admits.
  • The tables you did not write are reported too, separately. entities is what your source declares. provided is what your app has without declaring it: everything a using brought in, plus the platform's always-applied core baseline. They are kept apart because "what did I write" and "what tables does this app have" are different questions — but a file-manager app whose whole subject is FileAsset was reporting five entities and mentioning none of them. Each provided type names the using it came from, and is flagged internal when the app cannot name it at all (so no partial entity can state security for it). Read one with Reading a capability's source.

The model says when it does not know. If the source did not fully resolve, resolved is false and the model is partial — it is not presented as the whole truth. If a function contains something the effect analysis could not classify, it reports unknown: true alongside whatever it did find, rather than quietly reading as "no effects".

--json writes the whole model as JSON — the form to hand to a coding agent, or to diff between two revisions of an app. Exit is non-zero when the model is partial.

For the shorter question "what names exist here?", osy symbols lists them. For "where does this app fall short of production?", see Checking your app.

Examples#

osy model                 # the resolved model, human-readable
osy model --json          # the same model as JSON

A workflow's slot answers "how do I drive this?" — the event to raise, who may raise it, and what must be true first:

{ "event": "Resolve", "alias": "Fix",
  "candidates": "u => u.Team == Team.Support",
  "requires": [ { "name": "RootCause",
                  "must": "!Text.IsEmpty(this.Item.RootCause)",
                  "message": "Record the root cause before resolving." } ] }

What a published API hands out — the address, the credential kinds, and the columns an exposure puts on the wire:

{ "name": "Public", "route": "public", "version": "2.0",
  "basePath": "/api/rest/v2/public",
  "auth": { "methods": ["apiKey"] },
  "expose": [ { "entity": "Order", "operations": ["read", "create"],
                "url": "/api/rest/v2/public/entities/Order",
                "fields": ["Reference", "Cost", "PrivateNotes", "Id", "CreatedAt", "ModifiedAt", "CreatedBy", "ModifiedBy"],
                "maskedFields": ["Cost"] } ],
  "endpoints": [ { "function": "Restock", "method": "POST", "path": "/restock",
                   "url": "/api/rest/v2/public/restock", "successStatus": 201 } ] }

A function's effects read like this — PlaceOrder writes nothing itself, but calling it does:

function PlaceOrder(string code) → void
  reads Customer · creates Order · calls out to Shipping.CreateShipment

See also#

Checking your app — check the app against production best-practice rules.

Explaining your app's security — the same app's access rules, in plain English (who can do what).

Compiling your app — compile the source into your local app.

secure by default (deny-all) — why an entity with no security block grants nothing.

Related

Compiling your app

Compiles your app's source into the app on the local platform — the inner-loop compile-and-apply. It applies additive…

Checking your app

Checks your app against production best-practice rules and reports where it falls short — the maturity signal, beside…

Explaining your app's security

Explains your app's declared authorization in plain English — who can read, create, update and delete each entity…

secure by default (deny-all)

Deny-all is the posture, and it is the only one: an entity that declares no `security { }` block is denied to every…

relations

One entity points at another by declaring it as a member — that is the foreign key. The parent reads its children back…