> ## Documentation Index
> Fetch the complete documentation index at: https://checkfu.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Provider grading and honest limits

> Why a third-party sandbox provider pins to advisory_filter, which facts cannot be self-attested and why, and exactly what putting a live process behind a network hop costs.

Two things a partner engineer should learn before integrating rather than after: the ceiling on
what your provider may claim, and the guarantees that do not survive a network hop. Both are
below, stated as limits rather than as caveats.

Read [Sandbox providers](/concepts/sandbox-providers) for the concept and the capability
envelope, and [Sandbox provider contract](/concepts/sandbox-provider-contract) for the
operations.

## The grading rule

**A third-party sandbox provider is pinned to `advisory_filter`. `security_boundary` is earned
only through a conformance matrix Checkfu runs itself.**

`egress_certification` is the capability-honesty grade for one exact
`(provider, provider_revision)` pair. It is a total order:

| Grade               | Means                                                                          |
| ------------------- | ------------------------------------------------------------------------------ |
| `unsupported`       | no destination selector at all                                                 |
| `advisory_filter`   | a selector exists, but the evidence for what it enforces is incomplete         |
| `security_boundary` | the exact revision proved the complete fact set, signed by a reviewed attester |

A SandboxProfile's `allowlist` policy may name
`required_egress_certification: "advisory_filter" | "security_boundary"`. Selection compares
the requirement against the selected provider's advertised grade and fails typed **before any
provider work starts**. It never infers, upgrades, or averages, and `unsupported` is not a
spelling a policy may require — a policy may not ask that nothing apply its own restriction.

<Note>
  **An `allowlist` policy that names nothing still requires `advisory_filter`.** The
  requirement is opt-in, never retroactive, so an omitted field falls to the floor an
  `allowlist` profile has always been admitted under — not to `unsupported`. The practical
  consequence for a day-one provider: advertising `egress_certification: "unsupported"` means
  **no `allowlist` profile can ever select you**, whether or not any profile mentions the
  requirement. `none` and `unrestricted` policies are unaffected, because they declare no
  destination restriction for a grade to be honest about.
</Note>

Two structural details are worth copying into your own thinking. The field lives only on the
`allowlist` member of the policy union, so "require a boundary while asking for unrestricted
egress" is unrepresentable rather than merely rejected. And the requirement is optional and
omitted by default, so no retained profile changes meaning — or template digest — when the
field was added.

The grade is not purchasable by configuration, and it is not inferable from anything else. In
particular it is deliberately independent of `enforcement.network_policy`: a provider declaring
`provider_attested` there has said "I own my configured filter," which is a different and much
weaker claim than D130's fact set. That independence is test-pinned precisely so
`security_boundary` can never be back-derived from an attestation.

## The five facts, and the three that cannot be self-attested

A `security_boundary` receipt must prove all five together. A receipt missing one certifies
nothing:

| Fact                          | Claim                                                                   |
| ----------------------------- | ----------------------------------------------------------------------- |
| `exact_destination_match`     | exact hostname and exact port matching, granting no subdomains          |
| `connect_time_public_address` | every connection and every DNS answer classified public at connect time |
| `redirect_recheck`            | the same check after redirects and every other reconnect                |
| `authority_separation`        | workload and plane authorities separately sealed                        |
| `default_deny`                | denial when any of those facts is absent                                |

Two of the five — `exact_destination_match` and `redirect_recheck` — are **positive and
enumerable**. Hand the implementation a host and a port and watch it accept or refuse; hand it
a redirect and watch it recheck the target. Anyone can construct those inputs, and the
observations are about what the code does, not about what it lacks.

The other three are not that shape, and this is why the rule is a structural one rather than a
judgment about partners.

<Note>
  **"Un-self-attestable" does not mean unprovable.** Checkfu's own Docker matrix proves two of
  these three — it owns the resolver and it owns the topology, so it can construct the
  adversarial cases. The claim is narrower and sharper: they cannot be proved *by the party
  making the claim about itself*, because each is a statement about a case that party would
  have to choose to construct, about something it does not hold, or about paths it did not
  think of. That is why the grade is earned through a matrix Checkfu runs rather than through
  a document you send.
