# Shibumi Stack DX Plan ## The Five Pieces - **Bun**: runtime, bundler, test runner, package manager - **Hono**: routes, middleware, SSR - **Drizzle**: schema, queries, migrations - **Alpine**: interactivity, no build step - **Zod**: validation, type inference Five tools, full stack. Each replaceable, none redundant. Shibumi is not a framework. It's the opinionated glue: a scaffolder (`create-shibumi`) that wires these five together with the right config for your deploy target, plus shared conventions (project structure, base.css, CSRF). Like create-t3-app, but for the small, self-hostable, no-Next end of the spectrum. Everything else is an extension. ## Core Principle Every command is just Bun. Shibumi sets up the right defaults then gets out of the way. No wrapper runtime, no magic CLI that hides what's happening. ## 1. Create ``` npm create shibumi@latest myapp bun create shibumi@latest myapp ``` Package: `create-shibumi` (already claimed on npm). ### CLI Design Dependencies: `@clack/prompts`, `ora` (spinners), `chalk` (colors). Color palette: - Kanji (渋み): bold, terracotta (#c76647) - "shibumi": bold white - "Refined simplicity": dim gray - Step pills (dir, tmpl, deploy, git, deps): Moss green (#64b464) bg, bold dark text - "next" pill: dark orange (#8a3f25) bg, non-bold white text - Selected radio (●): Moss green - Unselected options: ANSI dim (adapts to terminal theme) - "✓ Project ready": Moss green - Final command: terracotta, && in white - Docs link: dim Default project name: random zen-themed (quiet-bamboo, still-water, bare-stone). All prompts skippable with `--yes` for CI. ### Prompt Flow ``` 渋み shibumi Refined simplicity dir Where should we create your project? ./quiet-bamboo tmpl How would you like to start? ○ Bare: minimal, start from scratch ○ Blog: markdown posts, RSS feed, SEO ready ● SSR: full stack with API routes ○ Static: pre-built, outputs to dist/ deploy Where will you deploy? ● Self-hosted (Bun + Docker) ○ Cloudflare Workers/Pages ○ Vercel ○ Fly.io ○ Static CDN git Initialize a repository? Yes deps Install dependencies? Yes ✓ Project ready ■ Template copied ■ Dependencies installed next cd quiet-bamboo && bun dev Docs: shibumistack.dev/docs ``` Preview script: `/tmp/shibumi-cli-preview.sh` ## 2. Develop ``` cd myapp bun dev ``` Hot reload, SQLite locally, zero config. Works immediately. ## 3. Build ``` bun run build ``` SSG outputs `dist/`. SSR outputs optimized bundle. Same command either way. ## 4. Deploy Push to GitHub. Platform picks it up. No deploy CLI needed. Templates ship with the right config already in place based on the deploy target chosen during create: ### Self-hosted (Bun + Docker) - `Dockerfile` (Bun alpine image, minimal) - `docker-compose.yml` (app + Caddy) - `Caddyfile` (auto-HTTPS, reverse proxy) - SQLite data as volume mount ### Cloudflare Workers / Pages - `wrangler.toml` - Entry point uses `export default { fetch: app.fetch }` ### Vercel - `vercel.json` - Entry point uses `hono/vercel` adapter ### Fly.io - `fly.toml` - Same Dockerfile as self-hosted - Volume for SQLite persistence ### Static (CDN) - Build command + output dir in `package.json` - Works on Cloudflare Pages, Netlify, Vercel, any static host ## 5. Hono as the Universal Layer Hono is the secret weapon. App code, routes, templates stay identical across all deploy targets. Only three things change per target: 1. Entry point adapter 2. Deploy config file 3. `devDependencies` ```ts // self-hosted (Bun) import { serve } from 'bun' serve({ fetch: app.fetch, port: 3000 }) // cloudflare workers export default { fetch: app.fetch } // vercel import { handle } from 'hono/vercel' export default handle(app) ``` ## 6. Data Layer Drizzle owns schema, queries, and migrations. Same schema everywhere, different driver per target: ```ts // schema.ts — shared, always the same export const posts = sqliteTable('posts', { id: integer('id').primaryKey(), title: text('title'), }) // db.ts — generated by create-shibumi per deploy target // self-hosted: local file export const db = drizzle(new Database('data/app.db')) // cloudflare: D1 binding export const db = drizzle(env.DB) // vercel: Turso export const db = drizzle(createClient({ url, authToken })) ``` Default per platform (no prompt, just the right answer): - Self-hosted / Fly.io: SQLite file (volume mount) - Cloudflare: D1 - Vercel: Turso Dev is always local SQLite regardless of target. D1's `wrangler dev --local` also uses local SQLite under the hood. Commands: ``` bun run db:generate # migration SQL from schema diff bun run db:migrate # apply locally or to target bun run db:seed # seed data bun run db:studio # local browser UI ``` ## 7. Deploy Targets at a Glance | Target | Runtime | SSR | SSG | SQLite via | |----------------|------------|-----|-----|------------------| | Self-hosted | Bun | yes | yes | file (volume) | | Fly.io | Bun | yes | yes | file (volume) | | Cloudflare | Workers | yes | yes | D1 | | Vercel | Serverless | yes | yes | Turso / external | | Static CDN | none | no | yes | n/a | ## 8. Core: CSRF The only helper that ships with every project. Security shouldn't be opt-in. Hono middleware for token generation + validation: ```ts import { csrf } from 'shibumistack' app.use(csrf()) ``` ## 9. Extensions Ecosystem Everything beyond the five pieces + CSRF is an extension. Added explicitly, owned by the user. ### Mechanism ``` bun run shibumi add auth bun run shibumi add flash bun run shibumi add html-helpers bun run shibumi add uploads bun run shibumi add email ``` Extensions are **copy-paste, not dependencies** (like shadcn/ui): 1. Source files copied into your project, not hidden in node_modules 2. A migration if it needs DB tables 3. An `agents.md` fragment appended to the project's `agents.md` You own the code. Modify it, delete it, fork it. No lock-in. ### Available Extensions (planned) | Extension | What it adds | |------------------|-------------------------------------------------------| | `auth` | Cookie sessions, login/logout routes, session schema | | `flash` | Session-based one-time messages after redirects | | `html-helpers` | Tagged template literals, layouts, partials | | `uploads` | File upload handling, S3/local storage | | `email` | Transactional email via Resend/SMTP | | `stripe` | Checkout, webhooks, subscription helpers | | `admin` | Auto-generated CRUD UI from Drizzle schema | ### agents.md: AI-Native DX Each extension ships an `agents.md` fragment. When you `shibumi add auth`, it appends instructions to your project's `agents.md`: ```markdown ## Auth (added via shibumi add auth) - Sessions live in src/lib/session.ts - Use createSession()/destroySession(), never set cookies directly - Protected routes use the auth() middleware - Don't inline Alpine state for auth forms, use x-component="login-form" ``` The base scaffolder also generates a root `agents.md` with stack conventions: ```markdown ## Stack Bun + Hono + Drizzle + Alpine + Zod. See .plans/dx.md for architecture. ## Conventions - Alpine: use x-component="name" pattern, not inline x-data blobs - Routes: file-based in src/routes/, each exports a Hono sub-app - Validation: Zod schemas in src/schemas/, use safeParse at boundaries - DB: Drizzle schema in src/db/schema.ts, never raw SQL ``` AI agents read this file and know how to work with the project. The more extensions you add, the smarter agents get about your specific setup. ### Community Extensions Anyone can publish a Shibumi extension as an npm package (`@shibumi/auth`, `shibumi-ext-stripe`, etc.). The contract is simple: 1. Export a `files/` directory with source to copy 2. Export an `agents.md` fragment 3. Optionally export a `migration.sql` `shibumi add ` resolves from npm, copies files in, appends agents.md, runs migration. ## Example Workflows ### Solo dev, new SaaS ``` npm create shibumi@latest invoicely → template: SSR → deploy: Self-hosted (Docker) cd invoicely && bun dev bun run shibumi add auth bun run shibumi add stripe bun run shibumi add email ``` Three extensions = login, payments, transactional email. Full SaaS skeleton in minutes. Docker + Caddy = auto-HTTPS on any $5 VPS. ### Content creator, blog ``` npm create shibumi@latest my-blog → template: Blog → deploy: Static CDN bun dev # write markdown posts bun run build # outputs dist/ ``` Markdown + frontmatter, RSS feed, SEO tags. No database, no server. Deploys anywhere static files go. ### Agency shipping client projects ``` npm create shibumi@latest client-dashboard --yes → defaults: SSR + Self-hosted bun run shibumi add auth bun run shibumi add admin ``` `--yes` skips prompts for CI/scripting. Admin extension gives instant CRUD UI from schema. ### Edge deploy on Cloudflare ``` npm create shibumi@latest edge-app → template: SSR → deploy: Cloudflare Workers bun dev # local SQLite, same as always ``` Same Hono routes, same Drizzle schema. Dev is local SQLite, prod is D1. Zero code changes. ### Team migrating off Next.js ``` bun run shibumi migrate --from next ``` Not a full auto-migration (Next surface area too large for that). Analysis + scaffolding tool: 1. Scaffolds new shibumi project with matching deploy target 2. Maps `pages/` or `app/` routes to `src/routes/` stubs 3. Extracts API routes into Hono route files (translate cleanly) 4. Generates migration report: what mapped, what needs manual work ``` 渋み migrate Scanned 23 routes, 8 API endpoints ✓ mapped 8 API routes → src/routes/api/ ✓ mapped 12 pages → src/routes/ (markup only) ⚠ manual 3 pages use RSC server components ⚠ manual next/image → replace with or extension ⚠ manual next-auth → run shibumi add auth Report: migration-report.md ``` API routes are the real win (basically Hono handlers already). Pages become route stubs with TODOs. React interactivity flagged for Alpine rewrite. Priority: after core CLI + auth/admin extensions are solid. Could ship as `shibumi-ext-migrate-next`. ### Extension author ``` mkdir shibumi-ext-comments && cd $_ ``` Three exports, that's the whole contract: - `files/` directory with source to copy - `agents.md` fragment - `migration.sql` (optional) ``` npm publish # users install with: bun run shibumi add comments ``` ## shibumi-server Personal mini-PaaS for self-hosted deploys. One Bun process + Caddy API. No dashboard. Single source of truth: `deploy.json` maps domain → path, port, secret. What it does: 1. Receives webhook from any git host (GitHub, Codeberg, Gitea, anything) 2. Runs git pull + podman-compose up --build 3. Registers domain with Caddy API (auto-TLS, reverse proxy) CLI: ``` shibumi-server init # deploy.json + systemd unit + Caddy API setup shibumi-server add myapp.com # adds entry, generates secret, registers with Caddy shibumi-server ls # lists apps, ports, status shibumi-server logs myapp.com # tails podman logs ``` Integration: `create-shibumi` can wire up the webhook URL when user picks "Self-hosted". Git-host agnostic. No SSH keys in CI. No Actions/Woodpecker dependency. Package: `shibumi-server` (claim on npm). Priority: after create-shibumi. ## Resolved Decisions - **GitHub Actions**: optional, not default. Default self-hosted deploy uses shibumi-server webhook - **Blog template**: Markdown with frontmatter, no MDX. Parsed at build time. - **base.css**: ships with every template. Delete if you don't want it. - **Extension registry**: npm-only. `@shibumi/*` for official, `shibumi-ext-*` for community. Listing page on shibumistack.dev. - **Extension updates**: don't auto-update. Ship a changelog, users diff manually. Same tradeoff as shadcn. - **`shibumi add --dry-run`**: yes. Shows files created/modified, deps added, agents.md changes.