Saphan StudioDocs
Security/Egress control

Where this stands today

THREE STATES, and a security reviewer must not collapse them into two. This legend is the ONLY place that says what they mean, and the other chapters use the same three words and point back here…

THREE STATES, and a security reviewer must not collapse them into two. This legend is the ONLY place in either document that says what they mean — §8 and Security §11 use the same three words and point back here rather than restate them, because a vocabulary written out in N places drifts in N−1 of them, and the product refuses a second copy. Nothing in either document may use a fourth word:

statewhat it means for someone auditing thiswhere to look
NOT BUILTthere is no code to audit. No file, no symbol, nothing to read.the "what unblocks it" cell in §8
BUILT, NOT WIREDthe code is in the build, you can exercise it, and no production path reaches it. This is the state that needs your eyes: it ships.the named file, and §8's claim block, which names the absence that keeps it unreached
BUILT AND LIVEthe code is in the build, and the field a run needs is assigned in non-test code, so a production run executes it the moment one asks. ⚠ What is MEASURED is the assignment, not an execution — §8.1's assigned: predicate, whose own blind spot 6 says it "proves that SOME non-test file assigns a field of that NAME, not that a run is given one."§8.1's blind spot 6

The third state is defined here because the day one row moves into it the word must already mean something, and because a reader who sees only two states cannot tell "shipped and unreached" from "absent". Whether any row is in it today is stated once, in §8, and measured there against the claim block rather than re-read.

the piecestate
the part that judges a destination — normalising a hostname, matching a policy, resolving it once, the hard floor, the TLS hostname check, the connection ceilings, the refusal vocabularyBUILT AND LIVE — the product, normalize.go, resolve.go, sni.go, denyrange.go, limits.go, refuse.go. ⚠ This row carried the MIDDLE state NewCorefor a run started with--egress-policy`, so a production path reaches it. Only that run — a run passing no flag reaches none of it (§8)
the proxy's wire protocol (SOCKS5)BUILT AND LIVE — the product. ⚠ In the middle state Serve` on every conversation a doored run's agent opens, and the product opens that door. Its other caller is the measurement instrument the product (§8)
a second wire protocol — the HTTP-style CONNECT doorBUILT, NOT WIRED — the product. Nothing at all carries a client to it: that transport is an open question, not an unwritten task (§8)
the daemon that would run itNOT BUILT
the policy document — a file you could author, and the parse from it into rulesBUILT AND LIVE — the product: a schema-versioned JSON document, its parser, its load-time refusals (§4.3), the SHA-256 digest that is the policy's identity, and DocumentPath, the ONE definition of where it lives. A run that passes --egress-policy is judged by it (§4, §8)
the rule editor in the consoleBUILT AND LIVE — the product and the product. ⚠ This row denied the editor existed 1's blind spot 6 predicted in writing: the row's undeclared: predicate named EgressPolicyEditor and its no-path: named the product, and the editor was built under other names at another path. the product routes GET/POST /console/egress behind a console session (§8)
the per-run socket in the run lifecycle — minting the socket directory, opening the door, handing the agent ALL_PROXY, unlinking on exit, and the confinement half on all three armsBUILT AND LIVE — the product, the product, and the opener the product wires into runner.Deps. This row had no place in this table Exercised only by that package's tests: nothing outside the product calls it (§4.5, §8)
the assignment — anything that gives a particular run a particular policyBUILT AND LIVEsaphan run --egress-policy gives that run the one canonical document (§8). It was the single gap that left every built row above unreached, and closing it is what moved them. ⚠ Only the explicit per-run form ships; deriving a policy for a seat or a machine is a separate row of §8 (the effective policy) and has not shipped
the audit record surfaceBUILT, NOT WIRED — the product (Fact, FactSink) and the product (FactStore, AppendEgressFact, EgressFacts), over the egress_fact table migration 53 creates on both backends. ⚠ **This row denied there was anything to audit At. What keeps it unreached: no non-test file constructs a FactStoreor gives aCore` one, so a governed run judges and records nothing (§7, §8)
the network-layer enforcement that makes the proxy mandatoryBUILT AND LIVE — the product, the ratified ladder walked to a refusal: netns composes bwrap --unshare-net, darwin's rung composes (deny network*), and the two rungs this build cannot compose (cgroup v2, separate uid) refuse the run by name rather than falling through. The ladder landed 2026-08-28 and became REACHABLE on 2026-08-30, on exactly one condition: a spawn that declares an egress policy. ⚠ Reachable is not reached — no run in this fleet has declared one yet (§8). A run that declares none is never asked the ladder

A run that asks is routed through the judging part; no run in this fleet has asked. That is the honest state as of 2026-08-30, and it is two facts, not one: the path is reachablesaphan run --egress-policy assigns the run the canonical document, the engine mints a socket, opens a real door on it and composes the network cut — and it is unused, because the flag is opt-in and no spawn in the record has passed it. So no connection in your fleet is being judged today, and the reason is a choice nobody has made rather than a wire nobody has run.

This paragraph has now been wrong in BOTH directions, which is why it reads as narrowly as it does. It first said the judging part "is a library that no running component calls" — wrong, the run lifecycle composes the door around it (§8, §9A). It then said "no run is ever assigned an egress policy, and no proxy is ever wired for the door to reach" — true EgressPolicyand the product wiresDeps.EgressDoor`. ⛔ Do not read the correction as an over-claim: a run that passes no flag composes no cut, mints no socket and never has a door opened for it, and is byte-for-byte the run it was before this arc. What to audit is the narrow claim: the path is reachable on one condition — the run declares a policy (§8).

§8 is the audited table; this one is its summary. Every row of §8 names an entry point and carries a claim that one of the product's own tests re-measures against the build (the product). Where the two tables could disagree, §8 is the one with an alarm on it.

For a manager, the whole of §2 reduces to one sentence: this control is under construction, it protects nothing yet, and the release notes will say otherwise on the day it does.