Osy#betaa language · its runtime Osyrin · a hosted platform
Why Osy#Built for agentsAgents as declarationsWorkflows that waitRuns exactly onceSecure by defaultNothing to mockThe editor is the compilerUI in the languageDocuments are dataOne program

Built for agents · beta · hosting opening

SaaS you own.

We're building the platform for software that people can have AI build, actually own, and run as a service. Osy# is the language: everything between an idea and a running app, compiled away — the data, the rules and the screen in one program. One command runs it on your laptop and, when hosting opens, ships it. And whichever coding agent you use can ask the compiler instead of guessing.

381 reference pages · 929 examples compiled by CI · 45 UI controls · 20 complete apps

What you would normally buildcompiled away · replay ↻
  • REST controllers, routes, an API surface
  • DTOs, view models, mappers
  • A serialization layer
  • fetch / axios, URLs, CORS
  • A client store, reducers, cache invalidation
  • Change tracking, dirty flags, a save pipeline
  • An ORM, a repository, a DbContext
  • async / await / Task<T>
  • Migrations you write and remember to run
  • Auth middleware, per-endpoint guards
  • Session / JWT plumbing
  • Background jobs, queues, retry logic

→ Nothing. This is the whole app:

entity Order {
  string Reference; bool Shipped;
  security { allow read, update when IsAuthenticated; }
}
[Page("/")] component Board() {
  // a server read, a server write, and the screen
  live var open = Order.Where(o => !o.Shipped);
  action Ship(Order o) { o.Shipped = true; }
  render { foreach (var o in open)
    Row { Text(o.Reference);
          Button("Ship", onPress: () => Ship(o)); } }
}

Three ways in

Build it yourself. Use ours. Fork someone else's. You own the software.

Osy# changes the SaaS model. Applications are built, shared and deployed as software you own — not software you merely rent access to. Build your own application with Osy#, start from one of our MIT-licensed applications, or use software created by the Osy# community. Run it yourself for free, or have Osysharp host and operate it for you — and pay for the running, never per seat.

You own the software. We make running it easy.

00 · Get it

Coming soon The download is not published yet. Everything on this site describes the build we run today; the binaries and the package channels follow. Want to be told the day it lands?

One binary. Yours in a minute.

The compiler, the local platform and the toolchain are one download. The first run fetches its database bundle, a real PostgreSQL, once. Nothing else to install. It is a beta: the language and the local platform are ready to build with, and the surfaces marked not open yet are not.

  • Betathings will move between versions. --new-version is how nothing you already shipped breaks when they do.
  • Binariesthrough the package manager you already use channels to be confirmed
  • Editorosy vscode install puts the bundled extension into VS Code, Cursor, VSCodium or Windsurf
  • Twenty appsosy init <name> writes a complete app with its tests; see §08
  • Feedbackissues and ideas go to a public tracker on GitHub; osy feedback opens one prefilled with your version and the last diagnostic opening
  • Statisticsthe toolchain sends anonymous usage counts: command names, versions, OS, error codes. Never your source, your data or your paths. osy telemetry off stops it; the first run tells you so. Exactly what is sent.
Start here: install, the inner loop, the daily verbs Watch a first session (110 s)
install · the commands below are placeholders until the channels are live# macOS
$ brew install osysharp/tap/osy
# Windows
$ winget install Osysharp.Osy
# Linux
$ curl -fsSL https://osysharp.com/get | sh

$ osy --version
osy 0.9 · platform bundle cached · compiler ready

$ osy init && osy launch
http://hello.localhost:8136/

01 · Zero to running

Two commands. No Postgres to install, no Docker, no server.

That is the whole install-to-running path. The local platform starts itself and brings its own storage: real PostgreSQL with pgvector, started and stopped with the app, collated to match the hosted platform's. Below, a real first session: install, init, launch, then change it with the agent you already use.

The first session, 110 s. Recorded against osy 1.0.0 on 2026-09-03, unedited except for length. Every command is in the transcript beside it.
  1. One binary.$ osy --versionThe first run fetches the database bundle once.
  2. A handful of files, and a note for your agent.$ osy init --agent claudeNo package.json, no Dockerfile, no connection string.
  3. Running.$ osy launchReal Postgres, embedded. Nothing else installed.
  4. Now change it.$ claude"Turn the greeting into a todo list, with a test. Run osy check when done."
  5. The agent asks the compiler, not a forum.$ osy checkOne verdict it can act on; a refusal carries its fix.
  6. It ran.$ osy launch · osy explainNothing between the file and the screen was yours to build.

