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

A language, its runtime and its toolchain

A language that answers questions, for a reader that cannot ask a colleague.

More of your code is written by something with no tribal knowledge, no teammate to interrupt and no memory of why this field is nullable — only the source, the compiler, and whatever it will say.

ask the compiler
$ osy model --json   // what the app IS — resolved, not inferred
$ osy explain        // who may read what, from the declarations
$ osy inspect        // what actually ran, end to end, step by step
$ osy check          // one verdict — and a refusal carries its fix

> Posture: deny-all. An entity with no `security { }`
  block grants no access to anyone.

Why that matters now, in full →

Getting started

From nothing to a running app

$ osy init
✓ app.osy · model/greeting.osy · model/pages/home.osy · tests/hello.test.osy

$ osy launch
Local platform ready · instance hello-9fba09ec
✓ compiled and applied to Hello (4 files)
http://hello.localhost:8136/

That is the whole install-to-running path.

What you did not do

  • Install PostgreSQL. One is embedded — real Postgres with pgvector, started and stopped with the app. Its collation matches the hosted platform's, so it is not a stand-in that sorts differently later.
  • Run Docker. Nothing is containerised. There is no compose file to read.
  • Start a web server. The local platform is one.
  • Write a connection string, a schema file, or a seed script. The compile creates the schema and evolves it on the next one. (A rename or a removal does take a migration — §02, and it is written for you.)

One binary. The first run downloads its database bundle (about 550 MB, once) and caches it.

What those two commands gave you

A themed page with live state, dark mode, and a test that runs — a greeting, so the scaffold is something you delete rather than something you study. Replace model/greeting.osy with the shape everything else on this page is about: one component holding a server read, a server write and the screen, with nothing between them.

entity Order {
  [Required, MaxLength(40)] string Reference;
  bool Shipped;
  security { allow read, update when IsAuthenticated; }
}

[Page("/")]
component Board() {
  // A SERVER read. `live` refetches it when the data changes —
  // no store, no cache key, no invalidation.
  live var open = Order.Where(o => !o.Shipped);

  // A SERVER write, transactional. No endpoint, no route, no DTO.
  action Ship(Order order) { order.Shipped = true; }

  render {
    Stack(gap: 3) {
      Text($"{open.Count} to ship");
      foreach (var o in open) {
        Row(gap: 3) {
          Text(o.Reference);
          Button("Ship", onPress: () => Ship(o));
        }
      }
    }
  }
}

Schema, data access and the screen — the three things an application is made of, in one file, with nothing in between for you to build or keep in step. The compiler decides where each part runs from what it touches, and osy validate says so out loud: reads the SERVER holds: Board.open. Nothing here is a sketch; this file compiles as printed.

Then ask the compiler, not the docs

Every question you would otherwise answer by reading a framework's source has a verb — and it answers about the app you just wrote.

$ osy explain
> Posture: deny-all. An entity with no `security { }`
  block grants no access to anyone.

## `Order` — default-deny
- Read — Any signed-in user.
- Create — No one (denied by default).
- Delete — No one (denied by default).

## `Board` @ `/`
- Access — Any signed-in user (secure-by-default).
- Reads — Order
- Writes — Order (update)

$ osy model --json
{ "name": "Order", "fields": [
   { "name": "Reference", "type": "string", "required": true,
     "constraints": [{ "kind": "MaxLength", "value": "40" }] },
   { "name": "Shipped", "type": "bool" }],
  "security": { "posture": "deny", "rules": [ … ] } }

Nobody wrote that report. It is computed from the four lines of security { } above, so it cannot drift from what the runtime does — and note what it says about the page: which entity it reads, which it writes, and that it is closed to anonymous callers because nothing marked it open. osy check is validate, lint and test as one verdict; osy inspect replays what actually ran, end to end, under one correlation id.

01

Nobody manages memory any more. You are still managing the network by hand.

Every paradigm ends the same way. A compiler quietly takes a category of work that everyone agreed was essential — registers, then memory, then threads — and within a decade nobody can remember arguing for it. Not one of those was won by programmers getting better at the work. They were won by the work stopping.

The category still on your desk is distance: the fact that your code and your data are in two places, and that carrying things between them is your job. That is a 1995 constraint you are still paying for in 2026, and the bill arrives as a list you can read.

What you would normally buildWhat replaces it
REST controllers, routes, an API surface for your own UI
Nothing. The UI calls functions by name.
DTOs, view models, mappers
Nothing. The entity is the wire type.
A serialization layer
Nothing. One shared codec, for every app.
fetch/axios, URLs, CORS config
Nothing. There is no URL to call.
A client store, reducers, cache invalidation
Nothing. Reads land in a shared store; a live read refetches on a change signal.
Change tracking — dirty flags, diffing, a save pipeline
Nothing. The platform tracks what you created and changed. You say only when to persist — and on the server, not even that.
An ORM, a repository, a DbContext
Nothing. You query the entity type directly.
async / await / Task<T>
Nothing. There is no async in Osy#.
Migrations you write, and remember to run
A compiled artefact. Additive changes just apply; a rename or a drop is refused until a migration says what you meant — and that file is generated, versioned and checked at deploy. §02.
Auth middleware, per-endpoint guards
security { } on the entity. Written once, enforced beneath every read.
Session/JWT plumbing
Security.IssueJwt + Session.SignIn.
Background jobs, queues, retry logic
A workflow. Durable states that survive a restart. Durable.

