← the tour · concept

concept xii

Vendored, not imported

Code lands in your repo. You own it, fix it, read it. The dependency you don't have can't hurt you.

Most frameworks ask you to install them. orion does something different: it arrives as code in your repository. The belt, the patterns, the recipes — none of them live behind a module boundary you didn't write. You can read every line with grep, fix a bug with one edit, and delete anything you don't use. This is the shadcn move applied to a backend toolbelt, and it has sharper consequences here than it does for UI components.

Three tiers, one move

There are three distinct tiers, and understanding the boundary between them is the whole model:

the belt zero-dep core · yours package.json: {} patterns vendored HTML + CSS edit freely recipes glue → a seam the dep is yours
Generators copy code in; the belt never imports a recipe's dependency. "Zero-dep" stays literal.

The belt

The belt is orion's zero-dependency core: the SSE writer, the router, the store adapter, the pub/sub bus, the templating helpers, the auth primitives — roughly six hundred lines of TypeScript that implement the Datastar protocol and the one loop with nothing but Node builtins. Its package.json is literally {}. When you run orion new, the belt is copied into your project. It isn't installed from npm; it's in your tree. You own it outright.

Patterns

Patterns are pre-built UI interactions — toast notifications, modals, inline-edit fields, comboboxes, presence avatars. Each one is a pair: a patterns/*.ts view-function file with the server-side HTML and helpers, and a public/*.css file with its semantic styles. orion gen pattern toast copies both into your project and prints the <link> line to add to your layout. The Datastar attributes arrive already correct — the dense ones (combobox keyboard navigation, inline-edit commit/cancel) are exactly why these are generated rather than documented. After generation they're yours: edit the HTML, adjust the CSS, delete the parts you don't need.

Recipes

Recipes cover the problems that do need an outside dependency — a job queue with durable workflows, transactional email, billing webhooks. A recipe is a small glue file, generated into your project by orion gen recipe <name>, that wires a specific library into one of the belt's seams (Store, Bus, Jobs). The dependency lands in your package.json. The belt's package.json never sees it. Zero-dep stays literal.

Current recipe list: jobs-honker (durable queues and workflow primitives inside your SQLite file), email-upyo (transactional mail), billing-stripe (checkout + webhooks → entitlements), config-database-url, flags-launchdarkly.

The seam test

The boundary between a recipe and documentation is sharp, and the test is simple: a recipe must wire into the loop. It must connect to a belt seam — the Store interface, the Bus interface, the Jobs interface — so a feature that uses it never changes a line when you swap the implementation underneath. "Here's a good library" with no loop integration is documentation, not a recipe. The belt still never imports it.

This matters because it keeps the belt honest. A recipe for honker gives you durable workflow primitives inside your SQLite file and enqueue still participates in the command's own transaction. A recipe for upyo gives you transactional email through the same send() call your commands already know. The dependency is real; the seam means no feature ever touches it directly.

example The Jobs seam in the belt has a zero-dep default: enqueue() is a plain INSERT on your store, atomic inside the command's own transaction. orion gen recipe jobs-honker copies a glue file that swaps in honker's SQLite extension — NOTIFY/LISTEN wakeups instead of polling, cron, durable workflow steps. Every feature that calls enqueue() is unchanged. The belt imports nothing; your project now depends on honker.

Why this compounds

Three reasons, and they reinforce each other:

The bet: a codebase whose mechanisms are readable is cheaper to maintain, cheaper to debug, and cheaper to hand to an agent than one that delegates understanding to a dependency graph. Vendoring is the posture; the three tiers are the shape it takes.

What the CLI does

The generator is the delivery mechanism. There's no install step for patterns or recipes — just a copy into your tree and a short list of things to wire:

orion gen feature todos       # four-file slice, yours to edit
orion gen pattern combobox    # SQL-backed, keyboard-navigable, vendored in
orion gen recipe jobs-honker  # durable queues; the dep lands in your package.json

Each generator prints exactly what it added and, for recipes, the npm install line. After that it's your code: grep it, edit it, delete what you don't use. There is no eject step because there was never anything to eject from.

The upgrade model

Because the belt is in your tree, upgrades are guided diffs, not semver bumps. The CLI will eventually ship orion upgrade — a command that shows what changed in the upstream belt since you vendored your copy and lets you accept, reject, or merge changes file by file. This is the tradeoff made explicit: you give up automatic upgrades; you gain the ability to see exactly what you're running and decide whether you want it. For a backend toolbelt whose mechanisms participate in every request, that tradeoff is worth making.

orion ✦ a belt of stars · built on datastar