</Note>

### `connect_time_public_address`

The claim is negative, per-connection, and about a moment that has not happened yet: *every*
resolved address, on *every* connection, at connect time, was public. Testing it requires
controlling the answer — you must make an admitted hostname resolve to a private address and
observe the refusal. Only a party that owns the resolver can construct that case, and a
self-attester constructs only the cases they thought of. DNS rebinding is precisely an attack on
the gap between check and connect; a statement cannot close a gap.

The sharper problem is that the obvious test passes against an implementation that does not
implement the fact. Checkfu's own matrix owns the private-address answer end to end through
Docker's embedded DNS, and its target deliberately **accepts TCP on 443** — because the guard
answers `403` for an upstream connection error exactly as it does for a policy denial. Point
that case at a host with nothing listening and it goes green against a guard whose connect-time
classification had been deleted. With a live listener it reds. A self-attester has no reason to
build the harder version, and no way to prove they did.

### `authority_separation`

This is a claim about what the provider **does not have** — that the workload egress edge holds
no plane credential, and that workload and plane traffic ride separate sealed authorities. A
party cannot attest to its own non-possession. The evidence for "I do not hold this" is the
absence of something inside a boundary only that party can see.

Docker is at least *architecturally positioned* to prove it rather than assert it: two
containers on two routes, and a guard whose environment is compiled by the Runner, so its
contents are Checkfu-authored fact rather than a provider report. Note that positioning is not
proof, and Checkfu does not claim it as one — `authority_separation` is one of the two facts
the owned Docker matrix still does **not** establish, because it needs a real Run through the
transport. The difference between "structurally provable" and "proved" is exactly the
difference this grade is measuring.

The rule is not aimed at strangers, and E2B is the proof. E2B validates the two authorities
separately and then flattens them into the provider's single `allowOut` list, because the
provider API exposes no authority dimension at all. That fact alone holds a shipped first-party
integration at `advisory_filter`. A partner is being held to a bar Checkfu already failed to
clear on one of its own providers.

### `default_deny`

The claim quantifies over the complement of the implementation: when any other fact is absent,
*nothing* gets out. A suite the implementer wrote enumerates the paths the implementer thought
of — which is exactly the set default-deny exists to backstop.

Checkfu's matrix includes a case that only exists because the tester assumed the workload would
be hostile to the mechanism: no route at all when the workload simply ignores the proxy
environment variables. That case is not discoverable from a cooperative reading of your own
design.

### What a signature proves

Receipts travel as a detached Ed25519 signature over domain-separated canonical bytes
(`checkfu.sandbox-egress-conformance.v1`) from an attester named in the reviewed
`SANDBOX_EGRESS_CONFORMANCE_ATTESTERS` set. That mechanism proves **authorship and integrity**:
this exact statement came from this exact key and was not altered.

It does not, and cannot, prove the statement. Adding a partner key to the attester set would not
make a partner's claims true — it would make Checkfu's reviewed set include a signer whose
claims Checkfu has not verified, which is the same thing as deleting the grade.

## The current posture

`SANDBOX_EGRESS_CONFORMANCE_ATTESTERS` is **empty**.

<Note>
  **No provider has `security_boundary` today — including Checkfu's own.** A
  `security_boundary`-requiring profile is currently refused by every provider. That is the
  decision working, not a gap: the grade is not purchasable by configuration, so the
  requirement stays unsatisfiable until an owned matrix runs and the release authority publishes
  an attester key in a reviewed change.
</Note>

Where the in-tree providers actually sit:

