PROMPTLOG: DYNAMIC WORKER LOADER POC

Run user code in sandboxed Workers via Dynamic Worker Loader API

CLOUDFLARE WORKERS 2025.01.29

View source on GitHub

THE CONCEPT

Problem: How do you safely run user-provided JavaScript in a serverless environment?

Solution: Use Cloudflare's Dynamic Worker Loader API to create isolated Workers on-demand. Each piece of user code gets its own sandboxed environment with zero network access.

POST /submit → Hash code → Create Worker → Execute safely

1. WRANGLER CONFIG

Enable the Dynamic Worker Loader API in your wrangler.json:

{
  "name": "promptlog",
  "main": "src/index.ts",
  "compatibility_date": "2024-01-15",
  "experimental": {
    "worker_loader": true
  }
}

2. MAIN WORKER

Accept code, hash it, create a dynamic worker:

import { Hono } from 'hono';

const app = new Hono();

// Submit user code and get execution hash
app.post('/submit', async (c) => {
  const { code } = await c.req.json();

  // Hash the code to create unique worker ID
  const hash = await hashCode(code);

  // Create dynamic worker from user code
  await createDynamicWorker(hash, code, c.env);

  return c.json({ hash, url: `/run/${hash}` });
});

// Execute the user's code
app.get('/run/:hash', async (c) => {
  const hash = c.req.param('hash');
  const prompt = c.req.query('prompt') || '';

  // Get the dynamic worker and run it
  const worker = c.env.WORKER_LOADER.get(hash);
  const response = await worker.fetch(
    new Request(`https://dummy/?prompt=${encodeURIComponent(prompt)}`)
  );

  return response;
});

async function hashCode(code: string): Promise<string> {
  const encoder = new TextEncoder();
  const data = encoder.encode(code);
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 8);
}

export default app;

3. DYNAMIC WORKER CREATION

The core: create isolated workers from user code:

async function createDynamicWorker(hash: string, userCode: string, env: any) {
  // Wrap user code in a secure worker template
  const workerCode = `
    export default {
      async fetch(request) {
        const url = new URL(request.url);
        const prompt = url.searchParams.get('prompt') || '';

        try {
          // Execute user code
          ${userCode}

          // Return the result
          return new Response(JSON.stringify(result), {
            headers: { 'content-type': 'application/json' }
          });
        } catch (error) {
          return new Response(JSON.stringify({
            error: error.message
          }), {
            status: 500,
            headers: { 'content-type': 'application/json' }
          });
        }
      }
    };
  `;

  // Create the worker with network isolation
  await env.WORKER_LOADER.put(hash, workerCode, {
    globalOutbound: null  // No network access
  });
}

4. USER CODE EXAMPLE

What users can submit (gets wrapped and executed):

// User submits this JavaScript:

const words = prompt.split(' ');
const reversed = words.reverse().join(' ');

const result = {
  original: prompt,
  reversed: reversed,
  wordCount: words.length,
  timestamp: new Date().toISOString()
};

// The wrapper will return this as JSON

HOW IT WORKS

  1. Submit code: POST JavaScript to /submit
  2. Hash creation: Generate SHA-256 hash of the code
  3. Worker creation: Use Dynamic Worker Loader to create isolated environment
  4. Execute safely: Run user code with globalOutbound: null (no network)
  5. Return result: Send back JSON response

SECURITY

  • Network isolation: globalOutbound: null blocks all external requests
  • Memory limits: 128MB per Worker isolate
  • CPU limits: 50ms execution time
  • Sandboxed execution: Complete V8 isolate per user code

USE CASES

  • Code playgrounds and educational platforms
  • User-defined data transformations
  • Custom business logic execution
  • Serverless function-as-a-service