Skip to content

Changelog

New updates and improvements at Cloudflare.

DeepSeek V4 Flash and Pro now available on Workers AI

@cf/deepseek-ai/deepseek-v4-pro-0813 and @cf/deepseek-ai/deepseek-v4-flash-0731 are now available on Workers AI.

DeepSeek V4 Flash and DeepSeek V4 Pro are the first Workers AI models with a full one million (1,048,576) token context window. Use them for long-horizon agentic workflows, large codebases, and multi-step reasoning that exceed the context limits of every other model hosted on the platform.

DeepSeek V4 Flash is the faster, lower-cost sibling. This release supersedes the preview version with substantially enhanced agentic capabilities.

Key capabilities:

  • Reasoning: Both models support thinking mode for complex, step-by-step problem-solving.
  • Function calling: Build agents that invoke tools and APIs across multiple conversation turns.
  • Long context: Both models support a full 1,048,576 token context window.

Both models require the Workers Paid plan or prepaid AI Gateway credits.

Use these models through the Workers AI binding (env.AI.run()), the REST API, the OpenAI-compatible endpoint, or AI Gateway.

For more information, refer to the DeepSeek V4 Pro model page, the DeepSeek V4 Flash model page, and pricing.

You can now enable Access on a Worker or all Workers at once

You now have two new ways to protect your Workers with Cloudflare Access.

Protect an application across all its domains at once

Until now, if a Worker was reachable on a route, a Custom Domain, and a workers.dev URL, you had to manually add each one to an Access application and keep the list in sync whenever routes or domains changed.

Now, Access attaches the policy to the Worker itself, so every associated domain and preview URL stays protected even when its routes or domains change.

Access setting for protecting a single Worker

Protect all new and existing Workers by default

Make all Workers private by default, so every existing and newly created Worker requires sign-in before anyone can reach it.

Account-wide Access setting that protects all Workers

If a specific Worker should remain publicly accessible, add a Worker-level bypass to exempt it.

Make a Worker public when all Workers are protected

Whether you protect a single application or all Workers at once, you can choose whether to protect preview deployments only or both previews and production, and control who can sign in by Cloudflare account membership, email address, or email domain.

For more advanced policy options, edit the policy in Zero Trust.

Access policy configuration for controlling who can sign in

View all of your Worker Access policies

You can view and manage all of your Access policies in the Access tab of the Workers & Pages section in the dashboard.

Access tab showing all configured Access policies

See who is accessing your Worker

When Access is enabled on your Worker, every authenticated request includes ctx.access. Call ctx.access.getIdentity() to get the user's email, name, and groups — no manual JWT validation required.

export default {
  async fetch(request, env, ctx) {
    if (!ctx.access) {
      return new Response("Access did not run", { status: 401 });
    }

    const identity = await ctx.access.getIdentity();
    return Response.json({ aud: ctx.access.aud, email: identity?.email });
  },
};

Test Access locally

You can now test Cloudflare Access locally with wrangler dev. Add a dev block to your wrangler.jsonc:

{
  "access": {
    "dev": {
      "aud": "my-app",
      "identity": { "email": "admin@example.com" }
    }
  }
}

Your Worker will receive this identity through ctx.access and ctx.access.getIdentity(), letting you test authenticated and unauthenticated flows without deploying. Remove the dev block to simulate unauthenticated requests.

API and programmatic access

You can also set up these policies through the Workers API instead of the dashboard.

Data localization support for Artifacts

Artifacts now supports jurisdictions, allowing you to select the European Union or the United States as the only location where repo data is stored and processed.

Select a jurisdiction when you create a namespace. Every repo in that namespace automatically uses the selected jurisdiction.

curl --request POST \
  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/artifacts/namespaces" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "namespace": "my-eu-namespace",
    "jurisdiction": "eu"
  }'

Jurisdictions cannot be changed after namespace creation. If you omit the jurisdiction, Artifacts creates an unrestricted namespace.

For supported jurisdictions and usage details, refer to Data localization.

Control Realtime SFU DataChannel delivery

Cloudflare Realtime SFU is a WebRTC selective forwarding unit that runs on Cloudflare's global network. It forwards audio, video, and application data between WebRTC clients without requiring you to manage SFU infrastructure or regions.

DataChannels are WebRTC channels for application messages. A client publishes a named DataChannel to Realtime SFU, and the SFU forwards its messages to every client that subscribes to that channel. Use DataChannels for low-latency payloads such as chat messages, game state, sensor updates, and control events.

What changed

Realtime SFU DataChannels now support unordered and partially reliable delivery. DataChannels remain reliable and ordered by default, so existing channels keep their current behavior.

With ordered delivery, a delayed message can block later messages. For game state or sensor updates, recent data may be more useful than recovering an older message. Unordered delivery lets later messages proceed, while partial reliability limits retransmission attempts or delivery time.

