Three new properties are now available on request.cf in Workers that expose Layer 4 transport telemetry from the client connection. These properties let your Worker make decisions based on real-time connection quality signals — such as round-trip time and data delivery rate — without requiring any client-side changes.
Previously, this telemetry was only available via the Server-Timing: cfL4 response header. These new properties surface the same data directly in the Workers runtime, so you can use it for routing, logging, or response customization.
New properties
Property
Type
Description
clientTcpRtt
number | undefined
The smoothed TCP round-trip time (RTT) between Cloudflare and the client in milliseconds. Only present for TCP connections (HTTP/1, HTTP/2). For example, 22.
clientQuicRtt
number | undefined
The smoothed QUIC round-trip time (RTT) between Cloudflare and the client in milliseconds. Only present for QUIC connections (HTTP/3). For example, 42.
edgeL4
Object | undefined
Layer 4 transport statistics. Contains deliveryRate (number) — the most recent data delivery rate estimate for the connection, in bytes per second. For example, 123456.
Four new fields are now available on request.cf.tlsClientAuth in Workers for requests that include a mutual TLS (mTLS) client certificate. These fields encode the client certificate and its intermediate chain in RFC 9440 ↗︎ format — the same standard format used by the Client-Cert and Client-Cert-Chain HTTP headers — so your Worker can forward them directly to your origin without any custom parsing or encoding logic.
New fields
Field
Type
Description
certRFC9440
String
The client leaf certificate in RFC 9440 format (:base64-DER:). Empty if no client certificate was presented.
certRFC9440TooLarge
Boolean
true if the leaf certificate exceeded 10 KB and was omitted from certRFC9440.
certChainRFC9440
String
The intermediate certificate chain in RFC 9440 format as a comma-separated list. Empty if no intermediates were sent or if the chain exceeded 16 KB.
certChainRFC9440TooLarge
Boolean
true if the intermediate chain exceeded 16 KB and was omitted from certChainRFC9440.
Example: forwarding client certificate headers to your origin
export default { async fetch(request) { const tls = request.cf.tlsClientAuth; // Only forward if cert was verified and chain is complete if (!tls || !tls.certVerified || tls.certRevoked || tls.certChainRFC9440TooLarge) { return new Response("Unauthorized", { status: 401 }); } const headers = new Headers(request.headers); headers.set("Client-Cert", tls.certRFC9440); headers.set("Client-Cert-Chain", tls.certChainRFC9440); return fetch(new Request(request, { headers })); },};
MCP server portals support Code Mode MCP server patterns, a technique that reduces context window usage by replacing individual tool definitions with a single code execution tool. Code Mode is turned on by default on all portals.
To turn it off, edit the portal in Access controls > AI controls and turn off Code Mode under Basic information.
When Code Mode is active, the portal exposes a single code tool instead of listing every tool from every upstream MCP server. The connected AI agent writes JavaScript that calls typed codemode.* methods for each upstream tool. The generated code runs in an isolated Dynamic Worker environment, keeping authentication credentials and environment variables out of the model context.
To use Code Mode, append ?codemode=search_and_execute to your portal URL when connecting from an MCP client:
MCP server portals support two context optimization options that reduce how many tokens tool definitions consume in the model's context window. Both options are activated by appending the optimize_context query parameter to the portal URL.
minimize_tools
Strips tool descriptions and input schemas from all upstream tools, leaving only their names. The portal exposes a special query tool that agents use to retrieve full definitions on demand. This provides up to 5x savings in token usage.
Hides all upstream tools and exposes only two tools: query and execute. The query tool searches and retrieves tool definitions. The execute tool runs the upstream tools in an isolated Dynamic Worker environment. This reduces the initial token cost to a small constant, regardless of how many tools are available through the portal.
DLP now processes ZIP files using a streaming handler that scans archive contents element-by-element as data arrives. This removes previous file size limitations and improves memory efficiency when scanning large archives.
Microsoft Office documents (DOCX, XLSX, PPTX) also benefit from this improvement, as they use ZIP as a container format.
This improvement is automatic — no configuration changes are required.
HTTP Archive (HAR) files are used by engineering and support teams to capture and share web traffic logs for troubleshooting. However, these files routinely contain highly sensitive data — including session cookies, authorization headers, and other credentials — that can pose a significant risk if uploaded to third-party services without being reviewed or cleaned first.
Gateway now includes a predefined DLP profile called Unsanitized HAR that detects HAR files in HTTP traffic. You can use this profile in a Gateway HTTP policy to either block HAR file uploads entirely or redirect users to a sanitization tool before allowing the upload to proceed.
How to configure a HAR file policy
In the Cloudflare dashboard ↗︎, go to Zero Trust > Traffic policies > Firewall Policies > HTTP and create a new HTTP policy using the DLP Profile selector:
Selector
Operator
Value
Action
DLP Profile
in
Unsanitized HAR
Then choose one of the following actions:
Block: Prevents the upload of any HAR file that has not been sanitized by Cloudflare's sanitizer. Use this for strict environments where HAR file sharing must be disallowed entirely.
Block with Gateway Redirect: Intercepts the upload and redirects the user to https://har-sanitizer.pages.dev/, where they can sanitize the file. Once sanitized, the user can re-upload the clean file and proceed with their workflow.
Sanitized HAR recognition
HAR files processed by the Cloudflare HAR sanitizer receive a tamper-evident sanitized marker. DLP recognizes this marker and will not re-trigger the policy on a file that has already been sanitized and has not been modified since. If a previously sanitized file is edited, it will be treated as unsanitized and flagged again.
Visibility in Gateway logs
Gateway logs will reflect whether a detected HAR file was classified as Unsanitized or Sanitized, giving your security team full visibility into HAR file activity across your organization.
The new secrets configuration property lets you declare the secret names your Worker requires in your Wrangler configuration file. Required secrets are validated during local development and deploy, and used as the source of truth for type generation.
When secrets is defined, wrangler dev and vite dev load only the keys listed in secrets.required from .dev.vars or .env/process.env. Additional keys in those files are excluded. If any required secrets are missing, a warning is logged listing the missing names.
Type generation
wrangler types generates typed bindings from secrets.required instead of inferring names from .dev.vars or .env. This lets you run type generation in CI or other environments where those files are not present. Per-environment secrets are supported — the aggregated Env type marks secrets that only appear in some environments as optional.
Deploy
wrangler deploy and wrangler versions upload validate that all secrets in secrets.required are configured on the Worker before the operation succeeds. If any required secrets are missing, the command fails with an error listing which secrets need to be set.
Cloudflare Gateway now supports OIDC Claims as a selector in Firewall, Resolver, and Egress policies. Administrators can use custom OIDC claims from their identity provider to build fine-grained, identity-based traffic policies across all Gateway policy types.
With this update, you can:
Filter traffic in DNS, HTTP, and Network firewall policies based on OIDC claim values.
Apply custom resolver policies to route DNS queries to specific resolvers depending on a user's OIDC claims.
Control egress policies to assign dedicated egress IPs based on OIDC claim attributes.
For example, you can create a policy that routes traffic differently for users with department=engineering in their OIDC claims, or restrict access to certain destinations based on a user's role claim.
To get started, configure custom OIDC claims on your identity provider and use the OIDC Claims selector in the Gateway policy builder.
Dynamic Workers are now in open beta ↗︎ for all paid Workers users. You can now have a Worker spin up other Workers, called Dynamic Workers, at runtime to execute code on-demand in a secure, sandboxed environment. Dynamic Workers start in milliseconds, making them well suited for fast, secure code execution at scale.
Use Dynamic Workers for
Code Mode: LLMs are trained to write code. Run tool-calling logic written in code instead of stepping through many tool calls, which can save up to 80% in inference tokens and cost.
AI agents executing code: Run code for tasks like data analysis, file transformation, API calls, and chained actions.
Running AI-generated code: Run generated code for prototypes, projects, and automations in a secure, isolated sandboxed environment.
Fast development and previews: Load prototypes, previews, and playgrounds in milliseconds.
Custom automations: Create custom tools on the fly that execute a task, call an integration, or automate a workflow.
Executing Dynamic Workers
Dynamic Workers support two loading modes:
load(code) — for one-time code execution (equivalent to calling get() with a null ID).
get(id, callback) — caches a Dynamic Worker by ID so it can stay warm across requests. Use this when the same code will receive subsequent requests.
export default { async fetch(request, env) { const worker = env.LOADER.load({ compatibilityDate: "2026-01-01", mainModule: "src/index.js", modules: { "src/index.js": ` export default { fetch() { return new Response("Hello from a dynamic Worker"); }, }; `, }, // Block all outbound network access from the Dynamic Worker. globalOutbound: null, }); return worker.getEntrypoint().fetch(request); },};
export default { async fetch(request: Request, env: Env): Promise<Response> { const worker = env.LOADER.load({ compatibilityDate: "2026-01-01", mainModule: "src/index.js", modules: { "src/index.js": ` export default { fetch() { return new Response("Hello from a dynamic Worker"); }, }; `, }, // Block all outbound network access from the Dynamic Worker. globalOutbound: null, }); return worker.getEntrypoint().fetch(request); },};
Helper libraries for Dynamic Workers
Here are 3 new libraries to help you build with Dynamic Workers:
@cloudflare/codemode ↗︎: Replace individual tool calls with a single code() tool, so LLMs write and execute TypeScript that orchestrates multiple API calls in one pass.
@cloudflare/worker-bundler ↗︎: Resolve npm dependencies and bundle source files into ready-to-load modules for Dynamic Workers, all at runtime.
@cloudflare/shell ↗︎: Give your agent a virtual filesystem inside a Dynamic Worker with persistent storage backed by SQLite and R2.
Try it out
Dynamic Workers Starter
Use this starter ↗︎ to deploy a Worker that can load and execute Dynamic Workers.
Dynamic Workers Playground
Deploy the Dynamic Workers Playground ↗︎ to write or import code, bundle it at runtime with @cloudflare/worker-bundler, execute it through a Dynamic Worker, and see real-time responses and execution logs.
Dynamic Workers pricing is based on three dimensions: Dynamic Workers created daily, requests, and CPU time.
Included
Additional usage
Dynamic Workers created daily
1,000 unique Dynamic Workers per month
+$0.002 per Dynamic Worker per day
Requests ¹
10 million per month
+$0.30 per million requests
CPU time ¹
30 million CPU milliseconds per month
+$0.02 per million CPU milliseconds
¹ Uses Workers Standard rates and will appear as part of your existing Workers bill, not as separate Dynamic Workers charges.
Note: Dynamic Workers requests and CPU time are already billed as part of your Workers plan and will count toward your Workers requests and CPU usage. The Dynamic Workers created daily charge is not yet active — you will not be billed for the number of Dynamic Workers created at this time. Pricing information is shared in advance so you can estimate future costs.
Cloudflare Access supports managed OAuth, which allows non-browser clients — such as CLIs, AI agents, SDKs, and scripts — to authenticate with Access-protected applications using a standard OAuth 2.0 authorization code flow.
Previously, non-browser clients that attempted to access a protected application received a 302 redirect to a login page they could not complete. The established workaround was cloudflared access curl, which required installing additional tooling.
With managed OAuth, clients instead receive a 401 response with a WWW-Authenticate header that points to Access's OAuth discovery endpoints (RFC 8414 ↗︎ and RFC 9728 ↗︎). The client opens the end user's browser to the Access login page. The end user authenticates with their identity provider, and the client receives an OAuth access token for subsequent requests.
Access enforces the same policies as a browser login; the OAuth layer is a new transport mechanism, not a separate authentication path.
Managed OAuth can be enabled on any self-hosted Access application or MCP server portal. It is opt-in for existing applications to avoid interfering with those that run their own OAuth servers and rely on their own WWW-Authenticate headers.
To enable managed OAuth, go to Zero Trust > Access controls > Applications, edit the application, and turn on Managed OAuth under Advanced settings.
You can also enable it via the API by setting oauth_configuration.enabled to true on the Access applications endpoint.
When Gateway routing is turned on, portal traffic appears in your Gateway HTTP logs. You can create Gateway HTTP policies with DLP profiles to detect and block sensitive data sent to upstream MCP servers.
To enable Gateway routing, go to Access controls > AI controls, edit the portal, and turn on Route traffic through Cloudflare Gateway under Basic information.
In the Cloudflare One dashboard, the overview page for a specific Cloudflare Tunnel now shows all replicas of that tunnel and supports streaming logs from multiple replicas at once.
Previously, you could only stream logs from one replica at a time. With this update:
Replicas on the tunnel overview — All active replicas for the selected tunnel now appear on that tunnel's overview page under Connectors. Select any replica to stream its logs.
Multi-connector log streaming — Stream logs from multiple replicas simultaneously, making it easier to correlate events across your infrastructure during debugging or incident response. To try it out, log in to Cloudflare One ↗︎ and go to Networks > Connectors > Cloudflare Tunnels. Select View logs next to the tunnel you want to monitor.
You can now manage Cloudflare Tunnels directly from Wrangler, the CLI for the Cloudflare Developer Platform. The new wrangler tunnel commands let you create, run, and manage tunnels without leaving your terminal.
Available commands:
wrangler tunnel create — Create a new remotely managed tunnel.
wrangler tunnel list — List all tunnels in your account.
wrangler tunnel info — Display details about a specific tunnel.
wrangler tunnel delete — Delete a tunnel.
wrangler tunnel run — Run a tunnel using the cloudflared daemon.
wrangler tunnel quick-start — Start a free, temporary tunnel without an account using Quick Tunnels.
Wrangler handles downloading and managing the cloudflared binary automatically. On first use, you will be prompted to download cloudflared to a local cache directory.
These commands are currently experimental and may change without notice.
You can now use a Workers binding to transform videos with Media Transformations. This allows you to resize, crop, extract frames, and extract audio from videos stored anywhere, even in private locations like R2 buckets.
The Media Transformations binding is useful when you want to:
Transform videos stored in private or protected sources
Optimize videos and store the output directly back to R2 for re-use
Extract still frames for classification or description with Workers AI
Extract audio tracks for transcription using Workers AI
To get started, add the Media binding to your Wrangler configuration:
Investigations now support unlimited result paging in both the dashboard and the API, removing the previous 1,000-record cap. Security teams can page through complete result sets when searching across large mail volumes, giving SOC analysts and automated workflows deeper visibility for forensics and threat hunting.
In the dashboard, infinite paging is now supported in the Investigations view. The 1,000-record ceiling has been removed, so you can navigate through the full result set directly in the UI. The Investigations API now returns up to 10,000 records per page (up from 1,000), with no cap on total result volume across pages.
For high-volume use cases, we recommend:
Logpush to a SIEM for full-fidelity datasets and long-term retention.
SOAR playbooks against the async bulk action API for large-scale remediation. Bulk actions initiated from the dashboard remain capped at 1,000 messages per action.
The Investigations API for report exports larger than 1,000 results, which is the dashboard download cap.
This release contains minor fixes and introduces a brand new visual style for the client interface. The new Cloudflare One Client interface changes connectivity management from a toggle to a button and brings useful connectivity settings to the home screen. The redesign also introduces a collapsible navigation bar. When expanded, more client information can be accessed including connectivity, settings, and device profile information. If you have any feedback or questions, visit the Cloudflare Community forum and let us know.
Changes and improvements
Empty MDM files are now rejected instead of being incorrectly accepted as a single MDM config.
Fixed an issue in proxy mode where the client could become unresponsive due to upstream connection timeouts.
Fixed emergency disconnect state from a previous organization incorrectly persisting after switching organizations.
Consumer-only CLI commands are now clearly distinguished from Zero Trust commands.
Added detailed QUIC connection metrics to diagnostic logs for better troubleshooting.
Added monitoring for tunnel statistics collection timeouts.
Switched tunnel congestion control algorithm to Cubic for improved reliability across platforms.
Fixed initiating managed network detection checks when no network is available, which caused device profile flapping.
Known issues
The client may become stuck in a Connecting state. To resolve this issue, reconnect the client by selecting Disconnect and then Connect in the client user interface. Alternatively, change the client's operation mode.
The client may display an empty white screen upon the device waking from sleep. To resolve this issue, exit and then open the client to re-launch it.
Canceling login during a single MDM configuration setup results in an empty page with no way to resume authentication. To work around this issue, exit and relaunch the client.
This release contains minor fixes and introduces a brand new visual style for the client interface. The new Cloudflare One Client interface changes connectivity management from a toggle to a button and brings useful connectivity settings to the home screen. The redesign also introduces a collapsible navigation bar. When expanded, more client information can be accessed including connectivity, settings, and device profile information. If you have any feedback or questions, visit the Cloudflare Community forum and let us know.
Changes and improvements
Consumer-only CLI commands are now clearly distinguished from Zero Trust commands.
Added detailed QUIC connection metrics to diagnostic logs for better troubleshooting.
Added monitoring for tunnel statistics collection timeouts.
Switched tunnel congestion control algorithm to Cubic for improved reliability across platforms.
Fixed packet capture failing on tunnel interface when the tunnel interface is renamed by SCCM VPN boundary support.
Fixed unnecessary registration deletion caused by RDP connections in multi-user mode.
Fixed increased tunnel interface start-up time due to a race between duplicate address detection (DAD) and disabling NetBT.
Fixed tunnel failing to connect when the system DNS search list contains unexpected characters.
Empty MDM files are now rejected instead of being incorrectly accepted as a single MDM config.
Fixed an issue in proxy mode where the client could become unresponsive due to upstream connection timeouts.
Fixed emergency disconnect state from a previous organization incorrectly persisting after switching organizations.
Fixed initiating managed network detection checks when no network is available, which caused device profile flapping.
Known issues
The client may unexpectedly terminate during captive portal login. To work around this issue, use a web browser to authenticate with the captive portal and then re-launch the client.
An error indicating that Microsoft Edge can't read and write to its data directory may be displayed during captive portal login; this error is benign and can be dismissed.
The client may become stuck in a Connecting state. To resolve this issue, reconnect the client by selecting Disconnect and then Connect in the client user interface. Alternatively, change the client's operation mode.
The client may display an empty white screen upon the device waking from sleep. To resolve this issue, exit and then open the client to re-launch it.
Canceling login during a single MDM configuration setup results in an empty page with no way to resume authentication. To work around this issue, exit and relaunch the client.
For Windows 11 24H2 users, Microsoft has confirmed a regression that may lead to performance issues like mouse lag, audio cracking, or other slowdowns. Cloudflare recommends users experiencing these issues upgrade to a minimum Windows 11 24H2 version KB5062553 or higher for resolution.
Devices with KB5055523 installed may receive a warning about Win32/ClickFix.ABA being present in the installer. To resolve this false positive, update Microsoft Security Intelligence to version 1.429.19.0 or later. This warning will be omitted from future release notes. This Microsoft Security Intelligence update was released in May 2025.
DNS resolution may be broken when the following conditions are all true:
The client is in Secure Web Gateway without DNS filtering (tunnel-only) mode.
A custom DNS server address is configured on the primary network adapter.
The custom DNS server address on the primary network adapter is changed while the client is connected.
To work around this issue, reconnect the client by selecting Disconnect and then Connect in the client user interface.
You can now use user risk scores in your Access policies. The new User Risk Score selector allows you to create Access policies that respond to user behavior patterns detected by Cloudflare's risk scoring system, including impossible travel, high DLP policy matches, and more.
Previously, proxy endpoints relied on static source IP addresses to authorize traffic, providing no user-level identity in logs or policies. The new authorization proxy replaces IP-based authorization with Cloudflare Access authentication, verifying who a user is before applying Gateway filtering without installing the WARP client.
This is ideal for environments where you cannot deploy a device client, such as virtual desktops (VDI), mergers and acquisitions, or compliance-restricted endpoints.
Key capabilities
Identity-aware proxy traffic — Users authenticate through your identity provider (Okta, Microsoft Entra ID, Google Workspace, and others) via Cloudflare Access. Logs now show exactly which user accessed which site, and you can write identity-based policies like "only the Finance team can access this accounting tool."
Multiple identity providers — Display one or multiple login methods simultaneously, giving flexibility for organizations managing users across different identity systems.
Cloudflare-hosted PAC files — Create and host PAC files directly in Cloudflare One with pre-configured templates for Okta and Azure, hosted at https://pac.cloudflare-gateway.com/<account-id>/<slug> on Cloudflare's global network.
Simplified billing — Each user occupies a seat, exactly like they do with the Cloudflare One Client. No new metrics to track.
Get started
In Cloudflare One ↗︎, go to Networks > Resolvers & Proxies > Proxy endpoints.
You can now copy Cloudflare One resources as JSON or as a ready-to-use API POST request directly from the dashboard. This makes it simple to transition workflows into API calls, automation scripts, or infrastructure-as-code pipelines.
To use this feature, click the overflow menu (⋮) on any supported resource and select Copy as JSON or Copy as POST request. The copied output includes only the fields present on your resource, giving you a clean and minimal starting point for your own API calls.
Initially supported resources:
Access applications
Access policies
Gateway policies
Resolver policies
Service tokens
Identity providers
We will continue to add support for more resources throughout 2026.
You can now configure clipboard controls for browser-based RDP with Cloudflare Access. Clipboard controls allow administrators to restrict whether users can copy or paste text between their local machine and the remote Windows server.
This feature is useful for organizations that support bring-your-own-device (BYOD) policies or third-party contractors using unmanaged devices. By restricting clipboard access, you can prevent sensitive data from being transferred out of the remote session to a user's personal device.
Configuration options
Clipboard controls are configured per policy within your Access application. For each policy, you can independently allow or deny:
Copy from local client to remote RDP session — Users can copy/paste text from their local machine into the browser-based RDP session.
Copy from remote RDP session to local client — Users can copy/paste text from the browser-based RDP session to their local machine.
By default, both directions are denied for new policies. For existing Access applications created before this feature was available, clipboard access remains enabled to preserve backwards compatibility.
When a user attempts a restricted clipboard action, the clipboard content is replaced with an error message informing them that the action is not allowed.
MCP server portals now supports Logpush integration. You can automatically export MCP server portal activity logs to third-party storage destinations or security information and event management (SIEM) tools for analysis and auditing.
Available log fields
The MCP server portal logs dataset includes fields such as:
Datetime — Timestamp of the request
PortalID / PortalAUD — Portal identifiers
ServerID / ServerURL — Upstream MCP server details
Method — JSON-RPC method (for example, tools/call, prompts/get, resources/read)
Gateway Protocol Detection now supports seven additional protocols in beta:
Protocol
Notes
IMAP
Internet Message Access Protocol — email retrieval
POP3
Post Office Protocol v3 — email retrieval
SMTP
Simple Mail Transfer Protocol — email sending
MYSQL
MySQL database wire protocol
RSYNC-DAEMON
rsync daemon protocol
LDAP
Lightweight Directory Access Protocol
NTP
Network Time Protocol
These protocols join the existing set of detected protocols (HTTP, HTTP2, SSH, TLS, DCERPC, MQTT, and TPKT) and can be used with the Detected Protocol selector in Network policies to identify and filter traffic based on the application-layer protocol, without relying on port-based identification.
If protocol detection is enabled on your account, these protocols will automatically be logged when detected in your Gateway network traffic.
Pywrangler ↗︎, the CLI tool for managing Python Workers and packages,
now supports Windows, allowing you to develop and deploy Python Workers from Windows environments.
Previously, Pywrangler was only available on macOS and Linux.
You can install and use Pywrangler on Windows the same way you would on other platforms.
Specify your Worker's Python dependencies in your pyproject.toml file,
then use the following commands to develop and deploy:
All existing Pywrangler functionality, including package management, local development, and deployment, works on Windows without any additional configuration.
Requirements
This feature requires the following minimum versions:
wrangler >= 4.64.0
workers-py >= 1.72.0
uv >= 0.9.28
To upgrade workers-py (which includes Pywrangler) in your project, run:
uv tool upgrade workers-py
To upgrade wrangler, run:
npm install -g wrangler@latest
To upgrade uv, run:
uv self update
To get started with Python Workers on Windows, refer to the Python packages documentation for full details on Pywrangler.
Workers Observability now includes a query language that lets you write structured queries directly in the search bar to filter your logs and traces. The search bar doubles as a free text search box — type any term to search across all metadata and attributes, or write field-level queries for precise filtering.
Queries written in the search bar sync with the Query Builder sidebar, so you can write a query by hand and then refine it visually, or build filters in the Query Builder and see the corresponding query syntax. The search bar provides autocomplete suggestions for metadata fields and operators as you type.
The query language supports:
Free text search — search everywhere with a keyword like error, or match an exact phrase with "exact phrase"
Field queries — filter by specific fields using comparison operators (for example, status = 500 or $workers.wallTimeMs > 100)
Operators — =, !=, >, >=, <, <=, and : (contains)
Functions — contains(field, value), startsWith(field, prefix), regex(field, pattern), and exists(field)
Boolean logic — add conditions with AND, OR, and NOT
Select the help icon next to the search bar to view the full syntax reference, including all supported operators, functions, and keyboard shortcuts.