Anything Advertising

Workers

Run JavaScript on a request, a schedule, or an object event.

A worker is an ES module. You deploy it from the console and it runs on AnythingCloud's nodes, not on your servers. Each worker has its own address, versions, environment, bucket bindings, and triggers. A trigger decides when the code runs: an HTTP request, a timer, or a change to an object in a bucket.

The worker page has six tabs: Overview (the default), Metrics, Code, Triggers, Logs, and Settings. Each worker runs in its own process and stays warm between deploys and node restarts. A stopped or daily-recycled worker can return a 503 while it starts. New workers are placed on the least-loaded configured node.

Create and deploy

  1. Open Workers and choose Create worker.
  2. Enter a name and a worker id. The id is fixed and is used for the default ID.workers.anything-cloud.com address. You can point the worker at a different label under the same suffix later in Settings → Address.
  3. Choose an address: AnythingCloud address, No address, or Your domain for a root domain you have already verified.
  4. Pick a template, then edit the code. A worker exports fetch, scheduled, or event.
  5. Choose Create worker. The first version is stored and the deploy is queued for a node.

The Code tab is the editor, with live syntax highlighting. Paste code, choose Upload .js, or drop a .js / .mjs file onto the editor to load it. Press Ctrl + Enter or Cmd + Enter to open the deploy dialog, add a changelog line, and save. A deploy of new code stores an immutable version; deploying code identical to an existing version reuses that version. The last 20 are kept. The Versions page lists every kept version; roll back to the previous one from the page header, or pick any older version from its row, without re-uploading code.

A deploy starts the new version beside the one that is serving, waits for it to pass a health check, and then sends new requests to it. Requests that are already running finish on the old version. If the new version does not become healthy, the old version keeps serving.

Addresses and custom domains

Every worker can have one generated address: ID.workers.anything-cloud.com. Choose No address when the worker only runs on schedules or object events; you can add a host later.

To use your own hostname, verify the root domain under Networking → Domains, then open Settings → Address or Triggers → Add host. Pick the domain and enter a label such as www, api, or @ for the root. The host is added to the worker's trigger list and appears in Networking → Hosts next to bucket hosts. The Settings picker can also change which domain and subdomain the worker advertises.

Point a CNAME at edge.anything-cloud.com and set it to DNS only. The edge controller issues the certificate after DNS resolves to it, so there is no separate TLS step. A custom host that shows Setting up is waiting for DNS or the certificate.

When a custom host becomes active it becomes the worker's primary address, and the console shows it instead of the generated address. An existing generated host stays in the trigger list as a fallback. You can remove it once a custom host exists; when you remove the last custom host, the generated address is created again if it is missing.

Requests reach the worker with their method, path, query string, and body. The edge sets X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, and X-Real-IP, preserves the Host header, and strips internal invocation headers. The worker's status code, headers, and body are returned to the caller.

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const agent = request.headers.get("user-agent") || "unknown";

    return Response.json({
      path: url.pathname,
      query: Object.fromEntries(url.searchParams),
      agent,
    });
  },
};

Triggers

Open Triggers to see everything that invokes the worker in one list. Each row is one of three types: Request host, Schedule, or Object event. Schedules and object events have an on/off switch; request hosts can be copied or removed.

Request hosts

A request host sends HTTP requests to the worker's fetch handler. The generated address and each custom hostname are request triggers. A host shows Active once it is routed and Setting up while DNS or TLS is pending. Copy its URL from the row, or remove it when you no longer need it.

Schedule triggers

A schedule runs the scheduled handler. Choose an interval from five minutes to one day, or enter a five-field cron expression (minute hour day month weekday) evaluated in the time zone you select.

export default {
  async scheduled(event) {
    // event.scheduledTime is the run time as an ISO 8601 string.
    const response = await fetch("https://example.com/ping");
    console.log("ping", response.status);
  },
};

Schedules are best-effort and can start a few seconds late under load. Runs do not overlap: the next run starts after the current one finishes, so keep the handler idempotent and short.

Object event triggers

An object event runs the event handler when an object changes through the console in a bucket you own. Choose the bucket, one or more event types (created, updated, deleted, renamed, or restored), and an optional key prefix. Reads never fire an event. Writes made through the S3 API, a presigned URL, or env.BUCKET.put do not fire console object events.

The handler receives one event object:

export default {
  async event(event, env) {
    // event.type      "created", "updated", "deleted", "renamed", or "restored"
    // event.bucket    the bucket name
    // event.key       the object key
    // event.size      bytes, currently 0 for console events
    // event.etag      the object ETag, currently empty for console events
    // event.oldKey    the previous key, for a rename
    // event.eventTime when the change happened, as an ISO 8601 string

    console.log(event.type, event.bucket, event.key);
  },
};

A prefix such as uploads/ keeps a busy bucket from waking the worker for every file. The event is delivered after the bucket change completes, so a slow handler never delays the upload or delete that produced it.

Environment

Open Settings → Environment and add key/value rows. Values arrive in the handler as env.KEY:

export default {
  async fetch(request, env) {
    const key = env.API_KEY;
    if (!key) return new Response("Not configured", { status: 500 });
    // use the key
  },
};

Keys use uppercase letters, digits, and underscores, and start with a letter. Values are encrypted at rest and injected into the worker process; saving a variable applies to the worker without a new code deploy. Secret values are hidden after creation and shown once in the response that creates them. Turn off the secret option for a value you want to see in the list.