Choose delivery behavior

Delivery settings answer two questions: whether newer messages can bypass a delayed message, and when the transport should stop retrying delivery.

Choose the policy that matches how long your payload remains useful:

Goal Settings Use when
Reliable, ordered delivery (default) Omit ordered, maxRetransmits, and maxPacketLifeTime Messages remain useful and must arrive in order
Reliable, unordered delivery Set ordered: false; omit both retry fields Messages remain useful, but later messages should not wait for earlier messages
No retries or ordering Set ordered: false and maxRetransmits: 0 The application tolerates message loss and discards out-of-date updates
Limited retries Set maxRetransmits: <COUNT> Brief recovery is useful, but repeated retries are not
Time-bounded delivery Set maxPacketLifeTime: <MILLISECONDS> A message loses value after a known time window

ordered controls ordering independently from retries. maxRetransmits and maxPacketLifeTime are alternative retry budgets, so set at most one for each channel. Omit both for reliable delivery, whether ordered or unordered.

Apply the policy end to end

Realtime DataChannels use negotiated IDs, so browsers do not receive delivery settings from the remote peer. Apply the same settings when the publisher creates the local channel, each subscriber pulls the remote channel, and each client calls createDataChannel().

The following example configures unordered delivery with no retransmissions. It begins after you establish a DataChannel transport on both sessions and complete any required SDP exchange. Run the API requests from your backend with APP_ID, APP_TOKEN, PUBLISHER_SESSION_ID, and SUBSCRIBER_SESSION_ID set in your environment.

  1. On the publisher session, create the local DataChannel:
curl --request POST \
	--url "https://rtc.live.cloudflare.com/v1/apps/$APP_ID/sessions/$PUBLISHER_SESSION_ID/datachannels/new" \
	--header "Authorization: Bearer $APP_TOKEN" \
	--header "Content-Type: application/json" \
	--data @- <<EOF
{
	"dataChannels": [
		{
			"location": "local",
			"dataChannelName": "player-state",
			"ordered": false,
			"maxRetransmits": 0
		}
	]
}
EOF
  1. On each subscriber session, pull the remote DataChannel with the same delivery settings:
curl --request POST \
	--url "https://rtc.live.cloudflare.com/v1/apps/$APP_ID/sessions/$SUBSCRIBER_SESSION_ID/datachannels/new" \
	--header "Authorization: Bearer $APP_TOKEN" \
	--header "Content-Type: application/json" \
	--data @- <<EOF
{
	"dataChannels": [
		{
			"location": "remote",
			"sessionId": "$PUBLISHER_SESSION_ID",
			"dataChannelName": "player-state",
			"ordered": false,
			"maxRetransmits": 0
		}
	]
}
EOF
  1. In the publisher and subscriber clients, create the negotiated browser DataChannel with the same settings. In this example, pc is the active RTCPeerConnection, and channelId is the ID returned by the corresponding API request:
const channel = pc.createDataChannel("player-state", {
	negotiated: true,
	id: channelId,
	ordered: false,
	maxRetransmits: 0,
});

Hostname routing is now generally available, with a new public IP range for initial resolved IPs

Hostname routing is now generally available. Instead of managing static IP lists and routes, you can route traffic by hostname across multiple Cloudflare One connectors:

  • Cloudflare Tunnel: route a private hostname (for example, wiki.internal.local) to a private application behind your tunnel, or a public hostname (for example, bank.example.com) to egress through a specific tunnel and anchor traffic to a dedicated exit node.
  • Cloudflare Mesh: attract a private or public hostname's traffic to a Mesh node.

Alongside GA, the default IPv4 range used for initial resolved IPs (also called token IPs) is changing from a Carrier-Grade NAT (CGNAT) range to a public Cloudflare-owned range:

  • IPv4: 172.64.128.0/20
  • IPv6: 2606:4700:0cf1:4000::/64

This is the default range. You can configure a custom initial resolved IP range for IPv4 if it conflicts with your existing network.

Why this is changing: Starting with Chrome 142, Local Network Access (LNA) restrictions block background requests to CGNAT addresses (100.64.0.0/10), which included the previous initial resolved IP default (100.80.0.0/16). LNA is implemented at the Chromium engine level, so it affects all Chromium-based browsers (for example, Microsoft Edge, Brave, and Opera), not only Google Chrome. This could silently break hostname-based Gateway features for users of these browsers, and required Chrome Enterprise policy workarounds. The new default range is public Cloudflare address space, so it is not affected by this restriction.

What is affected: Initial resolved IPs are used by several features that associate a DNS query with the network connection that follows it:

You can check your account's current range, or configure a custom range, at any time from Zero Trust > Team & Resources > Devices > Device profiles, or using the Initial Resolved IP Subnet API.

