Ties one sandbox's resolved allowlist to the /30 its traffic arrives from,
and takes both back together (005 T060a2/T060a6, 005-FR-011a–FR-011e).
Why the pair is a module rather than two calls
Acquiring an address and registering a policy are two operations that must succeed or fail as one. Split across a caller they are two lines that read independently and can be reordered, half-applied, or partly rolled back — and every one of those states is silent:
- address without policy: the sandbox is denied everything, which is indistinguishable from correct operation under checks that test denial,
- policy without address: the entry is filed under a
/30no sandbox will ever send from, so it enforces nothing and never expires, - released address with a live policy: the next tenant to receive that
/30inherits it (ExSandbox.Egress.Registry's reuse race).
acquire/2 rolls the address back if registration fails, so the pool never
holds an entry the caller does not know about.
Ordering on the way out
release/2 drops the policy first, then the address — and passes the
registry check to ExSandbox.Egress.Allocator.release/3 as a predicate
rather than relying on having done it. If a later refactor reorders these two
lines the allocator still refuses to recycle a /30 whose policy stands, and
the address stays out rather than being handed to the next tenant. That is
the difference between an invariant and a comment.
Summary
Types
What a launched sandbox needs to build its namespace, and what a destroyed one needs to give back.
Types
@type t() :: %ExSandbox.Egress.Binding{ gateway_address: String.t(), sandbox_address: String.t(), source_key: ExSandbox.Egress.Policy.source_key() }
What a launched sandbox needs to build its namespace, and what a destroyed one needs to give back.
Functions
@spec acquire( [ExSandbox.Egress.Policy.destination()], keyword() ) :: {:ok, t()} | {:error, ExSandbox.Egress.Allocator.refusal()}
Takes a /30 and files allowed under it.
Refuses with {:error, :pool_exhausted} rather than issuing an unpoliced
sandbox: a tenant who cannot be given a policy must not be given a sandbox
that reaches everything instead.
Gives back the policy and then the /30.
Idempotent, and safe for a binding this host never issued (003-FR-013):
destroy runs for sandboxes that failed to provision, and runs twice for
sandboxes that did.