What this library needs from its host, and whether it is actually there (012 T013, T022, FR-016).
Determined, never assumed
FR-016 requires a library state what it needs and report at runtime when
it is unavailable, rather than assuming it. The distinction is not academic:
005 R9 found that six of ten isolation criteria require Linux, and 013
Finding V2 found a capability that is absent under some container
configurations. A library that assumed them would provide no isolation on
those hosts while claiming to.
Availability is evidence, not configuration
A capability is reported available only on FR-012a's evidence standard —
observed behaviour, not the presence of a mechanism. 005 R9b measured the
failure this guards against: taskpolicy -m 100 sandbox-exec ... ./hog 300
allocates 300 MB under a nominal 100 MB cap and exits 0, because the limit is
silently lost across the intervening exec. Every check short of trigger a
breach and watch it stop reports that composition as working.
So a check here answers "could this host enforce the cap at all", and it
errs toward false. Whether a particular mechanism actually enforces it
is ExSandbox.Conformance's question, answered by breaching it.
The coarse name and its decomposition (014-FR-013a, T004)
:resource_limits remains the coarse, Linux-facing name, and it stays.
ExSandbox.Mechanism.Beam's required_capabilities/0 callback names it, and its comment
records why one name suffices there: the memory and CPU caps "both come from
the same cgroup scope, and a host with one has the other". On Linux that is
true, and a single gate over a single mechanism is the honest shape.
It stops being true off Linux, which is what 014-FR-013a is about. 005
R9b measured a macOS host where the caps come apart: RLIMIT_CPU is honoured
by the kernel while RLIMIT_AS/DATA/RSS fail setrlimit with EINVAL,
and the memory cap survives or is lost depending on where taskpolicy sits
in the process tree. One summary level cannot report that host without lying
in one direction or the other.
So :process_separation, :memory_cap, :cpu_cap and :time_budget are
:resource_limits' per-capability decomposition — an additional, finer
vocabulary, not a replacement. Two rules keep the two from drifting apart,
and both exist because a split verdict on one underlying fact is a defect
this file has already carried more than once (005 T060a5c, T060c):
On Linux the three host-enforced names are derived from the existing cgroup v2 probe rather than re-probing it. Two probes of one fact are two things that must stay equal forever, and the symptom of their drifting is a cap reported enforced on a host that does not enforce it.
:time_budgetis derived from nothing, because it is not a host fact at all.014-FR-014bplaces its enforcement in the supervising BEAM, so no probe here can observe it and no cgroup verdict says anything about it. Seetime_budget_not_a_host_capability/1.
Three Darwin names now report available, and what that cost (014 T020)
Every Darwin clause below used to report unavailable, on the correct
grounds that no backend existed to watch stopping a breach. 014 Phase 3
built one, and Phase 4 observed the breaches: :memory_cap, :cpu_cap and
:process_separation are derived from
ExSandbox.Hardening.Darwin.capabilities/0, whose probe runs the real
composition rather than looking for binaries on the PATH.
The claim rests on ExSandbox.Hardening.DarwinOrderingTest (SC-003), which
runs R9b's misordered composition and the backend's own composition in one
run and requires them to differ — 0 with 300 MB allocated against 137.
Without that pair, FR-014a's standard is not met and these names go back.
Everything else on Darwin still reports unavailable, and each detail names
what is missing rather than merely asserting absence:
:time_budget— not a host fact anywhere, seetime_budget_not_a_host_capability/1.:privilege_separation— a deny-listsandbox-execprofile is not default-deny confinement (T021).:filesystem_confinement,:network_restriction,:disk_quota,:resource_limits— each for the reason its clause states.
Summary
Functions
Checks one capability against the running host.
Checks every known capability.
The capabilities a mechanism is gated on when it declares none.
Every capability this library knows how to check.
The capabilities in required that this host cannot provide.
True when every capability in required is available.
Types
Functions
Checks one capability against the running host.
Returns a report; it never raises, because "cannot determine" is a legitimate
answer that must be reported rather than thrown (FR-012b).
@spec check_all() :: [t()]
Checks every known capability.
@spec gating_defaults() :: [name()]
The capabilities a mechanism is gated on when it declares none.
Deliberately narrower than known/0 -- see the note above it. A name that is
unavailable on every host belongs in the report and never in the gate.
@spec known() :: [name()]
Every capability this library knows how to check.
The capabilities in required that this host cannot provide.
Returned rather than raised so a caller can report which one is missing — "unavailable" with no detail is the kind of message that gets ignored.
True when every capability in required is available.
This is what an entry point calls before starting a sandbox: a mechanism whose
required capability is missing must refuse rather than start unconfined
(spec Edge Cases; 005 R9's macOS rule).