For full instructions, refer to Configure initial resolved IPs. The IPv6 range (2606:4700:0cf1:4000::/64) is unchanged and is not affected by this restriction.

If you were relying on a Chrome Enterprise policy workaround (such as LocalNetworkAccessRestrictionsTemporaryOptOut) while your account was still on the legacy CGNAT-based range, refer to Google Chrome restricts access to private hostnames for next steps.

Stream live logs from Cloudflare Tunnel in the dashboard

Real-time Tunnel log streaming is now available in the Cloudflare dashboard under Networking > Tunnels. This brings the same live debugging capability previously only available in the Cloudflare One dashboard, including multi-connector aggregated streaming for high-availability deployments.

Stream live logs from a tunnel in the Cloudflare dashboard

In the tunnel detail view, a new Live logs tab lets you:

  • Stream logs from single or multiple connectors — In highly available deployments with multiple cloudflared replicas, logs from all connectors are merged into a single stream grouped by hostname, making it easy to identify which host machine produced each log entry.
  • Filter by log level, event type, and HTTP method — Narrow the stream to only the events you care about (HTTP, TCP, UDP, or cloudflared internal), at any log level.
Go to Tunnels ↗

For more information, refer to Monitor tunnels and Tunnel log streams.

Turnstile Spin is now generally available

Turnstile Spin is now generally available with three setup paths for creating a Turnstile widget and wiring canonical server-side siteverify into your existing backend. Start in the dashboard, with Wrangler, or from your AI coding agent. All three paths create the same widget. You can complete the integration by hand or have your agent embed the widget, wire siteverify, and validate it.

Server-side verification

Turnstile setup has two parts: embed the widget in your frontend, then call siteverify from your backend. Without the second part, the widget appears on the page but does not protect the request.

  • The skill includes insertion snippets for Next.js (App Router and Pages Router), Astro, SvelteKit, Hugo, and vanilla HTML. For other frameworks, the agent proposes a generic pattern and asks you to confirm it first.
  • The Turnstile dashboard flags existing widgets with no matching siteverify traffic. Select Fix with Spin to copy a prompt that guides your agent through wiring siteverify into your backend.
  • Before finishing, the agent runs a real Turnstile token through your protected endpoint, checks that it passes, then replays the token to confirm the endpoint rejects it on the second try. If a check fails, the agent stops and shows you where.

Run Spin

You can run Spin three ways:

  • In the Turnstile dashboard, select Set up with Spin, enter your domains, then select Set up. Spin creates the widget and returns the sitekey, secret, and a prompt for your agent.
  • From the Wrangler CLI, run wrangler turnstile widget create. Wrangler prints the sitekey and secret. You wire the frontend and siteverify by hand.
  • From your AI coding agent, paste the Spin prompt into Claude Code, Cursor, Codex, OpenCode, or GitHub Copilot Chat. Your agent fetches the skill, creates the widget, then embeds it and wires siteverify.

To get started, refer to the Turnstile Spin documentation.

Workers AI and AI Gateway unify model access and billing

Workers AI and AI Gateway now provide a unified path for accessing models and managing inference traffic. Use the same AI binding and REST API to call models hosted on Workers AI or by supported third-party providers, with AI Gateway providing observability, logging, caching, security, and billing controls.

Unified entrypoints and observability

The AI binding supports both Workers AI and third-party models through env.AI.run(). The REST API provides shared /ai/ endpoints with Cloudflare authentication across providers.

Route a Workers AI request through AI Gateway by specifying a gateway ID. Use default to automatically create a gateway on the first authenticated request, or specify an existing gateway to separate applications and workloads:

const response = await env.AI.run(
	"@cf/zai-org/glm-5.2",
	{
		messages: [{ role: "user", content: "What is the capital of France?" }],
	},
	{
		gateway: { id: "default" },
	},
);
const response = await env.AI.run(
	"@cf/zai-org/glm-5.2",
	{
		messages: [{ role: "user", content: "What is the capital of France?" }],
	},
	{
		gateway: { id: "default" },
	},
);

Requests routed through AI Gateway can be logged and included in analytics for request volume, errors, latency, token usage, and costs. You can also configure controls such as caching, rate limiting, and request retries on the gateway.

Unified billing and higher rate limits

You can now use prepaid AI Gateway credits to pay for Workers AI inference. This provides one credit balance for Workers AI and supported third-party model providers. To use credits for Workers AI, set the gateway's Workers AI billing setting to Unified billing. Workers AI requests routed through that gateway deduct from your credit balance in real time.

Prepaid credits also provide access to the following Workers AI frontier models without requiring the Workers Paid plan. Each frontier Workers AI model has a rate limit of 50 requests per minute per account, per model when billed with AI Gateway credits, compared to 20 requests per minute through standard Workers AI billing:

