Compile and run untrusted Erlang-shaped guest programs.
The whole public API is four functions:
program/1compiles an immutable source value into aHyperlang.Program. The manifest, the binding MFA set, the export set and the atom universe are fixed here and enter the program hash.program/2returns a program cached under a caller-owned key and calls the loader only on a miss.run/4runs one declared export with exactly the authority in its spec.module/1returns the program's confined OTP callback module. Every callback goes throughrun/4.
Errors are sanitized maps whose :category comes from a closed taxonomy;
see doc/SECURITY_SPECIFICATION.md.
Summary
Functions
Return the loaded confined module for an immutable program value.
Compile an immutable, closed-world source value.
Return a program cached under a caller-owned key, compiling the loader's source value on a miss.
Run a declared export using only the authority present in spec.
Types
Functions
@spec module(Hyperlang.Program.t()) :: module()
Return the loaded confined module for an immutable program value.
The module is a GenServer callback module. Start it with
GenServer.start(Hyperlang.module(program), {spec, arg}); init/1
receives arg, and every callback runs under spec. Raises
ArgumentError when the program cannot be loaded.
From init/1 until it exits, an actor pins the program's code slot, so
the module stays loaded under it even after release or code-pool
pressure. The pin is charged to the actor's scope (spec.policy.scope,
else the actor's own default scope), which may pin at most max_code_pins
distinct slots. init/1 stops with a code_pool error when that limit is
reached (pin_limit) or when the slot was reclaimed before the actor
started (not_loaded); call module/1 again and restart.
@spec program(binary() | map()) :: {:ok, Hyperlang.Program.t()} | {:error, errors()}
Compile an immutable, closed-world source value.
Source maps use %{entry: "name.erl", files: %{"name.erl" => source}} and
may carry a compiler manifest under :manifest. A binary is the compact
single-source form.
@spec program(term(), (-> binary() | map() | {:ok, binary() | map()} | {:error, term()})) :: {:ok, Hyperlang.Program.t()} | {:error, errors()}
Return a program cached under a caller-owned key, compiling the loader's source value on a miss.
@spec run(map(), Hyperlang.Program.t(), atom() | binary(), list()) :: {:ok, term(), %{ stdout: [binary()], reductions: non_neg_integer(), wall_us: non_neg_integer() }} | {:error, term()}
Run a declared export using only the authority present in spec.
spec is %{bindings: %{name => {module, state}}, policy: map} plus an
optional syscall host door. bindings must name exactly the bindings the
program's manifest declares, with the same modules. export is :entry
(the manifest result root, called with hd(args)), an atom, or an
"name/arity" export id.
Success returns {:ok, value, meta} where meta is
%{stdout: [binary], reductions: non_neg_integer, wall_us: non_neg_integer}:
the sanitized guest stdout chunks in order, the reductions the guest call
consumed in its own process, and the wall time of the call in
microseconds. Failure returns {:error, error} with a sanitized error map.