Build a satellite
One command scaffolds an app that opens inside the Shell, already signed in — no login screen to build, no second URL, no auth code of your own. From there you add what you need, one capability at a time: an API, a database, rules about who may edit, people from other systems, tenants. Each one is a veni enable away.
The worked example is Calendar — a month view that grows into a real, multi-tenant, shipped app. Follow it end to end and you'll have written a calendar; more usefully, you'll have seen every seam the platform gives you.
This page is where you get ready. The building happens on the six pages below — read on.
Set up
Four things, once. Get them in place and the first command works on the first try; skip one and it fails quietly — veni create errors when there's no Shell to reach.
- Toolchain — Docker (Compose v2), Bun, Node 20.12+ (full list).
- A running Shell to develop against. Pick one and use it throughout:
- Local — bring the whole platform up with Get Started (one command:
bash dev-up.sh). The Shell UI lands athttp://localhost:5173, its API at:3000. - Dev — nothing to run, but you need a Dev account with the
developerrole (ask the platform team): veni.dev.venizia.ai.
- Local — bring the whole platform up with Get Started (one command:
- Signed in as a
developer. Registering apps is a developer-role action (self-service "my apps"), not something you do asadmin. The local seed account isdeveloper@venizia.local/Developer123!. - The
veniCLI — one line, no token to create first, against whichever Shell you picked:
curl -fsSL http://localhost:3010/api/cli/install.sh | bash
veni # prints the command list — add ~/.local/bin to your PATH if "command not found"curl -fsSL https://veni-builder-api.dev.venizia.ai/api/cli/install.sh | bash
veni # prints the command list — add ~/.local/bin to your PATH if "command not found"The installer signs you in as it goes: it prints a short code, opens your browser, and waits while you approve — then saves a 90-day token named after your machine. Nothing to copy-paste, no token to mint by hand. Already have a Personal Access Token and want no browser? -H "X-Veni-Token: veni_…" skips the approval step.
Every command but login, update, uninstall, status and the diagnostics (whoami, doctor, version) needs that token — veni create on a machine that never signed in stops with "Not signed in — run veni login first." (why). No CLI at all? Scaffold in the browser with the Builder UI wizard. Switching Shells later? veni login --shell <url>.
New to terms like federation, token exchange, repository or event bus? Skim Core concepts — it's a one-screen glossary. Everything else you'll pick up as you go.
The path
Six pages, in order. Each opens with a requirement the calendar has to meet, resolves it with the platform piece that answers it, ends with a Done when you can check — and hands off to the next. It's one continuous build on one app: start at the first card and keep going.
Only layer 1 is mandatory — stop at any card. veni enable on its own lists every capability, marks what's on, and resolves prerequisites for you (enabling database pulls in api first); veni status shows where you are.
What you end up with
Each command writes real files into your app — this is the shape you're building toward. Tags: [create] ships from veni create; [+api] and [+db] are added by veni enable api / veni enable database.
calendar/ # [create] your app — its own repo, database, deployment
├─ veni.json # [create] manifest: theme, capabilities, ports, Shell
├─ ui/src/ # [create] React + Vite, federated into the Shell
│ ├─ federation/shell-entry.tsx # [create] ./ShellEntry — loaded at /calendar
│ └─ providers/UserProvider.tsx # [create] gets the Shell token on mount
└─ api/ # [+api] Bun + Hono + Ignis backend
├─ .env # [+api] signing secret · JWKS URL · API port
├─ drizzle/ # [+db] migrations + seed (roles, example perms)
└─ src/ # [+api]
├─ application.ts # [+api] composition root — wire DI at // veni: anchors
├─ services/auth.service.ts # [+api] Shell-token → service-token exchange
├─ utils/auth-guards.ts # [+api] requireAuth / requirePermission / ...
├─ services/user.service.ts # [+db] local users (turns on APP_ENV_DATABASE_URL)
├─ models/schema.ts # [+db] Drizzle tables — yours to define
└─ repositories/base.repository.ts # [+db] AuditedRepository — created_by fills itselfauthz, integration and tenancy add to these rather than new folders — their pages show exactly what.
See also
- Common snags — the quiet failures, keyed by symptom. Bookmark it before you start.
- Register an app — every way to get a running app into a Shell's launchpad.
- The veni CLI — every command and flag in one table.