These limits are designed for typical agentic and coding workloads, where requests to frontier models can take longer to complete.

For details, refer to Workers AI limits, Workers AI pricing, Unified Billing, and the AI Gateway model catalog.

MySQL support in Hyperdrive is now generally available

Support for MySQL in Hyperdrive is now generally available. You can connect to any MySQL database from your Workers using Hyperdrive.

Hyperdrive makes your regional, MySQL databases fast when connecting from Cloudflare Workers. It eliminates unnecessary network roundtrips during connection setup, pools database connections globally, and can cache query results to provide the fastest possible response times.

You can connect using your existing drivers, ORMs, and query builders with Hyperdrive's secure credentials, with no code changes required. MySQL support is available at the same pricing as Postgres.

import { createConnection } from "mysql2/promise";

export default {
	async fetch(request, env, ctx) {
		const connection = await createConnection({
			host: env.HYPERDRIVE.host,
			user: env.HYPERDRIVE.user,
			password: env.HYPERDRIVE.password,
			database: env.HYPERDRIVE.database,
			port: env.HYPERDRIVE.port,
			disableEval: true, // Required for Workers compatibility
		});

		const [results, fields] = await connection.query("SHOW tables;");

		ctx.waitUntil(connection.end());

		return new Response(JSON.stringify({ results, fields }), {
			headers: {
				"Content-Type": "application/json",
				"Access-Control-Allow-Origin": "*",
			},
		});
	},
};
import { createConnection } from "mysql2/promise";

export interface Env {
	HYPERDRIVE: Hyperdrive;
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		const connection = await createConnection({
			host: env.HYPERDRIVE.host,
			user: env.HYPERDRIVE.user,
			password: env.HYPERDRIVE.password,
			database: env.HYPERDRIVE.database,
			port: env.HYPERDRIVE.port,
			disableEval: true, // Required for Workers compatibility
		});

		const [results, fields] = await connection.query("SHOW tables;");

		ctx.waitUntil(connection.end());

		return new Response(JSON.stringify({ results, fields }), {
			headers: {
				"Content-Type": "application/json",
				"Access-Control-Allow-Origin": "*",
			},
		});
	},
} satisfies ExportedHandler<Env>;

Learn more about how Hyperdrive works and get started building Workers that connect to MySQL with Hyperdrive.

Sandbox SDK 1.0 preview on @next

Sandbox SDK 1.0 is available to preview under the npm @next tag. For existing applications, the current stable package remains published on the 0.12.x line.

Sandbox SDK first shipped to provide a rich library for running untrusted and agent-driven work on Cloudflare Containers. Since then, both Sandbox and Containers have matured. This preview is a thinner SDK built on a richer Cloudflare Containers foundation.

npm i @cloudflare/sandbox@next

What this preview is

  • A single execution interfacesandbox.exec() takes an argument list, returns when the process starts, and gives you a handle for output, logs, waits, and signals. Both short commands and long-running services use the same API.
  • Removed session execution — the SDK no longer maintains shell state between executions. Each launch is independent. Pass cwd and env when you need them, or put multi-step shell syntax in one explicit shell command.
  • RPC as the only transport — the SDK talks to the container exclusively over RPC. Remove SANDBOX_TRANSPORT, transport on getSandbox(), and setTransport().
  • Improved PTY and terminal interface — interactive PTYs use createTerminal / connect, not the older session-shaped helpers.
  • Code interpreter as an extension — configure the code interpreter on your Sandbox subclass so you only ship what you need.

Start new projects on @next. Migrate existing apps when you can so you are ready when 1.0 becomes stable. Deploy the Worker package and container image from the same @next line.

Coding agents: install Cloudflare Skills (Agent setup). Use sandbox-next for @next (recommended for new projects), sandbox-stable for the current stable package, and sandbox-migrate-to-next when you are ready to port. Stable-package deprecated-API cleanup is in the 2026 deprecation guide.

The main Sandbox documentation still describes today's stable package. Preview docs:

The self-deployed Sandbox bridge is not currently part of this preview. We are working on bringing it in line with the latest code. Until then, use the stable bridge with the matching stable package and container image.

Timeline for 1.0

Further Cloudflare Containers features will let us keep reducing the size of the Sandbox SDK. We aim to ship Sandbox SDK 1.0 once those are in. In the meantime we continue to support and maintain the 1.0 preview (@next) alongside the current stable release.

AI Search makes it easier to build a search engine for your data

AI Search gets you from a data source to a working search endpoint quickly. This release adds what you need to put that endpoint in front of real users: your own domain, authentication, and one endpoint across several instances. It also adds crawling for sites without a complete sitemap, so your index covers everything you want it to find.

Each of the following is a new option. The previous behavior is still the default, so nothing changes until you change it.

