Hyperlang (Hyperlang v0.2.0)

Copy Markdown View Source

Compile and run untrusted Erlang-shaped guest programs.

The public API is four functions and one callback module:

  • program/1 compiles an immutable source value into a Hyperlang.Program. The manifest, the binding MFA set, the export set and the atom universe are fixed here and enter the program hash.
  • program/2 returns a program cached under a caller-owned key and calls the loader only on a miss.
  • run/4 runs one declared export with exactly the authority in its spec.
  • load/1 loads a program into the code pool ahead of its first use.
  • Hyperlang.Actor is the GenServer callback module that runs a program's OTP callbacks (GenServer.start(Hyperlang.Actor, {spec, program, arg})). Every callback goes through run/4.

Errors are sanitized maps whose :category comes from a closed taxonomy; see doc/SECURITY_SPECIFICATION.md.

Summary

Functions

Load an immutable program value into the code pool ahead of its first use.

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

errors()

@type errors() :: [map()]

manifest()

@type manifest() :: map()

source_bundle()

@type source_bundle() :: binary() | %{optional(atom() | binary()) => term()}

Functions

load(program)

@spec load(Hyperlang.Program.t()) :: :ok | {:error, term()}

Load an immutable program value into the code pool ahead of its first use.

Loading is optional: run/4 and a Hyperlang.Actor load a program that is not loaded. A host calls load/1 to pay the compile and load cost at a time of its choosing (for example at boot, outside a request). The loaded code is not pinned; the pool may reclaim it later, and the next use loads it again.

Returns :ok, or {:error, error} with a sanitized error map (for example category: :code_pool when every slot of the program's window is pinned or still running).

program(code)

@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.

program(key, loader_fn)

@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.

run(spec, program, export, args)

@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.