Eleven of those twelve are answered with the word "nothing", and that is not a slogan — it is the reason this page can be short. And it is not a trick that only works while an app is small. The platform's own Admin — 55 files, about 7,800 lines of authentication, grids, tabs and forms — declares no API, no DTO, no fetch layer and no store, and has never needed a migration file. Its entire authentication surface is one file of about forty lines, most of which are comments. Nothing was added to the list as it grew, because there is no list to add to.

The list you no longer write is the smaller half. The list you no longer hold in your head is the one that costs you every day: where a function runs, whether a read is authorized, whether a value on screen is stale, whether a step ran twice, what shape crosses the wire. The old model makes you decide each of those again on every feature, forever — and none of them is about the thing you were trying to build. Deleting a category of work is what a new paradigm IS. You already accepted this bargain for memory, and you would not take it back. See where the compiler put each line →

02

You do not own a schema.

There is no schema file and no migration runner. The entity declaration is the schema, and a compile applies it. Add a field, add an entity, ship — nothing to write, nothing to run.

And then the honest half, which is the better one. "No migrations" has been promised before by tools that meant "we will guess", and the guess is how people lose columns. This does not guess:

Add an entity, add a field
Just applies. osy compile, and it is there.
Rename something. Drop something. Change a type.
Refused. The deploy stops, having changed nothing, until you say what you meant.
Writing that down
Generated for youosy compile --generate-migration diffs what is deployed against what you have.

It asks because a rename and a removal are identical from the outside — both are just "this name is gone" — and they want opposite handling. Guess wrong either way and you lose data: treat a rename as a removal and every row is stranded in a table your app can no longer name; treat a removal as a rename and an unrelated table's rows are silently adopted. So it stops once, and writes the answer for you.

migration "contacts became people" {
  rename entity   Job       -> Assignment;
  rename property Job.Notes -> Job.Remarks;
  drop   property Job.Spare;
}

And the migration is a compiled artefact, not a script you run. That is the half that matters. It is generated from the difference between what is deployed and what you have, it travels with the version it belongs to, and the deploy checks it against both — so there is no ordering to get right, no runner to remember, and no way to end up having applied half of one. A workflow migration works the same way: it says, state by state, what happens to the runs still parked in a state you renamed, and a plan that could not be applied stops the deploy rather than surfacing days later as a run nobody can move.

The refusal is the feature. A tool that would rather stop than quietly reshape your data is the one you want holding it. The whole story →   Runs that are still in flight →

03

Shipping it is the command you already ran.

There is no second story for production. No image to build, no pipeline to author, no database to provision on the far side, and no second command to learn — the compile evolves the deployed app's schema exactly as it evolved your laptop's.

And the comparison worth making is not a CI/CD deploy. Pushing a change to a stack that already exists is fast anywhere, and nothing here claims to beat it. What this replaces is the standing up — the work between "I have an app" and "other people can use it".

A new app, the usual way

A server
yours
Chosen, sized, patched, and still yours at 3am.
PostgreSQL
yours
Procured or run, sized, connection-pooled, upgraded.
A domain, TLS, a firewall, a load balancer
yours
Each one a decision, a bill and a thing that can be wrong.
A backup model
yours
Schedule, retention, point-in-time recovery — and a restore you have actually rehearsed, or you do not have backups.
Migrations in the pipeline
yours
A runner, an ordering, and a plan for the deploy that half-applies one.
All of it, here
the runtime's
Not "good defaults" — there is nothing there for you to own.

That list is measured in decisions, not in typing. It is why a new app is a week before anyone outside the room can use it, while the tenth deploy of that same app is thirty seconds. Osy# is aimed at the first number.

On your own machine the part that exists today is already measured: one second to scaffold a project, and about ten seconds for a change to be compiled, applied and on screen. The first launch of a project takes about a minute — your laptop is being given its own PostgreSQL cluster — which is a fact about a laptop rather than about deployment: putting an app on a platform that is already running is a compile, not a cluster.

On your machine works today

$ osy init
$ osy launch          // compiled, running, in a browser
$ osy check           // validate + lint + test, one verdict

In production not open yet

$ osyrin login  --server <url>      // once
$ osyrin app create "Reelo"       // once
$ osyrin app compile --new-version

Hosting is not open to everyone yet, and the block on the right is what it looks like when it is. Everything on the left works today, on the binary you just downloaded. We would rather show you the whole line and tell you which half you can walk than pretend the story stops at your laptop.

Two details that make the last line honest. The local database is real PostgreSQL, initdb'd to reproduce the hosted platform's collation — so what you tested against is not a stand-in that sorts differently later. And --new-version freezes the deploy: a workflow run already in flight finishes on the code and data shape it started under, while new runs use what you just shipped. The three-day approval that was mid-flight when you deployed is not a problem you have.

Where to go next

Start here

Install, the inner loop, and the verbs you will use every day.

Guides

One subject top to bottom — the client/server seam, agents, workflows.

356Reference

Every construct, one page each. 827 of its examples compiled by CI.

Specification

What the language is, in one document — including what it refuses, and why.

20Samples

Runnable apps, each showing one idea rather than a whole product.

45 UI controls ship with it. Source on GitHub; binaries through the package manager you already use.