Serve search from your own domain

A public endpoint is a URL that a site or app can query directly, with no authentication in front of it. By default that URL is a generated hostname on search.ai.cloudflare.com. You can now serve the same endpoint from a custom domain, a hostname in a zone that you own:

https://search.example.com/search

Restrict who can query your content

Once your endpoint is on your own domain, you can put Cloudflare Access in front of it. For example, you usually want to give /mcp to specific agents rather than to anyone who finds the URL. Agents authenticate with an Access service token, and people who open the endpoint in a browser sign in through your identity provider.

Search several instances from one URL

A namespace can expose its own public endpoint with /search, /chat/completions, and /mcp paths that fan out across the instances you choose:

curl https://ns-<NAMESPACE_ENDPOINT_ID>.search.ai.cloudflare.com/search \
  --header "Content-Type: application/json" \
  --data '{
    "messages": [{ "content": "How do I configure AI Search?", "role": "user" }],
    "ai_search_options": { "instance_ids": ["docs", "support"] }
  }'

Index your sites without a sitemap

Website data sources support a new discover parse type. It starts at the source URL and collects pages from both your sitemaps and the links it finds while crawling:

curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-search/instances" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-ai-search",
    "type": "web-crawler",
    "source": "example.com",
    "source_params": {
      "web_crawler": {
        "parse_type": "discover",
        "discover_options": { "source": "links", "limit": 5000, "depth": 3 }
      }
    }
  }'

To learn more, refer to the AI Search documentation.

Introducing Kitesurf, an agent-first browser on Browser Run

Kitesurf is Cloudflare's new stateless, highly scalable browser that runs entirely on top of Workers and is designed for AI agents. It is available for free while in beta.

Compared to Chromium, Kitesurf uses 3–7× less CPU and memory for common agentic tasks like screenshots and HTML extraction, so you can run more sessions and scale better for bursty, AI-driven workloads.

Your existing clients already work. To opt in, add the browser=kitesurf parameter to any Browser Run CDP or Quick Action endpoint:

curl -X POST 'https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-run/screenshot?browser=kitesurf' \
  -H 'Authorization: Bearer <API_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com"
  }' \
  --output "screenshot.png"

You can also explore Kitesurf without writing any code in the public playground.

For more information, refer to the Kitesurf documentation and the blog announcement.

Track AI spend and catch anomalous usage with User Insights

AI Gateway now includes User Insights, a dashboard that gives you two things at once: clear visibility into how much your organization spends on AI, and a security signal that surfaces users whose usage suddenly looks abnormal. It works on the traffic already flowing through your gateway, so there is no additional setup.

On the spend side, User Insights shows organization-wide totals for cost, requests, tokens, and adoption, and lets you drill into an individual user to see their spend, top models and providers, cache hit rate, and more. To attribute usage to individual users, add a user identifier with custom metadata or put your gateway behind Cloudflare Access.

On the security side, User Insights baselines each user's normal usage from their 95th percentile (p95) session cost over the last 30 days, then flags sessions that exceed both that baseline and an organization-level threshold. A sudden jump above a user's own pattern is often the first sign of a compromised credential or a misbehaving agent, so you can investigate before it shows up on your bill.

User Insights is available to all AI Gateway customers at no additional cost.

Identity-aware controls are now available in AI Gateway

AI Gateway now integrates with Cloudflare Access, giving you two new capabilities:

  • Protect your gateway endpoint. Put your AI Gateway behind Access so you can set policies that control who is allowed to call a specific gateway's endpoint.
  • Identity-aware controls. When traffic reaches AI Gateway through an Access-protected custom domain, AI Gateway can use the authenticated user's Access identity in logs, analytics, routing, and spend controls.

With identity-aware controls, you can set spend limits by authenticated user, control which gateways different users can access, filter logs by user, and build policies without passing user IDs from the client application. AI Gateway adds the verified Access user ID to request metadata as cf.user_id.

For setup instructions, refer to Cloudflare Access.

Agent traces for Think, Flue, and AI SDK instrumented by Agents SDK

Agent tracing is now available for applications built with the Agents SDK. Traces show each agent turn alongside model calls, tool runs, approvals, token usage, and Workers runtime operations.

Turn on Workers tracing in your Wrangler configuration:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "observability": {
    "traces": {
      "enabled": true
    }
  }
}
[observability.traces]
enabled = true

Think and Flue applications emit agent traces automatically. For direct AI SDK calls, wrap the AI SDK namespace once. wrapAISDK() supports AI SDK v6 and v7. This AI SDK v7 example also supplies the agent identity:

import * as ai from "ai";
import { wrapAISDK } from "agents/observability/ai";

const tracedAI = wrapAISDK(ai);

