Quickstart

Build an osgarden app: declare a schema, deploy it to a gateway, and talk to it with the TypeScript SDK. Nothing else — no servers, no billing code, no secrets.

Hosted deploys are invite-gated during preview. The fastest way to explore is to run the whole platform locally (it's one repo), or to poke at the live demo: chat.osgarden.net. Want to ship on the hosted platform? Open an issue and say hi.

1. Run the platform locally#

You need Bun and a local Postgres.

git clone https://github.com/CarsonJoe/osgarden.git
cd osgarden
bun install

# point the gateway at your Postgres + an Ory project (see packages/gateway/.env.example)
cp packages/gateway/.env.example packages/gateway/.env

bun run db:migrate     # ledger tables
bun run dev:gateway    # gateway on http://localhost:3000

The gateway is the only server there is: it authenticates callers, enforces access, meters usage, and fronts Postgres, storage, and the AI provider.

2. Declare your app's schema#

An app's entire backend is one file, .osgarden.schema.ts — tables, resource types, and default-deny access rules. This is the demo chat app's real schema, trimmed:

import { defineSchema } from "@osgarden/schema";

export default defineSchema({
  tables: {
    // Top-level tables are creator-owned: each row lives in its
    // author's namespace and can be published into resources.
    messages: (table) => {
      table.text("body").notNull();
      table.timestamps();
      table.index(["createdAt"]);
    },
  },

  resources: {
    // A "room" anyone can discover and join.
    room: (room) => {
      room.visibility("discoverable");
      room.defaultRole("writer");

      // Messages are shared into rooms: authors keep ownership,
      // admins can unlink (moderate) without deleting the author's copy.
      room.shares("messages", (m) => {
        m.onMemberRemove("remove");
        m.onOwnerDelete("tombstone");
        m.access({
          read: "reader",
          create: "writer",
          update: "creator",
          delete: "creator",
          unlink: "admin",
        });
      });
    },
  },
});

Access is default-deny — a verb you don't grant is denied. See Schema & deploy for the full DSL.

3. Deploy it#

bunx osgarden deploy \
  --gateway http://localhost:3000 \
  --deployment-id myapp \
  --token <DEPLOY_TOKEN>

The gateway compiles the schema into an isolated Postgres schema and the access metadata it enforces on every request. Safe changes (new tables/columns) apply automatically on redeploy; destructive ones are blocked until you write an explicit migration.

Optionally generate row types for end-to-end type safety:

bunx osgarden typegen   # → osgarden-env.d.ts

4. Talk to it from your app#

import { createClient } from "@osgarden/sdk";

const osgarden = createClient({
  gatewayUrl: "http://localhost:3000",
  clientId: "your-oauth-client-id",
});

// Sign in (OAuth + PKCE; tokens stay in httpOnly cookies).
await osgarden.auth.signIn();

// Private data — scoped to the signed-in user, paid by them.
await osgarden.table("notes").insert({ title: "Hello" });

// Shared data — create a room, post into it.
const room = await osgarden.resource.create("room", { name: "General" });
await osgarden.resource(room.id).table("messages").insert({ body: "hi!" });

// Read across every room you're a member of, as one query.
const latest = await osgarden
  .resources({ type: "room" })
  .table("messages")
  .orderBy("createdAt", "desc")
  .limit(50);

// AI — no key in your app; the caller pays their own tokens.
const res = await osgarden.ai.chat({
  model: "openai/gpt-4o",
  messages: [{ role: "user", content: "Hello" }],
});

Every call above is authenticated, access-checked, and metered by the gateway. If the user's balance can't cover an operation, it fails with 402 before any spend.

What you didn't write#

No auth pages, no session handling, no billing integration, no API keys, no row-level security bugs, no servers. The user's dashboard — balance, itemized spend, exports, per-app spend caps — exists already and is the same for every app.

Next steps#