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

Specification

§6 · The execution-side model

Status: DRAFT. The side vocabulary and one inference are probed. The correctness of inference at runtime, and what crosses the wire, are UNVERIFIED here by construction — see §6.6.

This is the language's most distinctive rule, and the one §11.1 and §11.2 both depend on.

6.1 One unit, more than one runtime

A single compilation unit produces code that runs in a browser and code that runs on a server. There is no wire protocol to declare, no endpoint to route, and no data-transfer type to keep in step, because there is no boundary in the source for those things to sit on.

Normative. A program MUST NOT be required to state where a member runs. An implementation MUST determine it.

6.2 The three sides

The model is three-valued, not two. A reader who assumes "client or server" will mispredict the third and most interesting case.

sidemeaning
Serverthe authority for data reads, security, and secrets — the server interpreter runs it
Clientdialogs, local UI, interaction — the client runtime runs it
Eitherpure compute and local data mutation (new, assignment) — runs wherever the cursor already is, and may span the boundary

Source: ExecutionSide, OsySharpExpressionEnums.cs:539.

Either is where the model earns itself. A pure helper does not need a home, so it does not get one — it runs on whichever side called it, and the same function called from a render slot and from a server function is not two functions. Local mutation is Either for the same reason: creating a row or assigning a field acts on the unit of work the cursor already holds, so it is meaningful on both sides and identical on both.

6.3 Inference

The side follows from what the code touches, not from what it is called or where it is written.

  • an entity read materialises rows under the declared row filter (§8) → Server
  • a render expression paints and performs no I/O → Client
  • pure compute and local mutation → Either

Probe (established, and run by CI). A live var holding an entity read is reported side=server:

using Osyrin.Ui;
entity Order { [MaxLength(40)] string Ref; int Total; }
[Page("/")] [AllowAnonymous] component Board() {
  live var orders = Order.Where(o => o.Total > 0);
  render { foreach (var o in orders) { Text(o.Ref); } }
}

osy model --json reports, for that component member:

component Board
   members: orders  side=server

6.4 Asking the compiler

Normative. An implementation MUST make the resolved side inspectable. A rule the author cannot observe is folklore.

Two different answers, and they are easy to confuse:

you haveosy model --json gives you
a component membera sideserver / client / either
a top-level functionan effects record — readsData, writesData, pure, durability, plus the entities read/created/modified — and its transitive closure

Probed. A top-level function has no side field; its keys are allowAnonymous · effects · file · isSystem · name · parameters · returnNullable · returns · scopedName · transitive · visibility. The effects record is the answer for a function — OpenOrders reports reads: [Order], readsData: true, pure: false.

Informative. The distinction is not an oversight. A component member is bound to one side because it is part of a rendered tree; a top-level function is frequently Either and takes its side from the caller, so "what does it touch" is the useful question and "where does it run" is not always answerable in isolation.

6.5 Forcing the side

[Server] and [Client] exist. They force a member onto a side.

Reaching for one is almost always the wrong instinct. They were de-advertised across 718 mentions in 174 files on 2026-08-27, and removed from osy init's scaffold — the first Osy# a downloader reads — precisely because their presence in examples taught readers to write them. The scaffold's explanatory comment was also false: it claimed a plain function runs on the server and a render expression cannot call one; measured, a plain pure top-level function is callable from a render.

Normative. An implementation MUST NOT require these attributes for ordinary programs, and diagnostics MUST NOT suggest adding one as the remedy for an inference the author has not been shown.

6.6 What this section does NOT establish

UNVERIFIED, deliberately. osy validate — the instrument behind every probe in this document — establishes what the compiler accepts and what it reports. It cannot establish:

  • that an inferred side is correct at runtime (that the server half really runs on the server);
  • what crosses the wire at a hand-off, and in which direction;
  • that a value which cannot cross is refused rather than silently dropped.

Those are runtime semantics and need a test class, per §1.5. ExecutionSidesHandlerTests is the closest existing guard and is named here so the gap is addressable rather than merely admitted. A future revision of this section MUST cite tests for the three claims above, or drop them.


Next: §9 Durability semantics — the other half of the model §11 depends on. Drafted.