await tracedAI.generateText({
	model,
	prompt: "Find an available appointment",
	runtimeContext: {
		agentId: "booking-agent-production",
		conversationId: "conversation-123",
	},
	telemetry: {
		functionId: "booking-agent",
		includeRuntimeContext: {
			agentId: true,
			conversationId: true,
		},
	},
});
import * as ai from "ai";
import { wrapAISDK } from "agents/observability/ai";

const tracedAI = wrapAISDK(ai);

await tracedAI.generateText({
	model,
	prompt: "Find an available appointment",
	runtimeContext: {
		agentId: "booking-agent-production",
		conversationId: "conversation-123",
	},
	telemetry: {
		functionId: "booking-agent",
		includeRuntimeContext: {
			agentId: true,
			conversationId: true,
		},
	},
});

Message and tool payload recording is off by default. Turn it on only when the payloads are safe to store:

const tracedAI = wrapAISDK(ai, {
	storeMessages: true,
	storeTools: true,
});
const tracedAI = wrapAISDK(ai, {
	storeMessages: true,
	storeTools: true,
});

Open the Agents tab in the Cloudflare dashboard to inspect sessions, replay conversations, and view trace waterfalls. For advanced setup, privacy controls, and trace structure, refer to Agent tracing.

Build and deploy Artifacts repos on every push

You can now run your CI/CD pipeline on your Artifacts repo by defining a CI Workflow with the CI SDK, automatically triggered on Artifacts push events.

This allows you to:

  • Automatically build and deploy application code stored in Artifacts.
  • Run linting, type checking, tests, and other checks on every push.
  • Reuse dependencies when the lockfile (i.e. pnpm-lock.yaml) has not changed.
  • Stop deployment when a check or build fails.
  • Restrict API token access to the deployment step.
  • Deploy the output to a Worker or a Workers for Platforms User Worker.

Define your CI steps with @cloudflare/ci. Each ci.runner() spins up an isolated sandbox, and the cache option reuses installed dependencies across each sandboxed step in your CI job.

Point cache.inputs at your lockfile (i.e. pnpm-lock.yaml, bun.lock), and the install step only runs again when that lockfile changes:

src/index.jsjs
const deps = await ci.runner({
	name: "install",
	command: "bun install --frozen-lockfile",
	cache: { inputs: ["package.json", "bun.lock"] },
});

await Promise.all([
	deps.runner({ name: "lint", command: "bun run lint" }),
	deps.runner({ name: "test", command: "bun run test" }),
	deps.runner({ name: "typecheck", command: "bun run typecheck" }),
	deps.runner({ name: "build", command: "bun run build" }),
]);

await deps.runner({ name: "deploy", command: "bun wrangler deploy" });
src/index.tsts
const deps = await ci.runner({
	name: "install",
	command: "bun install --frozen-lockfile",
	cache: { inputs: ["package.json", "bun.lock"] },
});

await Promise.all([
	deps.runner({ name: "lint", command: "bun run lint" }),
	deps.runner({ name: "test", command: "bun run test" }),
	deps.runner({ name: "typecheck", command: "bun run typecheck" }),
	deps.runner({ name: "build", command: "bun run build" }),
]);

await deps.runner({ name: "deploy", command: "bun wrangler deploy" });

To start the Workflow automatically after each push, add a cf.artifacts.repo.pushed trigger to your Wrangler configuration:

{
	"triggers": {
		"events": [
			{
				"type": "cf.artifacts.repo.pushed",
				"filter": {
					"namespace": "CI",
					"repoName": "my-repo",
				},
				"target": {
					"scriptName": "my-ci-worker",
					"workflowName": "ci-workflow",
				},
			},
		],
	},
}
[[triggers.events]]
type = "cf.artifacts.repo.pushed"

  [triggers.events.filter]
  namespace = "CI"
  repoName = "my-repo"

  [triggers.events.target]
  scriptName = "my-ci-worker"
  workflowName = "ci-workflow"

To learn more, refer to Build and deploy Artifacts repos.

Vectorize indexes now support up to 20 million vectors

You can now store up to 20 million vectors in a single Vectorize index, doubling the previous limit of 10 million vectors. This enables larger-scale semantic search, recommendation systems, and retrieval-augmented generation (RAG) applications without splitting data across multiple indexes.

Vectorize continues to support indexes with up to 1,536 dimensions per vector at 32-bit precision. Refer to the Vectorize limits documentation for complete details.

AI agents can debug Workers with local tracing

wrangler dev and vite dev automatically capture structured OpenTelemetry traces and correlated console logs during local Worker invocations.

Debug with AI agents

When the tooling detects an AI agent session, it prints a terminal hint pointing to the Local Explorer API at /cdn-cgi/explorer/api. The API serves an OpenAPI schema and exposes a read-only observability query endpoint for discovering telemetry, querying traces and logs, and inspecting binding state.

