SmolBox (SmolBox v0.1.0)

Copy Markdown View Source

Standalone managed sandbox executions over pinned, host-operated SmolVM workers.

Start a named child with child_spec/1. Submit a validated immutable execution specification under a host-authorized scope. Acceptance is durable only with a conforming durable store. An execution handle is {scope, execution_id}; it is independent of the submitting process and is not an authorization token.

await/3 observes stored evidence and has its own timeout. It never cancels the guest. cancel/3 persists intent; fetch the record to distinguish an observed exit, unknown outcome, confirmed termination, and eventual cleanup. No API automatically repeats an uncertain command.

Begin with the Getting started walkthrough. Configure the runtime with child_spec/1, describe work with SmolBox.ExecutionSpec.new/1, then call submit/2. Use SmolBox.Client when your host already manages the machine lifecycle and only needs individual worker operations.

Summary

Functions

Read a bounded page of namespace candidates against stored machine assignments.

Wait up to timeout milliseconds for a terminal or unknown stored outcome.

Persist cancellation intent and return the existing execution handle.

Build a named runtime child for your application's supervision tree.

Exclude a worker from new admission-task launches; already active work may continue.

Read {:ok, %SmolBox.Execution{}} for an existing scoped identity.

Schedule existing evidence for observation; this never authorizes command replay.

Accept an immutable execution and return {:ok, {scope, id}}.

Read ephemeral notification counters; an epoch change resets them.

Read configured workers and their latest controller health/drain observations.

Types

handle()

@type handle() :: SmolBox.Execution.key()

runtime()

@type runtime() :: Supervisor.supervisor()

Functions

audit_worker(runtime, worker_id, options \\ [])

@spec audit_worker(runtime(), String.t(), keyword()) ::
  {:ok, map()} | {:error, SmolBox.Error.t()}

Read a bounded page of namespace candidates against stored machine assignments.

This is an operator API across scopes; hosts must authorize access. It sends only a worker list request and store reads. Neither names nor findings authorize adoption, stopping, or deletion. :owned still relies on weak upstream creation evidence and exclusive namespace control. :untracked, :unverified, :conflict, and :cleanup_conflict require investigation; :unavailable is not absence. A cleanup conflict can be a concurrent deletion or a possible reappearance; these separate worker/store reads cannot establish their chronological order.

Options are :limit (1..100, default 20) and :cursor (the previous page's next_cursor). Pages are fresh observations, not a stable historical snapshot; periodic scans should restart at nil. The worker list has a one-second budget; store lookups have 500 ms each in groups of at most four. No stdout, code, command environment, or artifact content is returned.

await(runtime, arg2, timeout)

Wait up to timeout milliseconds for a terminal or unknown stored outcome.

timeout is 0–900,000 ms. Returns {:ok, %SmolBox.Execution{}} for a terminal state or :unknown, or {:error, %SmolBox.Error{category: :expired}} when this caller's wait expires. Store and validation errors are returned normally.

A returned record can have a nonzero command exit, collection failure, or no known exit. Cleanup and capacity release may still be pending. This function never cancels the command; it is safe to await the same handle again. Use cancel/3 for cancellation and fetch/3 to inspect continuing cleanup.

cancel(runtime, scope, id)

@spec cancel(runtime(), String.t(), String.t()) ::
  {:ok, handle()} | {:error, SmolBox.Error.t()}

Persist cancellation intent and return the existing execution handle.

Repeated calls preserve the first cancellation timestamp. An acknowledgment is not proof the VM has stopped. After dispatch, missing exit evidence can remain unknown even after confirmed termination. A concurrently observed exit is preserved. Use fetch/3 to follow termination, collection, and cleanup.

child_spec(options)

@spec child_spec(keyword()) :: Supervisor.child_spec()

Build a named runtime child for your application's supervision tree.

Start the store before this child. The artifact adapter is {module, context}; its context need not be a process. See the complete setup in Getting started and Host integration.

Required options:

OptionMeaning
:nameAn atom used to register this runtime and address the managed API
:namespaceExclusive machine-name prefix: 1–10 lowercase letters/digits, starting with a letter
:store{adapter_module, context} implementing SmolBox.Store
:artifact_store{adapter_module, context} implementing SmolBox.ArtifactStore
:fingerprint_keyStable host secret of 32–4096 bytes; keep it across durable restarts

Optional options:

OptionDefaultMeaning
:workers[]Up to 64 SmolBox.Runtime.WorkerConfig values with unique worker IDs/endpoints; an empty list permits inspection but cannot admit new work
:mode:durable:ephemeral explicitly permits a non-durable store such as SmolBox.Store.Memory
:max_pending128Pending-queue bound, 1–10,000
:max_active4Concurrent runtime work tasks, 1–64; worker reservations separately bound admitted guests
:poll_ms250Scan interval, 10–5000 ms
:lease_ms30_000Ownership lease, 1000–900,000 ms and at least four times :poll_ms
:cleanup_attempts5Automatic cleanup-attempt budget, 1–20
:telemetry_max_pending128Notification bound, 1–1024
:telemetry_timeout_ms100Handler delivery budget, 1–1000 ms
:clockSmolBox.Runtime.ClockHost clock module matching SmolBox.Runtime.Clock.now/0 and SmolBox.Runtime.Clock.monotonic/0; primarily useful for tests

Unknown options are rejected during startup. Stopping the runtime stops observers; it does not establish guest termination. Use durable storage when execution ownership must survive a controller restart.

drain_worker(runtime, worker_id)

@spec drain_worker(runtime(), String.t()) :: :ok | {:error, SmolBox.Error.t()}

Exclude a worker from new admission-task launches; already active work may continue.

fetch(runtime, scope, id)

@spec fetch(runtime(), String.t(), String.t()) :: SmolBox.Store.result()

Read {:ok, %SmolBox.Execution{}} for an existing scoped identity.

Returns a typed :not_found error only when the store reports absence. This is a read, with no worker command or implicit cancellation. The full record includes its specification and output; authorize access and avoid logging it. Inspect state, result, collection, cleanup, and reservation separately.

reconcile(runtime, scope, id)

@spec reconcile(runtime(), String.t(), String.t()) ::
  :ok | {:error, SmolBox.Error.t()}

Schedule existing evidence for observation; this never authorizes command replay.

submit(runtime, spec)

@spec submit(runtime(), SmolBox.ExecutionSpec.t()) ::
  {:ok, handle()} | {:error, SmolBox.Error.t()}

Accept an immutable execution and return {:ok, {scope, id}}.

Acceptance records intent; it does not mean the guest has started or the command has succeeded. The same scoped ID and semantic specification return the original handle. A changed specification under that ID returns :identity_conflict. At least one configured worker must support a new specification's exact profile and approved artifact; otherwise acceptance returns :unsupported_capability.

Preserve the spec and identity after an ambiguous store/transport failure. Inspect or resubmit the same identity to resolve acceptance; a fresh ID can authorize another execution. Host code must authorize spec.scope.

telemetry_stats(runtime)

@spec telemetry_stats(runtime()) :: {:ok, map()} | {:error, SmolBox.Error.t()}

Read ephemeral notification counters; an epoch change resets them.

workers(runtime)

@spec workers(runtime()) :: {:ok, [map()]} | {:error, SmolBox.Error.t()}

Read configured workers and their latest controller health/drain observations.

Results include configured capacity, allocation floor, platform, architecture, version, qualification and status. Capacity is declared admission capacity, not a live host free-memory/disk measurement. See Telemetry.