And nineteen more apps, one command each

from nothing to a running app$ osy init kanban
✓ app.osy         ✓ model/board.osy   ✓ .vscode/launch.json
✓ model/auth.osy  ✓ model/states.osy  ✓ tests/board.test.osy

$ osy launch
Local platform ready · instance kanban-2c41a7f0
✓ compiled and applied to Kanban (6 files)
http://kanban.localhost:8121/

What you did not do

  • Install PostgreSQL. One is embedded.
  • Run Docker. Nothing is containerised.
  • Start a web server. The local platform is one.
  • Write a connection string, a schema file, or a seed script.
One binary. The first run downloads its database bundle, a real PostgreSQL, once.
The kanban sample app running locally
kanban.localhost:8121 · one of 20 complete apps, one command away. A kanban board — cards move between lanes, and a workflow drives the states. 1 test file.

02 · Why now

More of your code is written by something that cannot ask a colleague.

It has no tribal knowledge, no teammate to interrupt, and no memory of why this field is nullable. It has the source, the compiler, and whatever the compiler will say. So Osy# says a lot.

Every question you would otherwise answer by reading a framework's source has a verb, and most of them answer --json. What the app is, resolved rather than inferred. Who may read what, from the declarations. What actually ran, end to end. One verdict, and a refusal that carries its own fix.

The other half is default-deny. When the writer is guessing, the blast radius of a wrong guess is bounded by a declaration, not by whether the guesser remembered to check.

And when it is done, it can prove the code works instead of telling you it does. A test here is the real app on a real database, run as a real person, in a real browser if you ask for pixels. Nothing is mocked, so a green means what it says.

Bring the agent you already use. osy init --agent claude today; the toolchain answers --json for any of them. Nothing here makes you switch editors, agents or hosts.

The full argument: built for agents
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.

 `Modes.Of(…)` is a STYLE DECLARATION, not a value,
  and this is a render block. To use it here: name the
  pair in your theme's Colors { } and write `Colors.Surface`.

03 · What disappears

Nobody manages memory any more. Why are you 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, 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: your code and your data are in two places, and carrying things between them is your job. That is a 1995 constraint you are still paying for in 2026, and the bill is a list you can read.

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 and checked at deploy.
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.

Eight of those twelve are answered with the word "nothing", and that is not a slogan. It is not a trick that only works while an app is small: nothing gets added to the list as an app grows, because there is no list to add to. And 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.

Stop building the network. Write the program.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.

04 · What the compiler takes over

The things that are always true are the compiler's job.

Authorization, durability, which half runs where, the database you never provisioned. What you write is your model, your rules and your screens. The rest is not a convenience. It is the language. And it is built for the apps that are hard: realtime, long-running, human-in-the-loop, agentic. Each of these has a chapter; each chapter opens with why.

Built for agentic apps

An agent that waits two days for a person is a line of code.

Not a wrapper around a model. An agent here is a principal with its own grants and only the tools you hand it, so it acts on exactly the data it was granted and nothing else. It runs inside a durable workflow: its answer streams into a page a token at a time, the people on the same record see each other, it parks on a person for as long as your policy says, and a crash between two of its calls charges nobody twice. And when a customer's procurement asks whether the AI can be switched off, the answer is yes, today: an operator turns it off while the app runs, generation and embedding separately, in seconds, from a control plane the app's own source cannot reach. Here that is a toggle. Anywhere else it is weeks of engineering across every call site, and the one you miss is the audit finding. And the switch has a dial beside it: the platform meters every model call and enforces hard daily budgets — tokens and dollars, per organisation, per app and per user — before the call leaves, fail-closed, set by the organisation's owner in the admin. Nobody wakes up to a $10,000 bill because an agent looped overnight; the eleventh call over the cap is refused and the app carries on without it. This is for the apps that are hard: realtime, long-running, human-in-the-loop.

How: agent declares it with a Principal and Roles; stream<T> streams its answer; a workflow state with subscribe … as parks the run on a person.

