Author: guillaume

  • Workflows, Workers – Run Workflows inside Dynamic Workers with the @cloudflare/dynamic-workflows library

    You can now use @cloudflare/dynamic-workflows to run a Workflow inside a Dynamic Worker, ensuring durable execution for code that is loaded at runtime.

    The Worker Loader loads Dynamic Workers on demand, which previously made durability challenging. Even within a Dynamic Worker, a Workflow might sleep for hours or days between steps, and by the time it resumes, the original Dynamic Worker code would no longer be in memory.

    The library solves this by tagging each Workflow instance with metadata that identifies which Dynamic Worker to load — for example, a tenant ID — then reloading the matching Dynamic Worker through the Worker Loader whenever a Workflow awakens.

    Because Dynamic Workers are created on-demand, you do not have to register each Workflow up front or manage them individually. Load the Workflow code in the Dynamic Worker when it is needed, and the Workflows engine handles persistence and retries behind the scenes. Your Workflow code itself is unaffected by the routing and behaves as normal.

    This unlocks patterns where the Workflow code itself is dynamic. For example, this is useful with:

    • SaaS platforms where each tenant defines their own automation, such as onboarding sequences, approval chains, or billing retry logic.
    • AI agent frameworks where agents generate and execute multi-step plans at runtime, surviving restarts and waiting for human approval between tool calls.
    • Multi-tenant job systems where each customer submits their own processing logic and every step persists progress and retries on failure.
    import {
    createDynamicWorkflowEntrypoint,
    DynamicWorkflowBinding,
    wrapWorkflowBinding,
    type WorkflowRunner,
    } from "@cloudflare/dynamic-workflows";
    export { DynamicWorkflowBinding };
    interface Env {
    WORKFLOWS: Workflow;
    LOADER: WorkerLoader;
    }
    function loadTenant(env: Env, tenantId: string) {
    return env.LOADER.get(tenantId, async () => ({
    compatibilityDate: "2026-01-01",
    mainModule: "index.js",
    modules: { "index.js": await fetchTenantCode(tenantId) },
    // The Dynamic Worker uses this exactly like a real Workflow binding;
    // every create() is tagged with { tenantId } automatically.
    env: { WORKFLOWS: wrapWorkflowBinding({ tenantId }) },
    }));
    }
    // The entrypoint name must match `class_name` in the workflows binding of your Wrangler config file.
    export const DynamicWorkflow = createDynamicWorkflowEntrypoint<Env>(
    async ({ env, metadata }) => {
    const stub = loadTenant(env, metadata.tenantId as string);
    return stub.getEntrypoint("TenantWorkflow") as unknown as WorkflowRunner;
    },
    );
    export default {
    fetch(request: Request, env: Env) {
    const tenantId = request.headers.get("x-tenant-id")!;
    return loadTenant(env, tenantId).getEntrypoint().fetch(request);
    },
    };

    For a full walkthrough, refer to the Dynamic Workflows guide.

  • Workflows, Workers – Run Workflows inside Dynamic Workers with the @cloudflare/dynamic-workflows library

    You can now use @cloudflare/dynamic-workflows to run a Workflow inside a Dynamic Worker, ensuring durable execution for code that is loaded at runtime.

    The Worker Loader loads Dynamic Workers on demand, which previously made durability challenging. Even within a Dynamic Worker, a Workflow might sleep for hours or days between steps, and by the time it resumes, the original Dynamic Worker code would no longer be in memory.

    The library solves this by tagging each Workflow instance with metadata that identifies which Dynamic Worker to load — for example, a tenant ID — then reloading the matching Dynamic Worker through the Worker Loader whenever a Workflow awakens.

    Because Dynamic Workers are created on-demand, you do not have to register each Workflow up front or manage them individually. Load the Workflow code in the Dynamic Worker when it is needed, and the Workflows engine handles persistence and retries behind the scenes. Your Workflow code itself is unaffected by the routing and behaves as normal.

    This unlocks patterns where the Workflow code itself is dynamic. For example, this is useful with:

    • SaaS platforms where each tenant defines their own automation, such as onboarding sequences, approval chains, or billing retry logic.
    • AI agent frameworks where agents generate and execute multi-step plans at runtime, surviving restarts and waiting for human approval between tool calls.
    • Multi-tenant job systems where each customer submits their own processing logic and every step persists progress and retries on failure.
    import {
    createDynamicWorkflowEntrypoint,
    DynamicWorkflowBinding,
    wrapWorkflowBinding,
    type WorkflowRunner,
    } from "@cloudflare/dynamic-workflows";
    export { DynamicWorkflowBinding };
    interface Env {
    WORKFLOWS: Workflow;
    LOADER: WorkerLoader;
    }
    function loadTenant(env: Env, tenantId: string) {
    return env.LOADER.get(tenantId, async () => ({
    compatibilityDate: "2026-01-01",
    mainModule: "index.js",
    modules: { "index.js": await fetchTenantCode(tenantId) },
    // The Dynamic Worker uses this exactly like a real Workflow binding;
    // every create() is tagged with { tenantId } automatically.
    env: { WORKFLOWS: wrapWorkflowBinding({ tenantId }) },
    }));
    }
    // The entrypoint name must match `class_name` in the workflows binding of your Wrangler config file.
    export const DynamicWorkflow = createDynamicWorkflowEntrypoint<Env>(
    async ({ env, metadata }) => {
    const stub = loadTenant(env, metadata.tenantId as string);
    return stub.getEntrypoint("TenantWorkflow") as unknown as WorkflowRunner;
    },
    );
    export default {
    fetch(request: Request, env: Env) {
    const tenantId = request.headers.get("x-tenant-id")!;
    return loadTenant(env, tenantId).getEntrypoint().fetch(request);
    },
    };

    For a full walkthrough, refer to the Dynamic Workflows guide.

  • Workflows, Workers – Run Workflows inside Dynamic Workers with the @cloudflare/dynamic-workflows library

    You can now use @cloudflare/dynamic-workflows to run a Workflow inside a Dynamic Worker, ensuring durable execution for code that is loaded at runtime.

    The Worker Loader loads Dynamic Workers on demand, which previously made durability challenging. Even within a Dynamic Worker, a Workflow might sleep for hours or days between steps, and by the time it resumes, the original Dynamic Worker code would no longer be in memory.

    The library solves this by tagging each Workflow instance with metadata that identifies which Dynamic Worker to load — for example, a tenant ID — then reloading the matching Dynamic Worker through the Worker Loader whenever a Workflow awakens.

    Because Dynamic Workers are created on-demand, you do not have to register each Workflow up front or manage them individually. Load the Workflow code in the Dynamic Worker when it is needed, and the Workflows engine handles persistence and retries behind the scenes. Your Workflow code itself is unaffected by the routing and behaves as normal.

    This unlocks patterns where the Workflow code itself is dynamic. For example, this is useful with:

    • SaaS platforms where each tenant defines their own automation, such as onboarding sequences, approval chains, or billing retry logic.
    • AI agent frameworks where agents generate and execute multi-step plans at runtime, surviving restarts and waiting for human approval between tool calls.
    • Multi-tenant job systems where each customer submits their own processing logic and every step persists progress and retries on failure.
    import {
    createDynamicWorkflowEntrypoint,
    DynamicWorkflowBinding,
    wrapWorkflowBinding,
    type WorkflowRunner,
    } from "@cloudflare/dynamic-workflows";
    export { DynamicWorkflowBinding };
    interface Env {
    WORKFLOWS: Workflow;
    LOADER: WorkerLoader;
    }
    function loadTenant(env: Env, tenantId: string) {
    return env.LOADER.get(tenantId, async () => ({
    compatibilityDate: "2026-01-01",
    mainModule: "index.js",
    modules: { "index.js": await fetchTenantCode(tenantId) },
    // The Dynamic Worker uses this exactly like a real Workflow binding;
    // every create() is tagged with { tenantId } automatically.
    env: { WORKFLOWS: wrapWorkflowBinding({ tenantId }) },
    }));
    }
    // The entrypoint name must match `class_name` in the workflows binding of your Wrangler config file.
    export const DynamicWorkflow = createDynamicWorkflowEntrypoint<Env>(
    async ({ env, metadata }) => {
    const stub = loadTenant(env, metadata.tenantId as string);
    return stub.getEntrypoint("TenantWorkflow") as unknown as WorkflowRunner;
    },
    );
    export default {
    fetch(request: Request, env: Env) {
    const tenantId = request.headers.get("x-tenant-id")!;
    return loadTenant(env, tenantId).getEntrypoint().fetch(request);
    },
    };

    For a full walkthrough, refer to the Dynamic Workflows guide.

  • Workflows, Workers – Run Workflows inside Dynamic Workers with the @cloudflare/dynamic-workflows library

    You can now use @cloudflare/dynamic-workflows to run a Workflow inside a Dynamic Worker, ensuring durable execution for code that is loaded at runtime.

    The Worker Loader loads Dynamic Workers on demand, which previously made durability challenging. Even within a Dynamic Worker, a Workflow might sleep for hours or days between steps, and by the time it resumes, the original Dynamic Worker code would no longer be in memory.

    The library solves this by tagging each Workflow instance with metadata that identifies which Dynamic Worker to load — for example, a tenant ID — then reloading the matching Dynamic Worker through the Worker Loader whenever a Workflow awakens.

    Because Dynamic Workers are created on-demand, you do not have to register each Workflow up front or manage them individually. Load the Workflow code in the Dynamic Worker when it is needed, and the Workflows engine handles persistence and retries behind the scenes. Your Workflow code itself is unaffected by the routing and behaves as normal.

    This unlocks patterns where the Workflow code itself is dynamic. For example, this is useful with:

    • SaaS platforms where each tenant defines their own automation, such as onboarding sequences, approval chains, or billing retry logic.
    • AI agent frameworks where agents generate and execute multi-step plans at runtime, surviving restarts and waiting for human approval between tool calls.
    • Multi-tenant job systems where each customer submits their own processing logic and every step persists progress and retries on failure.
    import {
    createDynamicWorkflowEntrypoint,
    DynamicWorkflowBinding,
    wrapWorkflowBinding,
    type WorkflowRunner,
    } from "@cloudflare/dynamic-workflows";
    export { DynamicWorkflowBinding };
    interface Env {
    WORKFLOWS: Workflow;
    LOADER: WorkerLoader;
    }
    function loadTenant(env: Env, tenantId: string) {
    return env.LOADER.get(tenantId, async () => ({
    compatibilityDate: "2026-01-01",
    mainModule: "index.js",
    modules: { "index.js": await fetchTenantCode(tenantId) },
    // The Dynamic Worker uses this exactly like a real Workflow binding;
    // every create() is tagged with { tenantId } automatically.
    env: { WORKFLOWS: wrapWorkflowBinding({ tenantId }) },
    }));
    }
    // The entrypoint name must match `class_name` in the workflows binding of your Wrangler config file.
    export const DynamicWorkflow = createDynamicWorkflowEntrypoint<Env>(
    async ({ env, metadata }) => {
    const stub = loadTenant(env, metadata.tenantId as string);
    return stub.getEntrypoint("TenantWorkflow") as unknown as WorkflowRunner;
    },
    );
    export default {
    fetch(request: Request, env: Env) {
    const tenantId = request.headers.get("x-tenant-id")!;
    return loadTenant(env, tenantId).getEntrypoint().fetch(request);
    },
    };

    For a full walkthrough, refer to the Dynamic Workflows guide.

  • Cloudflare One, Cloudflare WAN – Post-quantum IPsec interoperability with third-party devices

    Cloudflare IPsec now supports post-quantum key agreement with compatible third-party devices. Cisco and Fortinet are the first third-party vendors validated to interoperate with Cloudflare IPsec using ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism).

    Post-quantum IPsec uses RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem to negotiate hybrid key agreement during the IKEv2 IKE_INTERMEDIATE phase. This combines classical Diffie-Hellman (Group 20) with ML-KEM-768 or ML-KEM-1024 to protect against harvest-now, decrypt-later attacks.

    Key details:

    • Compatible with Cisco 8000 Series Secure Routers with IOS XR Release 26.1.1 and Fortinet FortiOS 7.6.6 and later.
    • Uses ML-KEM-768 or ML-KEM-1024 as an additional Key Exchange to DH Group 20.
    • Follows RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem standards.
    • No additional licensing required.

    Post-quantum IPsec with third-party devices is now generally available with confirmed interoperability for the platforms listed above. Cloudflare intends to support interoperability with more vendors as they build out support for draft-ietf-ipsecme-ikev2-mlkem. Contact your account team to discuss support for additional vendors.

    For supported key exchange methods and the list of validated platforms, refer to GRE and IPsec tunnels.

  • Data Loss Prevention – Classify sensitive content with Data Classification

    Cloudflare DLP now includes Data Classification, which lets administrators organize and label sensitive content using labels, templates, and reusable data classes.

    With Data Classification, administrators can define labels such as sensitivity schemas and levels, and data tag groups and tags. Administrators can also build from Cloudflare-managed templates and create reusable data classes that combine detection entries, other data classes, sensitivity levels, and data tags.

    You can then use those classifications in custom DLP profiles to identify the severity of sensitive content, understand where it exists, and apply that logic consistently across DLP profiles.

    For more information, refer to Data Classification.

  • R2 – Empty buckets and delete folders from the R2 dashboard

    You can now empty an entire R2 bucket or delete folders directly from the dashboard. Emptying a bucket is required before you can delete it. Previously, this required scripting or configuring lifecycle rules. Now, the dashboard can handle it in a single action.

    Empty a bucket

    Go to your bucket’s Settings tab and select Empty under the Empty Bucket section. This deletes all objects in the bucket while preserving the bucket and its configuration. For large buckets, the operation runs in the background and the dashboard displays progress.

    Emptying a bucket is also a prerequisite for deleting it. The dashboard now guides you through both steps in one place.

    Empty Bucket and Delete Bucket sections in the R2 dashboard Settings tab

    Delete folders

    R2 uses a flat object structure. The dashboard groups objects that share a common prefix into folders when the View prefixes as directories checkbox is selected. Deleting a folder removes every object under that prefix.

    From the Objects tab, you can select one or more folders and delete them alongside individual objects.

    For step-by-step instructions, refer to Delete buckets and Delete objects.

  • Cloudflare One, Cloudflare WAN – Post-quantum IPsec interoperability with third-party devices

    Cloudflare IPsec now supports post-quantum key agreement with compatible third-party devices. Cisco and Fortinet are the first third-party vendors validated to interoperate with Cloudflare IPsec using ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism).

    Post-quantum IPsec uses RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem to negotiate hybrid key agreement during the IKEv2 IKE_INTERMEDIATE phase. This combines classical Diffie-Hellman (Group 20) with ML-KEM-768 or ML-KEM-1024 to protect against harvest-now, decrypt-later attacks.

    Key details:

    • Compatible with Cisco 8000 Series Secure Routers with IOS XR Release 26.1.1 and Fortinet FortiOS 7.6.6 and later.
    • Uses ML-KEM-768 or ML-KEM-1024 as an additional Key Exchange to DH Group 20.
    • Follows RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem standards.
    • No additional licensing required.

    Post-quantum IPsec with third-party devices is now generally available with confirmed interoperability for the platforms listed above. Cloudflare intends to support interoperability with more vendors as they build out support for draft-ietf-ipsecme-ikev2-mlkem. Contact your account team to discuss support for additional vendors.

    For supported key exchange methods and the list of validated platforms, refer to GRE and IPsec tunnels.

  • R2 – Empty buckets and delete folders from the R2 dashboard

    You can now empty an entire R2 bucket or delete folders directly from the dashboard. Emptying a bucket is required before you can delete it. Previously, this required scripting or configuring lifecycle rules. Now, the dashboard can handle it in a single action.

    Empty a bucket

    Go to your bucket’s Settings tab and select Empty under the Empty Bucket section. This deletes all objects in the bucket while preserving the bucket and its configuration. For large buckets, the operation runs in the background and the dashboard displays progress.

    Emptying a bucket is also a prerequisite for deleting it. The dashboard now guides you through both steps in one place.

    Empty Bucket and Delete Bucket sections in the R2 dashboard Settings tab

    Delete folders

    R2 uses a flat object structure. The dashboard groups objects that share a common prefix into folders when the View prefixes as directories checkbox is selected. Deleting a folder removes every object under that prefix.

    From the Objects tab, you can select one or more folders and delete them alongside individual objects.

    For step-by-step instructions, refer to Delete buckets and Delete objects.

  • Cloudflare One, Cloudflare WAN – Post-quantum IPsec interoperability with third-party devices

    Cloudflare IPsec now supports post-quantum key agreement with compatible third-party devices. Cisco and Fortinet are the first third-party vendors validated to interoperate with Cloudflare IPsec using ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism).

    Post-quantum IPsec uses RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem to negotiate hybrid key agreement during the IKEv2 IKE_INTERMEDIATE phase. This combines classical Diffie-Hellman (Group 20) with ML-KEM-768 or ML-KEM-1024 to protect against harvest-now, decrypt-later attacks.

    Key details:

    • Compatible with Cisco 8000 Series Secure Routers with IOS XR Release 26.1.1 and Fortinet FortiOS 7.6.6 and later.
    • Uses ML-KEM-768 or ML-KEM-1024 as an additional Key Exchange to DH Group 20.
    • Follows RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem standards.
    • No additional licensing required.

    Post-quantum IPsec with third-party devices is now generally available with confirmed interoperability for the platforms listed above. Cloudflare intends to support interoperability with more vendors as they build out support for draft-ietf-ipsecme-ikev2-mlkem. Contact your account team to discuss support for additional vendors.

    For supported key exchange methods and the list of validated platforms, refer to GRE and IPsec tunnels.