| Provider              | Grade                                                               | Why                                                                                                                                                                                                                                                                                                                         |
| --------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Docker                | `unsupported` without an attached guard; `advisory_filter` with one | The owned matrix has behavioral cases for all five facts on a real daemon. It still **never emits a receipt**: no attester key exists, the run does not bind the published composite guard revision, and `authority_separation` is proven at the guard/network boundary rather than through a full Run's sealed plane grant |
| E2B                   | `advisory_filter`                                                   | The provider API preserves no authority dimension; no opaque caller string can promote it                                                                                                                                                                                                                                   |
| Daytona               | `advisory_filter`                                                   | Its `domainAllowList` is a real destination selector applied as iptables rules, so `allowlist` is honest to advertise — but no owned matrix has run it, and an attester-signed receipt naming the exact provider revision is the only thing that moves the grade                                                            |
| Cloudflare containers | `unsupported`                                                       | The Worker-hosted runner's native provider. The persisted host filter is enforced over intercepted traffic, which for HTTPS would mean the guest trusting an injected CA — TLS interception, not a network boundary — so the provider declines the grade instead of claiming it                                             |
| Memory                | `advisory_filter`                                                   | A deterministic test oracle that applies exact string membership to the admitted destination set. It declares the grade rather than leaving it at the `unsupported` default, because the default would have claimed it ignores the allowlist, which is false                                                                |

The Worker-hosted runner's cloud-vendor adapters (Daytona, E2B, Vercel) sit at `advisory_filter`
too, and their grade does **not** carry over from the identically named Node adapters: a receipt
for `checkfu:daytona-provider@2` says nothing about a `*-worker-provider@*` revision. Same
provider name, different exact revision, separate evidence.

So the third-party rule is not a stranger tax layered on top of a privilege Checkfu enjoys. It
is the same rule, and Checkfu is currently on the same side of it.

## The enforcement ceiling gap

Egress grading is the *solved* half of the honesty problem. The unsolved half is larger, and a
partner should know it is open because it gates when this seam can be published.

`SandboxEnforcement` — the nine-axis authority statement — is **not** a field of
`RunnerProviderCapability`. The server-owned enrollment ceiling that intersects a Runner's
`drivers`, `launch_kinds`, `tiers`, `platforms`, `network_modes`, `retention_modes`,
`mount_kinds`, `runtime_assurances`, `snapshot_clone`, `snapshot_custody`, and
`package_materialization` therefore never touches the enforcement statement.

Nor is enforcement the only axis that escapes it. `RunnerProviderCapability` also omits
`egress_certification` (compared Runner-locally at selection), `image_mode`,
`fixed_base_images`, `package_managers`, `snapshots`, and `list_dir` — every one of them
declared Runner-locally with nothing in the enrollment able to contradict it.

`list_dir` is the one that reaches a customer-visible outcome on its own. It gates memory
writeback under D38, so a provider admitted for `memory` mounts can decline writeback
Runner-locally and the Run records a visible skip — correct behavior, but the enrollment that
admitted the mount never had a say in it.

In-tree that is harmless: the only authors are Checkfu's own adapters, and `runner_enforced` is
true because the Runner set the flags itself. It stops being harmless the moment a stranger
authors one.

The concrete consequence is on the `openProcess` evidence cross-check. Today it compares a
provider's per-process `evidence.enforcement` against `capabilities.enforcement` — where the
right-hand side is a constant compiled into the trusted Runner. That is *provider statement
versus Runner-owned fact*. Over a wire, both sides are authored by the same partner, and the
comparison degenerates into a self-consistency check. It still catches a partner whose
per-process evidence disagrees with its own advertisement, which is a real class of bug. It
catches nothing about physical isolation.

Three existing mechanisms close it, and all three are reuse rather than invention: cap the
authority vocabulary by placement so a non-Runner may not say `runner_enforced`; add
`enforcement` to `RunnerProviderCapability` and intersect it with the operator-issued enrollment
ceiling every other axis already uses; and bind the envelope to an attributable signed
materialization statement. None of that makes the claim true. It makes it **ceilinged and
attributable**, which is the most a wire can offer — physical truth still comes from a
Checkfu-run matrix, exactly as `egress_certification` already requires.

<Warning>
  Publishing the provider seam before that ceiling lands would ship a weak tier that nothing in
  the system records as weak. That is the failure the host-process tier was explicitly designed
  to avoid, and it is why this gap is called blocking rather than pending.