Agents as declarations
demo/agent-expenses/model/review.osy · verbatim/// PARKED ON A PERSON. Everything in this state is POLICY — who is asked, how they hear
/// about it, how long they get — and it is all the app's.
state Waiting {
  subscribe Answered(string text) as Submitter {
    Assignee = ExpenseReport.Where(r => r.Id == this.Item.EntityId).FirstOrDefault().Employee;
    Finished { Within = TimeSpan.FromDays(2); Unfinished { goto Failed; } }
  }
  on Answered(string text, Slot slot) {
    var reply = Auditor.Answer(this.Item, text);

Runs exactly once

A crash cannot charge the card twice.

Your database is transactional. The world is not, and a rollback does not undo the payment you took. So every call that leaves the platform is a durable step, and a resume skips the ones already recorded.

How: the compiler classifies each call. No retry policy, no idempotency key, no dead-letter queue to write.

Runs exactly once
$ osy model --json"name": "ReadReceipt",
"effects": ["Llm.Extract", "UnitOfWork.Commit"],
"durability": "External",
"durabilityVia": "Llm.Extract"
// the route to the effect, not just a verdict

Secure by default

Say who may read it, once. Every query obeys.

An entity with no security block grants nothing to anyone. Declare the rule on the entity and it becomes the row filter inside every query your program can express. And a rule can be a real one: name each condition once as a policy, give it the row it is about, compose them, and the compiler inlines the result into the SQL. A query then says nothing about security at all: it filters for what the caller is looking for, and who they may see is already inside the statement. There is no check to remember at a call site, and no code you write afterwards can get around it.

How: a policy is a named predicate over the caller and a row; a where composes them; osy explain prints the resulting posture for every entity and page.

Secure by default
the rule · Docs/reference/security/naming-a-policy.md · compiled by CI// each rule named once; one ABOUT a row takes it
policy HandledByUnit(Unit u) =>
  RoleGrant.Any(g => g.Holder == user && g.Unit == u);
policy HasRecused(Guid caseId) =>
  Recusal.Any(r => r.Person == user
                && r.Case.Id == caseId
                && r.Lifted == false);
policy MaySeeRestricted =>
  RoleGrant.Any(g => g.Holder == user
                  && g.Level == Level.Senior);

entity Case {
  [Required, MaxLength(40)] string Reference;
  [Required] Scheme Scheme;
  bool Restricted;
  security {
    allow read where HandledByUnit(Scheme.OwningUnit)
              && !HasRecused(Id)
              && (!Restricted || MaySeeRestricted);
  }
}
// inlined into the SQL of every query on Case,
// and on everything that hangs off one
a read over it · not one word about who may see whatList<Case> RestrictedUnder(string scheme) {
  return Case.Where(c => c.Restricted
                  && c.Scheme.Name == scheme)
             .OrderBy(c => c.Reference)
             .ToList();
}
// outside the unit: an empty list, not an error.
// recused from this case: it is not in the list.
// not senior: no restricted case at all.
// one function, for all of them.

Workflows that wait

A process that waits three days for a person is still just source.

States, the events that move between them, who may raise each one, and what happens when a step waits for days. A workflow is a file you read top to bottom, and a run that outlives the deploy that started it.

How: the state is a column on your entity; a parked run keeps the code and data shape it started under.

Workflows that wait
workflowstate Paid {
  subscribe Ship(string tracking) as Fulfil {
    Candidates = u => u.Role == Fulfillment;
  }
  on Fulfil { goto Shipped; }
}

Nothing to mock

An agent can prove its code works. Not say so.

There is nothing to mock, because there is nothing in the way: your functions, your entities, your security rules, on a real Postgres that starts for you, offline, with no account. A test runs as a named person, and the same file drives a real browser and looks at the pixels. That is what makes code you did not write code you can trust.

How: osy test boots the embedded platform and a fresh database per run; runas acts as one of your users; --pixels runs the same file in a real browser.

Nothing to mock
demo/ember/tests/pages.test.osy · verbatim[Test(TwoPeople)]
void a_signed_in_person_with_no_channels_is_told_how_to_make_one() {
  Ui.SignInAs(Ada);
  Ui.Visit("/");

  Assert.Visible("Pick a channel");
  Assert.Visible("none yet — make one below");
}
// real app, real rules, real Postgres — and with --pixels, a real browser

Search and memory are capabilities

Mark a field searchable and it is findable.

You never touch a vector, an index or an embedder. Declare a dependency on the capability and the surface appears in your app; the runtime brings the storage, pgvector included.

How: capabilities are declared in app.osy; the local platform and the hosted one provide the same set.

Agents as declarations
app.osyapp Notes { use Osysharp.Memory; }

entity Note { [Searchable] string Body; }
var hits = Memory.Search("the thing about the courier");

The editor is the compiler

Hover, rename, debug across the seam, from one resolver.

The thing answering your editor is the compiler, not an approximation of it. A margin marks which lines leave the machine; the debugger stops on the line you wrote with one call stack across client and server; a parked run can be rewound.

How: the language server inside the bundled extension is the compiler's resolver. Grammar written once.

The editor is the compiler
$ osy vscode install$ osy init kanban
✓ .vscode/extensions.json   ✓ .vscode/launch.json

$ osy vscode install
// installs the bundled extension into VS Code
// (or Cursor, VSCodium, Windsurf). The language
// server inside it IS the compiler's resolver.

05 · Ask the compiler

One file. The compiler decides where each part runs, and says so.

Schema, data access and the screen: the three things an application is made of, in one file, with nothing between them for you to build or keep in step. This file compiles as printed.

model/board.osyentity 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)); }
      }
    }
  }
}
$ 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 validate
reads the SERVER holds: Board.open

