1.0.1 — 2026-08-28
boundary.md moved to priv/, so the documented lookup resolves
1.0.0 shipped docs/boundary.md and told consumers to read it with
Application.app_dir(:ex_sandbox, "docs/boundary.md"). That call cannot work. Mix links exactly
ebin and priv into an application's build directory, so a file shipped under any other
top-level directory is present in the tarball and absent from app_dir/2 -- and app_dir/2 is
the only path a consumer has at runtime.
Found the first time a consumer actually made the call: File.exists? on the documented path
returned false against an installed 1.0.0, while tar tzf on the same release listed the file.
A packaging check that stops at "is it in the tarball" cannot see this, because the tarball was
never the thing that was wrong.
The file is now priv/boundary.md. Its content is unchanged.
If you read the 1.0.0 path, update the call:
# before -- returns a path that does not exist
Application.app_dir(:ex_sandbox, "docs/boundary.md")
# after
Application.app_dir(:ex_sandbox, "priv/boundary.md")Nothing else changed: no module, function, behaviour or configuration key differs from 1.0.0.
1.0.0 — 2026-08-28
Extracted from the Axonn umbrella; first public release
This library was an application inside a larger umbrella project. It is now its own repository and
its own Hex package, with its own lockfile, config, CI and isolation harness. The extraction
preserved history (git subtree split, 143 commits) rather than re-creating the tree.
What changed for a consumer, as opposed to for the umbrella:
storage_rootnow defaults to/var/lib/ex_sandbox/sandboxes, was/var/lib/axonn/sandboxes. ⚠️ For an existing deployment this is a data migration, not a cosmetic rename — sandbox storage moves. Setconfig :ex_sandbox, :beam, storage_root: "/var/lib/axonn/sandboxes"to keep the old path.- The verdict socket's default prefix is
ex-sandbox-, wasaxonn-. - The dependency tree is
:telemetryand nothing else, enforced bytest/dependency_tree_test.exs, and the Elixir floor is~> 1.14rather than the platform's version. - The conformance suite's credentials group reports
capability_unavailablehere rather than passing, because this package has no data store to probe (012-FR-001). A consumer with a database instantiates the same suite with its own probe. The identifiers are explained in docs/requirement-ids.md.
No public function, callback, struct field or telemetry event changed in the move.
ExSandbox.Mechanism gained an optional callback, and the gate changed shape
ExSandbox.Mechanism.constructed_capabilities/0 declares the capabilities a
mechanism builds for whatever it runs — the opposite claim from
ExSandbox.Mechanism.required_capabilities/0, which says what it needs from
the host. ExSandbox's private ensure_capable/2 now subtracts the second list from the
first and asks the host probe only about the remainder.
⚠️ This is a change to a refusal, which is why it is a major version. The callback is optional and a mechanism that omits it is gated exactly as before, so nothing in the tree breaks — but a mechanism that declares one is now admitted on a host where it was previously refused, and a gate that admits more than it used to is a behavioural change to the thing this library exists to do. It is stated here rather than in the additive column.
The claim is not verified by the behaviour. ExSandbox.Conformance is what
establishes it, by observing a breach being stopped; until a mechanism is run
through the suite, what backs its list is whatever tests accompany it.
ExSandbox.Mechanism.Docker
A mechanism backed by a container runtime, for hosts whose own kernel cannot
construct the confinement ExSandbox.Mechanism.Beam requires — every macOS
host, where all five gating capabilities report unavailable and Beam is
therefore refused before it is reached.
It declares :resource_limits, :filesystem_confinement and
:network_restriction as constructed, each backed by an observed breach in
test/mechanism/. It deliberately claims neither
:disk_quota — MEASURED accepted-and-ignored on overlayfs — nor
:privilege_separation; both omissions are stated reductions and are documented
on the module.
ExSandbox.Sandbox gained workspace_path
An absolute host directory the sandbox's contents live in, supplied by the host
and made reachable from inside by whatever means the mechanism has. Additive:
nil means "no workspace", which a mechanism must read as mount nothing
rather than as mount somewhere sensible.