G · 16 guides

Guides / Deployment

Deployment

Build for production

governedbashbash29 B
bun run build
bun run preview

bun run build compiles all .aihu SFCs through the Rust compiler, bundles with Vite/Rolldown, and validates against the per-package size budgets in .size-limit.json. bun run preview serves the production build locally to verify output before deploying.

App configuration

The app configuration is inline in vite.config.ts, as the argument to viteAihuPlugin({...}) from @aihu/app:

governedtypescripttypescript393 B
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { defineConfig } from 'vite'

export default defineConfig({
  optimizeDeps: { exclude: ['@aihu/app'] },
  plugins: [
    viteAihuPlugin({
      output: 'static',            // 'spa' (default) | 'static' (prerendered)
      dir: { pages: 'src/pages' },
      build: { bundler: 'vite' },  // 'vite' | 'rolldown'
    }),
  ],
})

The config object is @aihu/app's AihuConfig. Key fields: dir (pages / layouts / public / components), output ('spa' | 'static'), site, app.head, css ({ shadowMode: 'light' | 'shadow' }), agentReadiness, adapter, and build.bundler ('vite' | 'rolldown'). Pass the same object to defineConfig from @aihu/app to type-check it in its own file.

A standalone aihu.config.ts that default-exports defineAihuConfig from @aihu/server still works as a legacy fallback for server/SSR build config — including a build.target of 'client', 'server', or 'universal' — but the scaffold no longer emits one, and the inline plugin config is the primary surface.

Cloudflare Workers

Use @aihu/adapter-cloudflare to deploy to Cloudflare Workers or Pages:

governedtypescripttypescript281 B
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { cloudflare } from '@aihu/adapter-cloudflare'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteAihuPlugin({
      adapter: cloudflare({ name: 'my-worker' }),
    }),
  ],
})

The adapter:

  • Writes _worker.js to the Vite output directory (SPA mode — all page requests served from Cloudflare CDN via the ASSETS binding).
  • Optionally creates wrangler.toml in the project root if absent (never overwrites an existing one).

Adapter options:

Option Type Default Description
name string from package.json Cloudflare Worker name in wrangler.toml
mode 'workers' | 'pages' 'workers' Deployment target
generateWrangler boolean true Write wrangler.toml if absent

Deploy after build:

governedbashbash38 B
wrangler deploy --config wrangler.toml

For a manual Worker without the adapter, use @aihu/server's request router directly:

governedtypescripttypescript245 B
import { createRequestRouter, defineRoute, json } from '@aihu/server'

const router = createRequestRouter({
  routes: [
    defineRoute('/api/hello', () => json({ hello: 'world' })),
  ],
})

// Cloudflare Worker
export default { fetch: router }

Vercel

Use @aihu/adapter-vercel to deploy using the Vercel Build Output API v3:

governedtypescripttypescript248 B
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { vercel } from '@aihu/adapter-vercel'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    viteAihuPlugin({
      adapter: vercel(),
    }),
  ],
})

The adapter:

  • Copies static assets to .vercel/output/static/.
  • Writes an Edge Function entry (default) or Serverless Function entry.
  • Emits config.json with the Build Output API v3 routes manifest.

Adapter options:

Option Type Default Description
runtime 'edge' | 'serverless' 'edge' Vercel function runtime
outputDir string '.vercel/output' Build Output API output directory
nodeVersion string 'nodejs18.x' Node.js version for serverless runtime

Deploy after build:

governedbashbash24 B
vercel deploy --prebuilt

Bun server

Run aihu server-side on Bun using @aihu/server's fetch-API router:

governedtypescripttypescript596 B
import { createRequestRouter, defineRoute, json } from '@aihu/server'
import { createAgentReadinessRoutes } from '@aihu-plugin/agent-readiness'

const ar = createAgentReadinessRoutes({
  name: 'My App',
  endpoint: 'https://myapp.example.com/mcp',
  summary: 'An aihu-powered app.',
})

const router = createRequestRouter({
  routes: [
    defineRoute('/llms.txt', ar.llmsTxt),
    defineRoute('/.well-known/mcp/server-card.json', ar.mcpServerCard),
    defineRoute('/robots.txt', ar.robotsTxt),
    defineRoute('/api/hello', () => json({ hello: 'world' })),
  ],
})

Bun.serve({ fetch: router })

Deno

The same router works on Deno Deploy — aihu uses only Web Standard APIs (Fetch, ReadableStream, URL):

governedtypescripttypescript210 B
import { createRequestRouter, defineRoute, json } from '@aihu/server'

const router = createRequestRouter({
  routes: [
    defineRoute('/api/hello', () => json({ hello: 'world' })),
  ],
})

Deno.serve(router)

Node.js

aihu output is standard ESM. Any Node.js ≥20.18.0 runtime can serve an aihu application:

governedbashbash39 B
npm run build
node dist/server/entry.js

The server entry is generated by the universal build and uses @aihu/server's request router.

On supported Node platforms @aihu/server lazily loads a native Rust addon to render SSR. Edge runtimes (Cloudflare, Vercel Edge, Deno) automatically skip it and use the TypeScript fallback. To force the fallback on Node — e.g. on an unsupported platform or to debug a parity issue — set SCRIBE_NATIVE_SKIP=1 in the server environment.

viteRouterIntegration() at build time

The Vite plugin performs these steps at build time:

  1. scanPages(dir) — discovers all .aihu files under src/pages/.
  2. For each page, reads the .route.json sidecar emitted by the Rust compiler.
  3. Assembles the route manifest into the virtual:aihu-routes module.
  4. Emits dist/routes.json for runtime consumption.

Route manifests are fully static after build — no filesystem scanning at runtime.