Nobody wrote that report. It is computed from the four lines of security { }, so it cannot drift from what the runtime does. 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.

One program: where the compiler put each line

06 · Your data is safe

You cannot lose data by renaming a field.

There is no schema file and no migration runner. The entity declaration is the schema. Add a field, add an entity, ship. Rename something, and the deploy stops, having changed nothing, until you say what you meant.

"No migrations" has been promised before by tools that meant "we will guess", and the guess is how people lose columns. A rename and a removal are identical from the outside, and want opposite handling. So this does not guess: it stops once, and writes the answer for you.

The migration is a compiled artefact, not a script you run. It travels with the version it belongs to, and the deploy checks it against both sides. No ordering to get right, no runner to remember, no way to apply half of one.

The whole story Runs that are still in flight
$ osy compile add entity Assignment          just applies
 add property Job.Remarks       just applies
 Job.Notes is gone — rename or drop?
  The deploy stopped, having changed nothing.
  osy compile --generate-migration writes the answer:
migrations/0007.osy · generated, versioned, checked at deploymigration "contacts became people" {
  rename entity   Job       -> Assignment;
  rename property Job.Notes -> Job.Remarks;
  drop   property Job.Spare;
}

The refusal is the feature.A tool that would rather stop than quietly reshape your data is the one you want holding it.

07 · Shipping

One command, three places to run it.

Everything SaaS did for you, for software you wrote. The hosting, the backups, the migrations, the versions, the 3am: that is what you were paying for, not the app. There is no second story for production here, no image to build, no pipeline to author, no second command to learn. The hosted platform is the local one, so what runs on your laptop runs anywhere Postgres does, and on ours when hosting opens.

On your machine works today

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

You set up nothing. One binary; the database is embedded.

On your own server opening · free

$ osyrin login  --server my.example.org
$ osyrin app create "Reelo"
$ osyrin app compile --new-version

You set up a server, PostgreSQL, a domain, TLS, a firewall, backups you have rehearsed. You do not set up a pipeline, a migration runner, or a deploy that half-applies one: the compile evolves the schema exactly as it did on your laptop. Free for one application per instance — and that application may serve as many organisations as you like. Many applications on one control plane is the platform licence.

On the hosted platform opening

$ osyrin login  --server osyrin.app
$ osyrin app create "Reelo"
$ osyrin app compile --new-version
https://reelo.osyrin.app/

You set up nothing. The same three lines, pointed somewhere that is not your laptop. None of it is yours: not the server, not the database, not the backups, not the 3am. A free app runs as long as someone uses it; one nobody has touched for ninety days is removed after a warning and a final backup — the terms.

