# Understanding your app

> 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.

<!-- id: local-understanding-your-app · area: local · stability: stable · html: https://osysharp.com/reference/local/understanding-your-app/ -->

## Summary        {#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      {#signature}

```console
osy model [path] [--json]
```

## Description    {#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)](https://osysharp.com/reference/security/secure-by-default/)) — 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](https://osysharp.com/reference/local/reading-a-capability/).

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 (`osy symbols` alone reads the current
project, like every local verb) — each name under the group it was DECLARED in: `Entities`, `Classes`, `Enums`,
`Functions`, `Workflows`, `Clients`, `Values`. A workflow is listed by its own name with what it tracks
(`InvitationFlow (tracks Invitation.Status)`), never as the entity it tracks, and a `class` is not an entity. A
`[Test]`/`[TestFixture]` function is under `Tests`, not `Functions` — it is the app's test-framework surface, not
production code — and a synthesized asset vocabulary (`Icons`, `Art`, `Textures`, `Sounds` — present in EVERY app,
since the built-in icon set is always merged in) is under `Assets`, not `Enums`, because the compiler wrote it from
the files the app ships rather than the app declaring it. For "where does this app fall short of production?", see
[Checking your app](https://osysharp.com/reference/local/checking-your-app/).

## Examples       {#examples}

```console
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:

```json
{ "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:

```json
{ "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:

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

## See also       {#see-also}

[Checking your app](https://osysharp.com/reference/local/checking-your-app/) — check the app against production best-practice rules.

[Explaining your app's security](https://osysharp.com/reference/local/explaining-your-app/) — the same app's access rules, in plain English (who can do what).

[Compiling your app](https://osysharp.com/reference/local/compiling-your-app/) — compile the source into your local app.

[secure by default (deny-all)](https://osysharp.com/reference/security/secure-by-default/) — why an entity with no security block grants nothing.
- [Reading a capability's source](https://osysharp.com/reference/local/reading-a-capability/) — read the source of a capability the model says you were provided
