Trust model and limitations

A description of the boundary Scafo draws, the surfaces it exposes, and what it does not do. This is a description, not an audit.

#Trust model

One Scafo installation is one trusted workload domain. Repositories, dependencies, build inputs, deployers, runtime code and operational credential holders must trust one another.

Application, operator and OIDC scopes authorize actions. They do not isolate hostile workloads.

Important

Do not use one installation for arbitrary customer code, public build submission, or mutually untrusted applications. Use separate installations, buckets, and host or VM boundaries for separate trust domains.

#Protected surfaces

SurfaceAuthNotes
/.well-known/scafo/healthnoneIntentionally public.
/v1/ingress/routes*ingress bearer tokenReveals every node's address for every host.
/v1/storage/**ingress bearer tokenCarries private keys in both directions.
everything elsenode, operator or workload tokenRoles and application scopes limit what each may do.

Both privileged surfaces live on the node's own listener. The ingress bearer token is all an ingress ever holds. The ingress enforces the rest of that boundary itself: it never proxies either prefix through the public platform API apex.

/.well-known/celld/health is the application runtime's health endpoint, not the Scafo node/API one.

Registry conditional writes prevent lost updates. Coordinator leases and fences protect stale placement decisions and other lease-gated work.

Node API and runtime listeners default to loopback. Wider binds require an explicit private-network or WireGuard transport decision.

#Limitations

#No OS sandboxing

Builds and runtime processes are not OS-sandboxed from the daemon or from each other. Worker egress can reach any address visible to its network namespace.

#Celld's operator API

Celld's internal operator API is mostly unauthenticated. Keep it on a private network or overlay; do not expose it publicly.

A private network does not keep it from the applications themselves. A Worker's outbound fetch starts on the node, so the code of one application can reach the operator API of every runtime process on that node — read its state, make it reload, shut it down. That is inside the trust model above, and it is the concrete reason the model is what it is: every application of an installation can act on every other one.

A node run with --network host shares the host's network namespace, so the same code also reaches whatever else listens on the host's loopback. Run such a host for Scafo alone, or treat everything on it as part of the same trust domain. See Celld's security guidance.

#Credentials reach the runtime

Celld and bucket credentials are administrative authority. Celld child processes receive fleet credentials. Scafo provides no OS credential boundary against trusted-workload compromise or native escape.

#The engine socket

A node that serves container classes is given the machine's container engine socket, and a node that can reach that socket is root-equivalent on that machine. Mounting it, and putting the node process in its group, is the operator's opt-in: Scafo never changes groups, mounts nothing and escalates nothing itself, and a node given no socket has no engine to reach. Such a node also runs in the host's network namespace, with what that means for Worker code above.

#Application variables are plain text

An application's variables live in its own wrangler.jsonc or wrangler.json, and Celld stores them as plain text in the bucket: anyone with bucket read access can read every variable of every application. Celld has no secret store. A value passed with scafo deploy --var is additionally visible in the process list and the CI log of the machine running the CLI. Keep defaults in the config's vars, pass the values that differ between applications from the CI's secret store as --var, mask them in CI logs, and treat bucket read access as access to every variable.

#The bucket is an availability boundary

The bucket is both the durable registry and the availability boundary. It must support conditional writes and ranged reads, and provide reliable, low-latency lease traffic. Scafo cannot make an unreliable object store highly available.

#Celld leases

Each runtime process renews its lease every third of CELLD_TTL_MS and retries a failed renewal inside the remaining lifetime. A process that loses its lease fences itself and exits with code 3; the node restarts it. Scafo preserves Celld's default lifetime.

#Deploy bounds are scheduling, not admission

Deploy admission bounds artifact bytes and entries, retained builder output, node and per-application concurrency and queue depth, staged bytes, and receive/queue/task deadlines. These are scheduling bounds, not OS resource admission. Apply host or container CPU, memory, process, filesystem and network limits.

#Supervision requirements

scafo sail closes deploy intake, cancels deploy work, joins builder cleanup, then drains runtimes on SIGINT/SIGTERM.

  • On a bare host, use the installer's systemd unit or an equivalent cgroup-owning supervisor that terminates the daemon first, removes remaining builders on exit or timeout, and allows at least 50 seconds of shutdown grace. If CELLD_SHUTDOWN_TOTAL_MS is larger, allow at least 10 additional seconds.
  • In containers, keep the direct Node entrypoint as PID 1 in a private PID namespace. Do not use host PID mode or a persistent wrapper.

Physical-host failure remains outside process-code guarantees.

#Operator responsibilities

Application authentication, public TLS, and ingress policy remain the operator's.

#Deploying safely

  • Keep node, runtime-peer and operator traffic on a private network or encrypted overlay. Publish only intended application ingress endpoints.
  • Protect bucket, node, ingress-token and deployment credentials as administrative secrets. Use a bucket scoped to one installation.
  • Test object-store conditional writes, latency and failure behaviour before production use.
  • Configure Celld runtime policy through runtime.celldEnv, --celld-env or SCAFO_CELLD_ENV. Scafo adds no Celld policy defaults.
  • Change CELLD_TTL_MS consistently on every node; an unplanned takeover waits up to that lifetime.
  • Pin and review the Scafo build and the current Celld release. Review Celld's security, limitations and Cloudflare compatibility notes before upgrading.

#The Celld boundary

Scafo requires Celld 0.5.0 — scafo sail refuses an older binary with capability-missing — and creates one Celld fleet per application; the image and the Linux installer ship 0.6.2. One application is one Celld fleet, and a fleet takes one version at a time, except across a rolling pair upstream declared (0.5.0 ↔ 0.5.1, 0.6.0 ↔ 0.6.1, 0.6.1 ↔ 0.6.2), whose nodes may be replaced one at a time: a node neither starts an application's process nor accepts a deploy for it while another answering node serving that application, or crash-looping on it, reports a Celld release it may not share a fleet with (version-skew, 409).

Celld ownsScafo owns
Deployment adoptionRegistry
Application healthAuthorization
CellsPlacement
Runtime resource policySupervision, deploy intake, and the routing every ingress reads

Celld exposes one current deployment per application fleet. Scafo records a monotonic deployment revision, not a selected version; it provides no per-deployment routing and no historical deployment selector. See Celld issue #181.

Scafo does not add a raw TCP bridge or transparent tunnel around missing Celld APIs.

Warning

Hostile workload isolation is not available. Do not infer it from scopes, deployment separation, or ingress routing.

Read more in Docs > Operators.