The agent can identify the exact failing operation, fix the code, rerun the request, and verify the result. This debug loop requires no deployment or temporary logs.

Inspect traces in Local Explorer

Humans can inspect the same traces and correlated console logs in the Local Explorer browser UI. Each trace shows spans, timing, attributes, and errors.

Local Explorer showing a failed Worker trace with spans, timing, and errors

Automatic spans cover handler calls, outbound fetch() calls, and binding calls. Custom spans appear alongside these automatic spans.

For more details, refer to the Local Explorer documentation.

Node.js compatibility is now enabled by default

Workers now enable the nodejs_compat and nodejs_compat_v2 compatibility flags by default for compatibility dates of 2026-08-04 or later. These flags are not used for these compatibility dates because the compatibility date enables the same behavior.

This means all Node.js built-in APIs supported by the Workers runtime are available by default, including node:crypto, node:buffer, node:stream, node:net, node:dns, node:fs, node:http, and more. npm packages that depend on these APIs will work without additional configuration.

Workers using an earlier compatibility date are not affected. They can still opt in by adding nodejs_compat to compatibility_flags.

New projects do not need to add either flag. Existing projects can update their compatibility date without removing them. Wrangler, Miniflare, the Cloudflare Vite plugin, and Vitest Pool Workers ignore these redundant flags when starting the runtime.

To turn off Node.js compatibility completely, remove any nodejs_compat and nodejs_compat_v2 flags. Then add both of the following flags:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  // Set this to today's date
  "compatibility_date": "2026-08-16",
  "compatibility_flags": [
    "no_nodejs_compat",
    "no_nodejs_compat_v2"
  ]
}
# Set this to today's date
compatibility_date = "2026-08-16"
compatibility_flags = ["no_nodejs_compat", "no_nodejs_compat_v2"]

For more information, refer to the Node.js compatibility documentation.

Preview: @cloudflare/computer agent runtime

We're releasing an early preview of @cloudflare/computer, an open-source agent runtime that gives every agent its own computer. The runtime dynamically orchestrates between fast, efficient isolates and full Linux containers, so the agent always runs on the right compute primitive for the task at hand.

@cloudflare/computer provides a virtual filesystem backed by SQLite, which you can populate from cloud storage, source control, or any files you choose. Agents can read, write, and edit files, run shell commands, and interact with Git repositories. All operations are gated, audited, and observed.

Install the package via npm:

npm install @cloudflare/computer

Instantiate a Workspace inside any Durable Object to give your agent a filesystem and execution runtime:

import { Workspace } from "@cloudflare/computer";

export class Agent {
	workspace = new Workspace({
		storage: this.ctx.storage,
	});
}

Several execution backends are included or you can write your own:

  • Isolate runtime — fast, horizontally scalable execution via just-bash and Dynamic Workers, ideal for file manipulation and data processing.
  • Container runtime — full Linux environment via Cloudflare Containers, mounted through FUSE, for tasks that need native binaries, package managers, or a complete userland.

The AI SDK-compatible toolkit provides common agent tools (read, write, edit, ls, exec) and guides the model to choose the appropriate backend for each task.

For more examples, including a step-by-step tutorial, visit the @cloudflare/computer repository.

Read the announcement blog post for more details: Your agent needs a computer, not a container.

Billing is now enabled for Pipelines

Billing is now enabled for Cloudflare Pipelines on non-enterprise accounts. Pipelines usage beyond the included free tier will appear on your next invoice.

Pipelines charges based on two usage dimensions. Ingress into a Pipeline stream remains free regardless of volume:

  • SQL transforms: $0.04 / GB for stateless transforms (filter, reshape, unnest, cast, compute).
  • Sinks (egress): $0.03 / GB for JSON output, $0.06 / GB for Parquet or Iceberg output.

Workers Paid plans include 50 GB / month for both SQL transforms and sinks. Standard R2 storage and operations charges apply for data written to R2 buckets, and R2 Data Catalog charges apply when writing to Iceberg tables.

For example, a pipeline that ingests 500 GB of event data per month, uses a SQL transform to filter and reshape it, and writes 300 GB to an R2 Data Catalog Iceberg table would be billed as follows:

Dimension Usage Included Billable Cost
Streams 500 GB Unlimited 0 GB $0.00
SQL transforms 500 GB 50 GB 450 GB $18.00
Sinks (Iceberg) 300 GB 50 GB 250 GB $15.00
Total $33.00

For full pricing details and billing examples, refer to Pipelines pricing.

Billing is now enabled for R2 Data Catalog

Billing is now enabled for R2 Data Catalog on non-enterprise accounts. R2 Data Catalog usage beyond the included free tier will appear on your next invoice.

