> ## Documentation Index
> Fetch the complete documentation index at: https://agentrail.co/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Install the edge

> Put agentrail-edge in front of your site on Cloudflare Workers or Vercel, so every request becomes a step, including those from agents that never run JavaScript.

The edge is a small worker in front of your site. Each request goes through it to your site, and the edge records one event once the answer has gone back. It sees every request, including those from agents that never run JavaScript, which the [browser script](https://agentrail.co/docs/guides/journeys-browser.md) cannot.

It also does two things for your journeys:

* It marks the links to your own hosts in answers meant for agents (Markdown and `llms.txt`), so the request an agent makes by following one joins the page it came from. The marker comes off before your site sees the request.
* It sets the session cookie the browser script reads, so a visitor's requests and page views share one session.

## Create the source

An Admin creates the source once, whichever platform you use.

**1. Open Set up**

Open **Journeys**, select **Set up** and choose **Edge**. If you are in **Settings · Journeys**, use **Add the edge**.

**2. Name the source**

Enter a **Name**, such as "Docs edge", and select **Create source**.

**3. Save the secret and hash key**

Agentrail shows the **Secret** and the **Hash key** once, with the environment variables and a Cloudflare Worker example filled in for your site. Copy them before you select **I have saved them**. If you lose one, revoke the source and create another.

Keep both in your platform's secrets, never in your code or a page:

* `AGENTRAIL_SECRET` lets the edge send this source's events.
* `AGENTRAIL_HASH_KEY` is your workspace's key. The edge hashes visitors' addresses and verified identities with it before anything leaves your site.

The examples below write the collector's address as `<collector>`. The example in Agentrail has yours in it.

## Cloudflare Workers

Install the package and put the two values in the Worker's secrets:

```sh theme={null}
npm install agentrail-edge
npx wrangler secret put AGENTRAIL_SECRET
npx wrangler secret put AGENTRAIL_HASH_KEY
```

Then write the Worker:

```js theme={null}
import { cloudflare, createEdge } from 'agentrail-edge';

let edge;

export default {
  fetch(request, env, ctx) {
    edge ??= createEdge({
      collector: {
        endpoint: 'https://<collector>/ingest/events',
        secret: env.AGENTRAIL_SECRET,
      },
      hashKey: env.AGENTRAIL_HASH_KEY,
      hosts: { 'docs.example.com': 'query' },
    });
    return edge.handle(request, cloudflare(ctx));
  },
};
```

Deploy it on a route in front of your site. Create the edge once, on the first request, as this does: the requests an isolate answers then share calls to the collector.

## Vercel

On Vercel, the edge runs in Routing Middleware. Install the package and Vercel's functions helper, and add `AGENTRAIL_SECRET` and `AGENTRAIL_HASH_KEY` to the project's environment variables:

```sh theme={null}
npm install agentrail-edge @vercel/functions
```

```ts theme={null}
// middleware.ts, at the root of the project
import { waitUntil } from '@vercel/functions';
import { createEdge, vercel } from 'agentrail-edge';

const edge = createEdge({
  collector: {
    endpoint: 'https://<collector>/ingest/events',
    secret: process.env.AGENTRAIL_SECRET,
  },
  hashKey: process.env.AGENTRAIL_HASH_KEY,
  hosts: { 'www.example.com': 'query' },
});

export default function middleware(request) {
  return edge.handle(
    request,
    vercel({
      waitUntil,
      origin: (forwarded) => {
        const url = new URL(forwarded.url);
        url.hostname = 'origin.example.com';
        return fetch(new Request(url, forwarded));
      },
    }),
  );
}
```

Middleware cannot change an answer it passes on, so `origin` fetches your site itself. Point it at an address of your site that this middleware does not run on: a fetch to the same address would come back through the middleware.

Create the edge once, outside the handler, as this does. In Next.js 16, the file is `proxy.ts` and the function it exports is `proxy`.

## Options

`collector`, `hashKey` and `hosts` are required. The rest are optional.

* **`hosts`**: your own hosts, and how a link to each is marked. `'query'` adds an `h` parameter. `'path'` adds an `/h/` prefix to the path, for a host whose calls an extra parameter would break.
* **`identify(request)`**: the verified identity behind a request, from your own check of its method, URL and headers. See [Identify a caller](#identify-a-caller).
* **`queryAllowlist`**: the query parameters a recorded path keeps. Every other parameter is dropped from what is recorded. Your site still receives them.
* **`sessionIdleTimeout`**: seconds without a request after which a session ends. The default is 1800, which is 30 minutes.
* **`sessionCookieDomain`**: the domain a session is shared across, such as `example.com` for `docs.example.com` and `app.example.com`. By default, a session stays on one host.
* **`sessionCookie`**: the session cookie's name. The default is `agentrail_session`.
* **`service`**: the service name on each event. The default is the request's host.
* **`onError(error)`**: called when events could not be sent or `identify` threw. These are logged otherwise, and neither ever reaches the visitor.
* **`fetch`**: the fetch the collector's calls go through.

If you run the [browser script](https://agentrail.co/docs/guides/journeys-browser.md) on the same site, give it the same session timeout and domain.

## Identify a caller

Return a verified identity from `identify`, or `null` when there is none. Use your own check, never something the caller merely says about itself:

```js theme={null}
identify: async (request) => {
  const account = await verifiedAccount(request.headers.get('authorization'));
  return account === null ? null : { kind: 'user', id: account.id };
},
```

`kind` is one of `user`, `agent_registration`, `agent_instance`, `oauth_client` or `api_key`. For an agent acting for a person, also return `actingUserId`. The ids are hashed before they leave the edge.

`identify` runs after the answer, so it does not slow your site down. If it throws, `onError` is told and the request is recorded with no identity.

## If something goes wrong

Recording happens after the answer, so a visitor never waits for it and never sees it fail. A failed call to the collector is tried again, and is given up within 25 seconds, when `onError` is told.

## Check that it works

Send a request through the edge, then open **Settings · Journeys**. The source moves from **Waiting for its first event** to **Receiving**, and journeys start to appear under **Journeys**.

For what the edge sends, see [What Agentrail records](https://agentrail.co/docs/guides/journeys-data.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.