@trybluesky/aen npm package (two lines) or a
single self-contained middleware.ts file you paste in. Either way it runs on Vercel Edge and
handles agent detection, serving, and telemetry. This page covers both, every configuration knob,
and what happens at runtime.
This is the shipped v0 form factor — an edge middleware you install in your own app, not a
reverse proxy in front of your site. It verifies answer-engine crawlers by published IP ranges.
A Cloudflare Worker adapter (see Cloudflare Worker) and a zero-code
reverse-proxy are also available / on the roadmap.
Prerequisites
- A Next.js project deployed on Vercel (or running locally with
next dev) - An edge token from Agent Analytics → Settings in the Bluesky app (format
aen_...), provisioned per site and revocable
Install
- npm package (recommended)
- Copy-paste (no dependency)
Install the package
init creates middleware.ts (or proxy.ts on Next.js 16+) wired to the package — a
two-line file. The real logic lives in the package, so you get updates without re-pasting code.Add your edge token
In Vercel → your project → Settings → Environment Variables, add
AEN_EDGE_TOKEN with
the key from Agent Analytics → Settings. For local dev, add it to .env.local.Deploy, then go live
Deploy. It starts in shadow mode (logs only, serves your normal page). Watch for the
first verified hit in Agent Analytics, then set
AEN_SERVE_MODE=default in your env and
redeploy to start serving. See Serve modes.Existing middleware
Already running a middleware — next-intl, auth, redirects? Don’t replace it. The package exports a composablehandle() that returns a Response when it served a verified AI crawler,
or null to fall through to your own code. Add two lines at the top of your middleware:
middleware.ts (next-intl example)
handle() fires telemetry in every mode and returns null in shadow mode, so your site behaves
exactly as before until you set AEN_SERVE_MODE=default. Running npx @trybluesky/aen init in a
project that already has a middleware prints this recipe automatically instead of overwriting your file.
The complete middleware.ts
Paste this entire file at the root of your Next.js project, then set AEN_EDGE_TOKEN as an
environment variable (see the step above). This is the shipped v0 drop-in — zero dependencies
beyond next/server, running on Vercel Edge.
middleware.ts
Configuration constants
All configuration lives at the top ofmiddleware.ts. You do not need to touch anything else
in the file.
middleware.ts (configuration section)
| Constant | Type | Description |
|---|---|---|
AEN_BASE | string | Base URL of the Bluesky control plane (https://app.trybluesky.com). Do not change this. |
EDGE_TOKEN | string | Read from process.env.AEN_EDGE_TOKEN. The middleware passes through all requests if this is empty. |
SERVE_MODE | "shadow" | "default" | "shadow" = log only, always serve the normal page. "default" = serve the variant to verified, eligible agents. |
ENABLED | Set<string> | Purpose classes eligible for serving. Defaults to USER_TRIGGERED (e.g. ChatGPT-User) and RETRIEVAL (e.g. OAI-SearchBot). |
This drop-in file exposes only
shadow and default. The full AEN adapter additionally
supports canary:<pct> (deterministic percentage ramp). See Serve modes.The config matcher
/_next/— Next.js build assets and hot-reload/api/— your API routes/favicon.ico— the favicon
How the middleware works
You do not need to understand the implementation to use it, but knowing the runtime flow helps you reason about edge cases and telemetry data.Guard on missing token
If
EDGE_TOKEN is empty, the middleware calls NextResponse.next() immediately and exits.
Your site behaves exactly as if the middleware file were not there.Extract UA and IP
The middleware reads
User-Agent from request headers, then resolves the client IP from
x-forwarded-for (first value) or x-real-ip.Fetch the bot registry (cached)
It calls
GET /api/v1/edge/botset with your edge token as a Bearer credential. The
response is cached in-process for one hour. On cache miss or error, the previous successful
response is reused as a fallback.Match UA against known bots
The middleware looks for the bot whose
uaToken matches the incoming User-Agent, with the
longest matching token winning. If no bot matches, the request passes through.Apply the cloaking firewall
If the matched bot has
isSearchIndex: true (Googlebot, Bingbot, Applebot), the request
passes through unconditionally — the variant is never served to a search-index crawler.
See Cloaking firewall.Verify the source IP
The middleware checks whether the client IP falls inside the bot’s published CIDR list. If it
does, the request is
IP_VERIFIED. If not, it is UA_ONLY — unverified — and always receives
your normal page. (This drop-in verifies IPv4 CIDRs only.)Fetch and serve the artifact
If the request is verified, the bot’s purpose is in
ENABLED, and SERVE_MODE is "default",
the middleware hashes the full request URL with SHA-256 and calls
GET /api/v1/edge/artifact?u=<hash>. If the response’s artifact contains a markdown field,
it is returned directly with Content-Type: text/markdown and header x-aen-served: variant.
Otherwise the normal page is served.The artifact is generated offline by Bluesky and read from cache — there is no LLM call on the
request path. A missing artifact simply degrades to passthrough; the middleware never fabricates
a page.
Telemetry hit shape
Every matched request generates one telemetry hit, posted inside a{ hits: [...] } body:
In
shadow mode, served is always "PASSTHROUGH" even for eligible bots, because no artifact
is fetched or returned. This lets you measure how many crawlers would be served before you
commit to "default" mode.Verification levels
| Trust level | What it means |
|---|---|
IP_VERIFIED | The source IP is inside the bot’s published CIDR range. Eligible for serving. |
UA_ONLY | UA matches but IP is outside published ranges. Treated as unverified — always receives the normal page. |
The AEN detection model also defines
SIGNED (Web Bot Auth — RFC 9421 HTTP Message Signatures
over Ed25519) and DNS_VERIFIED tiers. Those are implemented in the AEN core and full adapter;
this drop-in middleware.ts verifies by published IP ranges only.Troubleshooting
No hits appear in Agent Analytics
No hits appear in Agent Analytics
- Confirm the
middleware.tsfile is at the project root, not insidesrc/orapp/. - Confirm
AEN_EDGE_TOKENis set in your Vercel environment and you have redeployed after adding it. - Make sure the crawler you are testing is one the bot registry recognises — unverified, UA-only requests are not shown as verified hits.
Middleware is running but the variant is never served
Middleware is running but the variant is never served
Check that
SERVE_MODE is set to "default" (not "shadow"). Also confirm Bluesky has
generated an artifact for the URL you are testing — you can check this in Agent Analytics.I see hits with served: PASSTHROUGH even in default mode
I see hits with served: PASSTHROUGH even in default mode
This means either the request was not
IP_VERIFIED, the bot’s purpose is not in ENABLED, or
no artifact has been generated for that URL yet. Verified USER_TRIGGERED / RETRIEVAL agents
with a cached artifact receive the variant.Can I use this outside Vercel?
Can I use this outside Vercel?
The file uses the
NextRequest / NextResponse API and the Web Crypto API (crypto.subtle).
Vercel Edge is the supported target today. A Cloudflare Worker adapter and a zero-code
reverse-proxy are on the roadmap.