Everything is a plugin. Every plugin is your code. Like shadcn/ui, but for your entire stack.
How merging works
Installing a plugin doesn’t add a dependency you import fromnode_modules — it copies files into the three places your code already lives:
apps/api/src/resources/<name>/— endpoints, methods, handlers, middlewares, schemasapps/web/src/routes/<name>/— TanStack Router routes, auto-discovered by file pathpackages/<pkg>/— any shared package the plugin ships (e.g.@ship/ai,@ship/emails)
package.json, and any infra it needs (a docker-compose.<plugin>.yml, an infra:<plugin> script, extra .env lines) is wired in too.
After files land, codegen runs so the new resources and routes are live:
src/router.ts, src/contract.ts, and src/db.ts to include the plugin’s endpoints and tables. For a fresh scaffold, the CLI runs this for you after install.
Installing plugins
Pick plugins during scaffolding — theinit flow shows a multiselect, then merges your choices into the new project:
Auth is plugin-delivered. The base
apps/web ships only a landing page; the Auth plugin (auth-starter) adds the API wiring, the sign-in/up and reset pages, the authenticated app shell, and the typed oRPC client. A full-stack project that needs accounts starts with this plugin.The catalog
A plugin’s
requires are merged in automatically — selecting admin pulls in auth-starter, which pulls in mailer and cloud-storage.
Anatomy of a plugin
A plugin mirrors the resource-owns-everything layout of the app it merges into:@/endpoint base and the same @/middlewares/* gates (is-authorized, is-admin, can-access, can-edit) as the rest of your code — there’s nothing plugin-specific to learn. Web routes go under web/routes/** and TanStack Router discovers them by path.
plugin.json
The manifest names the plugin, lists what it requires, and declares which dependencies go to which app:
plugin.json
dependencies.api is appended to apps/api/package.json; dependencies.web to apps/web/package.json.
