Deploy AgentDoor on Vercel using Edge Middleware. The @agentdoor/vercel adapter provides a lightweight edge-compatible middleware using standard Web APIs.
Installation
npm install @agentdoor/vercel @agentdoor/core
Quick Start
Create middleware file
Create middleware.ts in your project root:import { createEdgeMiddleware } from "@agentdoor/vercel";
const agentdoor = createEdgeMiddleware({
scopes: [
{
id: "data.read",
description: "Read application data",
price: "$0.001/req",
rateLimit: "1000/hour",
},
{
id: "data.write",
description: "Write application data",
price: "$0.01/req",
rateLimit: "100/hour",
},
],
pricing: {
"data.read": "$0.001/req",
"data.write": "$0.01/req",
},
rateLimit: { requests: 1000, window: "1h" },
});
export default async function middleware(request: Request) {
const response = await agentdoor(request);
// If AgentDoor handled the request, return its response
if (response) return response;
// Otherwise, continue to origin
return null;
}
export const config = {
matcher: ["/api/:path*", "/.well-known/:path*", "/agentdoor/:path*"],
};
Access agent context in routes
In your API routes, check for agent headers:import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const isAgent = req.headers["x-agentdoor-authenticated"] === "true";
const agentId = req.headers["x-agentdoor-agent-id"];
if (isAgent) {
return res.json({
message: `Hello agent ${agentId}`,
data: "agent-specific data",
});
}
return res.json({ message: "Hello human" });
}
Complete Example
Here’s a full Vercel project with agent authentication:
import { createEdgeMiddleware } from "@agentdoor/vercel";
const agentdoor = createEdgeMiddleware({
scopes: [
{
id: "analytics.read",
description: "Read analytics data",
price: "$0.002/req",
rateLimit: "500/hour",
},
{
id: "analytics.write",
description: "Write analytics events",
price: "$0.01/req",
rateLimit: "100/hour",
},
],
pricing: {
"analytics.read": "$0.002/req",
"analytics.write": "$0.01/req",
},
rateLimit: { requests: 500, window: "1h" },
x402: {
network: "base",
currency: "USDC",
paymentAddress: process.env.X402_WALLET!,
},
});
export default async function middleware(request: Request) {
const url = new URL(request.url);
// Handle AgentDoor routes
if (
url.pathname.startsWith("/agentdoor") ||
url.pathname.startsWith("/.well-known")
) {
return await agentdoor(request);
}
// Apply auth guard to API routes
if (url.pathname.startsWith("/api/")) {
const response = await agentdoor(request);
if (response) return response;
}
// Continue to origin
return null;
}
export const config = {
matcher: ["/api/:path*", "/.well-known/:path*", "/agentdoor/:path*"],
};
import type { NextApiRequest, NextApiResponse } from "next";
type AnalyticsEvent = {
event: string;
timestamp: string;
properties: Record<string, any>;
};
const events: AnalyticsEvent[] = [];
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const isAgent = req.headers["x-agentdoor-authenticated"] === "true";
const agentId = req.headers["x-agentdoor-agent-id"];
if (req.method === "GET") {
// Read analytics
return res.json({
events,
total: events.length,
caller: isAgent ? `agent:${agentId}` : "human",
});
}
if (req.method === "POST") {
// Write analytics event
const event: AnalyticsEvent = {
...req.body,
timestamp: new Date().toISOString(),
};
events.push(event);
return res.status(201).json({
event,
createdBy: isAgent ? `agent:${agentId}` : "human",
});
}
return res.status(405).json({ error: "Method not allowed" });
}
How It Works
The Vercel Edge middleware:
- Runs at the edge: Low latency worldwide
- Uses Web APIs:
Request, Response, Web Crypto
- Handles routes: Discovery, registration, auth
- Validates requests: Checks
Authorization headers on protected routes
- Injects headers:
x-agentdoor-* headers for downstream handlers
- Returns null: When the request should continue to the origin
Advanced Configuration
Protected Paths
Customize which paths require agent authentication:
const agentdoor = createEdgeMiddleware({
scopes: [/* ... */],
protectedPaths: ["/api/protected", "/api/admin"], // Only these paths require auth
passthrough: false, // Block unauthenticated requests (default)
});
Passthrough Mode
Allow unauthenticated requests:
const agentdoor = createEdgeMiddleware({
scopes: [/* ... */],
passthrough: true, // Don't block unauthenticated requests
});
export default async function middleware(request: Request) {
const response = await agentdoor(request);
if (response) return response;
const url = new URL(request.url);
if (url.pathname.startsWith("/api/protected")) {
const isAgent = request.headers.get("x-agentdoor-authenticated") === "true";
if (!isAgent) {
return Response.json({ error: "Agent required" }, { status: 401 });
}
}
return null;
}
Environment Variables
Set environment variables in Vercel dashboard or .env.local:
X402_WALLET=0xYourWalletAddress
Access them in middleware:
const agentdoor = createEdgeMiddleware({
scopes: [/* ... */],
x402: {
network: "base",
currency: "USDC",
paymentAddress: process.env.X402_WALLET!,
},
});
AgentDoor sets these headers on authenticated requests:
x-agentdoor-authenticated: "true" or "false"
x-agentdoor-agent-id: Agent ID
x-agentdoor-agent: JSON-encoded agent context
Access them in your API routes:
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const isAgent = req.headers["x-agentdoor-authenticated"] === "true";
const agentId = req.headers["x-agentdoor-agent-id"] as string | undefined;
const agentJson = req.headers["x-agentdoor-agent"] as string | undefined;
if (agentJson) {
const agent = JSON.parse(agentJson);
console.log(agent.id, agent.scopes, agent.metadata);
}
}
Matcher Configuration
The matcher config determines which routes the middleware runs on:
export const config = {
// Match API routes, well-known paths, and AgentDoor paths
matcher: ["/api/:path*", "/.well-known/:path*", "/agentdoor/:path*"],
};
For more complex matching:
export const config = {
matcher: [
// Match all API routes
"/api/:path*",
// Match well-known paths
"/.well-known/:path*",
// Match AgentDoor paths
"/agentdoor/:path*",
// Exclude specific paths
"!(/_next/static|/_next/image|/favicon.ico)",
],
};
API Reference
createEdgeMiddleware(config)
Creates a Vercel Edge middleware function.
Parameters:
config: Configuration object
scopes (required): Scope definitions
protectedPaths: Path prefixes requiring auth (default: ["/api/"])
passthrough: Allow unauthenticated requests (default: false)
pricing: Price per scope
rateLimit: Default rate limit config
x402: X402 payment config
Returns: (request: Request) => Promise<Response | null>
Deployment
Local Development
Run locally with Vercel CLI:
Test endpoints:
- Discovery:
http://localhost:3000/.well-known/agentdoor.json
- Register:
POST http://localhost:3000/agentdoor/register
Production Deployment
Environment Variables
Set in Vercel dashboard:
- Go to Project Settings → Environment Variables
- Add
X402_WALLET with your wallet address
- Redeploy for changes to take effect
Or use Vercel CLI:
vercel env add X402_WALLET production
TypeScript Support
import type {
AgentDoorVercelConfig,
} from "@agentdoor/vercel";
import type { AgentContext } from "@agentdoor/core";
Vercel Edge Middleware uses in-memory Maps for storage. State resets on each deployment and region. For production persistence, use a database or KV store.
- Cold start: ~10-50ms
- Warm request: <1ms
- Edge latency: <50ms worldwide
- Max execution time: 30 seconds
Limitations
- No native
node:* modules
- No
fs, path, or crypto from Node.js
- State resets on each deployment
- Limited to 1MB response size
For persistent storage, use:
- Vercel KV (Redis)
- Vercel Postgres
- External database (Supabase, PlanetScale, etc.)
Next Steps