For values that change at runtime, create a KV namespace and bind it in Settings → KV namespaces. The alias you choose becomes env.KV or another name.

Bucket helpers

Bind a bucket under Settings → Buckets and it appears as env.ALIAS (the default alias is BUCKET). The binding is tied to one bucket, and the runtime signs requests for it, so no endpoint, access key, or private address is exposed to your code.

CallDoes
env.BUCKET.get(key)Reads an object. Returns null when the key is missing. The result has size, etag, text(), json(), arrayBuffer(), and body.
env.BUCKET.head(key)Metadata only: key, size, and etag. Returns null when the key is missing.
env.BUCKET.put(key, body, options)Writes an object. Pass a string, stream, or buffer, and set the type with { httpMetadata: { contentType } }.
env.BUCKET.delete(key)Deletes an object. Deleting a missing key is not an error.
env.BUCKET.list({ prefix, limit, cursor })Lists objects under a prefix, up to 1,000 per page. Returns { objects, truncated, cursor }, where each object has key, size, and etag.
env.BUCKET.url(key)The public URL for a key on the bucket's CDN host.

Returning a file

For a public bucket, do not stream object bytes through the worker. Redirect to the bucket URL so the download is served from the edge cache, with range requests and caching handled for you. Public bucket reads are edge-cached for up to five minutes, so an overwrite or delete can take that long to appear:

export default {
  async fetch(request, env) {
    const key = new URL(request.url).pathname.slice(1);
    if (!key) return new Response("Missing key", { status: 400 });

    const object = await env.BUCKET.head(key);
    if (!object) return new Response("Not found", { status: 404 });

    return Response.redirect(env.BUCKET.url(key), 302);
  },
};

The redirect hands the download to the bucket host; the browser address bar changes to that host. env.BUCKET.url(key) returns the public URL; it does not grant access to a private bucket. For a private object, read it with get and return a Response.

Receiving an upload

A worker can be the upload endpoint for a bucket: accept a multipart form, write the file into the bucket, and answer with the public URL.

export default {
  async fetch(request, env) {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    const form = await request.formData();
    const file = form.get("file");
    if (!file || typeof file === "string") {
      return new Response("Missing file", { status: 400 });
    }

    const key = `uploads/${crypto.randomUUID()}-${file.name}`;
    await env.BUCKET.put(key, file.stream(), {
      httpMetadata: { contentType: file.type || "application/octet-stream" },
    });

    return Response.json({ key, url: env.BUCKET.url(key) }, { status: 201 });
  },
};

Route it to a hostname such as upload.example.com and the form posts straight to the worker. The bridge caps a single put at 10 MB, and the bucket's public/private setting still applies. Plan quotas are enforced on console uploads; S3 API and worker writes are not capped by the plan, so keep them within the plan you are paying for.

Outbound requests

Workers can call public HTTP and HTTPS endpoints with fetch, which is useful for sending a webhook or calling another API:

export default {
  async fetch(request, env) {
    const response = await fetch("https://example.com/hooks/orders", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        authorization: `Bearer ${env.WEBHOOK_TOKEN}`,
      },
      body: JSON.stringify({ event: "order.created" }),
    });
    return Response.json({ delivered: response.ok });
  },
};

Public hosts on ports 80 and 443 are reachable. Private, loopback, link-local, and LAN addresses are blocked by the node firewall, including cloud metadata endpoints. Use a bucket binding for object storage; S3 credentials are not available to worker code.

Logs and metrics

console.log output and uncaught errors appear on the worker's Logs tab. Filter messages, select a level, then copy or download the result. Each worker keeps the newest 2,000 lines for 7 days; older lines are deleted automatically. Turn off Save logs to discard new output without clearing what is already stored, or use Clear to remove the stored lines. An uncaught error in a request handler returns a 500 to the caller and is recorded in the log.

Metrics

The Metrics tab covers 24 hours, 7 days, or 30 days. It shows invocations, errors and error rate, average duration, and egress, each compared with the previous window of the same length. A source breakdown separates requests, schedules, and object events, including each source's error rate and average duration.

The edge map shows where traffic enters. The edge currently runs in Columbus, US, so entries appear there on the map.

Billing

Workers are billed by running time, not per request. The base hourly rate applies to a 128 MB worker, and the price scales with the Memory you choose in Settings:

MemoryHourly price
64 MB0.5x the base rate
128 MB (default)The base rate
256 MB2x the base rate
512 MB4x the base rate

The default base rate is $0.0025 per hour for 128 MB. A 64 MB worker costs half that, while 256 MB doubles it and 512 MB quadruples it. The operator can change the base rate. Bucket storage and KV namespace storage are billed separately. The Billing page has a monthly budget with an optional hard cap.

Paused workers do not accrue runtime. Pausing an AnythingCloud account stops its workers and triggers until the account is resumed. The Billing page shows running workers, hours this month, and the worker share of the current bill.

Limits

LimitValue
Code formatES module exporting fetch, scheduled, and/or event
Bundle size1 MiB
Request body10 MB
HTTP edge timeout30 seconds
Environment value8 KB per variable
Environment variables50 per worker
Request hosts20 per worker
Memory64, 128, 256, or 512 MB (default 128 MB)
Request timeout1, 5, 10, or 30 seconds for scheduled and event runs (default 10 seconds); HTTP requests use the 30-second edge timeout
Versions kept20
Schedules and object events per worker20; request hosts are not counted
Workers per member10 by default
Log lines kept2,000 per worker

A run that exceeds its memory cap is stopped and counted as an error.