Class: Kobako::Sandbox
- Inherits:
-
Object
- Object
- Kobako::Sandbox
- Extended by:
- Forwardable
- Defined in:
- lib/kobako/sandbox.rb
Overview
Kobako::Sandbox — the user-facing entry point for executing guest mruby scripts inside a wasmtime-hosted Wasm module (docs/behavior.md B-01).
The Sandbox owns the Kobako::Wasm::Instance, the per-Sandbox Kobako::HandleTable (docs/behavior.md B-19), the per-instance RPC Server (which receives the HandleTable by injection so guest→host dispatch and host→guest auto-wrap share one allocator), and the per-channel byte caches for guest stdout / stderr capture. The underlying wasmtime Engine and compiled Module are cached at process scope by the native ext and never surface to Ruby —constructing many Sandboxes amortises both costs automatically.
Output capture policy (docs/behavior.md B-04): the per-channel cap (stdout_limit / stderr_limit) is enforced inside the WASI pipe — the host buffer stops growing at the cap, subsequent guest writes on that channel fail or are dropped, and #run still returns normally. #stdout / #stderr return the captured prefix as a UTF-8 String; the byte content never carries a truncation sentinel. #stdout_truncated? / #stderr_truncated? are the only way to observe that the cap was hit.
Instance Attribute Summary collapse
-
#instance ⇒ Object
readonly
Returns the value of attribute instance.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#services ⇒ Object
readonly
Returns the value of attribute services.
-
#snippets ⇒ Object
readonly
Returns the value of attribute snippets.
-
#usage ⇒ Object
readonly
Returns the
Kobako::Usagevalue object for the most recent invocation (docs/behavior.md B-35). -
#wasm_path ⇒ Object
readonly
Returns the value of attribute wasm_path.
Instance Method Summary collapse
-
#define(name) ⇒ Object
Declare or retrieve the Namespace named
nameon this Sandbox (docs/behavior.md B-07, B-09, B-10). -
#eval(code) ⇒ Object
Execute a guest mruby source string in a fresh
mrb_state(docs/behavior.md B-02 / B-03 / B-06). -
#initialize(wasm_path: nil, stdout_limit: nil, stderr_limit: nil, timeout: SandboxOptions::DEFAULT_TIMEOUT_SECONDS, memory_limit: SandboxOptions::DEFAULT_MEMORY_LIMIT) ⇒ Sandbox
constructor
Build a fresh Sandbox.
-
#preload(code: nil, name: nil, binary: nil) ⇒ Object
Register a snippet on this Sandbox in one of two forms (docs/behavior.md B-32):.
-
#run(target, *args, **kwargs) ⇒ Object
Dispatch into a preloaded entrypoint constant (docs/behavior.md B-31).
-
#stderr ⇒ Object
Returns the bytes the guest wrote to stderr during the most recent invocation as a UTF-8 String, clipped at
stderr_limit. -
#stderr_truncated? ⇒ Boolean
Returns
trueiff stderr capture during the most recent invocation exceededstderr_limit. -
#stdout ⇒ Object
Returns the bytes the guest wrote to stdout during the most recent invocation as a UTF-8 String, clipped at
stdout_limit. -
#stdout_truncated? ⇒ Boolean
Returns
trueiff stdout capture during the most recent invocation exceededstdout_limit(docs/behavior.md B-04).
Constructor Details
#initialize(wasm_path: nil, stdout_limit: nil, stderr_limit: nil, timeout: SandboxOptions::DEFAULT_TIMEOUT_SECONDS, memory_limit: SandboxOptions::DEFAULT_MEMORY_LIMIT) ⇒ Sandbox
Build a fresh Sandbox.
wasm_path is the absolute path to the Guest Binary; defaults to the gem-bundled data/kobako.wasm. The four caps (stdout_limit, stderr_limit, timeout, memory_limit) are forwarded verbatim to Kobako::SandboxOptions, which owns their DEFAULT fallback and normalisation. The constructed SandboxOptions is exposed as #options and the four caps remain readable directly on Sandbox via Forwardable delegation.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/kobako/sandbox.rb', line 99 def initialize(wasm_path: nil, stdout_limit: nil, stderr_limit: nil, timeout: SandboxOptions::DEFAULT_TIMEOUT_SECONDS, memory_limit: SandboxOptions::DEFAULT_MEMORY_LIMIT) @wasm_path = wasm_path || Kobako::Wasm.default_path @options = SandboxOptions.new(timeout: timeout, memory_limit: memory_limit, stdout_limit: stdout_limit, stderr_limit: stderr_limit) @handle_table = HandleTable.new @services = Kobako::RPC::Server.new(handle_table: @handle_table) @snippets = Snippet::Table.new @instance = Kobako::Wasm::Instance.from_path(@wasm_path, @options.timeout, @options.memory_limit, @options.stdout_limit, @options.stderr_limit) @instance.server = @services reset_invocation_state! end |
Instance Attribute Details
#instance ⇒ Object (readonly)
Returns the value of attribute instance.
41 42 43 |
# File 'lib/kobako/sandbox.rb', line 41 def instance @instance end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
41 42 43 |
# File 'lib/kobako/sandbox.rb', line 41 def @options end |
#services ⇒ Object (readonly)
Returns the value of attribute services.
41 42 43 |
# File 'lib/kobako/sandbox.rb', line 41 def services @services end |
#snippets ⇒ Object (readonly)
Returns the value of attribute snippets.
41 42 43 |
# File 'lib/kobako/sandbox.rb', line 41 def snippets @snippets end |
#usage ⇒ Object (readonly)
Returns the Kobako::Usage value object for the most recent invocation (docs/behavior.md B-35). Carries wall_time (Float seconds the guest export call spent inside wasmtime) and memory_peak (Integer bytes, high-water of the per-invocation memory.grow delta past the entry-time baseline). Returns Kobako::Usage::EMPTY before any invocation; populated on every outcome — including TrapError — so the Host App can read it after rescuing a trap to diagnose budget consumption.
88 89 90 |
# File 'lib/kobako/sandbox.rb', line 88 def usage @usage end |
#wasm_path ⇒ Object (readonly)
Returns the value of attribute wasm_path.
41 42 43 |
# File 'lib/kobako/sandbox.rb', line 41 def wasm_path @wasm_path end |
Instance Method Details
#define(name) ⇒ Object
Declare or retrieve the Namespace named name on this Sandbox (docs/behavior.md B-07, B-09, B-10). name must be a Symbol or String in constant form. Returns the Kobako::RPC::Namespace.
Raises ArgumentError when called after the first invocation, or when name does not match the constant-name pattern.
120 121 122 |
# File 'lib/kobako/sandbox.rb', line 120 def define(name) @services.define(name) end |
#eval(code) ⇒ Object
Execute a guest mruby source string in a fresh mrb_state (docs/behavior.md B-02 / B-03 / B-06). code is the mruby source as a UTF-8 String. Returns the deserialized last expression of the source.
Source delivery uses the WASI stdin three-frame protocol (docs/wire-codec.md Invocation channels): Frame 1 carries the msgpack-encoded preamble (Namespace / Member registry snapshot), Frame 2 carries the user source UTF-8 bytes, and Frame 3 carries the snippet table registered via #preload (B-32). Each frame is prefixed by a 4-byte big-endian u32 length; Frame 3 is mandatory-presence — an empty snippet table sends an empty msgpack array, never an absent frame.
The first invocation seals the Service registry and snippet table (docs/behavior.md B-07 / B-33); subsequent #define / #preload calls raise ArgumentError.
Raises Kobako::TrapError on a Wasm trap or wire-violation fallback; Kobako::SandboxError when the guest ran to completion but failed (including when code is nil or not a String, or when a preloaded snippet’s replay raises — E-36); Kobako::ServiceError on an unrescued Service capability failure.
200 201 202 203 204 205 206 |
# File 'lib/kobako/sandbox.rb', line 200 def eval(code) raise SandboxError, "code must be a String, got #{code.class}" unless code.is_a?(String) invoke!(:eval) do @instance.eval(@services.encoded_preamble, code.b, @snippets.encode) end end |
#preload(code: nil, name: nil, binary: nil) ⇒ Object
Register a snippet on this Sandbox in one of two forms (docs/behavior.md B-32):
* +preload(code: source, name: Name)+ — +source+ is mruby source
as a +String+ and +Name+ matches +/\A[A-Z]\w*\z/+. The +name+
becomes the snippet's +(snippet:Name)+ backtrace filename and
is the dedupe key for E-33.
* +preload(binary: bytes)+ — +bytes+ is precompiled RITE
bytecode as a +String+. The canonical name, when present,
lives in the bytecode's embedded +debug_info+ and is resolved
by the guest at load time; the host treats the bytes as
opaque. Structural failures
({docs/behavior.md E-37 / E-38}[link:../../docs/behavior.md])
surface as +Kobako::BytecodeError+ on the first invocation.
Subsequent invocations (#eval or #run) replay every registered snippet — in insertion order — against the fresh mrb_state before per-invocation source or entrypoint resolution.
Returns self to allow chaining.
Raises ArgumentError when neither form’s keyword set is supplied, when both forms are mixed (e.g., code: and binary: together, or binary: paired with name:), when code / bytes is not a String, when name does not match the constant pattern (docs/behavior.md E-34), when name duplicates an already-registered code: form snippet (docs/behavior.md E-33), or when called after the first invocation (docs/behavior.md E-35, B-33).
154 155 156 157 158 159 |
# File 'lib/kobako/sandbox.rb', line 154 def preload(code: nil, name: nil, binary: nil) raise ArgumentError, "cannot preload after first Sandbox invocation" if @services.sealed? @snippets.register(code: code, name: name, binary: binary) self end |
#run(target, *args, **kwargs) ⇒ Object
Dispatch into a preloaded entrypoint constant (docs/behavior.md B-31). Delegates host pre-flight (E-24 / E-25 / E-29 / E-30) and wire encoding to Kobako::Invocation / Kobako::Invocation#encode; the guest resolves target as a top-level constant, calls #call on it with args / kwargs, and returns the deserialized result. The first invocation seals the Service registry and snippet table (B-07 / B-33). Runtime errors follow the same three-class taxonomy as #eval.
170 171 172 173 174 175 |
# File 'lib/kobako/sandbox.rb', line 170 def run(target, *args, **kwargs) invocation = Invocation.new(entrypoint: target, args: args, kwargs: kwargs) invoke!(:run) do @instance.run(@services.encoded_preamble, @snippets.encode, invocation.encode(@handle_table)) end end |
#stderr ⇒ Object
Returns the bytes the guest wrote to stderr during the most recent invocation as a UTF-8 String, clipped at stderr_limit. Empty before any invocation. Mirror of #stdout.
61 62 63 |
# File 'lib/kobako/sandbox.rb', line 61 def stderr @stderr_capture.bytes end |
#stderr_truncated? ⇒ Boolean
Returns true iff stderr capture during the most recent invocation exceeded stderr_limit. Mirror of #stdout_truncated?.
75 76 77 |
# File 'lib/kobako/sandbox.rb', line 75 def stderr_truncated? @stderr_capture.truncated? end |
#stdout ⇒ Object
Returns the bytes the guest wrote to stdout during the most recent invocation as a UTF-8 String, clipped at stdout_limit. Empty before any invocation. docs/behavior.md B-04 — the byte content never contains a truncation sentinel; use #stdout_truncated? to observe overflow.
54 55 56 |
# File 'lib/kobako/sandbox.rb', line 54 def stdout @stdout_capture.bytes end |
#stdout_truncated? ⇒ Boolean
Returns true iff stdout capture during the most recent invocation exceeded stdout_limit (docs/behavior.md B-04). Resets to false at the start of the next invocation (docs/behavior.md B-03).
69 70 71 |
# File 'lib/kobako/sandbox.rb', line 69 def stdout_truncated? @stdout_capture.truncated? end |