Ship an agent
Scaffold a Next.js agent with Tailrace, verify block and tokenize locally, then deploy to Vercel for semi-prod use.
You will leave this tutorial with a Vercel-hosted Next.js agent that governs model and tool traffic with Tailrace: secrets blocked before the provider, PII tokenized, and values restored at egress.
Stack: Next.js App Router + AI SDK + Vercel. Scaffold with tailrace create next, then follow the steps below. For Cloudflare Workers or OpenAI Agents SDK, see Other stacks.
Prerequisites
- Node 20+
- A package manager (
npm,pnpm, oryarn) - Optional for live / deploy: an OpenAI API key and the Vercel CLI
Snippet-only setup (no scaffold): Quickstart.
Step 1: Scaffold and run
npx @tailrace/cli create next my-agent
cd my-agent
npm install # or pnpm / yarn
npm run devIf you already have @tailrace/cli installed: tailrace create next my-agent.
Open http://localhost:3000. The route uses an in-process mock when OPENAI_API_KEY is unset - no provider key required for local verify.
From source / contributing
To run the published monorepo example instead of a scaffolded app:
git clone https://github.com/tailrace/tailrace.git
cd tailrace
pnpm install
pnpm --filter @tailrace/core build
pnpm --filter @tailrace/ai-sdk build
pnpm --filter example-nextjs-ai-sdk devThat path is for contributors and anyone comparing against examples/nextjs-ai-sdk. The rest of this tutorial assumes a scaffolded my-agent directory; adjust paths if you cloned the monorepo.
Step 2: Verify block and tokenize locally
Run A: block a secret
Click Run A - block secret, or:
curl -s -X POST http://localhost:3000/api/chat \
-H 'content-type: application/json' \
-d '{"prompt":"Use sk_test_51FakeKeyForTailraceTests000FAKE"}' | jq .Expect 422 and "entity": "api_key". The fake key never reaches the model. Server logs show entity and hashes only - never the raw value.
Run B: tokenize + restore
Click Run B - tokenize + restore, or:
curl -s -X POST http://localhost:3000/api/chat \
-H 'content-type: application/json' \
-H 'x-workflow-id: test-session' \
-d '{"prompt":"Email customer@example.com"}' | jq .Expect 200, modelSaw containing <EMAIL_…>, and text containing customer@example.com.
Step 3: Understand the Tailrace wiring
Read app/api/chat/route.ts in your project (same shape as the monorepo example). The flow is:
createTailrace+withAiSdk- default policy (secrets → block, common PII → tokenize).tailrace.model(...)/tailrace.tools(...)- sameworkflowIdandagenton both.generateText- Tailrace scans prompts and tool traffic before work proceeds.tailrace.restoreat{ kind: "egress", sink: "ui" }- detokenize only at egress.PolicyViolationError→ HTTP 422 JSON.
| Option | Purpose |
|---|---|
agent | Selects identities overrides in your policy. Defaults to "default". |
workflowId | Vault scope for tokens. Same ID + same value → same token every time. |
Pass the same workflowId to model, tools, and restore within one conversation. The example reads it from x-workflow-id (or generates a per-request UUID).
Fluent vs standalone wrappers, streaming streamBlockBehavior, and full options: AI SDK reference.
Step 4: Switch to a live provider
cp .env.example .env.local| Variable | Purpose |
|---|---|
OPENAI_API_KEY | When set, the route uses openai("gpt-4o-mini") instead of mock |
TAILRACE_VAULT_KEY | Stable vault secret (required for live / deploy) |
Restart the dev server, then ask the model to use the governed tool:
Look up customer cust_42 and draft a short reply to their email.lookupCustomer returns synthetic customer@example.com. Tailrace tokenizes that result on the tool in boundary; egress restore puts the real address back in the UI response.
Step 5: Deploy to Vercel
- Deploy from your scaffolded app directory (Import Git in the dashboard, or CLI from that directory). Monorepo contributors: set the Vercel project root to
examples/nextjs-ai-sdk. - Add env vars in the Vercel project:
OPENAI_API_KEY- provider keyTAILRACE_VAULT_KEY- stable secret (for exampleopenssl rand -base64 32). Keep the same value across deploys so tokens remain decryptable.
- Deploy:
cd my-agent
vercelStep 6: Semi-prod verify
Production curls
Replace YOUR_DEPLOYMENT with your Vercel hostname:
# Expect 422 + entity api_key
curl -s -X POST "https://YOUR_DEPLOYMENT.vercel.app/api/chat" \
-H 'content-type: application/json' \
-d '{"prompt":"Use sk_test_51FakeKeyForTailraceTests000FAKE"}' | jq .
# Expect 200, modelSaw with <EMAIL_…>, text with customer@example.com
curl -s -X POST "https://YOUR_DEPLOYMENT.vercel.app/api/chat" \
-H 'content-type: application/json' \
-H 'x-workflow-id: ship-verify' \
-d '{"prompt":"Email customer@example.com"}' | jq .Use a stable x-workflow-id across related requests in a conversation so tokens stay consistent.
memoryVault vs kvVault
Within a single request, default memoryVault is enough for tokenize + restore (the vault lives for the request lifetime).
For multi-invocation token stability on serverless (same token across separate function invocations), use kvVault with a Redis/Upstash shim - a short adapter over { get, put, delete } per docs/vault.md §2. See the example README for a concrete Upstash-shaped shim. Tailrace does not bundle a Redis package.
Audit hygiene
Pass onDecision (as the example does) if you want structured logs. Decisions carry entity, rule, and hashes - never raw sensitive values. Do not log prompts or vault plaintext in production.
Troubleshooting
Tokens change between requests
You are not passing the same workflowId to model and restore. Thread the session ID through both.
restore throws INVARIANT
You called restore at a model or tool boundary. Use { kind: "egress", sink: "…" } only.
Stream aborts on benign output
A secret-class pattern matched in model output. Default policy blocks echoed secrets. Adjust policy only with care - secrets cannot be overridden to allow without dangerouslyAllowSecrets: true.
Other stacks
tailrace create also scaffolds Cloudflare Workers and OpenAI Agents SDK apps:
npx @tailrace/cli create cloudflare my-agent
npx @tailrace/cli create openai my-agent| Target | Guide | CLI |
|---|---|---|
cloudflare | Cloudflare Agents | CLI create |
openai | OpenAI Agents SDK | CLI create |
Full flags and template details: CLI reference.
Related
- CLI
create- scaffold next / cloudflare / openai - Quickstart - snippet-only install
- Next.js integration - boundaries and Edge notes
- wrapModel reference
- streamBlockBehavior reference