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.
$ 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.
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.
fetch/axios, URLs, CORS configlive read
refetches on a change signal.DbContextasync / await / Task<T>async in Osy#.Session.SignIn.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:
osy compile, and it is there.osy 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
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
Install, the inner loop, and the verbs you will use every day.
One subject top to bottom — the client/server seam, agents, workflows.
Every construct, one page each. 827 of its examples compiled by CI.
What the language is, in one document — including what it refuses, and why.
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.