Deploy with TanStack Start
Deploy a generated storefront or headless Nitro server while keeping payment authority server-side.
The TanStack Start adapter has two generated application shapes. Both use file
routes that delegate to @lucid-agents/tanstack handlers; neither should copy
entrypoint execution or payment verification into a loader/component.
Choose a variant
| Variant | Includes | Use when |
|---|---|---|
tanstack-ui | SSR endpoint table derived from the public service model | Humans need a quick directory of endpoints and prices |
tanstack-headless | API routes and minimal directory page | The service is machine-facing and UI is deployed elsewhere |
Generate from the Next workspace
bun install --frozen-lockfile
bun run build:packages
bun packages/cli/dist/index.js my-service --adapter=tanstack-ui
# or
bun packages/cli/dist/index.js my-service --adapter=tanstack-headless
cd my-service
bun install --frozen-lockfile
bun run type-check
bun run buildDo not use an unpinned public CLI when targeting the repository-only Next surface.
Generated routes cover root well-known discovery and the main API under
/api/agent, including invoke, stream, and task routes. Route-tree generation
is part of the build; CI should fail when regeneration changes committed files.
Server/runtime boundary
The generated src/lib/agent.ts is the server-only source of the runtime and
handlers. Route handlers dynamically import it and pass the original request:
export const Route = createFileRoute('/api/agent/entrypoints/$key/invoke')({
server: {
handlers: {
POST: async ({ request, params }) => {
const { handlers } = await import('@/lib/agent');
return handlers.invoke({ request, params: { key: params.key } });
},
},
},
});Keep provider/database imports out of components, loaders serialized to the browser, and shared utility barrels that client code can reach. Inspect the client bundle for secret-bearing packages before release.
Build and start the Node server
The generated Nitro configuration produces a server bundle:
bun run type-check
bun run build
NODE_ENV=production node --env-file=.env .output/server/index.mjsA static hosting target is insufficient; invoke/payment routes require the server output. Pin the Node version/image, copy the complete Nitro output and required production dependencies, and inject secrets at runtime.
The exact Nitro preset/hosting integration controls request limits, filesystem, connection reuse, streaming, and shutdown. Verify the target rather than assuming portability from the Fetch handler interface.
Storefront production checks
The UI renders a read-only subset of public Agent Card/service data. Before publishing it:
- validate public origin and
/api/agentURLs behind the production proxy; - verify each invoke and stream row uses the canonical external base path;
- confirm payment method, network, and per-operation price match discovery;
- check long endpoint paths and descriptions on narrow screens; and
- confirm the page contains no schemas, raw Agent Card JSON, credentials, or invoke controls.
The storefront is not an AP2 mandate/checkout implementation and the Agent Card is not x402 Bazaar publication.
Durability and process model
The default Node server can be long-lived, but the selected hosting preset may still scale instances independently. In every case:
- in-memory payment/SIWX/idempotency/task/scheduler state is per instance;
- use shared Postgres/custom stores before horizontal scaling;
- SQLite needs persistent local disk and a compatible single-host topology;
- run scheduler and recoverable task execution in explicit long-lived workers;
- bound database pools and provider concurrency per instance;
- inject the shared Postgres MPP challenge store before scaling paid routes.
The package ships only memory stores for tasks and schedules. Implement and test their durable ports rather than naming a database without an adapter.
Streaming and assets
Test SSE through Nitro, the hosting adapter, load balancer, and CDN. Disable buffering/cache/compression that waits for the full body, propagate disconnect, and set duration/idle limits. A successful local Vite dev stream is not production evidence.
Serve hashed static assets with immutable caching, but never cache Agent Cards, health/readiness, payment challenges, invoke results, task tokens, or SSE responses as ordinary public assets.
Health, readiness, and shutdown
/api/agent/health proves the Lucid handler responds. Add a protected
readiness check for database schema/atomic operations, facilitator support,
wallet/provider access, and critical downstreams.
Confirm the chosen Nitro host sends a termination signal, stops new traffic,
allows request/stream drain, and closes runtime/database resources. If the
framework host cannot call runtime.close() reliably, own resource lifecycle
outside the request bundle or choose a long-lived Hono/Express topology.
Canary
curl --fail https://service.example/api/agent/health
curl --fail https://service.example/.well-known/agent-card.json
curl -i https://service.example/api/agent/entrypoints/quote/invoke \
-H 'content-type: application/json' \
-H 'idempotency-key: deploy-tanstack-canary-000001' \
--data '{"input":{"symbol":"ETH"}}'Require 402, then complete one low-limit paid call from both a server buyer
and—if UI is enabled—the supported browser wallet path. Reconcile output,
settlement evidence, external transaction, and local record. Restart/scale to a
second instance and confirm same-key behavior plus any task lease recovery.
Rollback
Deploy immutable server and asset versions together so the storefront never targets incompatible handlers. Keep database changes backward-compatible during overlap. If payment/fulfillment evidence disagrees, stop new paid calls, preserve state, reconcile, and route both UI and API back to a compatible release.
See the TanStack package reference, durable storage, and deployment overview.