</Warning>

## What `openProcess` costs across a wire

`openProcess` is the one member of the contract that is a live object rather than a value. It
does cross a network boundary — the design is complete and a spike drove a real ACP agent
across it, including a connection severed mid-turn — but it crosses at a stated cost. Three
guarantees the in-process handle makes do not survive, and all three are named here rather than
engineered around.

### Real stdin backpressure becomes a credit window

In-process, the writable stream handed to the driver is the child's actual OS pipe: a slow guest
slows the writer, and the flow control is physical.

Across a wire it cannot be. TCP or HTTP/2 flow control on the Runner↔provider hop does not reach
the provider↔guest stdin pipe — the provider must accept and buffer. So backpressure becomes an
**explicit credit window**: the provider grants `input_credit` bytes at open and refreshes it on
every accepted write, the Runner may not have more than that outstanding, and overrun is refused
as `input_buffer_full` and retried after the next grant. When the guest's stdin pipe blocks, the
provider simply stops granting.

**This is a new failure mode that does not exist today, and the exposure is not small.** An
earlier revision of this page said it was, citing a 1 MiB frame ceiling and a one-shot driver
that writes a single prompt and closes. Both citations were wrong and are withdrawn:

* `ACP_MAX_FRAME_BYTES` bounds the **other** direction. It is read in exactly one place, inside
  `boundAcpOutput`, which is applied to `output` and only to `output` — `input` is passed raw.
  There is no frame bound on the stdin direction anywhere in the repository.
* The one-shot reassurance describes a harness nobody can currently launch. `oneshot-v1`'s sole
  catalog entry is `availability: "blocked"`; every other packaged entry is `acp-v1`, so the ACP
  case is the real one.

Nothing on the upstream path has a byte ceiling. The sharp edge is `resume_blob`, whose entire
schema constraint is a minimum length — no maximum, and harness-authored. It is bounded only
*transitively and by accident*: the blob arrives as a `session/update` on the output side, so
`boundAcpOutput` errors the stream above 1 MiB and no larger blob is ever received. Nothing on
the stdin path re-checks it, and base64 turns ≈1 MiB into ≈1.37 MiB inside one `session/new`
frame. Its neighbours have no accidental ceiling at all: `mcpServers` is unbounded by server
count, and `builtinTools`, `continuity`, and `runtimeConfig` are unbounded outright.

`input_buffer_full` is therefore a condition your implementation must have a real answer for,
not a corner case.

### stdout is lossless only within a declared replay window

In-process, the readable stream **is** the child's pipe. Between child and driver nothing is
dropped, and no window is involved.

Across a wire, resume after a dropped connection requires a bounded replay journal. A Runner
disconnected longer than `output_window_bytes` cannot be served a correct suffix, and asking for
a cursor below the journal's low-water mark is refused as `output_truncated`.

The design fails closed: `output_truncated` **terminates the process** rather than delivering a
hole, because a gapped NDJSON stream cannot be resynchronised and a silent hole would corrupt
the transcript. That is the right behavior and it is still a loss. Stated without softening:

<Warning>
  **A long enough disconnection kills a Run that would have survived in-process.** The window is
  the tunable. It has not been sized against real outage data — no disconnect-duration
  instrumentation exists on the Runner↔control-plane connection yet — so today it would be
  guessed, not derived.
</Warning>

Downstream "backpressure" is this journal bound, not flow control. It is a fail-closed ceiling,
not throttling.

### `evidence` stops being an isolation cross-check

Restated here because it is the loss most likely to be forgotten. `evidenceMatches` is sixteen
clauses. Nine bind evidence to state the Runner already holds — instance, provider and
materialization revisions, root image, platform, profile digest, network mode, resource limits —
and those survive a wire **intact and unweakened**, because the reference side is
Runner-owned. A remote provider that starts a process on another image or under another network
mode is caught exactly as it is today. Six more are internal consistency of the evidence object
and were never cross-party.

