README states the consent guarantee unconditionally; the spec enforces it on one transport. On stdio — the primary binding, the one every reference provider and all four SDKs use — a provider that sends workspace content to the internet while declaring egress: false is never detected, and the host queries it without ever asking the user.
In plain words
The README tells readers, unconditionally, "a provider that sends your data off-machine is never queried without your permission." That is only true for network (HTTP) providers. For local (stdio) providers — the kind every reference provider and every SDK actually uses today — nothing watches whether the provider opens a socket and phones home, so a misbehaving stdio provider can exfiltrate workspace content with zero consent prompt and zero detection.
README.md, in the seven-guarantees table:
Consent enforcement — A provider that sends data off-machine is never queried until you record named, revocable consent. The query payload is not transmitted first.
The spec is considerably more careful, and the gap between them is the whole issue:
- C3: "A provider SHOULD declare
egress: true honestly if data leaves the machine, directly or indirectly." Marked advisory in the Verified-by column.
- C4: "A host's HTTP transport MUST treat every non-loopback provider as egress regardless of its handshake claim."
§4 then says exactly why C4 exists: "C3 is a claim, and a protocol that trusted claims about egress would have no security story at all. The transport overrides the declaration because the transport knows." That reasoning is correct, and it is the argument for why the stdio case is a hole rather than a gap — over stdio the transport does not know, so the only thing standing between workspace content and the network is C3, the advisory SHOULD the same paragraph says a protocol cannot rely on.
A stdio provider is a child process with a socket. Nothing in the protocol, the host, or the conformance suite observes whether it opens one.
The loopback case makes it concrete
Even on HTTP, C4 exempts loopback. A provider listening on 127.0.0.1 is classified local and can proxy every query straight out. (The reference host is stricter than C4 here — http.rs forces egress = true for all HTTP providers including loopback — which is the right call and is itself undocumented as a deliberate divergence from the spec's text.)
So the enforced guarantee is: a host will not send a payload to a non-loopback HTTP endpoint without consent. The claimed guarantee is: a provider that sends data off-machine is never queried without consent. The second is not a stronger statement of the first; it is a different statement, about a property no transport can observe.
Why this is worth an issue rather than a README edit
Four things need to move together, and the wording of the guarantee depends on which of them the project wants:
- The claim. README needs to say what is enforced. But the honest sentence depends on (2)–(4), so editing it alone risks understating a guarantee that is about to get stronger.
- Whether stdio gets a sandbox story.
stdio.rs already spawns children with a scrubbed environment (there is a test for it). Network confinement for a child process is platform-specific — a sandbox profile on macOS, a namespace on Linux — and whether the reference host takes that on is a scope decision, not a patch.
- Whether
egress should be attestable. §11.1 lists what is "genuinely unchecked" and is scrupulous about it; the stdio egress hole is not on that list, and it is a larger hole than the three that are.
- Whether C3 should be MUST. It cannot be checked either way, but a MUST makes a dishonest declaration a conformance violation a deployment can act on contractually, which is how every other unverifiable-but-load-bearing declaration in the industry is handled.
Pillar
Reliability. The seven guarantees are the protocol's entire claim on a reader's trust, and README calls the combination load-bearing: "Remove any one and the trust model collapses back to the blob-pipe." A guarantee that holds on one of two bindings should not be stated as holding on both, in the document most people read first.
Done looks like
- README's consent row states the enforced property and names the transport it binds, in the register §4 already uses.
- §11.1 gains the stdio egress hole in its What remains genuinely unchecked list, beside C4/C7/C8 — the section exists precisely so a suite "that quietly omitted the rules it cannot check" is not what this project ships.
- A decision recorded on C3's strength and on whether child-process network confinement is in scope for the reference host.
- The reference host's stricter-than-C4 treatment of loopback HTTP documented as deliberate, so it is not read as a bug and relaxed later.
- A witness for the gap itself: a test (or documented manual repro) showing a stdio provider opening an outbound connection while declaring
egress: false goes undetected today, so a future fix — sandboxing, or a MUST-strength C3 — has a fail→pass case to prove against.
- Any new SPEC.md/README prose this produces cites code by symbol (
Host::add_stdio, stdio.rs, http.rs's loopback handling) rather than a pinned line number, matching this org's citation convention.
- Lands as a SPEC.md/README update plus any host-side detection code in the crate that already owns transport classification (
contextgraph-host), without growing a file past this workspace's size conventions.
README states the consent guarantee unconditionally; the spec enforces it on one transport. On stdio — the primary binding, the one every reference provider and all four SDKs use — a provider that sends workspace content to the internet while declaring
egress: falseis never detected, and the host queries it without ever asking the user.In plain words
The README tells readers, unconditionally, "a provider that sends your data off-machine is never queried without your permission." That is only true for network (HTTP) providers. For local (stdio) providers — the kind every reference provider and every SDK actually uses today — nothing watches whether the provider opens a socket and phones home, so a misbehaving stdio provider can exfiltrate workspace content with zero consent prompt and zero detection.
README.md, in the seven-guarantees table:The spec is considerably more careful, and the gap between them is the whole issue:
egress: truehonestly if data leaves the machine, directly or indirectly." Marked advisory in the Verified-by column.§4 then says exactly why C4 exists: "C3 is a claim, and a protocol that trusted claims about egress would have no security story at all. The transport overrides the declaration because the transport knows." That reasoning is correct, and it is the argument for why the stdio case is a hole rather than a gap — over stdio the transport does not know, so the only thing standing between workspace content and the network is C3, the advisory SHOULD the same paragraph says a protocol cannot rely on.
A stdio provider is a child process with a socket. Nothing in the protocol, the host, or the conformance suite observes whether it opens one.
The loopback case makes it concrete
Even on HTTP, C4 exempts loopback. A provider listening on
127.0.0.1is classified local and can proxy every query straight out. (The reference host is stricter than C4 here —http.rsforcesegress = truefor all HTTP providers including loopback — which is the right call and is itself undocumented as a deliberate divergence from the spec's text.)So the enforced guarantee is: a host will not send a payload to a non-loopback HTTP endpoint without consent. The claimed guarantee is: a provider that sends data off-machine is never queried without consent. The second is not a stronger statement of the first; it is a different statement, about a property no transport can observe.
Why this is worth an issue rather than a README edit
Four things need to move together, and the wording of the guarantee depends on which of them the project wants:
stdio.rsalready spawns children with a scrubbed environment (there is a test for it). Network confinement for a child process is platform-specific — a sandbox profile on macOS, a namespace on Linux — and whether the reference host takes that on is a scope decision, not a patch.egressshould be attestable. §11.1 lists what is "genuinely unchecked" and is scrupulous about it; the stdio egress hole is not on that list, and it is a larger hole than the three that are.Pillar
Reliability. The seven guarantees are the protocol's entire claim on a reader's trust, and README calls the combination load-bearing: "Remove any one and the trust model collapses back to the blob-pipe." A guarantee that holds on one of two bindings should not be stated as holding on both, in the document most people read first.
Done looks like
egress: falsegoes undetected today, so a future fix — sandboxing, or a MUST-strength C3 — has a fail→pass case to prove against.Host::add_stdio,stdio.rs,http.rs's loopback handling) rather than a pinned line number, matching this org's citation convention.contextgraph-host), without growing a file past this workspace's size conventions.