Skip to main content
The Bluesky Agent Edge Network (AEN) installs as a Next.js middleware — either the @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

Existing middleware

Already running a middleware — next-intl, auth, redirects? Don’t replace it. The package exports a composable handle() 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 of middleware.ts. You do not need to touch anything else in the file.
middleware.ts (configuration section)
ConstantTypeDescription
AEN_BASEstringBase URL of the Bluesky control plane (https://app.trybluesky.com). Do not change this.
EDGE_TOKENstringRead 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.
ENABLEDSet<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

This tells Next.js to run the middleware on every public page route while skipping:
  • /_next/ — Next.js build assets and hot-reload
  • /api/ — your API routes
  • /favicon.ico — the favicon
You should not need to change the matcher. If you have additional paths that must never be intercepted, add them to the negative lookahead.

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.
1

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.
2

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.
3

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.
4

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.
5

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.
6

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.)
7

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.
8

Fire telemetry (non-blocking)

Regardless of what was served, one hit is posted to POST /api/v1/edge/telemetry. The call is fire-and-forget — errors are silently swallowed and the response never waits on it.
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.
These fields feed the Agent Analytics breakdowns — hits grouped by purpose class and by trust level, the total hit count, the number of served URLs, and the share of served pages later cited.

Verification levels

Trust levelWhat it means
IP_VERIFIEDThe source IP is inside the bot’s published CIDR range. Eligible for serving.
UA_ONLYUA 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

  1. Confirm the middleware.ts file is at the project root, not inside src/ or app/.
  2. Confirm AEN_EDGE_TOKEN is set in your Vercel environment and you have redeployed after adding it.
  3. Make sure the crawler you are testing is one the bot registry recognises — unverified, UA-only requests are not shown as verified hits.
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.
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.
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.