feat: readonly connections — restrict WebSocket clients from modifying agent state#610
Merged
threepointone merged 10 commits intomainfrom Feb 8, 2026
Merged
feat: readonly connections — restrict WebSocket clients from modifying agent state#610threepointone merged 10 commits intomainfrom
threepointone merged 10 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 029a193 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
e426f6f to
d3c190a
Compare
This comment was marked as resolved.
This comment was marked as resolved.
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Nov 25, 2025
Adds comprehensive documentation for the new readonly connections feature introduced in PR #610. Key additions: - Server-side methods: shouldConnectionBeReadonly(), setConnectionReadonly(), isConnectionReadonly() - Client-side API: onStateUpdateError callback for error handling - Multiple usage examples covering common scenarios (query params, RBAC, admin dashboards, dynamic permissions) - Implementation details including SQL persistence and hibernation support - Best practices for authentication, user feedback, and access control This feature allows restricting certain WebSocket connections from modifying agent state while still allowing them to receive state updates and call RPC methods. Related PR: cloudflare/agents#610 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Introduces readonly connections to restrict certain WebSocket clients from modifying agent state while allowing state updates and RPC calls. Adds server-side methods for managing readonly status, persists status in SQL for hibernation, and client-side error handling via onStateUpdateError. Updates documentation and relevant types, client, and React hook implementations.
Introduces a new TestReadonlyAgent Durable Object and comprehensive tests for readonly connection behavior, including state update restrictions, RPC permissions, persistence, and cleanup. Updates wrangler config to register the new agent for testing.
Improved type safety in readonly-connections.test.ts by introducing explicit message interfaces and type guards, replacing 'any' with specific types in test helpers and assertions. Also updated TestReadonlyAgent to ignore unused connection parameter in shouldConnectionBeReadonly. These changes enhance code reliability and maintainability in the test suite.
05da9cb to
66d1916
Compare
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Nov 27, 2025
Synced from cloudflare/agents PR #610 (cloudflare/agents#610) Introduces documentation for the readonly connections feature which allows restricting certain WebSocket connections from modifying Agent state while still allowing them to receive state updates and call RPC methods. Key features documented: - Server-side methods: shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly - Client-side API: onStateUpdateError callback - Usage examples for query parameter based access, role-based access control, admin dashboards, and dynamic permission changes - Behavior details, best practices, and migration guide - Implementation details including persistence across hibernation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
whoiskatrin
approved these changes
Nov 27, 2025
Contributor
Author
|
moving to draft, might have a better implementation here |
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # packages/agents/src/ai-types.ts # packages/agents/src/client.ts # packages/agents/src/index.ts # packages/agents/src/tests/worker.ts # packages/agents/src/tests/wrangler.jsonc
Replace biome-ignore comments in packages/agents/src/index.ts with oxlint-disable-next-line typescript-eslint(no-explicit-any) to satisfy the linter while allowing variadic args to be passed through. Also update the test import in packages/agents/src/tests/readonly-connections.test.ts from "../ai-types" to "../types" to match the correct module path.
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 8, 2026
Sync documentation for PR #610 from cloudflare/agents repository. Adds comprehensive documentation for the new readonly connections feature, which allows restricting WebSocket connections from modifying agent state while still allowing them to receive state updates and call RPC methods. Key additions: - Server-side methods: shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly - Client-side API: onStateUpdateError callback - Usage examples for query parameters, RBAC, admin dashboards, and dynamic permissions - Behavior details including state sync and connection cleanup - Best practices for authentication, user feedback, and permission checks - Migration guide for existing agents Source PR: cloudflare/agents#610
4 tasks
elithrar
added a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 8, 2026
* Add readonly connections documentation Sync documentation for PR #610 from cloudflare/agents repository. Adds comprehensive documentation for the new readonly connections feature, which allows restricting WebSocket connections from modifying agent state while still allowing them to receive state updates and call RPC methods. Key additions: - Server-side methods: shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly - Client-side API: onStateUpdateError callback - Usage examples for query parameters, RBAC, admin dashboards, and dynamic permissions - Behavior details including state sync and connection cleanup - Best practices for authentication, user feedback, and permission checks - Migration guide for existing agents Source PR: cloudflare/agents#610 * Split mixed TS+JSX code block to fix build Co-authored-by: elithrar <elithrar@users.noreply.github.com> * Fix build: unwrap JSX code blocks from TypeScriptExample TypeScriptExample uses ts-blank-space to strip TypeScript types, but ts-blank-space doesn't understand JSX syntax — it misinterprets JSX tags as generics, 'as' in JSX text as type assertions, and '!' as non-null assertions. This caused: 1. GameComponent block: <div> tag blanked out entirely, breaking Prettier's babel parser with 'Adjacent JSX elements' error 2. EditButton block: !canEdit silently corrupted to canEdit in the generated JavaScript tab Fix by removing the TypeScriptExample wrapper from these two JSX blocks, using plain fenced tsx code blocks instead (matching the pattern used in quick-start.mdx and guides/webhooks.mdx). --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: elithrar <elithrar@users.noreply.github.com>
Introduce readonly connections to prevent certain WebSocket clients from modifying agent state while still allowing them to receive updates and call non-mutating RPCs. Adds server APIs (shouldConnectionBeReadonly, setConnectionReadonly, isConnectionReadonly), client onStateUpdateError handling, and enforces restrictions in both the client message handler and Agent.setState(). Internally stores the flag in a namespaced connection attachment (_cf_readonly) and wraps connection.state/setState to preserve the flag across hibernation. Includes design doc, user docs, tests, and a playground demo (ReadonlyDemo + readonly-agent). Also updates example manifests and bumps minor dependencies (hono) where applicable.
Introduce a suite of test agent classes under packages/agents/src/tests/agents (callable, email, mcp, oauth, race, readonly, schedule, state, workflow) to cover RPC, streaming, MCP tooling, OAuth flows, state management, schedules, workflows and concurrency/read-only behaviors. Add an agents index that re-exports these test agents and update packages/agents/src/tests/worker.ts to re-export the agents from ./agents, simplify imports, and expose an Env type via import-types to avoid runtime circulars. These changes centralize test helpers and streamline test worker wiring.
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 8, 2026
Update documentation for the readonly connections feature which restricts WebSocket clients from modifying agent state while still receiving updates. Key changes: - Updated readonly-connections.mdx with new enforcement model (blocks both client setState and @callable methods that call setState) - Updated storage implementation details (connection state wrapping instead of SQL tables) - Added caveats about side effects in callables - Added link to readonly connections in store-and-sync-state.mdx Source PR: cloudflare/agents#610 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
4 tasks
Introduce onStatePersisted as the new server-side state notification hook and deprecate onStateUpdate. Add internal dispatch that calls onStatePersisted (or the deprecated onStateUpdate), emits a one-time console warning per class when the old hook is used, and throws if a class overrides both hooks. Ensure validateStateChange rejections propagate a CF_AGENT_STATE_ERROR message back to the client. Update docs, examples, tests, and test harness (wrangler) to cover the new hook and error behavior. Add changeset documenting the patch.
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 8, 2026
Updates documentation for readonly connections feature and deprecates onStateUpdate in favor of onStatePersisted. Changes: - Update readonly-connections.mdx with new implementation using connection state attachment instead of SQL storage - Clarify that readonly blocks both client-side setState() and callable methods that call this.setState() - Add enforcement diagram showing how readonly works - Add caveats section about side effects and limitations - Rename onStateUpdate to onStatePersisted across all documentation files - Update API reference tables and examples Related PR: cloudflare/agents#610 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rename the server-side persistence hook from `onStatePersisted` to `onStateChanged` across the codebase (docs, READMEs, examples, tests, playground, and package implementation). Update Agent internals to detect and call `onStateChanged`, adjust deprecation/error messages (one-time warning for `onStateUpdate`, and error if both hooks are overridden), and update tests/assertions to match the new name. Also update the changeset metadata to deprecate `onStateUpdate` in favor of `onStateChanged`. (Note: validateStateChange rejection behavior that propagates a `CF_AGENT_STATE_ERROR` message to clients is preserved as documented in the changeset.)
agents-git-bot bot
pushed a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 8, 2026
Updates from cloudflare/agents PR #610 (feat: readonly connections): ## Major changes 1. **Readonly connections feature** (readonly-connections.mdx): - Updated implementation details: uses connection state wrapping instead of SQL - Clarified enforcement for @callable() methods that call setState() - Added "What readonly does and does not restrict" table - Added caveats section about side effects in callables - Removed outdated SQL storage implementation details - Updated "How it works" section to reflect connection attachment storage 2. **onStateUpdate → onStateChanged rename**: - Renamed server-side hook from onStateUpdate to onStateChanged - Updated across all documentation files - Client-side onStateUpdate callback remains unchanged ## Updated files - api-reference/readonly-connections.mdx - major rewrite with corrected implementation - api-reference/store-and-sync-state.mdx - renamed onStateUpdate to onStateChanged - api-reference/agents-api.mdx - renamed hook references - api-reference/client-sdk.mdx - renamed hook references - concepts/agent-class.mdx - renamed hook references - getting-started/quick-start.mdx - renamed hook references - model-context-protocol/mcp-agent-api.mdx - renamed hook references Related upstream PR: cloudflare/agents#610 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Contributor
Author
|
ok, I'm feeling good about this, landing it. |
Merged
whoiskatrin
added a commit
to cloudflare/cloudflare-docs
that referenced
this pull request
Feb 11, 2026
…ebSocket clients from modifying agent state (#28197) * Sync documentation for PR #610: readonly connections and onStateChanged Updates from cloudflare/agents PR #610 (feat: readonly connections): ## Major changes 1. **Readonly connections feature** (readonly-connections.mdx): - Updated implementation details: uses connection state wrapping instead of SQL - Clarified enforcement for @callable() methods that call setState() - Added "What readonly does and does not restrict" table - Added caveats section about side effects in callables - Removed outdated SQL storage implementation details - Updated "How it works" section to reflect connection attachment storage 2. **onStateUpdate → onStateChanged rename**: - Renamed server-side hook from onStateUpdate to onStateChanged - Updated across all documentation files - Client-side onStateUpdate callback remains unchanged ## Updated files - api-reference/readonly-connections.mdx - major rewrite with corrected implementation - api-reference/store-and-sync-state.mdx - renamed onStateUpdate to onStateChanged - api-reference/agents-api.mdx - renamed hook references - api-reference/client-sdk.mdx - renamed hook references - concepts/agent-class.mdx - renamed hook references - getting-started/quick-start.mdx - renamed hook references - model-context-protocol/mcp-agent-api.mdx - renamed hook references Related upstream PR: cloudflare/agents#610 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix build: wrap @callable() code snippets in class declarations The Prettier/Babel parser requires decorators to be attached to class declarations. Three code blocks in readonly-connections.mdx had standalone @callable() decorators on methods outside of a class body, causing the build to fail with 'Leading decorators must be attached to a class declaration'. * Wrap @callable() snippets in class bodies Co-authored-by: whoiskatrin <whoiskatrin@users.noreply.github.com> * Apply suggestions from code review * Delete package-lock.json --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: whoiskatrin <whoiskatrin@users.noreply.github.com> Co-authored-by: Jun Lee <junlee@cloudflare.com> Co-authored-by: whoiskatrin <kreznykova@cloudflare.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #255
Summary
Adds a readonly connections API that lets you restrict certain WebSocket clients from modifying agent state while still receiving state updates and calling non-mutating RPC methods.
Server-side API
shouldConnectionBeReadonly(connection, ctx)— hook evaluated on connect; returntrueto mark readonlysetConnectionReadonly(connection, readonly?)— toggle readonly status dynamically at any timeisConnectionReadonly(connection)— check a connection's current statusEnforcement
Readonly connections are blocked at two levels:
setState()— the server rejects the message and sends acf_agent_state_errorback@callable()methods that callthis.setState()— the framework throwsError("Connection is readonly")insidesetState(), which surfaces as an RPC error to the callerNon-mutating callables (reads, queries, permission checks) work normally for readonly connections.
Storage
The readonly flag is stored in a namespaced key (
_cf_readonly) inside the connection's WebSocket attachment viaconnection.setState(). This means:connection.stateandconnection.setState()are wrapped to hide and preserve the internal keyClient-side
onStateUpdateErrorcallback onuseAgent/AgentClientfor handling rejected state writesWhat's included
packages/agents/src/index.ts— readonly hooks,setState()gating, connection wrappingpackages/agents/src/client.ts,react.tsx—onStateUpdateErrorcallbackpackages/agents/src/types.ts—CF_AGENT_STATE_ERRORmessage typepackages/agents/src/tests/readonly-connections.test.ts(17 tests)packages/agents/src/tests/agents/readonly.tsdocs/readonly-connections.md— user-facing guide with API referencedesign/readonly-connections.md— internal design, tradeoffs, caveatsexamples/playground/src/demos/core/ReadonlyDemo.tsx+readonly-agent.ts.changeset/empty-eels-unite.mdTest plan
isConnectionReadonly, state broadcasts to readonly connections, reconnection, connection state wrapping (hidden_cf_readonly, preserved acrosssetStatevalue/callback forms), sequential mutation failuresnpx tsc --noEmitcleannpx oxlintclean