ReferenceSecurity
Reference

Security

What isolates what, and what doesn't.

The runner runs code that you or your agents wrote. It is not a sandbox for code from unknown or untrusted people: workerd describes itself as not a hardened sandbox, and every service runs as the same user in one container. To host untrusted code, run one runner per tenant, each isolated from the others: a VM, or a container with a sandboxed kernel such as gVisor. That is how Zeroko Cloud runs workspaces: a runner each, in its own gVisor sandbox.

What isolates what

  • Services from each other. Each service runs in its own workerd process, with its own heap limit: one service's crash, leak or endless loop stops only that service. Services reach data only through their bindings: each process has a random token that the runner maps to its service, and binding names come from the process's config, not from the service's code. A service reaches another only through a service: binding it declares.
  • Services from the runner. A service's code cannot read the token or reach the control socket: both live in a separate Worker of the process. node:fs sees a virtual file system (the service's modules and a temporary directory), not the container's disk.
  • The network from services. Outbound requests and sockets go through the runner's egress proxy, which connects to public addresses only, checked after DNS: never loopback or private networks, so services cannot reach the runner's listeners or the host's network. Each service can be limited to a list of hosts, or have no outbound access at all.
  • The disk from services. Each service has a quota for its databases and files together.
  • The files from service SQL. An authorizer refuses ATTACH and DETACH, transactions outside batch(), extension loading and the pragmas that change storage.
  • The API from services. Services are served on subdomains, and the API takes only bearer tokens, never cookies, so a service's page cannot act on the API for its visitor. Files served by the API carry Content-Security-Policy: sandbox.

The container

The image holds only the two binaries, the C library and CA certificates. The runner starts as root only to give /data to its user (1000 by default), then drops to it before anything else, with no supplementary groups; workerd runs as the same user.

Access

  • The root token, from ZEROKO_TOKEN, is for the app that manages the runner. It is compared by its SHA-256, in constant time.
  • API tokens (zk_…) are for agents. They have full access to every service. The runner stores only their SHA-256 and shows each one once.
  • The runner has no users or sign-in: the panel in front of it does. The open source panel has one admin, with a password; the root token never reaches the browser.

Secrets

Service secrets are encrypted with AES-256-GCM, bound to their service, with /data/master.key, created on first start. They are decrypted only to start a service's process and never returned by the API. Anyone with the volume or a backup has the key: protect both.

Not covered

  • Rate limits are per client address (rate_limit, off by default): a spread-out flood needs the proxy or firewall in front.
  • TLS on sockets from connect(): workerd doesn't proxy it yet. Plain TCP works, and HTTPS goes through fetch.