R2 Data Catalog charges based on two dimensions, in addition to standard R2 storage and operations:

  • Catalog operations: $9.00 / million operations for metadata requests such as creating tables, reading table metadata, and updating table properties.
  • Compaction: $0.005 / GB processed and $2.00 / million objects processed. These charges only apply when automatic compaction is turned on for a table.

Each dimension includes a monthly free tier: 1 million catalog operations, 10 GB of compaction data processed, and 1 million compaction objects processed.

For example, a single Iceberg table with 50 GB of data, 500,000 catalog operations per month, and compaction turned on that processes 20 GB across 200,000 files would be billed as follows:

Dimension Usage Included Billable Cost
Catalog operations 500,000 1,000,000 0 $0.00
Compaction (data processed) 20 GB 10 GB 10 GB $0.05
Compaction (objects) 200,000 1,000,000 0 $0.00
Total (Data Catalog) $0.05

Standard R2 storage charges ($0.015 / GB-month) apply separately for the 50 GB of data stored.

For full pricing details and billing examples, refer to R2 Data Catalog pricing.

Billing is now enabled for R2 SQL

Billing is now enabled for R2 SQL on non-enterprise accounts. R2 SQL usage beyond the included free tier will appear on your next invoice.

R2 SQL charges based on a single dimension:

  • Data scanned: $0.0025 / GB ($2.50 / TB) of compressed data read from R2 to execute your query.

All plans include 10 GB of data scanned per month. Each query is billed for a minimum of 10 MB of data scanned. R2 SQL pricing is additive to standard R2 storage and operations and R2 Data Catalog charges. R2 does not charge for egress, so there is no additional data transfer cost.

For example, a user who stores 500 GB of Parquet data in R2 Data Catalog and runs queries that scan a total of 50 GB of compressed data during the month would be billed as follows:

Dimension Usage Included Billable Cost
R2 storage 500 GB-month 10 GB-month 490 GB-month $7.35
R2 SQL (data scanned) 50 GB 10 GB 40 GB $0.10
Total $7.45

For full pricing details and billing examples, refer to R2 SQL pricing.

Python and JavaScript Workers can now call each other via RPC

You can now call methods between Python and JavaScript Workers using Workers RPC. This works through Service bindings without extra dependencies, schema definitions, or serialization code.

Cross-language RPC calls behave like ordinary function calls. Exceptions propagate to the call site. You can pass structured cloneable types as parameters or return values, and Pyodide Foreign Function Interface (FFI) automatically converts types between languages.

Call a TypeScript Worker from Python

Define a method in a TypeScript Worker:

index.jsjs
import { WorkerEntrypoint } from "cloudflare:workers";

export class RpcService extends WorkerEntrypoint {
	async add(a, b) {
		return a + b;
	}
}
index.tsts
import { WorkerEntrypoint } from "cloudflare:workers";

export class RpcService extends WorkerEntrypoint {
	async add(a: number, b: number): Promise<number> {
		return a + b;
	}
}

Call it from a Python Worker through a Service binding:

from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def fetch(self, request):
		rpc = self.env.RPC
		result = await rpc.add(42, 144)
		return Response.json({"result": result})

Configure the Service binding in the Python Worker's Wrangler configuration:

{
	"services": [
		{
			"binding": "RPC",
			"service": "ts-rpc-server",
			"entrypoint": "RpcService"
		}
	]
}
[[services]]
binding = "RPC"
service = "ts-rpc-server"
entrypoint = "RpcService"

Call a Python Worker from JavaScript

Define a method in a Python Worker:

from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
	async def highlight_code(self, code: str, language: str) -> dict:
		from pygments.formatters import HtmlFormatter
		from pygments import highlight
		from pygments.lexers import get_lexer_by_name

		lexer = get_lexer_by_name(language, stripall=True)
		formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
		highlighted_html = highlight(code, lexer, formatter)
		css = formatter.get_style_defs(".highlight")

		return {
			"html": highlighted_html,
			"css": css
		}

Call it from a JavaScript Worker through a Service binding:

index.jsjs
export default {
	async fetch(request, env) {
		const rpc = env.PYTHON_RPC;
		const result = await rpc.highlight_code("print(42)", "python");
		return Response.json(result);
	},
};
index.tsts
export default {
	async fetch(request, env) {
		const rpc = env.PYTHON_RPC;
		const result = await rpc.highlight_code("print(42)", "python");
		return Response.json(result);
	},
};

Configure the Service binding in the JavaScript Worker's Wrangler configuration:

{
	"services": [
		{
			"binding": "PYTHON_RPC",
			"service": "py-rpc-server"
		}
	]
}
[[services]]
binding = "PYTHON_RPC"
service = "py-rpc-server"

For more details on the announcement, read the blog post.

For more information, refer to the Workers RPC documentation and the Python Workers overview.