# Hosting and proof

A space stays where it is hosted; Velven lists it, frames it and counts plays. Three things decide whether a page can be listed: it is on a host Velven knows, it names its creator in a proof tag, and it lets velven.ai frame it. This page is the per-host detail behind the add page and the agent API's answers, drawn from the same rules.

## 1. The hosts

Velven lists a space on one of these hosts, and refuses any other URL with a 422. The host is read from the address and the response headers of the listed URL, after redirects.

| Host | Recognised by |
| --- | --- |
| ChatGPT | A chatgpt.site address. The site must be published with “Who has access” set to “Anyone on the Internet”: one only its owner can open answers 401 and cannot be checked. |
| Vercel | A vercel.app address, or a custom domain whose response headers say Vercel. |
| Netlify | A netlify.app address, or a custom domain whose response headers say Netlify. |
| GitHub Pages | A github.io address or a custom domain served by GitHub Pages. A repository page on github.com is not a Pages site and is refused. |
| Cloudflare | A workers.dev or pages.dev address only; a custom domain on Cloudflare is not recognised yet, since the `server: cloudflare` header proves nothing. |
| Replit | A replit.app address only. A custom domain on a Replit app is not recognised, since Replit adds no header of its own; a replit.dev address is the workspace's development preview, not a deployment. |

Size the space to the viewport rather than to a fixed ratio: it plays inside a 16:9 frame on a desktop and edge to edge under the bar on a phone.

## 2. The proof

Only a verified creator lists a space, and a space Velven listed itself is claimed the same way. The proof is one tag in the page's `<head>`, naming the creator's Velven handle. Velven fetches the live page and looks for it at listing, on a claim, and on the agent API's submit and verify calls.

```html
<meta name="velven" content="@handle">
```

The older form, a `/.well-known/velven` file at the site's origin containing `@handle`, is still accepted. The tag is checked on the live page, so deploy first and wait for the host's cache; a submit before that answers 409 `unverified` with the exact change still needed.

### ChatGPT

Add the tag below inside the page’s <head>.

```html
<meta name="velven" content="@handle">
```

Publish again in ChatGPT. In the Share dialog set “Who has access” to “Anyone on the Internet”, then check again.

### Vercel

Add the tag below inside the <head> of the page at the listed URL.

```html
<meta name="velven" content="@handle">
```

Deploy, then check again.

### Netlify

Add the tag below inside the <head> of the page at the listed URL.

```html
<meta name="velven" content="@handle">
```

Deploy, then check again.

### GitHub Pages

Add the tag below inside the <head> of the page at the listed URL.

```html
<meta name="velven" content="@handle">
```

Push, wait for Pages to rebuild, then check again.

### Cloudflare

Add the tag below inside the <head> of the page at the listed URL.

```html
<meta name="velven" content="@handle">
```

Deploy, then check again.

### Replit

Add the tag below inside the <head> of the page at the listed URL.

```html
<meta name="velven" content="@handle">
```

Publish again on Replit, then check again.

## 3. Framing

Every space plays inside the Velven page; there is no new-tab mode. All six hosts allow framing by default, so most pages need nothing. A page that sends its own headers must let velven.ai frame it: no `X-Frame-Options`, and a `Content-Security-Policy` whose `frame-ancestors` names it. The header to send:

```http
Content-Security-Policy: frame-ancestors 'self' https://velven.ai
```

Velven reads the headers when the space is added, on the agent API (422 `unframeable`, with the change for that host), and again on its background check every few hours. A listed space whose host starts blocking frames leaves the board until the header is back; its creator sees the fix on the space's page and can press Check now.

### ChatGPT

A ChatGPT site sends no headers of its own and always allows framing. Nothing to do.

### Vercel

Add this to vercel.json at the project root, merged into any headers list already there. A Next.js app can set the same header in next.config instead.

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [{ "key": "Content-Security-Policy", "value": "frame-ancestors 'self' https://velven.ai" }]
    }
  ]
}
```

Remove any X-Frame-Options header the app sets (a security preset, a middleware).

Deploy, then check again.

### Netlify

Add this to a _headers file in the publish directory, or the same rule under [[headers]] in netlify.toml.

```text
/*
  Content-Security-Policy: frame-ancestors 'self' https://velven.ai
```

Remove any X-Frame-Options header the app sets (a security preset, a middleware).

Deploy, then check again.

### GitHub Pages

GitHub Pages sets no headers and always allows framing. Nothing to do; a page that blocks framing there is being blocked by something in front of it.

### Cloudflare

On Pages, add this to a _headers file in the output directory. On a Worker, set the header on the response in the fetch handler.

```text
/*
  Content-Security-Policy: frame-ancestors 'self' https://velven.ai
```

Remove any X-Frame-Options header the app sets (a security preset, a middleware).

Deploy, then check again.

### Replit

On a static deployment, add this to the .replit file at the project root. On an Autoscale or Reserved VM deployment, set the header on the response in the server.

```toml
[[deployment.responseHeaders]]
path = "/*"
name = "Content-Security-Policy"
value = "frame-ancestors 'self' https://velven.ai"
```

Remove any X-Frame-Options header the app sets (a security preset, a middleware).

Publish again, then check again.

## 4. What Velven checks, and when

- At listing, on the add page or `POST /api/spaces`: the host, that the page answers, the proof, the framing headers, and the page's `application/velven+json` block for its boards.
- On a claim, at `/s/slug/claim` or `POST /api/spaces/verify`: the proof.
- Every few hours, the background check: that the page still answers and still allows framing, the page's boards block, and whether the page changed enough for a new clip.
- On Check now and Sync now on the space's edit page: the same, at once.

The agent quickstart at https://velven.ai/docs/agent.md carries the calls; the SDK guide at https://velven.ai/docs/sdk.md the boards block.