One clause is genuinely cross-party, and it does not survive: the comparison of
`evidence.enforcement` against `capabilities.enforcement`. See
[the enforcement ceiling gap](#the-enforcement-ceiling-gap).

**On the wire seam, `evidenceMatches` is an identity and self-consistency check. It is not, and
cannot be, an isolation check.**

### What is *not* lost

Worth stating, because it bounds the anxiety:

* **Cross-channel interleaving.** stdout and stderr have no ordering guarantee relative to each
  other today either — the in-process shape is two independent pipes. A wire loses nothing that
  exists.
* **Chunk boundaries.** Nothing observes them. ACP reframes on newlines; the one-shot driver
  concatenates to EOF. Re-chunk freely.
* **Multiplexing.** At most one live process per instance. The Docker adapter reserves it
  today and the wire design inherits the rule, so there is no multiplexing to build — though
  the reservation is yours to keep, not the runtime's.
* **The stream identity check.** A transport shim constructs the three Web Streams itself, so
  the check passes by construction. It is a requirement to satisfy, not a thing to transport.
* **stderr fidelity.** It is never a semantic channel — piped to the Runner's own stderr under
  a debug flag, or discarded. It needs a bound and best-effort delivery, not exactly-once.
* **Exactly-once stdout across a mid-process drop.** Enforced by the reader's sequence filter,
  so a provider that lazily replays its whole journal on reconnect is still correct. Proved by
  the spike, including a drop injected after frames had actually been applied.

## What is not yet proven

The `openProcess` wire was spiked end to end and every case passed, five consecutive runs. The
spike's green output is narrower than it reads, and these are the gaps:

| Not covered                        | Consequence for you                                                                                                                                        |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `out_end` / `err_end` frames       | Per-channel EOF ahead of process exit is **specified but unbuilt**; the spike closes both streams at exit                                                  |
| The push `window` frame            | Credit is returned on write responses only; the backpressure path was exercised in its degenerate form                                                     |
| The real sandbox runtime           | The shim was never driven through `evidenceMatches`, adapter selection, or the lifecycle checks                                                            |
| Transport reality                  | Loopback HTTP/1.1 only: no TLS, no auth, no proxy, no load balancer, no idle timeout, no HTTP/2. Every SSE hazard that lives in intermediaries is untested |
| Scale and duration                 | One process, seconds of runtime, kilobytes of output. Nothing about a 24-hour Run, sustained throughput, or journal memory growth                          |
| Multi-Runner and takeover          | One reader throughout; two Runners racing a cursor is untested                                                                                             |
| Epoch change during a live process | A *stale* epoch is proved refused on a control call; an in-flight stream being torn down when the epoch advances is not                                    |
| Real isolation                     | The spike's child was a plain host process. No container, no network policy, no mounts, no enforcement of any kind                                         |

## Summary: what to treat as settled

**Settled and safe to build against:** the grading rule and its total order; the five facts and
which three you cannot self-attest; the admission comparison and where it fails.

**Two of the three wire losses fail closed and announce themselves** — `input_buffer_full` is
typed and retryable, `output_truncated` terminates the process rather than serving a gap. **The
third does not.** The evidence cross-check does not fail when it degrades; it keeps passing and
simply means less, because both sides of the comparison end up authored by the same partner.
Nothing in the system currently records that difference, which is exactly why the enforcement
ceiling is called blocking rather than pending.

**Not settled:** the enforcement ceiling (blocking), the egress-grade ceiling on
`RunnerProviderCapability` (blocking), the `out_end`/`err_end` and `window` frames, the sizing
of `output_window_bytes`, and everything in
[the readiness table](/concepts/sandbox-providers#publication-readiness).

## Next steps

* [Sandbox providers](/concepts/sandbox-providers) — the concept page and the publication
  readiness table.
* [Sandbox provider contract](/concepts/sandbox-provider-contract) — the operations, the
  optional members, and the minimum provider.
* [Security](/concepts/security) — where a SandboxProfile's network policy sits relative to a
  Connection's egress rules, and why the latter is the stronger boundary.