The middle and the right are the same commands with a different address, and the left is the same compiler. Going from an idea to a public URL is one step, and it is a step you already know. --new-version freezes each deploy: a run that was mid-flight when you shipped keeps the code and data shape it started under. The hosted platform does not run your container and hope; it operates the guarantees the compiler made, which is why nobody else can host this for you. Want to be told when hosting opens? Leave your address  ·  Where it runs, in full

SaaS you own.The app is yours, the source is yours, the data is plain PostgreSQL. What you never own is the operating of it.

08 · Twenty complete apps

Not snippets. Whole apps with their tests, one command each.

The arcade sample, running
$ osy init arcadeA collection of small arcade games — the demo that pushes the UI engine hardest.
The agent-expenses sample, running
$ osy init agent-expensesagent-expenses — an expense system where agents do real work, and every cent is attributed to an outcome you can judge.
The chart-demo sample, running
$ osy init chart-demoCHART DEMO — the consumer that makes `kits/chart` a library rather than a claim.
The chat-room sample, running
$ osy init chat-roomA group chat — conversations, participants and messages, behind a login.
The ember sample, running
$ osy init emberEMBER — a small team workspace, and the first app built entirely on the topic surface ([[M256]]/[[M261]]/[[M262]]).
The dialog-demo sample, running
$ osy init dialog-demoTHE CANONICAL DIALOG SAMPLE (D60/D212).
The entity-inheritance sample, running
$ osy init entity-inheritanceentity-inheritance — one table, four kinds of document, three levels deep.
The file-manager sample, running
$ osy init file-managerFiles and folders — upload, download, move and delete, with a folder tree.
The kanban sample, running
$ osy init kanbanA kanban board — cards move between lanes, and a workflow drives the states.
The markdown-demo sample, running
$ osy init markdown-demoMarkdown demo — the built-in markdown control, running against real sectioned storage.
The memory-lab sample, running
$ osy init memory-labAn agent with long-term memory — notes it keeps about a subject, across conversations.
The wf-approvals sample, running
$ osy init wf-approvalsA purchase-order approval workflow — it parks and waits for a human at each step.
The wf-signup-invite sample, running
$ osy init wf-signup-inviteAn invite-and-accept signup — send an invitation, then wait for the person to accept it.
The dropdown-demo sample, running
demo/dropdown-demo · in the repositoryA project's LEAD, chosen from a dropdown — the sample that proves M121's `Slot(item)` renders, and that a dropdown bound to an ENTITY-typed property…
The wf-order-saga sample, running
demo/wf-order-saga · in the repositorywf-order-saga — the ORDER-LOB workflow demo.
The wf-expense-hitl sample, running
demo/wf-expense-hitl · in the repositorywf-expense-hitl — the SLOT-DEPENDENCY workflow demo (ExpenseApproval).
The wf-fanout-quorum sample, running
demo/wf-fanout-quorum · in the repositorywf-fanout-quorum — the FAN-OUT + QUORUM workflow demo.
The wf-nightly-digest sample, running
demo/wf-nightly-digest · in the repositorywf-nightly-digest — the SCHEDULED trigger, end to end.
The wf-support-sla sample, running
demo/wf-support-sla · in the repositorywf-support-sla — the SUPPORT-TICKET workflow demo.

09 · Not locked in

The web's controls are yours to bring.

Osy# ships a kit of controls, and the kit is not the boundary. A control block declares the contract of any foreign widget — its typed props, its events, the shape of data it takes — and a small JavaScript module implements it. The compiler type-checks every call site against that contract, the same as for a control we wrote. Heavy pieces load in chunks, on demand, so a page that never opens the maths renderer never pays for it. A kit is one line to depend on, pinned and hashed. Three already ship this way, as proof rather than as the limit:

A rich editor Milkdown

Sections, partial saves, tables, find and replace, diagrams — and maths, loaded as a chunk the first time a document needs it. The markdown editor kit; the whole story in the documents chapter.

A PDF reader pdf.js

A viewer with fit, zoom and a real text layer, and a thumbnail that draws one page as a picture — over a file the app stored. The PDF kit.

A QR scanner WebAssembly

A live camera scanner for QR codes and barcodes, decoding in a WebAssembly module over the platform's own camera API; your app gets scanned(text, format). The barcode kit.

What we bring is a starting point. What the browser can do is the ceiling — and the contract is one block of Osy#.