Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f10c677
feat(config): add ai.gateway config field
Brooooooklyn Jul 8, 2026
3c2b52c
feat(bindings): inject Workers AI binding + AI gateway var for self-h…
Brooooooklyn Jul 8, 2026
cdee224
feat(ai): route ai.provider() through user AI Gateway on self-host
Brooooooklyn Jul 8, 2026
a45dfb8
test(ai): cover self-host AI binding, gateway providerFetch, config v…
Brooooooklyn Jul 8, 2026
2e09808
refactor(isr): extract shared collectRevalidate/hasRevalidate
Brooooooklyn Jul 8, 2026
52f2a72
feat(isr): add single-tenant worker ISR cache module
Brooooooklyn Jul 8, 2026
93f050f
fix(isr): drop dead JSON-miss edge populate + clarify cf/JSON comments
Brooooooklyn Jul 8, 2026
85df4d7
fix(isr): fail open on corrupt KV cache entries (never 500)
Brooooooklyn Jul 8, 2026
a433548
feat(isr): wire ISR into worker entry with baked deployment id
Brooooooklyn Jul 8, 2026
d3143e8
build(void): export #self/runtime/isr-cache
Brooooooklyn Jul 8, 2026
214490b
feat(isr): emit ISR into the worker entry on non-managed builds only
Brooooooklyn Jul 8, 2026
057a44e
feat(isr): purge own KV + edge cache on self-host revalidate()
Brooooooklyn Jul 8, 2026
9c919be
test(isr): cover cache ladder, revalidate purge, entry emit, config
Brooooooklyn Jul 8, 2026
bb674ae
test(isr): strengthen X-VoidPages/waitUntil/corrupt-KV/exact-purge as…
Brooooooklyn Jul 8, 2026
c9828ba
fix(isr): type proxy fetch mock params so isr.test.ts typechecks
Brooooooklyn Jul 8, 2026
7b48721
test(isr): assert {all} revalidate lists with the deployment prefix
Brooooooklyn Jul 8, 2026
d189587
test(isr): assert exact waitUntil task count on stale revalidation
Brooooooklyn Jul 8, 2026
e02d8c5
test(selfhost): non-regression for AI manifest binding + serve-gate h…
Brooooooklyn Jul 8, 2026
cb4d694
docs: document self-host AI gateway + ISR on Cloudflare
Brooooooklyn Jul 8, 2026
c6bfc32
docs: correct ai.gateway scope + AI self-host trust-boundary wording
Brooooooklyn Jul 8, 2026
30c859f
fix(isr): stamp deployment id in cron/queue, hide ISR_CACHE from void…
Brooooooklyn Jul 8, 2026
c8a7ec9
fix(isr): fail open on absent Cache API in handleIsr + cron/queue pur…
Brooooooklyn Jul 8, 2026
6ed278d
fix(isr): gate generated c.env ISR_CACHE filter on ISR emission (rest…
Brooooooklyn Jul 9, 2026
a4a1065
test(isr): strip remote-only ai binding before kitchen-sink preview
Brooooooklyn Jul 9, 2026
80aed50
Merge remote-tracking branch 'origin/main' into feat/selfhost-ai-isr
Brooooooklyn Jul 9, 2026
0faa462
fix(isr,ai): stamp X-VoidPages on cached JSON variant; correct non-Be…
Brooooooklyn Jul 9, 2026
9df3936
fix(isr): segment-anchored revalidate matching + host-scoped self-hos…
Brooooooklyn Jul 9, 2026
5602b3f
fix(isr): preserve Set-Cookie on live ISR miss (keep cache copy strip…
Brooooooklyn Jul 9, 2026
27c0fd2
fix(ai): add parallel provider auth default (x-api-key)
Brooooooklyn Jul 9, 2026
5001ca9
fix(env): gate void/env ISR_CACHE hiding on ISR being active (match c…
Brooooooklyn Jul 9, 2026
1c7dffc
fix(isr): bypass ISR for Range requests + only cache full 200 responses
Brooooooklyn Jul 9, 2026
0131eef
fix(isr): refresh __VOID_ISR_HOST per request + retry failed host reg…
Brooooooklyn Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions docs/integrations/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,110 @@ That's it. The Cloudflare Vite plugin produces a complete build output with a me
### Local development

`pnpm dev` continues to work as before -- Miniflare creates local instances of all bindings regardless of the IDs in your `wrangler.jsonc`. Your real resource IDs are only used when you run `wrangler deploy`.

### AI (self-host)

`void/ai` works on your own Cloudflare account, along two paths:

- **Workers AI** (`ai.run`, `ai.stream`, `ai.image`) works out of the box. When your app imports `void/ai`, `vite build` infers that you need AI and adds a Workers AI binding (`env.AI`) to the generated `wrangler.json` automatically -- you do **not** add it to `wrangler.jsonc`.
- **Provider models** (`ai.provider("openai").fetch(...)`) route through _your own_ Cloudflare AI Gateway. Set its id in `void.json` and add the provider's API key as a Worker secret.

```jsonc
// void.json
{
"ai": {
"gateway": "my-gateway", // an AI Gateway in YOUR Cloudflare account
},
}
```

```bash
# provider API key, stored as a Worker secret (never committed)
wrangler secret put OPENAI_API_KEY
```

```ts
// routes/chat.ts
import { defineHandler } from 'void';
import { ai } from 'void/ai';

export const POST = defineHandler(async (c) => {
// Workers AI -- uses env.AI directly
const summary = await ai.run('@cf/meta/llama-3.1-8b-instruct', {
prompt: 'Summarize the changelog.',
});

// Provider model -- routes through your "my-gateway" AI Gateway,
// authed with the OPENAI_API_KEY secret above
const res = await ai.provider('openai').fetch('chat/completions', {
method: 'POST',
body: JSON.stringify({ model: 'gpt-4o-mini', messages: [] }),
});

return c.json({ summary, provider: await res.json() });
});
```

`ai.gateway` is required for `ai.provider().fetch()` -- without it, that call returns a `501`. No AI traffic or provider secret passes through Void's shared proxy: requests go directly to _your own_ Cloudflare AI Gateway, authenticated with provider secrets from _your_ Worker's environment. (The request and that secret are of course still sent onward to the AI Gateway and the upstream provider you call.)

Notes:

- **No cross-tenant usage metering.** On the Void platform, AI calls are metered and billed through a shared proxy. Self-hosted, there is no metering -- you get your own [Cloudflare AI Gateway analytics](https://developers.cloudflare.com/ai-gateway/) instead.
- **The runtime reads the `env.AI` binding by name** -- a custom-named root AI binding is not supported.
- **`void dev` has no local Workers AI emulation.** In development, AI still routes through Void, so `void dev` AI requires `void auth login` even when you deploy self-hosted.

### ISR (self-host)

[Revalidation (ISR)](../guide/edge/revalidation.md) works self-hosted, but the cache KV is **not** auto-injected (a KV binding needs a real namespace id). Create a namespace and bind it as `ISR_CACHE`:

```bash
wrangler kv namespace create ISR_CACHE
```

```jsonc
// wrangler.jsonc
{
"kv_namespaces": [
{
"binding": "ISR_CACHE",
"id": "<your-namespace-id>",
},
],
}
```

Configure revalidation exactly as on the platform -- globally or per-path in `void.json`:

```jsonc
// void.json
{
"routing": {
"revalidate": { "/blog/*": 3600, "*": 60 },
},
}
```

...or per page with an exported `revalidate` literal in a `.server.ts` companion (Pages mode):

```ts
// pages/blog/[slug].server.ts
export const revalidate = 3600; // seconds
```

On-demand purges work through `revalidate()`:

```ts
import { revalidate } from 'void/isr';

await revalidate({ paths: ['/blog/hello'] });
// or purge every ISR page:
await revalidate({ all: true });
```

Self-hosted, `revalidate()` purges this worker's own KV entries (global, authoritative) plus the current colo's edge cache.

Limits (the single-worker cache ladder can't do everything the platform's dispatch layer does):

- **No fleet-wide / cross-colo edge purge.** `revalidate()` clears KV globally and the _local_ colo's edge cache; other colos keep serving their edge copy until it expires (bounded by the response's `s-maxage`), then re-render on the next edge miss.
- **The pages-protocol JSON variant is not served from a cold colo's KV.** A colo that hasn't rendered the HTML yet re-renders the JSON live; the JSON edge cache is warmed only as a side effect of the HTML render path.
- **The warm cache is dropped on every redeploy.** Each `vite build` bakes a fresh deployment id into the cache keys, so cached HTML never outlives the hashed assets it references -- the first request after a deploy is a cold render.
Loading