
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sun, 12 Apr 2026 17:05:50 GMT</lastBuildDate>
        <item>
            <title><![CDATA[How Cloudflare runs more AI models on fewer GPUs: A technical deep-dive ]]></title>
            <link>https://blog.cloudflare.com/how-cloudflare-runs-more-ai-models-on-fewer-gpus/</link>
            <pubDate>Wed, 27 Aug 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare built an internal platform called Omni. This platform uses lightweight isolation and memory over-commitment to run multiple AI models on a single GPU. ]]></description>
            <content:encoded><![CDATA[ <p>As the demand for AI products grows, developers are creating and tuning a wider variety of models. While adding new models to our <a href="https://developers.cloudflare.com/workers-ai/models/"><u>growing catalog</u></a> on Workers AI, we noticed that not all of them are used equally – leaving infrequently used models occupying valuable GPU space. Efficiency is a core value at Cloudflare, and with GPUs being the scarce commodity they are, we realized that we needed to build something to fully maximize our GPU usage.</p><p>Omni is an internal platform we’ve built for running and managing AI models on Cloudflare’s edge nodes. It does so by spawning and managing multiple models on a single machine and GPU using lightweight isolation. Omni makes it easy and efficient to run many small and/or low-volume models, combining multiple capabilities by:  </p><ul><li><p>Spawning multiple models from a single control plane,</p></li><li><p>Implementing lightweight process isolation, allowing models to spin up and down quickly,</p></li><li><p>Isolating the file system between models to easily manage per-model dependencies, and</p></li><li><p>Over-committing GPU memory to run more models on a single GPU.</p></li></ul><p>Cloudflare aims to place GPUs as close as we possibly can to people and applications that are using them. With Omni in place, we’re now able to run more models on every node in our network, improving model availability, minimizing latency, and reducing power consumed by idle GPUs.</p><p>Here’s how. </p>
    <div>
      <h2>Omni’s architecture – at a glance</h2>
      <a href="#omnis-architecture-at-a-glance">
        
      </a>
    </div>
    <p>At a high level, Omni is a platform to run AI models. When an <a href="https://www.cloudflare.com/learning/ai/inference-vs-training/"><u>inference</u></a> request is made on Workers AI, we load the model’s configuration from <a href="https://developers.cloudflare.com/kv/"><u>Workers KV</u></a> and our routing layer forwards it to the closest Omni instance that has available capacity. For inferences using the <a href="https://developers.cloudflare.com/workers-ai/features/batch-api/"><u>Asynchronous Batch API</u></a>, we route to an Omni instance that is idle, which is typically in a location where it’s night.</p><p>Omni runs a few checks on the inference request, runs model specific pre and post processing, then hands the request over to the model.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4zlObplZsGgpxPyUoD5NXe/ddd1cb8af444460d54fa5e0ab6e58c87/1.png" />
          </figure>
    <div>
      <h2>Elastic scaling by spawning multiple models from a single control plane</h2>
      <a href="#elastic-scaling-by-spawning-multiple-models-from-a-single-control-plane">
        
      </a>
    </div>
    <p>If you’re developing an AI application, a typical setup is having a container or a VM dedicated to running a single model with a GPU attached to it. This is simple. But it’s also heavy-handed — because it requires managing the entire stack from provisioning the VM, installing GPU drivers, downloading model weights, and managing the Python environment. At scale, managing infrastructure this way is incredibly time consuming and often requires an entire team. </p><p>If you’re using Workers AI, we handle all of this for you. Omni uses a single control plane for running multiple models, called the scheduler, which automatically provisions models and spawns new instances as your traffic scales. When starting a new model instance, it downloads model weights, Python code, and any other dependencies. Omni’s scheduler provides fine-grained control and visibility over the model’s lifecycle: it receives incoming inference requests and routes them to the corresponding model processes, being sure to distribute the load between multiple GPUs. It then makes sure the model processes are running, rolls out new versions as they are released, and restarts itself when detecting errors or failure states. It also collects metrics for billing and emits logs.</p><p>The inference itself is done by a per-model process, supervised by the scheduler. It receives the inference request and some metadata, then sends back a response. Depending on the model, the response can be various types; for instance, a JSON object or a SSE stream for text generation, or binary for image generation.</p><p>The scheduler and the child processes communicate by passing messages over Inter-Process Communication (IPC). Usually the inference request is buffered in the scheduler for applying features, like prompt templating or tool calling, before the request is passed to the child process. For potentially large binary requests, the scheduler hands over the underlying TCP connection to the child process for consuming the request body directly.</p>
    <div>
      <h2>Implementing lightweight process and Python isolation</h2>
      <a href="#implementing-lightweight-process-and-python-isolation">
        
      </a>
    </div>
    <p>Typically, deploying a model requires its own dedicated container, but we want to colocate more models on a single container to conserve memory and GPU capacity. In order to do so, we needed finer-grained controls over CPU memory and the ability to isolate a model from its dependencies and environment. We deploy Omni in two configurations; a container running multiple models or bare metal running a single model. In both cases, process isolation and Python virtual environments allow us to isolate models with different dependencies by creating namespaces and are limited by <a href="https://en.wikipedia.org/wiki/Cgroups"><u>cgroups</u></a>. </p><p>Python doesn’t take into account cgroups memory limits for memory allocations, which can lead to OOM errors. Many AI Python libraries rely on <a href="https://pypi.org/project/psutil/"><u>psutil</u></a> for pre-allocating CPU memory. psutil reads /proc/meminfo to determine how much memory is available. Since in Omni each model has its own configurable memory limits, we need psutil to reflect the current usage and limits for a given model, not for the entire system.</p><p>The solution for us was to create a virtual file system, using <a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace"><u>fuse</u></a>, to mount our own version of /proc/meminfo which reflects the model’s current usage and limits.</p><p>To illustrate this, here’s an Omni instance running a model (running as pid 8). If we enter the mount namespace and look at /proc/meminfo it will reflect the model’s configuration:</p>
            <pre><code># Enter the mount (file system) namespace of a child process
$ nsenter -t 8 -m

$ mount
...
none /proc/meminfo fuse ...

$ cat /proc/meminfo
MemTotal:     7340032 kB
MemFree:     7316388 kB
MemAvailable:     7316388 kB</code></pre>
            <p>In this case the model has 7Gib of memory available and the entire container 15Gib. If the model tries to allocate more than 7Gib of memory, it will be OOM killed and restarted by the scheduler’s process manager, without causing any problems to the other models.</p><p>For isolating Python and some system dependencies, each model runs in a Python virtual environment, managed by <a href="https://docs.astral.sh/uv/"><u>uv</u></a>. Dependencies are cached on the machine and, if possible, shared between models (uv uses symbolic links between its cache and virtual environments).</p><p>Also separated processes for models allows to have different CUDA contexts and isolation for error recovery. </p>
    <div>
      <h2>Over-committing memory to run more models on a single GPU</h2>
      <a href="#over-committing-memory-to-run-more-models-on-a-single-gpu">
        
      </a>
    </div>
    <p>Some models don’t receive enough traffic to fully utilize a GPU, and with Omni we can pack more models on a single GPU, freeing up capacity for other workloads. When it comes to GPU memory management, Omni has two main jobs: safely over-commit GPU memory, so that more models than normal can share a single GPU, and enforce memory limits, to prevent any single model from running out of memory while running.      </p><p>Over-committing memory means allocating more memory than is physically available to the device. </p><p>For example, if a GPU has 10 Gib of memory, Omni would allow 2 models of 10Gib each on that GPU.</p><p>Right now, Omni is configured to run 13 models and is allocating about 400% GPU memory on a single GPU, saving up 4 GPUs. Omni does this by injecting a CUDA stub library that intercepts CUDA memory allocations (cuMalloc* or cudaMalloc*) calls and forces memory allocations to be performed in <a href="https://developer.nvidia.com/blog/unified-memory-in-cuda-6/"><u>unified memory mode</u></a>.</p><p>In Unified memory mode CUDA shares the same memory address space for both the GPU and the CPU:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2G5zd0TDi15ZeFAmcJy812/1b292429140ec2c4bd0a81bee4954150/2.png" />
          </figure><p><sup><i>CUDA’s </i></sup><a href="https://developer.nvidia.com/blog/maximizing-unified-memory-performance-cuda/"><sup><i><u>unified memory mode</u></i></sup></a><sup><i> </i></sup></p><p>In practice this is what memory over-commitment looks like: imagine 3 models (A, B and C). Models A+B fit in the GPU’s memory but C takes up the entire memory.</p><ol><li><p>Models A+B are loaded first and are in GPU memory, while model C is in CPU memory</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/xU141x0PaZRp83XlF6hWz/527915ee03309f619a64e6b43c62cd92/3.png" />
          </figure></li><li><p>Omni receives a request for model C so models A+B are swapped out and C is swapped in.
</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4fD3Y2xyyawGmo1gpdLsQz/1cd36ebaed6b7f9e95b3d31ead1c1098/4.png" />
          </figure></li><li><p>Omni receives a request for model B, so model C is partly swapped out and model B is swapped back in.
</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2v5JjDW0NCkVUfEBXwIgpL/62009bc970b0967a850cb31ef87be44b/5.png" />
          </figure></li><li><p>Omni receives a request for model A, so model A is swapped back in and model C is completely swapped out.</p></li></ol>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3cWGbEGgv3QckT7jgUIs9d/2c500a432be451a83dce0c71ccdcb89f/6.png" />
          </figure><p>The trade-off is added latency: if performing an inference requires memory that is currently on the host system, it must be transferred to the GPU. For smaller models, this latency is minimal, because with PCIe 4.0, the physical bus between your GPU and system, provides 32 GB/sec of bandwidth. On the other hand, if a model need to be “cold started” i.e. it’s been swapped out because it hasn’t been used in a while, the system may need to swap back the entire model – a larger sized model, for example, might use 5Gib of GPU memory for weights and caches, and would take ~156ms to be swapped back into the GPU. Naturally, over time, inactive models are put into CPU memory, while active models stay hot in the GPU.</p><p>Rather than allowing the model to choose how much GPU memory it uses, AI frameworks tend to pre-allocate as much GPU memory as possible for performance reasons, making co-locating models more complicated. Omni allows us to control how much memory is actually exposed to any given model to prevent a greedy model from over-using the GPU allocated to it. We do this by overriding the CUDA runtime and driver APIs (<a href="https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY_1g376b97f5ab20321ca46f7cfa9511b978"><u>cudaMemGetInfo</u></a> and <a href="https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__MEM.html#group__CUDA__MEM_1g808f555540d0143a331cc42aa98835c0"><u>cuMemGetInfo</u></a>). Instead of exposing the entire GPU memory, we only expose a subset of memory to each model.</p>
    <div>
      <h2>How Omni runs multiple models for Workers AI </h2>
      <a href="#how-omni-runs-multiple-models-for-workers-ai">
        
      </a>
    </div>
    <p>AI models can run in a variety of inference engines or backends: <a href="https://github.com/vllm-project/vllm"><u>vLLM</u></a>, Python, and now our very own inference engine, <a href="http://blog.cloudflare.com/cloudflares-most-efficient-ai-inference-engine/"><u>Infire</u></a>. While models have different capabilities, each model needs to support <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI features</u></a>, like batching and function calling. Omni acts as a unified layer for integrating these systems. It integrates into our internal routing and scheduling systems, and provides a Python API for our engineering team to add new models more easily. Let’s take a closer look at how Omni does this in practice:</p>
            <pre><code>from omni import Response
import cowsay


def handle_request(request, context):
    try:
        json = request.body.json
        text = json["text"]
    except Exception as err:
        return Response.error(...)

    return cowsay.get_output_string('cow', text)</code></pre>
            <p>Similar to how a JavaScript Worker works, Omni calls a request handler, running the model’s logic and returning a response. </p><p>Omni installs Python dependencies at model startup. We run an internal Python registry and mirror the public registry. In either case we declare dependencies in requirements.txt:</p>
            <pre><code>cowsay==6.1</code></pre>
            <p>The handle_request function can be async and return different Python types, including <a href="https://docs.pydantic.dev/latest/"><u>pydantic</u></a> objects. Omni will convert the return value into a Workers AI response for the eyeball.</p><p>A Python package is injected, named omni, containing all the Python APIs to interact with the request, the Workers AI systems, building Responses, error handling, etc. Internally we publish it as regular Python package to be used in standalone, for unit testing for instance:</p>
            <pre><code>from omni import Context, Request
from model import handle_request


def test_basic():
    ctx = Context.inactive()
    req = Request(json={"text": "my dog is cooler than you!"})
    out = handle_request(req, ctx)
    assert out == """  __________________________
| my dog is cooler than you! |
  ==========================
                          \\
                           \\
                             ^__^
                             (oo)\\_______
                             (__)\\       )\\/\\
                                 ||----w |
                                 ||     ||"""</code></pre>
            
    <div>
      <h2>What’s next </h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Omni allows us to run models more efficiently by spawning them from a single control plane and implementing lightweight process isolation. This enables quick starting and stopping of models, isolated file systems for managing Python and system dependencies, and over-committing GPU memory to run more models on a single GPU. This improves the performance for our entire Workers AI stack, reduces the cost of running GPUs, and allows us to ship new models and features quickly and safely.</p><p>Right now, Omni is running in production on a handful of models in the Workers AI catalog, and we’re adding more every week. Check out <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a> today to experience Omni’s performance benefits on your AI application. </p> ]]></content:encoded>
            <category><![CDATA[AI Week]]></category>
            <category><![CDATA[AI]]></category>
            <guid isPermaLink="false">KjxPspfQBaaHQ5K8ALjv8</guid>
            <dc:creator>Sven Sauleau</dc:creator>
            <dc:creator>Mari Galicer</dc:creator>
        </item>
        <item>
            <title><![CDATA[Using Fortran on Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/using-fortran-on-cloudflare-workers/</link>
            <pubDate>Tue, 07 May 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ Work on LLVM has enabled Fortran to compile to WebAssembly. So, today, we’re writing about running Fortran code on Cloudflare Workers ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In April 2020, we blogged about how to get <a href="/cloudflare-workers-now-support-cobol">COBOL running on Cloudflare Workers</a> by compiling to WebAssembly. The ecosystem around WebAssembly has grown significantly since then, and it has become a solid foundation for all types of projects, be they client-side or server-side.</p><p>As WebAssembly support has grown, more and more languages are able to compile to WebAssembly for execution on servers and in browsers. As Cloudflare Workers uses the V8 engine and supports WebAssembly natively, we’re able to support languages that compile to WebAssembly on the platform.</p><p>Recently, work on LLVM has enabled Fortran to compile to WebAssembly. So, today, we’re writing about running Fortran code on Cloudflare Workers.</p><p>Before we dive into how to do this, here’s a little demonstration of number recognition in Fortran. Draw a number from 0 to 9 and Fortran code running somewhere on Cloudflare’s network will predict the number you drew.</p><p>This is taken from the wonderful <a href="https://gws.phd/posts/fortran_wasm/">Fortran on WebAssembly</a> post but instead of running client-side, the Fortran code is running on Cloudflare Workers. Read on to find out how you can use Fortran on Cloudflare Workers and how that demonstration works.</p>
    <div>
      <h3>Wait, Fortran? No one uses that!</h3>
      <a href="#wait-fortran-no-one-uses-that">
        
      </a>
    </div>
    <p>Not so fast! Or rather, actually pretty darn fast if you’re doing a lot of numerical programming or have scientific data to work with. Fortran <a href="https://en.wikipedia.org/wiki/Fortran#Naming">(originally FORmula TRANslator)</a> is very well suited for scientific workloads because of its native functionality for things like arithmetic and handling large arrays and matrices.</p><p>If you look at the <a href="https://en.wikipedia.org/wiki/TOP500">ranking</a> of the fastest supercomputers in the world you’ll discover that the measurement of “fast” is based on these supercomputers running a piece of software called <a href="https://en.wikipedia.org/wiki/LINPACK">LINPACK</a> that was originally written in Fortran. LINPACK is designed to help with problems solvable using linear algebra.</p><p>The <a href="https://en.wikipedia.org/wiki/LINPACK_benchmarks">LINPACK benchmarks</a> use LINPACK to solve an n x n system of linear equations using matrix operations and, in doing so, determine how fast supercomputers are. The code is available in <a href="https://www.netlib.org/benchmark/linpackd">Fortran</a>, <a href="https://www.netlib.org/benchmark/linpackc">C</a> and <a href="https://www.netlib.org/benchmark/linpackjava/">Java</a>.</p><p>A related Fortran package, <a href="https://www.netlib.org/blas/">BLAS</a>, also does linear algebra and forms the basis of the number identifying code above. But other Fortran packages are still relevant. Back in 2017, NASA ran a <a href="https://www.bbc.com/news/technology-39803425">competition</a> to make FUN3D (used to perform calculations of airflow over simulated aircraft). <a href="https://fun3d.larc.nasa.gov/">FUN3D</a> is written in Fortran.</p><p>So, although Fortran (or at the time FORTRAN) first came to life in 1957, it’s alive and well and being used widely for scientific applications (there’s even <a href="https://developer.nvidia.com/cuda-fortran">Fortran for CUDA</a>). One particular application left Earth 20 years after Fortran was born: Voyager. The Voyager probes use a combination of <a href="https://www.popularmechanics.com/space/a17991/voyager-1-voyager-2-retiring-engineer/">assembly language and Fortran</a> to keep chugging along.</p><p>But back in our solar system, and back on Region: Earth, you can now use Fortran on Cloudflare Workers. Here’s how.</p>
    <div>
      <h3>How to get your Fortran code running on Cloudflare Workers</h3>
      <a href="#how-to-get-your-fortran-code-running-on-cloudflare-workers">
        
      </a>
    </div>
    <p>To make it easy to run your Fortran code on Cloudflare Workers, we created a tool called <a href="https://github.com/cloudflare/fortiche">Fortiche</a> (translates to smart in French). It uses <a href="https://flang.llvm.org/docs/">Flang</a> and <a href="https://emscripten.org">Emscripten</a> under the hood.</p><p><a href="https://flang.llvm.org/docs/">Flang</a> is a frontend in <a href="https://en.wikipedia.org/wiki/LLVM">LLVM</a> and, if you read the <a href="https://gws.phd/posts/fortran_wasm/">Fortran on WebAssembly</a> blog post, we currently have to patch <a href="https://en.wikipedia.org/wiki/LLVM">LLVM</a> to work around a few issues.</p><p><a href="https://emscripten.org">Emscripten</a> is used to compile <a href="https://en.wikipedia.org/wiki/LLVM">LLVM</a> output and produce code that is compatible with Cloudflare Workers.</p><p>This is all packaged in the <a href="https://github.com/cloudflare/fortiche">Fortiche</a> Docker image. Let’s see a simple example.</p><p>add.f90:</p>
            <pre><code>SUBROUTINE add(a, b, res)
    INTEGER, INTENT(IN) :: a, b
    INTEGER, INTENT(OUT) :: res

    res = a + b
END</code></pre>
            <p>Here we defined a subroutine called add that takes <code>a</code> and <code>b</code>, sums them together and places the result in <code>res</code>.</p><p>Compile with <a href="https://github.com/cloudflare/fortiche">Fortiche</a>:</p>
            <pre><code>docker run -v $PWD:/input -v $PWD/output:/output xtuc/fortiche --export-func=add add.f90</code></pre>
            <p>Passing <code>--export-func=add</code> to <a href="https://github.com/cloudflare/fortiche">Fortiche</a> makes the Fortran <code>add</code> subroutine available to JavaScript.</p><p>The output folder contains the compiled WebAssembly module and JavaScript from <a href="https://emscripten.org">Emscripten</a>, and a JavaScript endpoint generated by <a href="https://github.com/cloudflare/fortiche">Fortiche</a>:</p>
            <pre><code>$ ls -lh ./output
total 84K
-rw-r--r-- 1 root root 392 avril 22 12:00 index.mjs
-rw-r--r-- 1 root root 27K avril 22 12:00 out.mjs
-rwxr-xr-x 1 root root 49K avril 22 12:00 out.wasm</code></pre>
            <p>And finally the Cloudflare Worker:</p>
            <pre><code>// Import what Fortiche generated
import {load} from "../output/index.mjs"

export default {
    async fetch(request: Request): Promise&lt;Response&gt; {
        // Load the Fortran program
        const program = await load();

        // Allocate space in memory for the arguments and result
        const aPtr = program.malloc(4);
        const bPtr = program.malloc(4);
        const outPtr = program.malloc(4);

        // Set argument values
        program.HEAP32[aPtr / 4] = 123;
        program.HEAP32[bPtr / 4] = 321;

        // Run the Fortran add subroutine
        program.add(aPtr, bPtr, outPtr);

        // Read the result
        const res = program.HEAP32[outPtr / 4];

        // Free everything
        program.free(aPtr);
        program.free(bPtr);
        program.free(outPtr);

        return Response.json({ res });
    },
};</code></pre>
            <p>Interestingly, the values we pass to Fortran are all pointers, therefore we have to allocate space for each argument and result (the Fortran integer type is four bytes wide), and pass the pointers to the <code>add</code> subroutine.</p><p>Running the Worker gives us the right answer:</p>
            <pre><code>$ curl https://fortran-add.cfdemos.workers.dev

{"res":444}</code></pre>
            <p>You can find the <a href="https://github.com/cloudflare/fortiche/tree/main/examples/add">full example here</a>.</p>
    <div>
      <h3>Handwritten digit classifier</h3>
      <a href="#handwritten-digit-classifier">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4PKFBCv6bCWa20pqrFTEnV/58813ef64d5d8a8aa728ba4bcfde6840/image1-2.png" />
            
            </figure><p>This example is taken from <a href="https://gws.phd/posts/fortran_wasm/#mnist">https://gws.phd/posts/fortran_wasm/#mnist</a>. It relies on the <a href="https://www.netlib.org/blas/">BLAS</a> library, which is available in Fortiche with the flag: <code>--with-BLAS-3-12-0</code>.</p><p>Note that the <a href="https://en.wikipedia.org/wiki/LAPACK">LAPACK</a> library is also available in <a href="https://github.com/cloudflare/fortiche">Fortiche</a> with the flag: <code>--with-LAPACK-3-12-0</code>.</p><p>You can try it below:</p><p>And you can access the <a href="https://github.com/cloudflare/fortiche/tree/main/examples/handwritten-digit-classifier">source code here</a>.</p><p>Let us know what you write using Fortran and Cloudflare Workers!</p><p><code>END</code></p> ]]></content:encoded>
            <category><![CDATA[Fortran]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">4iGQQWfQlU6k8DiQ5ZO900</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
        <item>
            <title><![CDATA[polyfill.io now available on cdnjs: reduce your supply chain risk]]></title>
            <link>https://blog.cloudflare.com/polyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk/</link>
            <pubDate>Thu, 29 Feb 2024 17:51:32 GMT</pubDate>
            <description><![CDATA[ Polyfill.io is now available on cdnjs to reduce the risk of supply chain attacks. Replace your polyfill.io links today for a seamless experience ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4nxvskYVuRnRJHoLUMRY4l/e26d2d6b57eb9a286238ab84863dc947/image1-17.png" />
            
            </figure><p>Polyfill.io is a popular JavaScript library that nullifies differences across old browser versions. These differences often take up substantial development time.</p><p>It does this by adding support for modern functions (via <a href="https://developer.mozilla.org/en-US/docs/Glossary/Polyfill">polyfilling</a>), ultimately letting developers work against a uniform environment simplifying development. The tool is historically loaded by linking to the endpoint provided under the domain polyfill.io.</p><p>In the interest of providing developers with additional options to use polyfill, today we are launching an <a href="https://cdnjs.cloudflare.com/polyfill">alternative endpoint under cdnjs</a>. You can replace links to polyfill.io “as is” with our new endpoint. You will then rely on the same service and reputation that <a href="https://cdnjs.com/">cdnjs</a> has built over the years for your polyfill needs.</p><p>Our interest in creating an alternative endpoint was also sparked by some <a href="https://github.com/polyfillpolyfill/polyfill-service/issues/2834">concerns raised by the community</a>, and <a href="https://twitter.com/triblondon/status/1761852117579427975">main contributors</a>, following the transition of the domain polyfill.io to a new provider (Funnull).</p><p>The concerns are that any website embedding a link to the original polyfill.io domain, will now be relying on Funnull to maintain and secure the underlying project to avoid the risk of a supply chain attack. Such an attack would occur if the underlying third party is compromised or alters the code being served to end users in nefarious ways, causing, by consequence, all websites using the tool to be compromised.</p><p>Supply chain attacks, in the context of web applications, are a growing concern for security teams, and also led us to build a client side security product to detect and mitigate these attack vectors: <a href="https://developers.cloudflare.com/page-shield/">Page Shield</a>.</p><p>Irrespective of the scenario described above, this is a timely reminder of the complexities and risks tied to modern web applications. As maintainers and contributors of <a href="https://cdnjs.com/">cdnjs</a>, currently used by <a href="https://w3techs.com/technologies/overview/content_delivery">more than 12% of all sites</a>, this reinforces our commitment to help keep the Internet safe.</p>
    <div>
      <h3>polyfill.io on cdnjs</h3>
      <a href="#polyfill-io-on-cdnjs">
        
      </a>
    </div>
    <p>The full polyfill.io implementation has been deployed at the following URL:</p><p><a href="https://cdnjs.cloudflare.com/polyfill/"><code>https://cdnjs.cloudflare.com/polyfill/</code></a></p><p>The underlying bundle link is:</p><p>For minified: <a href="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js">https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js</a>For unminified: <a href="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.js">https://cdnjs.cloudflare.com/polyfill/v3/polyfill.js</a></p><p>Usage and deployment is intended to be identical to the original polyfill.io site. As a developer, you should be able to simply “replace” the old link with the new cdnjs-hosted link without observing any side effects, besides a possible improvement in performance and reliability.</p><p>If you don’t have access to the underlying website code, but your website is behind Cloudflare, replacing the links is even easier, as you can deploy a Cloudflare Worker to update the links for you:</p>
            <pre><code>export interface Env {}

export default {
    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise&lt;Response&gt; {
        ctx.passThroughOnException();

        const response = await fetch(request);

        if ((response.headers.get('content-type') || '').includes('text/html')) {
            const rewriter = new HTMLRewriter()
                .on('link', {
                    element(element) {
                        const rel = element.getAttribute('rel');
                        if (rel === 'preconnect') {
                            const href = new URL(element.getAttribute('href') || '', request.url);

                            if (href.hostname === 'polyfill.io') {
                                href.hostname = 'cdnjs.cloudflare.com';
                                element.setAttribute('href', href.toString());
                            }
                        }
                    },
                })

                .on('script', {
                    element(element) {
                        if (element.hasAttribute('src')) {
                            const src = new URL(element.getAttribute('src') || '', request.url);
                            if (src.hostname === 'polyfill.io') {
                                src.hostname = 'cdnjs.cloudflare.com';
                                src.pathname = '/polyfill' + src.pathname;

                                element.setAttribute('src', src.toString());
                            }
                        }
                    },
                });

            return rewriter.transform(response);
        } else {
            return response;
        }
    },
};</code></pre>
            <p>Instructions on how to deploy a worker can be found on our <a href="https://developers.cloudflare.com/workers/get-started/">developer documentation</a>.</p><p>You can also test the Worker on your website without deploying the worker. You can find instructions on how to do this in <a href="/workers-and-webpagetest/">another blog post we wrote in the past</a>.</p>
    <div>
      <h3>Implemented with Rust on Cloudflare Workers</h3>
      <a href="#implemented-with-rust-on-cloudflare-workers">
        
      </a>
    </div>
    <p>We were happy to discover that polyfill.io is a <a href="https://github.com/polyfillpolyfill/polyfill-service">Rust project</a>. As you might know, Rust has been a <a href="/workers-rust-sdk">first class citizen on Cloudflare Workers</a> from the start.</p><p>The polyfill.io service was hosted on Fastly and used their Rust library. We forked the project to add the compatibility for Cloudflare Workers, and plan to make the fork publicly accessible in the near future.</p>
    <div>
      <h3>Worker</h3>
      <a href="#worker">
        
      </a>
    </div>
    <p>The <code>https://cdnjs.cloudflare.com/polyfill/[...].js</code> endpoints are also implemented in a Cloudflare Worker that wraps our Polyfill.io fork. The wrapper uses <a href="https://github.com/cloudflare/workers-rs">Cloudflare’s Rust API</a> and looks like the following:</p>
            <pre><code>#[event(fetch)]
async fn main(req: Request, env: Env, ctx: Context) -&gt; Result&lt;Response&gt; {
    let metrics = {...};

    let polyfill_store = get_d1(&amp;req, &amp;env)?;
    let polyfill_env = Arc::new(service::Env { polyfill_store, metrics });
    
    // Run the polyfill.io entrypoint
    let res = service::handle_request(req2, polyfill_env).await;

    let status_code = if let Ok(res) = &amp;res {
        res.status_code()
    } else {
        500
    };
    metrics
        .requests
        .with_label_values(&amp;[&amp;status_code.to_string()])
        .inc();

    ctx.wait_until(async move {
        if let Err(err) = metrics.report_metrics().await {
            console_error!("failed to report metrics: {err}");
        }
    });

    res
}</code></pre>
            <p>The wrapper only sets up our internal <a href="https://developers.cloudflare.com/workers/observability/metrics-and-analytics/">metrics</a> and <a href="https://developers.cloudflare.com/workers/observability/logging/logpush/">logging</a> tools, so we can monitor uptime and performance of the underlying logic while calling the Polyfill.io entrypoint.</p>
    <div>
      <h3>Storage for the Polyfill files</h3>
      <a href="#storage-for-the-polyfill-files">
        
      </a>
    </div>
    <p>All the polyfill files are stored in a key-value store powered by <a href="https://developers.cloudflare.com/d1/">Cloudflare D1</a>. This allows us to fetch as many polyfill files as we need with a single SQL query, as opposed to the original implementation doing one KV get() per file.</p><p>For performance, we have one Cloudflare D1 instance per region and the SQL queries are routed to the nearest database.</p>
    <div>
      <h3>cdnjs for your JavaScript libraries</h3>
      <a href="#cdnjs-for-your-javascript-libraries">
        
      </a>
    </div>
    <p>cdnjs is hosting over 6k JavaScript libraries as of today. We are looking for ways to improve the service and provide new content. We listen to community feedback and welcome suggestions on our <a href="https://community.cloudflare.com/">community forum</a>, or <a href="https://github.com/cdnjs">cdnjs on GitHub</a>.</p><p><a href="https://developers.cloudflare.com/page-shield/">Page Shield</a> is also available to all paid plans. Log in to turn it on with a single click to increase visibility and security for your third party assets.</p> ]]></content:encoded>
            <category><![CDATA[CDNJS]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Supply Chain Attacks]]></category>
            <guid isPermaLink="false">64KHlCJKCdI1JNa3sCAqpL</guid>
            <dc:creator>Sven Sauleau</dc:creator>
            <dc:creator>Michael Tremante</dc:creator>
        </item>
        <item>
            <title><![CDATA[Wasm core dumps and debugging Rust in Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/wasm-coredumps/</link>
            <pubDate>Mon, 14 Aug 2023 13:00:33 GMT</pubDate>
            <description><![CDATA[ Debugging Rust and Wasm with Cloudflare Workers involves a lot of the good old time-consuming and nerve-wracking printf'ing strategy. What if there’s a better way? This blog is about enabling and using Wasm core dumps and how you can easily debug Rust in Cloudflare Workers ]]></description>
            <content:encoded><![CDATA[ <p></p><p>A clear sign of maturing for any new programming language or environment is how easy and efficient debugging them is. Programming, like any other complex task, involves various challenges and potential pitfalls. Logic errors, off-by-ones, null pointer dereferences, and memory leaks are some examples of things that can make software developers desperate if they can't pinpoint and fix these issues quickly as part of their workflows and tools.</p><p><a href="https://webassembly.org/">WebAssembly</a> (Wasm) is a binary instruction format designed to be a portable and efficient target for the compilation of high-level languages like <a href="https://www.rust-lang.org/">Rust</a>, C, C++, and others. In recent years, it has gained significant traction for building high-performance applications in web and serverless environments.</p><p>Cloudflare Workers has had <a href="https://github.com/cloudflare/workers-rs">first-party support for Rust and Wasm</a> for quite some time. We've been using this powerful combination to bootstrap and build some of our most recent services, like <a href="/introducing-d1/">D1</a>, <a href="/introducing-constellation/">Constellation</a>, and <a href="/automatic-signed-exchanges/">Signed Exchanges</a>, to name a few.</p><p>Using tools like <a href="https://github.com/cloudflare/workers-sdk">Wrangler</a>, our command-line tool for building with Cloudflare developer products, makes streaming real-time logs from our applications running remotely easy. Still, to be honest, debugging Rust and Wasm with Cloudflare Workers involves a lot of the good old time-consuming and nerve-wracking <a href="https://news.ycombinator.com/item?id=26925570">printf'ing</a> strategy.</p><p>What if there’s a better way? This blog is about enabling and using Wasm core dumps and how you can easily debug Rust in Cloudflare Workers.</p>
    <div>
      <h3>What are core dumps?</h3>
      <a href="#what-are-core-dumps">
        
      </a>
    </div>
    <p>In computing, a <a href="https://en.wikipedia.org/wiki/Core_dump">core dump</a> consists of the recorded state of the working memory of a computer program at a specific time, generally when the program has crashed or otherwise terminated abnormally. They also add things like the processor registers, stack pointer, program counter, and other information that may be relevant to fully understanding why the program crashed.</p><p>In most cases, depending on the system’s configuration, core dumps are usually initiated by the operating system in response to a program crash. You can then use a debugger like <a href="https://linux.die.net/man/1/gdb">gdb</a> to examine what happened and hopefully determine the cause of a crash. <a href="https://linux.die.net/man/1/gdb">gdb</a> allows you to run the executable to try to replicate the crash in a more controlled environment, inspecting the variables, and much more. The Windows' equivalent of a core dump is a <a href="https://learn.microsoft.com/en-us/troubleshoot/windows-client/performance/read-small-memory-dump-file">minidump</a>. Other mature languages that are interpreted, like Python, or languages that run inside a virtual machine, like <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/visualvm/coredumps.html">Java</a>, also have their ways of generating core dumps for post-mortem analysis.</p><p>Core dumps are particularly useful for post-mortem debugging, determining the conditions that lead to a failure after it has occurred.</p>
    <div>
      <h3>WebAssembly core dumps</h3>
      <a href="#webassembly-core-dumps">
        
      </a>
    </div>
    <p>WebAssembly has had a <a href="https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md">proposal for implementing core dumps</a> in discussion for a while. It's a work-in-progress experimental specification, but it provides basic support for the main ideas of post-mortem debugging, including using the <a href="https://yurydelendik.github.io/webassembly-dwarf/">DWARF</a> (debugging with attributed record formats) debug format, the same that Linux and gdb use. Some of the most popular Wasm runtimes, like <a href="https://github.com/bytecodealliance/wasmtime/pull/5868">Wasmtime</a> and <a href="https://github.com/wasmerio/wasmer/pull/3626">Wasmer</a>, have experimental flags that you can enable and start playing with Wasm core dumps today.</p><p>If you run Wasmtime or Wasmer with the flag:</p>
            <pre><code>--coredump-on-trap=/path/to/coredump/file</code></pre>
            <p>The core dump file will be emitted at that location path if a crash happens. You can then use tools like <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/wasmgdb">wasmgdb</a> to inspect the file and debug the crash.</p><p>But let's dig into how the core dumps are generated in WebAssembly, and what’s inside them.</p>
    <div>
      <h3>How are Wasm core dumps generated</h3>
      <a href="#how-are-wasm-core-dumps-generated">
        
      </a>
    </div>
    <p>(and what’s inside them)</p><p>When WebAssembly terminates execution due to abnormal behavior, we say that it entered a trap. With Rust, examples of operations that can trap are accessing out-of-bounds addresses or a division by zero arithmetic call. You can read about the <a href="https://webassembly.org/docs/security/">security model of WebAssembly</a> to learn more about traps.</p><p>The core dump specification plugs into the trap workflow. When WebAssembly crashes and enters a trap, core dumping support kicks in and starts unwinding the call <a href="https://webassembly.github.io/spec/core/exec/runtime.html#stack">stack</a> gathering debugging information. For each frame in the stack, it collects the <a href="https://webassembly.github.io/spec/core/syntax/modules.html#syntax-func">function</a> parameters and the values stored in locals and in the stack, along with binary offsets that help us map to exact locations in the source code. Finally, it snapshots the <a href="https://webassembly.github.io/spec/core/syntax/modules.html#syntax-mem">memory</a> and captures information like the <a href="https://webassembly.github.io/spec/core/syntax/modules.html#syntax-table">tables</a> and the <a href="https://webassembly.github.io/spec/core/syntax/modules.html#syntax-global">global variables</a>.</p><p><a href="https://dwarfstd.org/">DWARF</a> is used by many mature languages like C, C++, Rust, Java, or Go. By emitting DWARF information into the binary at compile time a debugger can provide information such as the source name and the line number where the exception occurred, function and argument names, and more. Without DWARF, the core dumps would be just pure assembly code without any contextual information or metadata related to the source code that generated it before compilation, and they would be much harder to debug.</p><p>WebAssembly <a href="https://webassembly.github.io/spec/core/appendix/custom.html#name-section">uses a (lighter) version of DWARF</a> that maps functions, or a module and local variables, to their names in the source code (you can read about the <a href="https://webassembly.github.io/spec/core/appendix/custom.html#name-section">WebAssembly name section</a> for more information), and naturally core dumps use this information.</p><p>All this information for debugging is then bundled together and saved to the file, the core dump file.</p><p>The <a href="https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md#coredump-file-format">core dump structure</a> has multiple sections, but the most important are:</p><ul><li><p>General information about the process;</p></li><li><p>The <a href="https://webassembly.github.io/threads/core/">threads</a> and their stack frames (note that WebAssembly is <a href="https://developers.cloudflare.com/workers/runtime-apis/webassembly/#threading">single threaded</a> in Cloudflare Workers);</p></li><li><p>A snapshot of the WebAssembly linear memory or only the relevant regions;</p></li><li><p>Optionally, other sections like globals, data, or table.</p></li></ul><p>Here’s the thread definition from the core dump specification:</p>
            <pre><code>corestack   ::= customsec(thread-info vec(frame))
thread-info ::= 0x0 thread-name:name ...
frame       ::= 0x0 ... funcidx:u32 codeoffset:u32 locals:vec(value)
                stack:vec(value)</code></pre>
            <p>A thread is a custom section called <code>corestack</code>. A corestack section contains the thread name and a vector (or array) of frames. Each frame contains the function index in the WebAssembly module (<code>funcidx</code>), the code offset relative to the function's start (<code>codeoffset</code>), the list of locals, and the list of values in the stack.</p><p>Values are defined as follows:</p>
            <pre><code>value ::= 0x01       =&gt; ∅
        | 0x7F n:i32 =&gt; n
        | 0x7E n:i64 =&gt; n
        | 0x7D n:f32 =&gt; n
        | 0x7C n:f64 =&gt; n</code></pre>
            <p>At the time of this writing these are the possible <a href="https://webassembly.github.io/spec/core/binary/types.html#binary-numtype">numbers types</a> in a value. Again, we wanted to describe the basics; you should track the full <a href="https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md#coredump-file-format">specification</a> to get more detail or find information about future changes. WebAssembly core dump support is in its early stages of specification and implementation, things will get better, things might change.</p><p>This is all great news. Unfortunately, however, the Cloudflare Workers <a href="https://github.com/cloudflare/workerd">runtime</a> doesn’t support WebAssembly core dumps yet. There is no technical impediment to adding this feature to <a href="https://github.com/cloudflare/workerd">workerd</a>; after all, it's <a href="https://developers.cloudflare.com/workers/learning/how-workers-works/">based on V8</a>, but since it powers a critical part of our production infrastructure and products, we tend to be conservative when it comes to adding specifications or standards that are still considered experimental and still going through the definitions phase.</p><p>So, how do we get core Wasm dumps in Cloudflare Workers today?</p>
    <div>
      <h3>Polyfilling</h3>
      <a href="#polyfilling">
        
      </a>
    </div>
    <p>Polyfilling means using userland code to provide modern functionality in older environments that do not natively support it. <a href="https://developer.mozilla.org/en-US/docs/Glossary/Polyfill">Polyfills</a> are widely popular in the JavaScript community and the browser environment; they've been used extensively to address issues where browser vendors still didn't catch up with the latest standards, or when they implement the same features in different ways, or address cases where old browsers can never support a new standard.</p><p>Meet <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/rewriter">wasm-coredump-rewriter</a>, a tool that you can use to rewrite a Wasm module and inject the core dump runtime functionality in the binary. This runtime code will catch most traps (exceptions in host functions are not yet catched and memory violation not by default) and generate a standard core dump file. To some degree, this is similar to how Binaryen's <a href="https://github.com/WebAssembly/binaryen/blob/main/src/passes/Asyncify.cpp">Asyncify</a> <a href="https://kripken.github.io/blog/wasm/2019/07/16/asyncify.html">works</a>.</p><p>Let’s look at code and see how this works. He’s some simple pseudo code:</p>
            <pre><code>export function entry(v1, v2) {
    return addTwo(v1, v2)
}

function addTwo(v1, v2) {
  res = v1 + v2;
  throw "something went wrong";

  return res
}</code></pre>
            <p>An imaginary compiler could take that source and generate the following Wasm binary code:</p>
            <pre><code>  (func $entry (param i32 i32) (result i32)
    (local.get 0)
    (local.get 1)
    (call $addTwo)
  )

  (func $addTwo (param i32 i32) (result i32)
    (local.get 0)
    (local.get 1)
    (i32.add)
    (unreachable) ;; something went wrong
  )

  (export "entry" (func $entry))</code></pre>
            <p><i>“;;” is used to denote a comment.</i></p><p><code>entry()</code> is the Wasm function <a href="https://webassembly.github.io/spec/core/exec/runtime.html#syntax-hostfunc">exported to the host</a>. In an environment like the browser, JavaScript (being the host) can call entry().</p><p>Irrelevant parts of the code have been snipped for brevity, but this is what the Wasm code will look like after <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/rewriter">wasm-coredump-rewriter</a> rewrites it:</p>
            <pre><code>  (func $entry (type 0) (param i32 i32) (result i32)
    ...
    local.get 0
    local.get 1
    call $addTwo ;; see the addTwo function bellow
    global.get 2 ;; is unwinding?
    if  ;; label = @1
      i32.const x ;; code offset
      i32.const 0 ;; function index
      i32.const 2 ;; local_count
      call $coredump/start_frame
      local.get 0
      call $coredump/add_i32_local
      local.get 1
      call $coredump/add_i32_local
      ...
      call $coredump/write_coredump
      unreachable
    end)

  (func $addTwo (type 0) (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add
    ;; the unreachable instruction was here before
    call $coredump/unreachable_shim
    i32.const 1 ;; funcidx
    i32.const 2 ;; local_count
    call $coredump/start_frame
    local.get 0
    call $coredump/add_i32_local
    local.get 1
    call $coredump/add_i32_local
    ...
    return)

  (export "entry" (func $entry))</code></pre>
            <p>As you can see, a few things changed:</p><ol><li><p>The (unreachable) instruction in <code>addTwo()</code> was replaced by a call to <code>$coredump/unreachable_shim</code> which starts the unwinding process. Then, the location and debugging data is captured, and the function returns normally to the <code>entry()</code> caller.</p></li><li><p>Code has been added after the <code>addTwo()</code> call instruction in <code>entry()</code> that detects if we have an unwinding process in progress or not. If we do, then it also captures the local debugging data, writes the core dump file and then, finally, moves to the unconditional trap unreachable.</p></li></ol><p>In short, we unwind until the host function <code>entry()</code> gets destroyed by calling unreachable.</p><p>Let’s go over the runtime functions that we inject for more clarity, stay with us:</p><ul><li><p><code>$coredump/start_frame(funcidx, local_count)</code> starts a new frame in the coredump.</p></li><li><p><code>$coredump/add_*_local(value)</code> captures the values of function arguments and in locals (currently capturing values from the stack isn’t implemented.)</p></li><li><p><code>$coredump/write_coredump</code> is used at the end and writes the core dump in memory. We take advantage of the first 1 KiB of the Wasm linear memory, which is unused, to store our core dump.</p></li></ul><p>A diagram is worth a thousand words:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/27DxZQioAhBsBiltjwIiyL/2dc57b370b6741120a5bb263c2795652/image2-7.png" />
            
            </figure><p>Wait, what’s this about the first 1 KiB of the memory being unused, you ask? Well, it turns out that most WebAssembly linters and tools, including <a href="https://github.com/emscripten-core/emscripten/issues/5775#issuecomment-344049528">Emscripten</a> and <a href="https://github.com/llvm/llvm-project/blob/121e15f96ce401c875e717992a4d054e308ba775/lld/wasm/Writer.cpp#L366">WebAssembly’s LLVM</a> don’t use the first 1 KiB of memory. <a href="https://github.com/rust-lang/rust/issues/50543">Rust</a> and <a href="https://github.com/ziglang/zig/issues/4496">Zig</a> also use LLVM, but they changed the default. This isn’t pretty, but the hugely popular Asyncify polyfill relies on the same trick, so there’s reasonable support until we find a better way.</p><p>But we digress, let’s continue. After the crash, the host, typically JavaScript in the browser, can now catch the exception and extract the core dump from the Wasm instance’s memory:</p>
            <pre><code>try {
    wasmInstance.exports.someExportedFunction();
} catch(err) {
    const image = new Uint8Array(wasmInstance.exports.memory.buffer);
    writeFile("coredump." + Date.now(), image);
}</code></pre>
            <p>If you're curious about the actual details of the core dump implementation, you can find the <a href="https://github.com/xtuc/wasm-coredump/blob/main/lib/asc-coredump/assembly/coredump.ts">source code here</a>. It was written in <a href="https://www.assemblyscript.org/">AssemblyScript</a>, a TypeScript-like language for WebAssembly.</p><p>This is how we use the polyfilling technique to implement Wasm core dumps when the runtime doesn’t support them yet. Interestingly, some Wasm runtimes, being optimizing compilers, are likely to make debugging more difficult because function arguments, locals, or functions themselves can be optimized away. Polyfilling or rewriting the binary could actually preserve more source-level information for debugging.</p><p>You might be asking what about performance? We did some testing and found that the <a href="https://github.com/xtuc/wasm-coredump-bench/blob/main/results.md">impact is negligible</a>; the cost-benefit of being able to debug our crashes is positive. Also, you can easily turn wasm core dumps on or off for specific builds or environments; deciding when you need them is up to you.</p>
    <div>
      <h3>Debugging from a core dump</h3>
      <a href="#debugging-from-a-core-dump">
        
      </a>
    </div>
    <p>We now know how to generate a core dump, but how do we use it to diagnose and debug a software crash?</p><p>Similarly to <a href="https://en.wikipedia.org/wiki/GNU_Debugger">gdb</a> (GNU Project Debugger) on Linux, <a href="https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md">wasmgdb</a> is the tool you can use to parse and make sense of core dumps in WebAssembly; it understands the file structure, uses DWARF to provide naming and contextual information, and offers interactive commands to navigate the data. To exemplify how it works, <a href="https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/demo.md">wasmgdb has a demo</a> of a Rust application that deliberately crashes; we will use it.</p><p>Let's imagine that our Wasm program crashed, wrote a core dump file, and we want to debug it.</p>
            <pre><code>$ wasmgdb source-program.wasm /path/to/coredump
wasmgdb&gt;</code></pre>
            <p>When you fire wasmgdb, you enter a <a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL (Read-Eval-Print Loop)</a> interface, and you can start typing commands. The tool tries to mimic the gdb command syntax; you can find the <a href="https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md#commands">list here</a>.</p><p>Let's examine the backtrace using the bt command:</p>
            <pre><code>wasmgdb&gt; bt
#18     000137 as __rust_start_panic () at library/panic_abort/src/lib.rs
#17     000129 as rust_panic () at library/std/src/panicking.rs
#16     000128 as rust_panic_with_hook () at library/std/src/panicking.rs
#15     000117 as {closure#0} () at library/std/src/panicking.rs
#14     000116 as __rust_end_short_backtrace&lt;std::panicking::begin_panic_handler::{closure_env#0}, !&gt; () at library/std/src/sys_common/backtrace.rs
#13     000123 as begin_panic_handler () at library/std/src/panicking.rs
#12     000194 as panic_fmt () at library/core/src/panicking.rs
#11     000198 as panic () at library/core/src/panicking.rs
#10     000012 as calculate (value=0x03000000) at src/main.rs
#9      000011 as process_thing (thing=0x2cff0f00) at src/main.rs
#8      000010 as main () at src/main.rs
#7      000008 as call_once&lt;fn(), ()&gt; (???=0x01000000, ???=0x00000000) at /rustc/b833ad56f46a0bbe0e8729512812a161e7dae28a/library/core/src/ops/function.rs
#6      000020 as __rust_begin_short_backtrace&lt;fn(), ()&gt; (f=0x01000000) at /rustc/b833ad56f46a0bbe0e8729512812a161e7dae28a/library/std/src/sys_common/backtrace.rs
#5      000016 as {closure#0}&lt;()&gt; () at /rustc/b833ad56f46a0bbe0e8729512812a161e7dae28a/library/std/src/rt.rs
#4      000077 as lang_start_internal () at library/std/src/rt.rs
#3      000015 as lang_start&lt;()&gt; (main=0x01000000, argc=0x00000000, argv=0x00000000, sigpipe=0x00620000) at /rustc/b833ad56f46a0bbe0e8729512812a161e7dae28a/library/std/src/rt.rs
#2      000013 as __original_main () at &lt;directory not found&gt;/&lt;file not found&gt;
#1      000005 as _start () at &lt;directory not found&gt;/&lt;file not found&gt;
#0      000264 as _start.command_export at &lt;no location&gt;</code></pre>
            <p>Each line represents a frame from the program's call <a href="https://webassembly.github.io/spec/core/exec/runtime.html#stack">stack</a>; see frame #3:</p>
            <pre><code>#3      000015 as lang_start&lt;()&gt; (main=0x01000000, argc=0x00000000, argv=0x00000000, sigpipe=0x00620000) at /rustc/b833ad56f46a0bbe0e8729512812a161e7dae28a/library/std/src/rt.rs</code></pre>
            <p>You can read the funcidx, function name, arguments names and values and source location are all present. Let's select frame #9 now and inspect the locals, which include the function arguments:</p>
            <pre><code>wasmgdb&gt; f 9
000011 as process_thing (thing=0x2cff0f00) at src/main.rs
wasmgdb&gt; info locals
thing: *MyThing = 0xfff1c</code></pre>
            <p>Let’s use the <code>p</code> command to inspect the content of the thing argument:</p>
            <pre><code>wasmgdb&gt; p (*thing)
thing (0xfff2c): MyThing = {
    value (0xfff2c): usize = 0x00000003
}</code></pre>
            <p>You can also use the <code>p</code> command to inspect the value of the variable, which can be useful for nested structures:</p>
            <pre><code>wasmgdb&gt; p (*thing)-&gt;value
value (0xfff2c): usize = 0x00000003</code></pre>
            <p>And you can use p to inspect memory addresses. Let’s point at <code>0xfff2c</code>, the start of the <code>MyThing</code> structure, and inspect:</p>
            <pre><code>wasmgdb&gt; p (MyThing) 0xfff2c
0xfff2c (0xfff2c): MyThing = {
    value (0xfff2c): usize = 0x00000003
}</code></pre>
            <p>All this information in every step of the stack is very helpful to determine the cause of a crash. In our test case, if you look at frame #10, we triggered an integer overflow. Once you get comfortable walking through wasmgdb and using its commands to inspect the data, debugging core dumps will be another powerful skill under your belt.</p>
    <div>
      <h3>Tidying up everything in Cloudflare Workers</h3>
      <a href="#tidying-up-everything-in-cloudflare-workers">
        
      </a>
    </div>
    <p>We learned about core dumps and how they work, and we know how to make Cloudflare Workers generate them using the wasm-coredump-rewriter polyfill, but how does all this work in practice end to end?</p><p>We've been dogfooding the technique described in this blog at Cloudflare for a while now. Wasm core dumps have been invaluable in helping us debug Rust-based services running on top of Cloudflare Workers like <a href="/introducing-d1/">D1</a>, <a href="/privacy-edge-making-building-privacy-first-apps-easier/">Privacy Edge</a>, <a href="/announcing-amp-real-url/">AMP</a>, or <a href="/introducing-constellation/">Constellation</a>.</p><p>Today we're open-sourcing the <a href="https://github.com/cloudflare/wasm-coredump">Wasm Coredump Service</a> and enabling anyone to deploy it. This service collects the Wasm core dumps originating from your projects and applications when they crash, parses them, prints an exception with the stack information in the logs, and can optionally store the full core dump in a file in an <a href="https://developers.cloudflare.com/r2/">R2 bucket</a> (which you can then use with wasmgdb) or send the exception to <a href="https://sentry.io/">Sentry</a>.</p><p>We use a <a href="https://developers.cloudflare.com/workers/configuration/bindings/about-service-bindings/">service binding</a> to facilitate the communication between your application Worker and the Coredump service Worker. A Service binding allows you to send HTTP requests to another Worker without those requests going over the Internet, thus avoiding network latency or having to deal with authentication. Here’s a diagram of how it works:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/gntGbV7rjDOncMZhFP7x1/3429a64f297c0edbf3327c677d56e0d3/image1-12.png" />
            
            </figure><p>Using it is as simple as npm/yarn installing <code>@cloudflare/wasm-coredump</code>, configuring a few options, and then adding a few lines of code to your other applications running in Cloudflare Workers, in the exception handling logic:</p>
            <pre><code>import shim, { getMemory, wasmModule } from "../build/worker/shim.mjs"

const timeoutSecs = 20;

async function fetch(request, env, ctx) {
    try {
        // see https://github.com/rustwasm/wasm-bindgen/issues/2724.
        return await Promise.race([
            shim.fetch(request, env, ctx),
            new Promise((r, e) =&gt; setTimeout(() =&gt; e("timeout"), timeoutSecs * 1000))
        ]);
    } catch (err) {
      const memory = getMemory();
      const coredumpService = env.COREDUMP_SERVICE;
      await recordCoredump({ memory, wasmModule, request, coredumpService });
      throw err;
    }
}</code></pre>
            <p>The <code>../build/worker/shim.mjs</code> import comes from the <a href="https://github.com/cloudflare/workers-rs/tree/main/worker-build">worker-build</a> tool, from the <a href="https://github.com/cloudflare/workers-rs/tree/main">workers-rs</a> packages and is automatically generated when <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/">wrangler</a> builds your Rust-based Cloudflare Workers project. If the Wasm throws an exception, we catch it, extract the core dump from memory, and send it to our Core dump service.</p><p>You might have noticed that we race the <a href="https://github.com/cloudflare/workers-rs">workers-rs</a> <code>shim.fetch()</code> entry point with another Promise to generate a timeout exception if the Rust code doesn't respond earlier. This is because currently, <a href="https://github.com/rustwasm/wasm-bindgen/">wasm-bindgen</a>, which generates the glue between the JavaScript and Rust land, used by workers-rs, has <a href="https://github.com/rustwasm/wasm-bindgen/issues/2724">an issue</a> where a Promise might not be rejected if Rust panics asynchronously (leading to the Worker runtime killing the worker with “Error: The script will never generate a response”.). This can block the wasm-coredump code and make the core dump generation flaky.</p><p>We are working to improve this, but in the meantime, make sure to adjust <code>timeoutSecs</code> to something slightly bigger than the typical response time of your application.</p><p>Here’s an example of a Wasm core dump exception in Sentry:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/gqZyPFslc9uqCV7jEgaqW/9425701b4209952518e3aef155d9b572/image3-4.png" />
            
            </figure><p>You can find a <a href="https://github.com/cloudflare/wasm-coredump/tree/main/example">working example</a>, the Sentry and R2 configuration options, and more details in the <a href="https://github.com/cloudflare/wasm-coredump">@cloudflare/wasm-coredump</a> GitHub repository.</p>
    <div>
      <h3>Too big to fail</h3>
      <a href="#too-big-to-fail">
        
      </a>
    </div>
    <p>It's worth mentioning one corner case of this debugging technique and the solution: sometimes your codebase is so big that adding core dump and DWARF debugging information might result in a Wasm binary that is too big to fit in a Cloudflare Worker. Well, worry not; we have a solution for that too.</p><p>Fortunately the DWARF for WebAssembly specification also supports <a href="https://yurydelendik.github.io/webassembly-dwarf/#external-DWARF">external DWARF files</a>. To make this work, we have a tool called <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/debuginfo-split">debuginfo-split</a> that you can add to the build command in the <code>wrangler.toml</code> configuration:</p>
            <pre><code>command = "... &amp;&amp; debuginfo-split ./build/worker/index.wasm"</code></pre>
            <p>What this does is it strips the debugging information from the Wasm binary, and writes it to a new separate file called <code>debug-{UUID}.wasm</code>. You then need to upload this file to the same R2 bucket used by the Wasm Coredump Service (you can automate this as part of your CI or build scripts). The same UUID is also injected into the main Wasm binary; this allows us to correlate the Wasm binary with its corresponding DWARF debugging information. Problem solved.</p><p>Binaries without DWARF information can be significantly smaller. Here’s our example:</p>
<table>
<thead>
  <tr>
    <th><span>4.5 MiB</span></th>
    <th><span>debug-63372dbe-41e6-447d-9c2e-e37b98e4c656.wasm</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>313 KiB</span></td>
    <td><span>build/worker/index.wasm</span></td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Final words</h3>
      <a href="#final-words">
        
      </a>
    </div>
    <p>We hope you enjoyed reading this blog as much as we did writing it and that it can help you take your Wasm debugging journeys, using Cloudflare Workers or not, to another level.</p><p>Note that while the examples used here were around using Rust and WebAssembly because that's a common pattern, you can use the same techniques if you're compiling WebAssembly from other languages like C or C++.</p><p>Also, note that the WebAssembly core dump standard is a hot topic, and its implementations and adoption are evolving quickly. We will continue improving the <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/rewriter">wasm-coredump-rewriter</a>, <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/debuginfo-split">debuginfo-split</a>, and <a href="https://github.com/xtuc/wasm-coredump/tree/main/bin/wasmgdb">wasmgdb</a> tools and the <a href="https://github.com/cloudflare/wasm-coredump">wasm-coredump service</a>. More and more runtimes, including V8, will eventually support core dumps natively, thus eliminating the need to use polyfills, and the tooling, in general, will get better; that's a certainty. For now, we present you with a solution that works today, and we have strong incentives to keep supporting it.</p><p>As usual, you can talk to us on our <a href="https://discord.cloudflare.com/">Developers Discord</a> or the <a href="https://community.cloudflare.com/c/developers/constellation/97">Community forum</a> or open issues or PRs in our GitHub repositories; the team will be listening.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[WASM]]></category>
            <category><![CDATA[WebAssembly]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">7xtevgzV4ycZa3fIFTQOP5</guid>
            <dc:creator>Sven Sauleau</dc:creator>
            <dc:creator>Celso Martinho</dc:creator>
        </item>
        <item>
            <title><![CDATA[Welcome to Wildebeest: the Fediverse on Cloudflare]]></title>
            <link>https://blog.cloudflare.com/welcome-to-wildebeest-the-fediverse-on-cloudflare/</link>
            <pubDate>Wed, 08 Feb 2023 19:00:00 GMT</pubDate>
            <description><![CDATA[ Today we're announcing Wildebeest, an open-source, easy-to-deploy ActivityPub and Mastodon-compatible server built entirely on top of Cloudflare's Supercloud. ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5BbRRixkMxcIiNYgdA9go/f2d1e27e932958951271d36ccffa2c16/Wildebeest.png" />
            
            </figure><p><a href="https://en.wikipedia.org/wiki/Fediverse">The Fediverse</a> has been a hot topic of discussion lately, with thousands, if not <a href="https://bitcoinhackers.org/@mastodonusercount">millions</a>, of new users creating accounts on platforms like <a href="https://joinmastodon.org/">Mastodon</a> to either move entirely to "the other side" or experiment and learn about this new social network.</p><p>Today we're introducing <a href="https://github.com/cloudflare/wildebeest">Wildebeest</a>, an open-source, easy-to-deploy ActivityPub and Mastodon-compatible server built entirely on top of Cloudflare's Supercloud. If you want to run your own spot in the Fediverse you can now do it entirely on Cloudflare.</p>
    <div>
      <h2>The Fediverse, built on Cloudflare</h2>
      <a href="#the-fediverse-built-on-cloudflare">
        
      </a>
    </div>
    <p>Today you're left with two options if you want to join the Mastodon federated network: either you join one of the <a href="https://joinmastodon.org/servers">existing servers</a> (servers are also called communities, and each one has its own infrastructure and rules), or you can run your self-hosted server.</p><p>There are a few reasons why you'd want to run your own server:</p><ul><li><p>You want to create a new community and attract other users over a common theme and usage rules.</p></li><li><p>You don't want to have to trust third-party servers or abide by their policies and want your server, under your domain, for your personal account.</p></li><li><p>You want complete control over your data, personal information, and content and visibility over what happens with your instance.</p></li></ul><p>The Mastodon gGmbH non-profit organization provides a server implementation using Ruby, Node.js, PostgreSQL and Redis. Running the <a href="https://github.com/mastodon/mastodon">official server</a> can be challenging, though. You need to own or rent a server or VPS somewhere; you have to install and configure the software, set up the database and public-facing web server, and configure and protect your network against attacks or abuse. And then you have to maintain all of that and deal with constant updates. It's a lot of scripting and technical work before you can get it up and running; definitely not something for the less technical enthusiasts.</p><p>Wildebeest serves two purposes: you can quickly deploy your Mastodon-compatible server on top of Cloudflare and connect it to the Fediverse in minutes, and you don't need to worry about maintaining or protecting it from abuse or attacks; Cloudflare will do it for you automatically.</p><p>Wildebeest is not a managed service. It's your instance, data, and code running in our cloud under your Cloudflare account. Furthermore, it's <a href="https://github.com/cloudflare/wildebeest">open-sourced</a>, which means it keeps evolving with more features, and anyone can <a href="https://github.com/cloudflare/wildebeest/pulls">extend</a> and improve it.</p><p>Here's what we support today:</p><ul><li><p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a>, <a href="https://www.rfc-editor.org/rfc/rfc7033">WebFinger</a>, <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/nodeinfo">NodeInfo</a>, <a href="https://datatracker.ietf.org/doc/html/rfc8030">WebPush</a> and <a href="https://docs.joinmastodon.org/api/">Mastodon-compatible</a> APIs. Wildebeest can connect to or receive connections from other Fediverse servers.</p></li><li><p>Compatible with the most popular Mastodon <a href="https://github.com/nolanlawson/pinafore">web</a> (like <a href="https://github.com/nolanlawson/pinafore">Pinafore</a>), desktop, and <a href="https://joinmastodon.org/apps">mobile clients</a>. We also provide a simple read-only web interface to explore the timelines and user profiles.</p></li><li><p>You can publish, edit, boost, or delete posts, sorry, toots. We support text, images, and (soon) video.</p></li><li><p>Anyone can follow you; you can follow anyone.</p></li><li><p>You can search for content.</p></li><li><p>You can register one or multiple accounts under your instance. Authentication can be email-based on or using any Cloudflare Access compatible IdP, like GitHub or Google.</p></li><li><p>You can edit your profile information, avatar, and header image.</p></li></ul>
    <div>
      <h2>How we built it</h2>
      <a href="#how-we-built-it">
        
      </a>
    </div>
    <p>Our implementation is built entirely on top of our <a href="https://www.cloudflare.com/cloudflare-product-portfolio/">products</a> and <a href="https://developers.cloudflare.com/">APIs</a>. Building Wildebeest was another excellent opportunity to showcase our technology stack's power and versatility and prove how anyone can also use Cloudflare to build larger applications that involve multiple systems and complex requirements.</p><p>Here's a birds-eye diagram of Wildebeest's architecture:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33R5UHXYSBDBUsoFLMkoC8/0304880c93af0a41d168616da4c73b90/Screenshot-2023-02-08-at-10.58.01-AM.png" />
            
            </figure><p>Let's get into the details and get technical now.</p>
    <div>
      <h3>Cloudflare Pages</h3>
      <a href="#cloudflare-pages">
        
      </a>
    </div>
    <p>At the core, Wildebeest is a <a href="https://pages.cloudflare.com/">Cloudflare Pages</a> project running its code using <a href="https://developers.cloudflare.com/pages/platform/functions/">Pages Functions</a>. Cloudflare Pages provides an excellent foundation for building and deploying your application and serving your bundled assets, Functions gives you full access to the Workers ecosystem, where you can run any code.</p><p>Functions has a built-in <a href="https://developers.cloudflare.com/pages/platform/functions/routing/">file-based router</a>. The <a href="https://github.com/cloudflare/wildebeest/tree/main/functions">/functions</a> directory structure, which is uploaded by Wildebeest’s continuous deployment builds, defines your application routes and what files and code will process each HTTP endpoint request. This routing technique is similar to what other frameworks like Next.js <a href="https://nextjs.org/docs/routing/introduction">use</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5NsNlFYtyqKVzhFFBuGeRW/767c9b102b9d97ad067c343df387c5db/2b.png" />
            
            </figure><p>For example, Mastodon’s <a href="https://docs.joinmastodon.org/methods/timelines/#public">/api/v1/timelines/public</a> API endpoint is handled by <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/api/v1/timelines/public.ts">/functions/api/v1/timelines/public.ts</a> with the onRequest method.</p>
            <pre><code>export onRequest = async ({ request, env }) =&gt; {
	const { searchParams } = new URL(request.url)
	const domain = new URL(request.url).hostname
...
	return handleRequest(domain, env.DATABASE, {})
}

export async function handleRequest(
    …
): Promise&lt;Response&gt; {
    …
}
</code></pre>
            <p>Unit testing these endpoints becomes easier too, since we only have to call the handleRequest() function from the testing framework. Check one of our <a href="https://jestjs.io/">Jest</a> tests, <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/test/mastodon.spec.ts">mastodon.spec.ts</a>:</p>
            <pre><code>import * as v1_instance from 'wildebeest/functions/api/v1/instance'

describe('Mastodon APIs', () =&gt; {
	describe('instance', () =&gt; {
		test('return the instance infos v1', async () =&gt; {
			const res = await v1_instance.handleRequest(domain, env)
			assert.equal(res.status, 200)
			assertCORS(res)

			const data = await res.json&lt;Data&gt;()
			assert.equal(data.rules.length, 0)
			assert(data.version.includes('Wildebeest'))
		})
       })
})
</code></pre>
            <p>As with any other regular Worker, Functions also lets you set up <a href="https://developers.cloudflare.com/pages/platform/functions/bindings/">bindings</a> to interact with other Cloudflare products and features like <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">KV</a>, <a href="https://developers.cloudflare.com/r2/data-access/workers-api/workers-api-reference/">R2</a>, <a href="https://developers.cloudflare.com/d1/">D1</a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/durable-objects/">Durable Objects</a>, and more. The list keeps growing.</p><p>We use Functions to implement a large portion of the official <a href="https://docs.joinmastodon.org/api/">Mastodon API</a> specification, making Wildebeest compatible with the existing ecosystem of other servers and client applications, and also to run our own read-only web frontend under the same project codebase.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Wz8EZKQyMvyEfDvH7cOV9/02183c976fe7c619c2fc4f8e99795463/3b.png" />
            
            </figure><p>Wildebeest’s web frontend uses <a href="https://qwik.builder.io/">Qwik</a>, a general-purpose web framework that is optimized for speed, uses modern concepts like the JSX JavaScript syntax extension and supports server-side-rendering (SSR) and static site generation (SSG).</p><p>Qwik provides a <a href="https://qwik.builder.io/integrations/deployments/cloudflare-pages/">Cloudflare Pages Adaptor</a> out of the box, so we use that (check our <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-qwik-site/">framework guide</a> to know more about how to deploy a Qwik site on Cloudflare Pages). For styling we use the <a href="https://tailwindcss.com/">Tailwind CSS</a> framework, which Qwik supports natively.</p><p>Our frontend website code and static assets can be found under the <a href="https://github.com/cloudflare/wildebeest/tree/main/frontend">/frontend</a> directory. The application is handled by the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/%5B%5Bpath%5D%5D.ts">/functions/[[path]].js</a> dynamic route, which basically catches all the non-API requests, and then <a href="https://github.com/cloudflare/wildebeest/blob/main/frontend/src/entry.cloudflare-pages.tsx">invokes</a> Qwik’s own internal router, <a href="https://qwik.builder.io/qwikcity/routing/overview/">Qwik City</a>, which takes over everything else after that.</p><p>The power and versatility of Pages and Functions routes make it possible to run both the backend APIs and a server-side-rendered dynamic client, effectively a full-stack app, under the same project.</p><p>Let's dig even deeper now, and understand how the server interacts with the other components in our architecture.</p>
    <div>
      <h3>D1</h3>
      <a href="#d1">
        
      </a>
    </div>
    <p>Wildebeest uses <a href="https://developers.cloudflare.com/d1/">D1</a>, <a href="https://www.cloudflare.com/developer-platform/products/d1/">Cloudflare’s first SQL database</a> for the Workers platform built on top of SQLite, now open to everyone in <a href="/d1-open-alpha/">alpha</a>, to store and query data. Here’s our schema:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/67Xq9kbn6qh2XgRveGSdHt/3a020d6c71a89f0020b8fb7e87433601/4b.png" />
            
            </figure><p>The schema will probably change in the future, as we add more features. That’s fine, D1 supports <a href="https://developers.cloudflare.com/d1/platform/migrations/">migrations</a> which are great when you need to update your database schema without losing your data. With each new Wildebeest version, we can create a <a href="https://github.com/cloudflare/wildebeest/blob/main/migrations/0001_add-unique-following.sql">new migration</a> file if it requires database schema changes.</p>
            <pre><code>-- Migration number: 0001 	 2023-01-16T13:09:04.033Z

CREATE UNIQUE INDEX unique_actor_following ON actor_following (actor_id, target_actor_id);
</code></pre>
            <p>D1 exposes a powerful <a href="https://developers.cloudflare.com/d1/platform/client-api/">client API</a> that developers can use to manipulate and query data from Worker scripts, or in our case, Pages Functions.</p><p>Here’s a simplified example of how we interact with D1 when you start following someone on the Fediverse:</p>
            <pre><code>export async function addFollowing(db, actor, target, targetAcct): Promise&lt;UUID&gt; {
	const query = `INSERT OR IGNORE INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) VALUES (?, ?, ?, ?, ?)`
	const out = await db
		.prepare(query)
		.bind(id, actor.id.toString(), target.id.toString(), STATE_PENDING, targetAcct)
		.run()
	return id
}
</code></pre>
            <p>Cloudflare’s culture of dogfooding and building on top of our own products means that we sometimes experience their shortcomings before our users. We did face a few challenges using D1, which is built on SQLite, to store our data. Here are two examples.</p><p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> uses <a href="https://www.rfc-editor.org/rfc/rfc4122.txt">UUIDs</a> to identify objects and reference them in URIs extensively. These objects need to be stored in the database. Other databases like PostgreSQL provide built-in functions to <a href="https://www.postgresql.org/docs/current/functions-uuid.html">generate unique identifiers</a>. SQLite and D1 don't have that, yet, it’s in our roadmap.</p><p>Worry not though, the Workers runtime supports <a href="https://developers.cloudflare.com/workers/runtime-apis/web-crypto/">Web Crypto</a>, so we use crypto.randomUUID() to get our unique identifiers. Check the <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/activitypub/actors/inbox.ts">/backend/src/activitypub/actors/inbox.ts</a>:</p>
            <pre><code>export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}</code></pre>
            <p>Problem solved.</p><p>The other example is that we need to store dates with sub-second resolution. Again, databases like PostgreSQL have that:</p>
            <pre><code>psql&gt; select now();
2023-02-01 11:45:17.425563+00</code></pre>
            <p>However SQLite falls short with:</p>
            <pre><code>sqlite&gt; select datetime();
2023-02-01 11:44:02</code></pre>
            <p>We worked around this problem with a small hack using <a href="https://www.sqlite.org/lang_datefunc.html">strftime()</a>:</p>
            <pre><code>sqlite&gt; select strftime('%Y-%m-%d %H:%M:%f', 'NOW');
2023-02-01 11:49:35.624</code></pre>
            <p>See our <a href="https://github.com/cloudflare/wildebeest/blob/main/migrations/0000_initial.sql">initial SQL schema</a>, look for the <i>cdate</i> defaults.</p>
    <div>
      <h3>Images</h3>
      <a href="#images">
        
      </a>
    </div>
    <p>Mastodon content has a lot of rich media. We don't need to reinvent the wheel and build an image pipeline; Cloudflare Images <a href="https://developers.cloudflare.com/images/">provides APIs</a> to upload, transform, and serve optimized images from our global CDN, so it's the perfect fit for Wildebeest's requirements.</p><p>Things like posting content images, the profile avatar, or headers, all use the Images APIs. See <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/media/image.ts">/backend/src/media/image.ts</a> to understand how we interface with Images.</p>
            <pre><code>async function upload(file: File, config: Config): Promise&lt;UploadResult&gt; {
	const formData = new FormData()
	const url = `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/images/v1`

	formData.set('file', file)

	const res = await fetch(url, {
		method: 'POST',
		body: formData,
		headers: {
			authorization: 'Bearer ' + config.apiToken,
		},
	})

      const data = await res.json()
	return data.result
}</code></pre>
            <p>If you're curious about Images for your next project, here's a tutorial on <a href="https://developers.cloudflare.com/images/cloudflare-images/tutorials/integrate-cloudflare-images/">how to integrate Cloudflare Images</a> on your website.</p><p>Cloudflare Images is also available from the dashboard. You can use it to browse or manage your catalog quickly.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1A4gwBFdbSGDvS4DAJRyhR/95849178c4b10c82d5f619ffc1153ba0/5b.png" />
            
            </figure>
    <div>
      <h3>Queues</h3>
      <a href="#queues">
        
      </a>
    </div>
    <p>The <a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> protocol is chatty by design. Depending on the size of your social graph, there might be a lot of back-and-forth HTTP traffic. We can’t have the clients blocked waiting for hundreds of Fediverse message deliveries every time someone posts something.</p><p>We needed a way to work asynchronously and launch background jobs to offload data processing away from the main app and keep the clients snappy. The official Mastodon server has a similar strategy using <a href="https://docs.joinmastodon.org/admin/scaling/#sidekiq">Sidekiq</a> to do background processing.</p><p>Fortunately, we don't need to worry about any of this complexity either. <a href="https://developers.cloudflare.com/queues/">Cloudflare Queues</a> allows developers to send and receive messages with guaranteed delivery, and offload work from your Workers' requests, effectively providing you with asynchronous batch job capabilities.</p><p>To put it simply, you have a queue topic identifier, which is basically a buffered list that scales automatically, then you have one or more producers that, well, produce structured messages, JSON objects in our case, and put them in the queue (you define their schema), and finally you have one or more consumers that subscribes that queue, receive its messages and process them, at their own speed.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5m1TSSTZesMX1jt7K7YpHS/c192aa543426e12c03b2c753f4e4b8c4/6b.png" />
            
            </figure><p>Here’s the <a href="https://developers.cloudflare.com/queues/learning/how-queues-works/">How Queues works</a> page for more information.</p><p>In our case, the main application produces queue jobs whenever any incoming API call requires long, expensive operations. For example, when someone posts, sorry, <i>toots</i> something, we need to broadcast that to their followers' inboxes, potentially triggering many requests to remote servers. <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/activitypub/deliver.ts">Here we are</a> queueing a job for that, thus freeing the APIs to keep responding:</p>
            <pre><code>export async function deliverFollowers(
	db: D1Database,
	from: Actor,
	activity: Activity,
	queue: Queue
) {
	const followers = await getFollowers(db, from)

	const messages = followers.map((id) =&gt; {
		const body = {
			activity: JSON.parse(JSON.stringify(activity)),
			actorId: from.id.toString(),
			toActorId: id,
		}
		return { body }
	})

	await queue.sendBatch(messages)
}</code></pre>
            <p>Similarly, we don't want to stop the main APIs when remote servers deliver messages to our instance inboxes. Here's Wildebeest creating asynchronous jobs when it <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/ap/users/%5Bid%5D/inbox.ts">receives messages</a> in the inbox:</p>
            <pre><code>export async function handleRequest(
	domain: string,
	db: D1Database,
	id: string,
	activity: Activity,
	queue: Queue,
): Promise&lt;Response&gt; {
	const handle = parseHandle(id)

	const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)

      // creates job
	await queue.send({
		type: MessageType.Inbox,
		actorId: actor.id.toString(),
		activity,
	})

	// frees the API
	return new Response('', { status: 200 })
}</code></pre>
            <p>And the final piece of the puzzle, our <a href="https://github.com/cloudflare/wildebeest/tree/main/consumer">queue consumer</a> runs in a separate Worker, independently from the Pages project. The consumer listens for new messages and processes them sequentially, at its rhythm, freeing everyone else from blocking. When things get busy, the queue grows its buffer. Still, things keep running, and the jobs will eventually get dispatched, freeing the main APIs for the critical stuff: responding to remote servers and clients as quickly as possible.</p>
            <pre><code>export default {
	async queue(batch, env, ctx) {
		for (const message of batch.messages) {
			…

			switch (message.body.type) {
				case MessageType.Inbox: {
					await handleInboxMessage(...)
					break
				}
				case MessageType.Deliver: {
					await handleDeliverMessage(...)
					break
				}
			}
		}
	},
}</code></pre>
            <p>If you want to get your hands dirty with Queues, here’s a simple example on <a href="https://developers.cloudflare.com/queues/examples/send-errors-to-r2/">Using Queues to store data in R2</a>.</p>
    <div>
      <h3>Caching and Durable Objects</h3>
      <a href="#caching-and-durable-objects">
        
      </a>
    </div>
    <p>Caching repetitive operations is yet another strategy for improving performance in complex applications that require data processing. A famous Netscape developer, Phil Karlton, once said: "There are only two hard things in Computer Science: <b>cache invalidation</b> and naming things."</p><p>Cloudflare obviously knows a lot about caching since <a href="https://developers.cloudflare.com/cache/">it's a core feature</a> of our global CDN. We also provide <a href="https://developers.cloudflare.com/workers/learning/how-kv-works/">Workers KV</a> to our customers, a global, low-latency, key-value data store that anyone can use to cache data objects in our data centers and build fast websites and applications.</p><p>However, KV achieves its performance by being eventually consistent. While this is fine for many applications and use cases, it's not ideal for others.</p><p>The ActivityPub protocol is highly transactional and can't afford eventual consistency. Here's an example: generating complete timelines is expensive, so we cache that operation. However, when you post something, we need to invalidate that cache before we reply to the client. Otherwise, the new post won't be in the timeline and the client can fail with an error because it doesn’t see it. This actually happened to us with one of the most popular clients.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7mBZfs5UZumkHzh9ITpUSn/1f9d5e53e7d61417d962a9fd566df9e6/7b.png" />
            
            </figure><p>We needed to get clever. The team discussed a few options. Fortunately, our API catalog has plenty of options. Meet <a href="https://developers.cloudflare.com/workers/learning/using-durable-objects/">Durable Objects</a>.</p><p>Durable Objects are single-instance Workers that provide a transactional storage API. They're ideal when you need central coordination, strong consistency, and state persistence. You can use Durable Objects in cases like handling the state of <a href="https://developers.cloudflare.com/workers/learning/using-websockets/#durable-objects-and-websocket-state">multiple WebSocket</a> connections, coordinating and routing messages in a <a href="https://github.com/cloudflare/workers-chat-demo">chatroom</a>, or even <a href="/doom-multiplayer-workers/">running a multiplayer game like Doom</a>.</p><p>You know where this is going now. Yes, we implemented our key-value caching subsystem for Wildebeest <a href="https://github.com/cloudflare/wildebeest/tree/main/do">on top of a Durable Object</a>. By taking advantage of the DO's native transactional storage API, we can have strong guarantees that whenever we create or change a key, the next read will always return the latest version.</p><p>The idea is so simple and effective that it took us literally a <a href="https://github.com/cloudflare/wildebeest/blob/main/do/src/index.ts">few lines of code</a> to implement a key-value cache with two primitives: HTTP PUT and GET.</p>
            <pre><code>export class WildebeestCache {
	async fetch(request: Request) {
		if (request.method === 'GET') {
			const { pathname } = new URL(request.url)
			const key = pathname.slice(1)
			const value = await this.storage.get(key)
			return new Response(JSON.stringify(value))
		}

		if (request.method === 'PUT') {
			const { key, value } = await request.json()
			await this.storage.put(key, value)
			return new Response('', { status: 201 })
		}
	}
}</code></pre>
            <p>Strong consistency it is. Let's move to user registration and authentication now.</p>
    <div>
      <h3>Zero Trust Access</h3>
      <a href="#zero-trust-access">
        
      </a>
    </div>
    <p>The official Mastodon server <a href="https://docs.joinmastodon.org/user/signup/">handles user registrations</a>, typically using email, before you can choose your local username and start using the service. Handling user registration and authentication can be daunting and time-consuming if we were to build it from scratch though.</p><p>Furthermore, people don't want to create new credentials for every new service they want to use and instead want more convenient OAuth-like authorization and authentication methods so that they can reuse their existing Apple, Google, or GitHub accounts.</p><p>We wanted to simplify things using Cloudflare’s built-in features. Needless to say, we have a product that handles user onboarding, authentication, and <a href="https://developers.cloudflare.com/cloudflare-one/policies/access/policy-management/">access policies</a> to any application behind Cloudflare; it's called <a href="https://developers.cloudflare.com/cloudflare-one/">Zero Trust</a>. So we put Wildebeest behind it.</p><p>Zero Trust Access can either do one-time PIN (<a href="https://en.wikipedia.org/wiki/One-time_password">OTP</a>) authentication using email or single-sign-on (SSO) with many identity providers (examples: Google, Facebook, GitHub, LinkedIn), including any generic one supporting <a href="https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/generic-saml/">SAML 2.0</a>.</p><p>When you start using Wildebeest with a client, you don't need to register at all. Instead, you go straight to log in, which will redirect you to the Access page and handle the authentication according to the policy that you, the owner of your instance, configured.</p><p>The policy defines who can authenticate, and how.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1zDpfgueYrKRmhmNvHCBGX/68b6b579fcb33110566b07ea6e5a3d3e/8b.png" />
            
            </figure><p>When authenticated, Access will redirect you back to Wildebeest. The first time this happens, we will detect that we don't have information about the user and ask for your Username and Display Name. This will be asked only once and is what will be to create your public Mastodon profile.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/76J7DmTtShD7slpawYXNAE/ccc908ed0dffb75a7ce6afc7b0b55510/9b.png" />
            
            </figure><p>Technically, Wildebeest implements the <a href="https://docs.joinmastodon.org/spec/oauth/#implementation">OAuth 2 specification</a>. <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/">Zero Trust</a> protects the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/oauth/authorize.ts">/oauth/authorize</a> endpoint and issues a valid <a href="https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/">JWT token</a> in the request headers when the user is authenticated. Wildebeest then reads and verifies the JWT and returns an authorization code in the URL redirect.</p><p>Once the client has an authorization code, it can use the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/oauth/token.ts">/oauth/token</a> endpoint to obtain an API access token. Subsequent API calls inject a bearer token in the Authorization header:</p><p><code>Authorization: Bearer access_token</code></p>
    <div>
      <h3>Deployment and Continuous Integration</h3>
      <a href="#deployment-and-continuous-integration">
        
      </a>
    </div>
    <p>We didn't want to run a managed service for Mastodon as it would somewhat diminish the concepts of federation and data ownership. Also, we recognize that ActivityPub and Mastodon are emerging, fast-paced technologies that will evolve quickly and in ways that are difficult to predict just yet.</p><p>For these reasons, we thought the best way to help the ecosystem right now would be to provide an open-source software package that anyone could use, customize, improve, and deploy on top of our cloud. Cloudflare will obviously keep improving Wildebeest and support the community, but we want to give our Fediverse maintainers complete control and ownership of their instances and data.</p><p>The remaining question was, how do we distribute the Wildebeest bundle and make it easy to deploy into someone's account when it requires configuring so many Cloudflare features, and how do we facilitate updating the software over time?</p><p>The solution ended up being a clever mix of using GitHub with <a href="https://github.com/features/actions">GitHub Actions</a>, <a href="https://developers.cloudflare.com/workers/platform/deploy-button/">Deploy with Workers</a>, and <a href="https://github.com/cloudflare/terraform-provider-cloudflare">Terraform</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5V8fRfu3U03n2ZVNtsh01L/404637763a8476b425a562ff5bbf8739/Screenshot-2023-02-08-at-11.13.05-AM-1.png" />
            
            </figure><p>The Deploy with Workers button is a specially crafted link that auto-generates a workflow page where the user gets asked some questions, and Cloudflare handles authorizing GitHub to deploy to Workers, automatically forks the Wildebeest repository into the user's account, and then configures and deploys the project using a <a href="https://github.com/marketplace/actions/deploy-to-cloudflare-workers-with-wrangler">GitHub Actions</a> workflow.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3MhyoOAbQEjlNnEhwl70Jm/5000c8c1dc1dfea549ee6ca62f8460b4/10b.png" />
            
            </figure><p>A GitHub Actions <a href="https://docs.github.com/en/actions/using-workflows/about-workflows">workflow</a> is a YAML file that declares what to do in every step. Here’s the <a href="https://github.com/cloudflare/wildebeest/blob/main/.github/workflows/deploy.yml">Wildebeest workflow</a> (simplified):</p>
            <pre><code>name: Deploy
on:
  push:
    branches:
      - main
  repository_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - name: Ensure CF_DEPLOY_DOMAIN and CF_ZONE_ID are defined
        ...
      - name: Create D1 database
        uses: cloudflare/wrangler-action@2.0.0
        with:
          command: d1 create wildebeest-${{ env.OWNER_LOWER }}
        ...
      - name: retrieve Zero Trust organization
        ...
      - name: retrieve Terraform state KV namespace
        ...
      - name: download VAPID keys
        ...
      - name: Publish DO
      - name: Configure
        run: terraform plan &amp;&amp; terraform apply -auto-approve
      - name: Create Queue
        ...
      - name: Publish consumer
        ...
      - name: Publish
        uses: cloudflare/wrangler-action@2.0.0
        with:
          command: pages publish --project-name=wildebeest-${{ env.OWNER_LOWER }} .</code></pre>
            
    <div>
      <h4>Updating Wildebeest</h4>
      <a href="#updating-wildebeest">
        
      </a>
    </div>
    <p>This workflow runs automatically every time the main branch changes, so updating the Wildebeest is as easy as synchronizing the upstream official repository with the fork. You don't even need to use git commands for that; GitHub provides a convenient Sync button in the UI that you can simply click.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6vkcs7XzLMZdihq7z5n2L5/b83e8499970012ebcf0b47e686b6518a/11b.png" />
            
            </figure><p>What's more? Updates are incremental and non-destructive. When the GitHub Actions workflow redeploys Wildebeest, we only make the necessary changes to your configuration and nothing else. You don't lose your data; we don't need to delete your existing configurations. Here’s how we achieved this:</p><p>We use <a href="https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs">Terraform</a>, a declarative configuration language and tool that interacts with our APIs and can query and configure your Cloudflare features. Here's the trick, whenever we apply a new configuration, we keep a copy of the Terraform state for Wildebeest in a <a href="https://developers.cloudflare.com/workers/learning/how-kv-works/">Cloudflare KV</a> key. When a new deployment is triggered, we get that state from the KV copy, calculate the differences, then change only what's necessary.</p><p>Data loss is not a problem either because, as you read above, D1 supports <a href="https://developers.cloudflare.com/d1/platform/migrations/">migrations</a>. If we need to add a new column to a table or a new table, we don't need to destroy the database and create it again; we just apply the necessary SQL to that change.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3UW6Qm1KE662wiFVgrWFPZ/ba80730c09161abc81c85bb89fd758b3/12b.png" />
            
            </figure>
    <div>
      <h3>Protection, optimization and observability, naturally</h3>
      <a href="#protection-optimization-and-observability-naturally">
        
      </a>
    </div>
    <p>Once Wildebeest is up and running, you can protect it from bad traffic and malicious actors. Cloudflare offers you <a href="https://www.cloudflare.com/ddos/">DDoS</a>, <a href="https://www.cloudflare.com/waf/">WAF</a>, and <a href="https://www.cloudflare.com/products/bot-management/">Bot Management</a> protection out of the box at a click's distance.</p><p>Likewise, you'll get instant network and content delivery optimizations from our products and <a href="https://www.cloudflare.com/analytics/">analytics</a> on how your Wildebeest instance is performing and being used.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4EYUh9pE5NNPnpj9mwVSfz/7d97cf99ad29cd9436b8e48c7918cd24/13b.png" />
            
            </figure>
    <div>
      <h3>ActivityPub, WebFinger, NodeInfo and Mastodon APIs</h3>
      <a href="#activitypub-webfinger-nodeinfo-and-mastodon-apis">
        
      </a>
    </div>
    <p>Mastodon popularized the Fediverse concept, but many of the underlying technologies used have been around for quite a while. This is one of those rare moments when everything finally comes together to create a working platform that answers an actual use case for Internet users. Let's quickly go through the protocols that Wildebeest had to implement:</p>
    <div>
      <h4>ActivityPub</h4>
      <a href="#activitypub">
        
      </a>
    </div>
    <p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> is a decentralized social networking protocol and has been around as a W3C recommendation since at least 2018. It defines client APIs for creating and manipulating content and server-to-server APIs for content exchange and notifications, also known as federation. ActivityPub uses <a href="https://www.w3.org/TR/activitystreams-core/">ActivityStreams</a>, an even older W3C protocol, for its vocabulary.</p><p>The concepts of <a href="https://www.w3.org/TR/activitypub/#actors">Actors</a> (profiles), messages or <a href="https://www.w3.org/TR/activitypub/#obj">Objects</a> (the toots), <a href="https://www.w3.org/TR/activitypub/#inbox">inbox</a> (where you receive toots from people you follow), and <a href="https://www.w3.org/TR/activitypub/#outbox">outbox</a> (where you send your toots to the people you follow), to name a few of many other actions and activities, are all defined on the ActivityPub specification.</p><p>Here’s our folder with the <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/activitypub">ActivityPub implementation</a>.</p>
            <pre><code>import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'

export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}
</code></pre>
            
    <div>
      <h4>WebFinger</h4>
      <a href="#webfinger">
        
      </a>
    </div>
    <p>WebFinger is a simple HTTP protocol used to discover information about any entity, like a profile, a server, or a specific feature. It resolves URIs to resource objects.</p><p>Mastodon uses <a href="https://www.rfc-editor.org/rfc/rfc7033">WebFinger</a> lookups to discover information about remote users. For example, say you want to interact with @<a>user@example.com</a>. Your local server would <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/webfinger/index.ts">request</a> <a href="https://example.com/.well-known/webfinger?resource=acct:user@example.com">https://example.com/.well-known/webfinger?resource=acct:user@example.com</a> (using the <a href="https://www.rfc-editor.org/rfc/rfc7565">acct scheme</a>) and get something like this:</p>
            <pre><code>{
    "subject": "acct:user@example.com",
    "aliases": [
        "https://example.com/ap/users/user"
    ],
    "links": [
        {
            "rel": "self",
            "type": "application/activity+json",
            "href": "https://example.com/ap/users/user"
        }
    ]
}
</code></pre>
            <p>Now we know how to interact with <code>@user@example.com</code>, using the <code>https://example.com/ap/users/user endpoint</code>.</p><p>Here’s our WebFinger <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/.well-known/webfinger.ts">response</a>:</p>
            <pre><code>export async function handleRequest(request, db): Promise&lt;Response&gt; {
	…
	const jsonLink = /* … link to actor */

	const res: WebFingerResponse = {
		subject: `acct:...`,
		aliases: [jsonLink],
		links: [
			{
				rel: 'self',
				type: 'application/activity+json',
				href: jsonLink,
			},
		],
	}
	return new Response(JSON.stringify(res), { headers })
}</code></pre>
            
    <div>
      <h4>Mastodon API</h4>
      <a href="#mastodon-api">
        
      </a>
    </div>
    <p>Finally, things like setting your server information, profile information, generating timelines, notifications, and searches, are all Mastodon-specific APIs. The Mastodon open-source project defines a catalog of REST APIs, and you can find all the documentation for them on <a href="https://docs.joinmastodon.org/api/">their website</a>.</p><p>Our Mastodon API implementation can be found <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/api">here</a> (REST endpoints) and <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/mastodon">here</a> (backend primitives). Here’s an example of Mastodon’s server information <a href="https://docs.joinmastodon.org/methods/instance/#v2">/api/v2/instance</a> implemented by <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/api/v2/instance.ts">Wildebeest</a>:</p>
            <pre><code>export async function handleRequest(domain, db, env) {

	const res: InstanceConfigV2 = {
		domain,
		title: env.INSTANCE_TITLE,
		version: getVersion(),
		source_url: 'https://github.com/cloudflare/wildebeest',
		description: env.INSTANCE_DESCR,
		thumbnail: {
			url: DEFAULT_THUMBNAIL,
		},
		languages: ['en'],
		registrations: {
			enabled: false,
		},
		contact: {
			email: env.ADMIN_EMAIL,
		},
		rules: [],
	}

	return new Response(JSON.stringify(res), { headers })
}</code></pre>
            <p>Wildebeest also implements <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/webpush">WebPush</a> for client notifications and <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/nodeinfo">NodeInfo</a> for server information.</p><p>Other Mastodon-compatible servers had to implement all these protocols <a href="https://pleroma.social/">too</a>; Wildebeest is one of them. The community is very active in discussing future enhancements; we will keep improving our compatibility and adding support to more features over time, ensuring that Wildebeest plays well with the Fediverse ecosystem of servers and clients emerging.</p>
    <div>
      <h3>Get started now</h3>
      <a href="#get-started-now">
        
      </a>
    </div>
    <p>Enough about technology; let's get you into the Fediverse. We tried to detail all the steps to deploy your server. To start using Wildebeest, head to the public GitHub repository and check our <a href="https://github.com/cloudflare/wildebeest/blob/main/README.md">Get Started tutorial</a>.</p><p>Most of Wildebeest's dependencies offer a generous free plan that allows you to try them for personal or hobby projects that aren't business-critical, however you will need to subscribe an <a href="https://www.cloudflare.com/products/cloudflare-images/">Images</a> plan (the lowest tier should be enough for most needs) and, depending on your server load, <a href="https://developers.cloudflare.com/workers/platform/limits/#unbound-usage-model">Workers Unbound</a> (again, the minimum cost should be plenty for most use cases).</p><p>Following our dogfooding mantra, Cloudflare is also officially joining the Fediverse today. You can start following our Mastodon accounts and get the same experience of having regular updates from Cloudflare as you get from us on other social platforms, using your favorite Mastodon apps. These accounts are entirely running on top of a Wildebeest server:</p><ul><li><p><a href="https://cloudflare.social/@cloudflare">@cloudflare@cloudflare.social</a> - Our main account</p></li><li><p><a href="https://cloudflare.social/@radar">@radar@cloudflare.social</a> - Cloudflare Radar</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2QJsY2PkGqLfVQCDJc1AlH/d52823cfd8d2d9e7de9845686790a3bf/14b.png" />
            
            </figure><p>Wildebeest is compatible with most client apps; we are confirmed to work with the official Mastodon <a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android">Android</a> and <a href="https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974">iOS</a> apps, <a href="https://pinafore.social/">Pinafore</a>, <a href="https://mastodon.social/@JPEGuin/109315609418460036">Mammoth</a>, and <a href="https://tooot.app/">tooot</a>, and looking into others like <a href="https://tapbots.com/ivory/">Ivory</a>. If your favorite isn’t working, please submit an <a href="https://github.com/cloudflare/wildebeest/issues">issue here</a>, we’ll do our best to help support it.</p>
    <div>
      <h3>Final words</h3>
      <a href="#final-words">
        
      </a>
    </div>
    <p>Wildebeest was built entirely on top of our <a href="/welcome-to-the-supercloud-and-developer-week-2022/">Supercloud</a> stack. It was one of the most complete and complex projects we have created that uses various Cloudflare products and features.</p><p>We hope this write-up inspires you to not only try deploying Wildebeest and joining the Fediverse, but also building your next application, however demanding it is, on top of Cloudflare.</p><p>Wildebeest is a minimally viable Mastodon-compatible server right now, but we will keep improving it with more features and supporting it over time; after all, we're using it for our official accounts. It is also open-sourced, meaning you are more than welcome to contribute with pull requests or feedback.</p><p>In the meantime, we opened a <a href="https://discord.com/channels/595317990191398933/1064925651464896552">Wildebeest room</a> on our <a href="https://discord.gg/cloudflaredev">Developers Discord Server</a> and are keeping an eye open on the GitHub repo <a href="https://github.com/cloudflare/wildebeest/issues">issues</a> tab. Feel free to engage with us; the team is eager to know how you use Wildebeest and answer your questions.</p><p><i>PS: The code snippets in this blog were simplified to benefit readability and space (the TypeScript types and error handling code were removed, for example). Please refer to the GitHub repo links for the complete versions.</i></p> ]]></content:encoded>
            <category><![CDATA[Wildebeest]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[D1]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Zero Trust]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[SASE]]></category>
            <guid isPermaLink="false">5dmHcGVas7xv8tKbRbWLWN</guid>
            <dc:creator>Celso Martinho</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
        <item>
            <title><![CDATA[UPDATE Supercloud SET status = 'open alpha' WHERE product = 'D1';]]></title>
            <link>https://blog.cloudflare.com/d1-open-alpha/</link>
            <pubDate>Wed, 16 Nov 2022 14:01:00 GMT</pubDate>
            <description><![CDATA[ As we continue down the road to making D1 production ready, it wouldn’t be “the Cloudflare way” unless we stopped for feedback first. D1 is now in Open Alpha! ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7w9UvQOVgrNbxPrz1tOWJz/611cdc1253d0c6971709f5dddacc0811/image1-48.png" />
            
            </figure><p>In May 2022, we <a href="/introducing-d1/">announced</a> our quest to simplify databases – building them, maintaining them, integrating them. Our goal is to empower you with the tools to run a database that is <a href="https://www.cloudflare.com/developer-platform/products/d1/">powerful, scalable, with world-beating performance</a> without any hassle. And we first set our sights on reimagining the database development experience for every type of user – not just database experts.</p><p>Over the past couple of months, we’ve <a href="/whats-new-with-d1/">been working</a> to create just that, while learning some very important lessons along the way. As it turns out, building a global relational database product on top of Workers pushes the boundaries of the developer platform to their absolute limit, and often beyond them, but in a way that’s absolutely thrilling to us at Cloudflare. It means that while our progress might seem slow from outside, every improvement, bug fix or stress test helps lay down a path for <i>all</i> of our customers to build the world’s most <a href="/welcome-to-the-supercloud-and-developer-week-2022/">ambitious serverless application</a>.</p><p>However, as we continue down the road to making D1 production ready, it wouldn’t be “the Cloudflare way” unless we stopped for feedback first – even though it’s not <i>quite</i> finished yet. In the spirit of Developer Week, <b>there is no better time to introduce the D1 open alpha</b>!</p><p>An “open alpha” is a new concept for us. You'll likely hear the term “open beta” on various announcements at Cloudflare, and while it makes sense for many products here, it wasn’t quite right for D1. There are still some crucial pieces that are still in active development and testing, so before we release the fully-formed D1 as a public beta for you to start building real-world apps with, we want to make sure everybody can start to get a feel for the product on their hobby apps or side-projects.</p>
    <div>
      <h2>What’s included in the alpha?</h2>
      <a href="#whats-included-in-the-alpha">
        
      </a>
    </div>
    <p>While a lot is still changing behind the scenes with D1, we’ve put a lot of thought into how you, as a developer, interact with it – even if you’re new to databases.</p>
    <div>
      <h3>Using the D1 dashboard</h3>
      <a href="#using-the-d1-dashboard">
        
      </a>
    </div>
    <p>In a few clicks you can get your D1 database up and running right from within your dashboard. In our D1 interface, you can create, maintain and view your database as you please. Changes made in the UI are instantly available to your Worker - no redeploy required!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6vOzmnP9cvUYbJanSvprvl/b4a01d4edcc3dcada5a326e352b5f0e2/image2-30.png" />
            
            </figure>
    <div>
      <h3>Use Wrangler</h3>
      <a href="#use-wrangler">
        
      </a>
    </div>
    <p>If you’re looking to get your hands a little dirty, you can also work with your database using our Wrangler CLI. Create your database and begin adding your data manually or bootstrap your database with one of two ways:</p><p><b>1.  Execute an SQL file</b></p>
            <pre><code>$ wrangler d1 execute my-database-name --file ./customers.sql</code></pre>
            <p>where your <code>.sql</code> file looks something like this:</p><p>customers.sql</p>
            <pre><code>DROP TABLE IF EXISTS Customers;
CREATE TABLE Customers (CustomerID INT, CompanyName TEXT, ContactName TEXT, PRIMARY KEY (`CustomerID`));
INSERT INTO Customers (CustomerID, CompanyName, ContactName) 
VALUES (1, 'Alfreds Futterkiste', 'Maria Anders'),(4, 'Around the Horn', 'Thomas Hardy'),(11, 'Bs Beverages', 'Victoria Ashworth'),(13, 'Bs Beverages', 'Random Name');</code></pre>
            <p><b>2. Create and run migrations</b></p><p>Migrations are a way to version your database changes. With D1, you can <a href="https://developers.cloudflare.com/d1/migrations/">create a migration</a> and then apply it to your database.</p><p>To create the migration, execute:</p>
            <pre><code>wrangler d1 migrations create &lt;my-database-name&gt; &lt;short description of migration&gt;</code></pre>
            <p>This will create an SQL file in a <code>migrations</code> folder where you can then go ahead and add your queries. Then apply the migrations to your database by executing:</p>
            <pre><code>wrangler d1 migrations apply &lt;my-database-name&gt;</code></pre>
            
    <div>
      <h3>Access D1 from within your Worker</h3>
      <a href="#access-d1-from-within-your-worker">
        
      </a>
    </div>
    <p>You can attach your D1 to a Worker by adding the D1 binding to your <code>wrangler.toml</code> configuration file. Then interact with D1 by executing queries inside your Worker like so:</p>
            <pre><code>export default {
 async fetch(request, env) {
   const { pathname } = new URL(request.url);

   if (pathname === "/api/beverages") {
     const { results } = await env.DB.prepare(
       "SELECT * FROM Customers WHERE CompanyName = ?"
     )
       .bind("Bs Beverages")
       .all();
     return Response.json(results);
   }

   return new Response("Call /api/beverages to see Bs Beverages customers");
 },
};</code></pre>
            
    <div>
      <h3>Or access D1 from within your Pages Function</h3>
      <a href="#or-access-d1-from-within-your-pages-function">
        
      </a>
    </div>
    <p>In this Alpha launch, D1 also supports integration with <a href="https://pages.cloudflare.com/">Cloudflare Pages</a>! You can add a D1 binding inside the Pages dashboard, and write your queries inside a Pages Function to build a full-stack application! Check out the <a href="https://developers.cloudflare.com/pages/platform/functions/bindings/#d1-database">full documentation</a> to get started with Pages and D1.</p>
    <div>
      <h2>Community built tooling</h2>
      <a href="#community-built-tooling">
        
      </a>
    </div>
    <p>During our private alpha period, the excitement behind D1 led to some valuable contributions to the D1 ecosystem and developer experience by members of the community. Here are some of our favorite projects to date:</p>
    <div>
      <h3>d1-orm</h3>
      <a href="#d1-orm">
        
      </a>
    </div>
    <p>An Object Relational Mapping (ORM) is a way for you to query and manipulate data by using JavaScript. Created by a Cloudflare Discord Community Champion, the <code>d1-orm</code> seeks to provide a strictly typed experience while using D1:</p>
            <pre><code>const users = new Model(
    // table name, primary keys, indexes etc
    tableDefinition,
    // column types, default values, nullable etc
    columnDefinitions
)

// TS helper for typed queries
type User = Infer&lt;type of users&gt;;

// ORM-style query builder
const user = await users.First({
    where: {
        id: 1,
    },
});</code></pre>
            <p>You can check out the <a href="https://docs.interactions.rest/d1-orm/">full documentation</a>, and provide feedback by making an issue on the <a href="https://github.com/Interactions-as-a-Service/d1-orm/issues">GitHub repository</a>.</p>
    <div>
      <h3>workers-qb</h3>
      <a href="#workers-qb">
        
      </a>
    </div>
    <p>This is a zero-dependency query builder that provides a simple standardized interface while keeping the benefits and speed of using raw queries over a traditional ORM. While not intended to provide ORM-like functionality, <code>workers-qb</code> makes it easier to interact with the database from code for direct SQL access:</p>
            <pre><code>const qb = new D1QB(env.DB)

const fetched = await qb.fetchOne({
  tableName: 'employees',
  fields: 'count(*) as count',
  where: {
    conditions: 'department = ?1',
    params: ['HQ'],
  },
})</code></pre>
            <p>You can read more about the query builder <a href="https://workers-qb.massadas.com/">here</a>.</p>
    <div>
      <h3>d1-console</h3>
      <a href="#d1-console">
        
      </a>
    </div>
    <p>Instead of running the <code>wrangler d1 execute</code> command in your terminal every time you want to interact with your database, you can interact with D1 from within the <code>d1-console</code>. Created by a Discord Community Champion, this gives the benefit of executing multi-line queries, obtaining command history, and viewing a cleanly formatted table output.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4QR9Tf5DXnp3brBVvlvgJq/7f5b5083198492190dfc9f24e4fb70e0/image3-23.png" />
            
            </figure><p>While this is a community project today, we plan to natively support a “D1 Console” in the future. For now, get started by checking out the <code>d1-console</code> package <a href="https://github.com/isaac-mcfadyen/d1-console">here</a>.</p>
    <div>
      <h3>D1 adapter for <a href="https://github.com/koskimas/kysely">Kysely</a></h3>
      <a href="#d1-adapter-for">
        
      </a>
    </div>
    <p>Kysely is a type-safe and autocompletion-friendly typescript SQL query builder. With this adapter you can interact with D1 with the familiar Kysely interface:</p>
            <pre><code>// Create Kysely instance with kysely-d1
const db = new Kysely&lt;Database&gt;({ 
  dialect: new D1Dialect({ database: env.DB })
});
    
// Read row from D1 table
const result = await db
  .selectFrom('kv')
  .selectAll()
  .where('key', '=', key)
  .executeTakeFirst();</code></pre>
            <p>Check out the project <a href="https://github.com/aidenwallis/kysely-d1">here</a>.</p>
    <div>
      <h2>What’s still in testing?</h2>
      <a href="#whats-still-in-testing">
        
      </a>
    </div>
    <p>The biggest pieces that have been disabled for this alpha release are replication and JavaScript transaction support. While we’ll be rolling out these changes gradually, we want to call out some limitations that exist today that we’re actively working on testing:</p><ul><li><p><b>Database location:</b> Each D1 database only runs a single instance. It’s created close to where you, as the developer, create the database, and does not currently move regions based on access patterns. Workers running elsewhere in the world will see higher latency as a result.</p></li><li><p><b>Concurrency limitations:</b> Under high load, read and write queries may be queued rather than triggering new replicas to be created. As a result, the performance &amp; throughput characteristics of the open alpha won’t be representative of the final product.</p></li><li><p><b>Availability limitations:</b> Backups will block access to the DB while they’re running. In most cases this should only be a second or two, and any requests that arrive during the backup will be queued.</p></li></ul><p>You can also check out a more detailed, up-to-date list on <a href="https://developers.cloudflare.com/d1/platform/limits/">D1 alpha Limitations</a>.</p>
    <div>
      <h2>Request for feedback</h2>
      <a href="#request-for-feedback">
        
      </a>
    </div>
    <p>While we can make all sorts of guesses and bets on the kind of databases you want to use D1 for, we are not the users – you are! We want developers from all backgrounds to preview the D1 tech at its early stages, and let us know where we need to improve to make it suitable for your production apps.</p><p>For general feedback about your experience and to interact with other folks in the alpha, join our <a href="https://discord.com/channels/595317990191398933/992060581832032316">#d1-open-alpha</a> channel in the <a href="https://discord.gg/cloudflaredev">Cloudflare Developers Discord</a>. We plan to make any important announcements and changes in this channel as well as on our <a href="https://discord.com/channels/595317990191398933/832698219824807956">monthly community calls</a>.</p><p>To file more specific feature requests (no matter how wacky) and report any bugs, create a thread in the <a href="https://community.cloudflare.com/c/developers/d1">Cloudflare Community forum</a> under the D1 category. We will be maintaining this forum as a way to plan for the months ahead!</p>
    <div>
      <h2>Get started</h2>
      <a href="#get-started">
        
      </a>
    </div>
    <p>Want to get started right away? Check out our <a href="https://developers.cloudflare.com/d1/">D1 documentation</a> to get started today. <a href="https://github.com/cloudflare/d1-northwind">Build</a> our classic <a href="https://northwind.d1sql.com/">Northwind Traders demo</a> to explore the D1 experience and deploy your first D1 database!</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Database]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Supercloud]]></category>
            <category><![CDATA[D1]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">1rFO7pAwS1HGnsa6rhrIXa</guid>
            <dc:creator>Nevi Shah</dc:creator>
            <dc:creator>Glen Maddern</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare's Handling of an RCE Vulnerability in cdnjs]]></title>
            <link>https://blog.cloudflare.com/cloudflares-handling-of-an-rce-vulnerability-in-cdnjs/</link>
            <pubDate>Sat, 24 Jul 2021 12:57:57 GMT</pubDate>
            <description><![CDATA[ Recently, a RCE vulnerability in the way cdnjs’ backend is automatically keeping web resources up to date has been disclosed. Read about how Cloudflare handled the security incident and what will prevent similar exploits in the future. ]]></description>
            <content:encoded><![CDATA[ <p></p><p><a href="https://cdnjs.com/">cdnjs</a> provides JavaScript, CSS, images, and fonts assets for websites to reference with more than 4,000 libraries available. By utilizing cdnjs, websites can load faster with less strain on one’s own origin server as files are served directly from Cloudflare’s edge. Recently, a <a href="https://blog.ryotak.me/post/cdnjs-remote-code-execution-en/">blog post</a> detailed a vulnerability in the way cdnjs’ backend automatically keeps the libraries up to date.</p><p>This vulnerability allowed the researcher to execute arbitrary code, granting the ability to modify assets. This blog post details how Cloudflare responded to this report, including the steps we took to block exploitation, investigate potential abuse, and remediate the vulnerability.</p><p>This vulnerability is not related to Cloudflare CDN. The <i>cdnjs</i> project is a platform that leverages Cloudflare’s services, but the vulnerability described below relates to <i>cdnjs</i>’ platform only. To be clear, no existing libraries were modified using this exploit. The researcher published a new package which demonstrated the vulnerability and our investigation concluded that the integrity of all assets hosted on cdnjs remained intact.</p>
    <div>
      <h3>Disclosure Timeline</h3>
      <a href="#disclosure-timeline">
        
      </a>
    </div>
    <p>As outlined in RyotaK’s blog post, the incident began on 2021-04-06. At around 1100 GMT, RyotaK published a package to npm exploiting the vulnerability. At 1129 GMT, cdnjs processed this package, resulting in a leak of credentials. This triggered GitHub alerting which notified Cloudflare of the exposed secrets.</p><p>Cloudflare disabled the auto-update service and revoked all credentials within an hour. In the meantime, our security team received RyotaK’s remote code execution report through HackerOne. A new version of the auto-update tool which prevents exploitation of the vulnerability RyotaK reported was released within 24 hours.</p><p>Having taken action immediately to prevent exploitation, we then proceeded to redesign the auto-update pipeline. Work to completely redesign it was completed on 2021-06-03.</p>
    <div>
      <h3>Blocking Exploitation</h3>
      <a href="#blocking-exploitation">
        
      </a>
    </div>
    <p>Before RyotaK reported the vulnerability via HackerOne, Cloudflare had already taken action. When GitHub notified us that credentials were leaked, one of our engineers took immediate action and revoked them all. Additionally, the GitHub token associated with this service was automatically revoked by GitHub.</p><p>The second step was to bring the vulnerable service offline to prevent further abuse while we investigated the incident. This prevented exploitation but also made it impossible for legitimate developers to publish updates to their libraries. We wanted to release a fixed version of the pipeline used for retrieving and hosting new library versions so that developers could continue to benefit from caching. However, we understood that a stopgap was not a long term fix, and we decided to review the entire current solution to identify a better design that would improve the overall security of cdnjs.</p>
    <div>
      <h3>Investigation</h3>
      <a href="#investigation">
        
      </a>
    </div>
    <p>Any sort of investigation requires access to logs and all components of our pipeline generate extensive logs that prove valuable for forensics efforts. Logs produced by the auto-update process are collected in a <a href="https://github.com/cdnjs/logs">GitHub repository</a> and sent to our logging pipeline. We also collect and retain logs from cdnjs’ Cloudflare account. Our security team began reviewing this information as soon as we received RyotaK’s initial report. Based on access logs, API token usage, and file modification metadata, we are confident that only RyotaK exploited this vulnerability during his research and only on test files. To rule out abuse, we reviewed the list of source IP addresses that accessed the Workers KV token prior to revoking it and only found one, which belongs to the cdnjs auto-update bot.</p><p>The cdnjs team also reviewed files that were pushed to the <a href="https://github.com/cdnjs/cdnjs">cdnjs/cdnjs GitHub repository</a> around that time and found no evidence of any other abuse across cdnjs.</p>
    <div>
      <h3>Remediating the Vulnerability</h3>
      <a href="#remediating-the-vulnerability">
        
      </a>
    </div>
    <p>Around half of the libraries on cdnjs use <a href="https://www.npmjs.com/">npm</a> to auto-update. The primary vector in this attack was the ability to craft a <code>.tar.gz</code> archive with a symbolic link and publish it to the npm registry. When our pipeline extracted the content it would follow symlinks and overwrite local files using the pipeline user privileges. There are two fundamental issues at play here: an attacker can perform <a href="https://en.wikipedia.org/wiki/Directory_traversal_attack">path traversal</a> on the host processing untrusted files, and the process handling the compressed file is <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege">overly privileged</a>.</p><p>We addressed the path traversal issue by checking that the destination of each file in the tarball will be contained within the target directory that the update process has designated for that package. If the file’s <a href="https://en.wikipedia.org/wiki/Canonicalization">full canonical path</a> doesn’t begin with the destination directory’s full path, we log this as a warning and skip extracting that file. This works fairly well, but as noted in the <a href="https://github.com/cdnjs/tools/pull/220/files#diff-0df1c31d75e0fcc18581b25b3e3e8f7584e0c6acdf38eef60ddcd06d01ac3734R59-R63">comment</a> above this check, if the compressed file uses UTF-8 encoding for filenames, this check may not properly canonicalize the path. If this canonicalization does not occur, the path may contain path traversal, even though it starts with the correct destination path.</p><p>To ensure that other vulnerabilities in cdnjs’ publication pipeline cannot be exploited, we configured an <a href="https://github.com/cdnjs/bot-ansible/pull/24/files">AppArmor profile</a> for it. This limits the <a href="https://man7.org/linux/man-pages/man7/capabilities.7.html">capabilities</a> of the service, so even if an attacker successfully instructed the process to perform an action, the operating system (kernel / security feature) will not allow any action outside of what it is allowed to do.</p><p>For illustration, here’s an example:</p>
            <pre><code>/path/to/bin {
  network,
  signal,
  /path/to/child ix,
  /tmp/ r,
  /tmp/cache** rw,
  ...
}</code></pre>
            <p>In this example, we only allow the binary (/path/to/bin) to:</p><ul><li><p>access all networking</p></li><li><p>use all signals</p></li><li><p>execute /path/to/child (which will inherit the AppArmor profile)</p></li><li><p>read from /tmp</p></li><li><p>read+write under /tmp/cache.</p></li></ul><p>Any attempt to access anything else will be denied. You can find the complete list of capabilities and more information on <a href="https://manpages.ubuntu.com/manpages/precise/en/man5/apparmor.d.5.html">AppArmor’s manual page</a>.</p><p>In the case of cdnjs’ autoupdate tool, we limit execution of applications to a very specific set, and we limit where files can be written.</p><p>Fixing the path traversal and implementing the AppArmor profile prevents similar issues from being exploited. However, having a single layer of defense wasn’t enough. We decided to completely redesign the auto-update process entirely to isolate each step, as well as each library it processes, thus preventing this entire class of attacks.</p>
    <div>
      <h3>Redesigning the system</h3>
      <a href="#redesigning-the-system">
        
      </a>
    </div>
    <p>The main idea behind the redesign of the pipeline was to move away from the monolithic auto-update process. Instead, various operations are done using microservices or daemons which have well-defined scopes. Here’s an overview of the steps:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/NaGyDtiWkMV4J2XvfbReq/4f4aafe30c6fbe07bfd684845dafef3d/image1-18.png" />
            
            </figure><p>First, to detect new library versions, two daemons (for both npm and git based updates) are regularly running. Once a new version has been detected, the files will be downloaded as an archive and placed into the incoming storage bucket.</p><p>Writing a new version in the incoming bucket triggers a function that adds all the information we need to update the library. The function also generates a signed URL allowing for writing in the outgoing bucket, but only in a specific folder for a given library, reducing the blast radius. Finally, a message is placed into a queue to indicate that the new version of the given library is ready to be published.</p><p>A daemon listens for incoming messages and spawns an unprivileged Docker container to handle dangerous operations (archive extraction, minifications, and compression). After the sandbox exits, the daemon will use the signed URL to store the processed files in the outgoing storage bucket.</p><p>Finally, multiple daemons are triggered when the finalized package is written to the outgoing bucket. These daemons publish the assets to cdnjs.cloudflare.com and to the main <a href="https://github.com/cdnjs/cdnjs">cdnjs repository</a>. The daemons also publish the version specific URL, cryptographic hash, and other information to Workers KV, cdnjs.com, and the <a href="https://cdnjs.com/api">API</a>.</p><p>In this revised design, exploiting a similar vulnerability would happen in the sandbox (Docker container) context. The attacker would have access to container files, but nothing else. The container is minimal, ephemeral, has no secrets included, and is dedicated to a single library update, so it cannot affect other libraries’ files.</p>
    <div>
      <h3>Our Commitment to Security</h3>
      <a href="#our-commitment-to-security">
        
      </a>
    </div>
    <p>Beyond maintaining a vulnerability disclosure program, we regularly perform internal security reviews and hire third-party firms to audit the software we develop. But it is through our vulnerability disclosure program that we receive some of the most interesting and creative reports. Each report has helped us improve the security of our services. In this case, we worked with RyotaK to not only address the vulnerability, but to also ensure that their blog post was detailed and accurate. We invite those that find a security issue in any of Cloudflare’s services to report it to us through <a href="https://hackerone.com/cloudflare">HackerOne</a>.</p> ]]></content:encoded>
            <category><![CDATA[CDNJS]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Bug Bounty]]></category>
            <guid isPermaLink="false">666ctQ9OY5rCiI6GZyKm9G</guid>
            <dc:creator>Jonathan Ganz</dc:creator>
            <dc:creator>Thomas Calderon</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
        <item>
            <title><![CDATA[Random Employee Chats at Cloudflare]]></title>
            <link>https://blog.cloudflare.com/random-employee-chats-cloudflare/</link>
            <pubDate>Sat, 20 Mar 2021 12:00:00 GMT</pubDate>
            <description><![CDATA[ We developed the Random Employee Chats application internally, with the goal of recreating the pre-pandemic informal interactions. Here's how we moved from a shared spreadsheet to Cloudflare Workers to automate the entire process. ]]></description>
            <content:encoded><![CDATA[ <p>Due to the COVID-19 pandemic, most Cloudflare offices closed in March 2020, and employees began working from home. Having online meetings presented its own challenges, but preserving the benefits of casual encounters in physical offices was something we struggled with. Those informal interactions, like teams talking next to the coffee machine, help form the social glue that holds companies together.</p><p>In an attempt to recreate that experience, David Wragg, an engineer at Cloudflare, introduced “Random Engineer Chats” (We’re calling them “Random Employee Chats” here since this can be applied to any team). The idea is that participants are randomly paired, and the pairs then schedule a 30-minute video call. There’s no fixed agenda for these conversations, but the participants might learn what is going on in other teams, gain new perspectives on their own work by discussing it, or meet new people.</p><p>The first iteration of Random Employee Chats used a shared spreadsheet to coordinate the process. People would sign up by adding themselves to the spreadsheet, and once a week, David would randomly form pairs from the list and send out emails with the results. Then, each pair would schedule a call at their convenience. This process was the minimum viable implementation of the idea, but it meant that the process relied on a single person.</p>
    <div>
      <h3>Moving to Cloudflare Workers</h3>
      <a href="#moving-to-cloudflare-workers">
        
      </a>
    </div>
    <p>We wanted to automate these repetitive manual tasks, and naturally, we wanted to use <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> to do it. This is a great example of a complete application that runs entirely in Cloudflare Workers on the edge with no backend or origin server.</p><p>The technical requirements included:</p><ul><li><p>A user interface so people can sign up</p></li><li><p>Storage to keep track of the participants</p></li><li><p>A program that automatically pairs participants and notifies each pair</p></li><li><p>A program that reminds people to register for the next sessions</p></li></ul><p>Workers met all of these requirements, and the resulting application runs in Cloudflare's edge network without any need to run code or store data on other platforms. The Workers script supplies the UI that returns static HTML and JavaScript assets, and for storage, Workers KV keeps track of people who signed in.</p><p>We also recently announced <a href="https://developers.cloudflare.com/workers/platform/cron-triggers">Workers Cron Triggers</a> which allow us to run a Cloudflare Workers script on a defined schedule. The Workers Cron Triggers are perfect for pairing people up before the sessions and reminding users to register for the next session.</p>
    <div>
      <h3>The User Interface</h3>
      <a href="#the-user-interface">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/26ih1wWQgNslI8kNjBYq1d/20676f1eef4c8052cc6ec2dda8c06eb7/Random-Engineer-Chat-Dashboard-1.png" />
            
            </figure><p>The interface is very simple. It shows the list of participants and allows users to register for the next session.</p><p>When a user clicks on the register button, it calls an API that adds a key in Workers KV:</p>
            <pre><code>key: register:ID
value: {"name":"Sven Sauleau","picture":"picture.jpg","email":"example@cloudflare.com"}</code></pre>
            <p>User information is stored in Workers KV and displayed in the interface to create the list of participants. The user information gets deleted during pairing so the list is ready for the next round of chats. We require weekly sign-ups from participants who want to participate in the chats to confirm their availability.</p><p>The code for the interface can be found <a href="https://github.com/cloudflare/random-employee-chat/tree/master/src/workers/randengchat/public">here</a> and the API is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/randengchat/server/index.js">here</a>.</p>
    <div>
      <h3>Forming the pairs</h3>
      <a href="#forming-the-pairs">
        
      </a>
    </div>
    <p>A Random Employee Chat is a one-on-one conversation, so at a set time, the application puts participants into pairs. Each Monday morning at 0800 UTC, a Workers cron job runs the pairing script which is deployed using <a href="https://developers.cloudflare.com/workers/cli-wrangler">Wrangler</a>.</p><p>Wrangler supports configuring the schedule for a job using the familiar cron notation. For instance, our wrangler.toml has:</p>
            <pre><code>name = "randengchat-cron-pair"
type = "webpack"
account_id = "..."
webpack_config = "webpack.config.js"
…

kv_namespaces = [...]

[triggers]
crons = ["0 8 * * 2"]</code></pre>
            <p>The pairing script is the most intricate part of the application, so let’s run through its code. First, we list the users that are currently registered. This is done using the <a href="https://developers.cloudflare.com/workers/runtime-apis/kv#listing-by-prefix">list</a> function in Workers KV extracting keys with the prefix <code>register:</code>.</p>
            <pre><code>const list = await KV_NAMESPACE.list({ prefix: "register:" });</code></pre>
            <p>If we don’t have an even number of participants, we remove one person from the list (David!).</p><p>Then, we create all possible pairs and attach a weight to them.</p>
            <pre><code>async function createWeightedPairs() {
  const pairs = [];
  for (let i = 0; i &lt; keys.length - 1; i++) {
    for (let j = i + 1; j &lt; keys.length; j++) {
      const weight = (await countTimesPaired(...)) * -1;
      pairs.push([i, j, weight]);
    }
  }
  return pairs;
}</code></pre>
            <p>For example, suppose four people have registered (Tom, Edie, Ivie and Ada), that’s 6 possible pairs (<a href="https://www.wolframalpha.com/input/?i=4+choose+2">4 choose 2</a>). We might end up with the following pairs and their associated weights:</p>
            <pre><code>(Tom, Edie, 1)
(Tom, Ivie, 0)
(Tom, Ada, 1)
(Edie, Ivie, 2)
(Edie, Ada, 0)
(Ivie, Ada, 2)</code></pre>
            <p>The weight is calculated using the number of times a pair matched in the past to avoid scheduling chats between people that already met. More sophisticated factors could be taken into account, such as the same office or timezone, when they last met, and etc.</p><p>We keep track of how many times the pair matched using a count kept in KV:</p>
            <pre><code>async function countTimesPaired(key) {
  const v = await DB.get(key, "json");
  if (v !== null &amp;&amp; v.count) {
    return v.count;
  }
  return 0;
}</code></pre>
            <p>The people form a complete graph with people as nodes and the edges weighted by the number of times the two people connected by the edge have met.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/anoEru6iUYXNlVFxpCk4w/67d7587b1440044c1a9072545dc8cce6/image5-20.png" />
            
            </figure><p>Next, we run a weighted matching algorithm, in our case the <a href="https://en.wikipedia.org/wiki/Blossom_algorithm">Blossom algorithm</a>, which will find a maximum matching on the graph (a set of edges that maximize the number of pairs of people connected with each person appearing exactly once). As we use the weighted form of the Blossom algorithm we also minimize the path weights. This has the effect of finding the optimal set of pairs minimizing the number of times people have met previously.</p><p>In the case above the algorithm suggests the optimal pairs are  (Tom, Ivie) and (Edie, Ada). In this case, those pairs have never met before.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Fi6gV8b7uZHZMVR7jouFF/0e7ef8ef4cdcd6dde01e4fb637bc964c/image2-18.png" />
            
            </figure><p>The pairs are recorded in Workers KV with their updated matching count to refine the weights at future sessions:</p>
            <pre><code>key: paired:ID
value: {"emails":["left@cloudflare.com","right@cloudflare.com", "count": 1]}</code></pre>
            <p>A notification is sent to each pair of users to notify them that they matched.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7xVSUWDwEFKqY2KS9Mupwj/0e7e502307c3a67fc6e7d124a08a5101/image4-17.png" />
            
            </figure><p>Once the pairing is done, all <code>register:</code> keys are deleted from KV.</p><p>All the pairing logic is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/cron-pair/index.js">here</a>.</p>
    <div>
      <h3>Reminders</h3>
      <a href="#reminders">
        
      </a>
    </div>
    <p>The application sends users a reminder to sign up every week. For the reminder, we use another Workers cron job that runs every Thursday at 1300 UTC. The schedule in Wrangler is</p>
            <pre><code>[triggers]
crons = ["0 13 * * 5"]</code></pre>
            <p>This script is much simpler than the pairing script. It simply sends a message to a room in our company messaging platform that notifies all members of the channel.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7ACIIWbP4COwxXbImfqiQE/78488aeabc86a3a1b6d382ffe32f42a2/image3-19.png" />
            
            </figure><p>All the reminder code is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/cron-reminder/index.js">here</a>.</p><p>We hope you find this code useful and that it inspires you to use Workers, Workers KV, Workers Unbound and Workers Cron Triggers to write large, real applications that run entirely without a backend server.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Workers KV]]></category>
            <category><![CDATA[Wrangler]]></category>
            <guid isPermaLink="false">3MzecqWNHhc0wbjnxE2kiX</guid>
            <dc:creator>Sven Sauleau</dc:creator>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[Building Automatic Platform Optimization for WordPress using Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/building-automatic-platform-optimization-for-wordpress-using-cloudflare-workers/</link>
            <pubDate>Fri, 02 Oct 2020 13:00:00 GMT</pubDate>
            <description><![CDATA[ zero-config edge caching and optimizations for improved performance for WordPress. Reduce WordPress plugin burden. ]]></description>
            <content:encoded><![CDATA[ <p>This post explains how we implemented the <a href="/automatic-platform-optimizations-starting-with-wordpress/">Automatic Platform Optimization</a> for WordPress. In doing so, we have defined a new place to run WordPress plugins, at the edge written with Cloudflare Workers. We provide the feature as a Cloudflare service but what’s exciting is that anyone could build this using the Workers platform.</p><p>The service is an evolution of the ideas explained in an earlier <a href="/improving-html-time-to-first-byte/">zero-config edge caching of HTML</a> blog post. The post will explain how Automatic Platform Optimization combines the best qualities of the regular Cloudflare cache with Workers KV to improve cache cold starts globally.</p><p>The optimization will work both with and without the <a href="https://support.cloudflare.com/hc/en-us/articles/227634427-Using-Cloudflare-with-WordPress">Cloudflare for WordPress plugin</a> integration. Not only have we provided a zero config edge HTML caching solution but by using the Workers platform we were also able to improve the performance of <a href="/fast-google-fonts-with-cloudflare-workers/">Google font loading</a> for all pages.</p><p>We are launching the feature first for WordPress specifically but the concept can be applied to any website and/or content management system (CMS).</p>
    <div>
      <h3>A new place to run WordPress plugins?</h3>
      <a href="#a-new-place-to-run-wordpress-plugins">
        
      </a>
    </div>
    <p>There are many individual WordPress plugins for performance that use similar optimizations to existing Cloudflare services. Automatic Platform Optimization is bringing them all together into one easy to use solution, deployed at the edge.</p><p>Traditionally you have to maintain server plugins with your WordPress installation. This comes with maintenance costs and can require a deep understanding of how to fine tune performance and security for each and every plugin. Providing the optimizations on the client side can also lead to performance problems due to the <a href="https://v8.dev/blog/cost-of-javascript-2019">costs of JavaScript</a> execution. In contrast most of the optimizations could be built-in in Cloudflare’s edge rather than running on the server or the client. Automatic Platform Optimization will be always up to date with the latest performance and <a href="https://www.cloudflare.com/learning/security/how-to-improve-wordpress-security/">security best practices</a>.</p>
    <div>
      <h3>How to optimize for WordPress</h3>
      <a href="#how-to-optimize-for-wordpress">
        
      </a>
    </div>
    <p>By default Cloudflare CDN caches assets based on <a href="https://support.cloudflare.com/hc/en-us/articles/200172516#h_a01982d4-d5b6-4744-bb9b-a71da62c160a">file extension</a> and doesn’t cache HTML content. It is possible to configure HTML caching with a <a href="https://support.cloudflare.com/hc/en-us/articles/202775670#3SVKvGhbS9BNT34zRCsPJ7">Cache Everything Page</a> rule but it is a manual process and often requires additional features only available on the Business and Enterprise plans. So for the majority of the WordPress websites even with a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a> in front them, HTML content is not cached. Requests for a HTML document have to go all the way to the origin.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/14SelFUXAc9SBPwqea8OUD/92f9e753055a0759bc0f87ef7c021bca/image3-2.png" />
            
            </figure><p>Even if a CDN optimizes the connection between the closest edge and the website’s origin, the origin could be located far away and also be <a href="https://www.cloudflare.com/learning/cdn/common-cdn-issues/">slow to respond</a>, especially under load.</p>
    <div>
      <h3>Move content closer to the user</h3>
      <a href="#move-content-closer-to-the-user">
        
      </a>
    </div>
    <p>One of the primary recommendations for speeding up websites is to move content closer to the end-user. This reduces the amount of time it takes for packets to travel between the end-user and the web server - the <a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">round-trip time (RTT)</a>. This improves the speed of establishing a connection as well as serving content from a closer location.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/VJdt6X0xF6xuDyMI0CHXu/4e17456b4b864eb1b5eabc08713a4438/image6-1.png" />
            
            </figure><p>We have previously blogged about the <a href="/improving-html-time-to-first-byte/">benefits of edge caching HTML</a>. Caching and serving from HTML from the Cloudflare edge will greatly improve the time to first byte (TTFB) by optimizing DNS, connection setup, SSL negotiation, and removing the origin server response time.If your origin is slow in generating HTML and/or your user is far from the origin server then all your performance metrics will be affected.</p><p>Most HTML isn’t really dynamic. It needs to be able to change relatively quickly when the site is updated but for a huge portion of the web, the content is static for months or years at a time. There are special cases like when a user is logged-in (as the admin or otherwise) where the content needs to differ but the vast majority of visits are of anonymous users.</p>
    <div>
      <h3>Zero config edge caching revisited</h3>
      <a href="#zero-config-edge-caching-revisited">
        
      </a>
    </div>
    <p>The goal is to make updating content to the edge happen automatically. The edge will cache and serve the previous version content until there is new content available. This is usually achieved by triggering a cache purge to remove existing content. In fact using a combination of our <a href="https://www.cloudflare.com/integrations/wordpress/">WordPress plugin</a> and <a href="https://api.cloudflare.com/#zone-purge-files-by-url">Cloudflare cache purge API</a>, we already support <a href="https://support.cloudflare.com/hc/en-us/articles/115002708027-Cloudflare-WordPress-Plugin-Automatic-Cache-Management-">Automatic Cache Purge on Website Updates</a>. This feature has been in use for many years.</p><p>Building automatic HTML edge caching is more nuanced than caching traditional static content like images, styles or scripts. It requires defining rules on what to cache and when to update the content. To help with that task we introduced a custom header to communicate caching rules between Cloudflare edge and origin servers.</p><p>The Cloudflare Worker runs from every edge data center, the serverless platform will take care of scaling to our needs. Based on the request type it will return HTML content from Cloudflare Cache using <a href="https://developers.cloudflare.com/workers/runtime-apis/cache">Worker’s Cache API</a> or serve a response directly from the origin. Specifically designed custom header provides information from the origin on how the script should handle the response. For example worker script will never cache responses for authenticated users.</p>
    <div>
      <h3>HTML Caching rules</h3>
      <a href="#html-caching-rules">
        
      </a>
    </div>
    <p>With or without Cloudflare for WordPress plugin, HTML edge caching requires all of the following conditions to be met:</p><ul><li><p>Origin responds with 200 status</p></li><li><p>Origin responds with "text/html" content type</p></li><li><p>Request method is GET.</p></li><li><p>Request path doesn’t contain query strings</p></li><li><p>Request doesn’t contain any WordPress specific cookies: "wp-*", "wordpress*", "comment_*", "woocommerce_*" unless it’s "wordpress_eli" or "wordpress_test_cookie".</p></li><li><p>Request doesn’t contain any of the following headers:</p><ul><li><p>"Cache-Control: no-cache"</p></li><li><p>"Cache-Control: private"</p></li><li><p>"Pragma:no-cache"</p></li><li><p>"Vary: *"</p></li></ul></li></ul><p>Note that the caching is bypassed if the devtools are open and the “Disable cache” option is active.</p>
    <div>
      <h3>Edge caching with plugin</h3>
      <a href="#edge-caching-with-plugin">
        
      </a>
    </div>
    <p>The preferred solution requires a <a href="https://support.cloudflare.com/hc/en-us/articles/227634427-Using-Cloudflare-with-WordPress">configured Cloudflare for WordPress plugin</a>. We provide the following features set when the plugin is activated:</p><ul><li><p>HTML edge caching with 30 days TTL</p></li><li><p>30 seconds or faster cache invalidation</p></li><li><p>Bypass HTML caching for logged in users</p></li><li><p>Bypass HTML caching based on presence of WordPress specific cookies</p></li><li><p>Decrease load on origin servers. If a request is fetched from Cloudflare CDN Cache we skip the request to the origin server.</p></li></ul>
    <div>
      <h3>How is this implemented?</h3>
      <a href="#how-is-this-implemented">
        
      </a>
    </div>
    <p>When an eyeball requests a page from a website and Cloudflare doesn’t have a copy of the content it will be fetched from the origin. As the response is sent from the origin and goes through Cloudflare’s edge, Cloudflare for WordPress plugin adds a custom header: <code>cf-edge-cache</code>. It allows an origin to configure caching rules applied on responses.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6COdQPrdIxdu1UiJgsdyVd/015cf3f6aa598f70cfe089d74292a1b9/image4-1.png" />
            
            </figure><p>Based on the <a href="/improving-html-time-to-first-byte/">X-HTML-Edge-Cache</a> proposal the plugin adds a cf-edge-cache header to every origin response. There are 2 possible values:</p><ul><li><p>cf-edge-cache: no-cache</p></li></ul><p>The page contains private information that shouldn’t be cached by the edge. For example, an active session exists on the server.</p><ul><li><p>cf-edge-cache: cache, platform=wordpress</p></li></ul><p>This combination of cache and platform will ensure that the HTML page is cached. In addition, we ran a number of checks against the presence of WordPress specific cookies to make sure we either bypass or allow caching on the Edge.</p><p>If the header isn’t present we assume that the Cloudflare for WordPress plugin is not installed or up-to-date. In this case the feature operates without a plugin support.</p>
    <div>
      <h3>Edge caching without plugin</h3>
      <a href="#edge-caching-without-plugin">
        
      </a>
    </div>
    <p>Using the Automatic Platform Optimization feature in combination with Cloudflare for WordPress plugin is our recommended solution. It provides the best feature set together with almost instant cache invalidation. Still, we wanted to provide performance improvements without the need for any installation on the origin server.</p><p>We provide the following features set when the plugin is not activated:</p><ul><li><p>HTML edge caching with 30 days TTL</p></li><li><p>Cache invalidation may take up to 30 minutes. A manual cache purge could be triggered to speed up cache invalidation</p></li><li><p>Bypass HTML caching based on presence of WordPress specific cookies</p></li><li><p>No decreased load on origin servers. If a request is fetched from Cloudflare CDN Cache we still require an origin response to apply cache invalidation logic.</p></li></ul><p>Without Cloudflare for WordPress plugin we still cache HTML on the edge and serve the content from the cache when possible. The logic of cache revalidation happens after serving the response to the eyeball. <a href="https://developers.cloudflare.com/workers/learning/fetch-event-lifecycle#waituntil">Worker’s waitUntil() callback</a> allows the user to run code without affecting the response to the eyeball and is run in background.</p><p>We rely on the following headers to detect whether the content is stale and requires cache update:</p><ul><li><p>ETag. If the cached version and origin response both include ETag and they are different we replace cached version with origin response. The behavior is the same for strong and weak ETag values.</p></li><li><p>Last-Modified. If the cached version and origin response both include Last-Modified and origin has a later Last-Modified date we replace cached version with origin response.</p></li><li><p>Date. If no ETag or Last-Modified header is available we compare cached version and origin response Date values. If there was more than a 30 minutes difference we replace cached version with origin response.</p></li></ul>
    <div>
      <h3>Getting content everywhere</h3>
      <a href="#getting-content-everywhere">
        
      </a>
    </div>
    <p>Cloudflare Cache works great for the frequently requested content. Regular requests to the site make sure the content stays in cache. For a typical personal blog, it will be more common that the content stays in cache only in some parts of our vast edge network. With the Automatic Platform Optimization release we wanted to improve loading time for cache cold start from any location in the world. We explored different approaches and decided to use <a href="/introducing-workers-kv/">Workers KV</a> to improve Edge Caching.</p><p>In addition to Cloudflare's CDN cache we put the content into Workers KV. It only requires a single request to the page to cache it and within a minute it is made available to be read back from KV from any Cloudflare data center.</p>
    <div>
      <h3>Updating content</h3>
      <a href="#updating-content">
        
      </a>
    </div>
    <p>After an update has been made to the WordPress website the plugin makes a request to Cloudflare’s API which both purges cache and marks content as stale in KV. The next request for the asset will trigger revalidation of the content. If the plugin is not enabled cache revalidation logic is triggered as detailed previously.</p><p>We serve the stale copy of the content still present in KV and asynchronously fetch new content from the origin, apply possible optimizations and then cache it (both regular local CDN cache and globally in KV).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1UGnbwtDfcNEmZXwV12urw/b1b58beeb7a4c5fee4ad8d5a8d755035/image5-2.png" />
            
            </figure><p>To store the content in KV we use a single namespace. It’s keyed with a combination of a zone identifier and the URL. For instance:</p>
            <pre><code>1:example.com/blog-post-1.html =&gt; "transformed &amp; cached content"</code></pre>
            <p>For marking content as stale in KV we write a new key which will be read from the edge. If the key is present we will revalidate the content.</p>
            <pre><code>stale:1:example.com/blog-post-1.html =&gt; ""</code></pre>
            <p>Once the content was revalidated the stale marker key is deleted.</p>
    <div>
      <h3>Moving optimizations to the edge</h3>
      <a href="#moving-optimizations-to-the-edge">
        
      </a>
    </div>
    <p>On top of caching HTML at the edge, we can pre-process and transform the HTML to make the loading of websites even faster for the user. Moving the development of this feature to our Cloudflare Workers environment makes it easy to add performance features such as improving <a href="/fast-google-fonts-with-cloudflare-workers/">Google Font loading</a>. Using Google Fonts can cause significant performance issues as to load a font requires loading the HTML page; then loading a CSS file and finally loading the font. All of these steps are using different domains.</p><p>The solution is for the worker to inline the CSS and serve the font directly from the edge minimizing the number of connections required.</p><p>If you read through the previous blog post’s implementation it required a lot of manual work to provide streaming HTML processing support and character encodings. As the set of worker APIs have improved over time it is now much simpler to implement. Specifically the addition of a streaming <a href="/html-parsing-2/">HTML rewriter/parser with CSS-selector based API</a> and the ability to suspend the parsing to <a href="/asynchronous-htmlrewriter-for-cloudflare-workers/">asynchronously fetch a resource</a> has reduced the code required to implement this from ~600 lines of source code to under 200.</p>
            <pre><code>export function transform(request, res) {
  return new HTMLRewriter()
    .on("link", {
      async element(e) {
        const src = e.getAttribute("href");
        const rel = e.getAttribute("rel");
        const isGoogleFont =
          src.startsWith("https://fonts.googleapis.com")

        if (isGoogleFont &amp;&amp; rel === "stylesheet") {
          const media = e.getAttribute("media") || "all";
          const id = e.getAttribute("id") || "";
          try {
            const content = await fetchCSS(src, request);
            e.replace(styleTag({ media, id }, content), {
              html: true
            });
          } catch (e) {
            console.error(e);
          }
        }
      }
    })
    .transform(res);
}</code></pre>
            <p>The HTML transformation doesn’t block the response to the user. It’s running as a background task which when complete will update kv and replace the global cached version.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ShahwSObRage83CdWSfD0/528448594de96d64c0bb442f7e2db875/image1-6.png" />
            
            </figure>
    <div>
      <h3>Making edge publishing generic</h3>
      <a href="#making-edge-publishing-generic">
        
      </a>
    </div>
    <p>We are launching the feature for WordPress specifically but the concept can be applied to any website and content management system (CMS).</p> ]]></content:encoded>
            <category><![CDATA[Automatic Platform Optimization]]></category>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[WordPress]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">TmM1U951RKeF4WEDa64px</guid>
            <dc:creator>Yevgen Safronov</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
    </channel>
</rss>