Module: SecurityBox::GuestRpc

Defined in:
lib/security_box/guest_rpc.rb

Overview

Host side of the guest RPC channel (stage 6).

The image statically links the sb_rpc gem, whose C extension declares the wasm import ("sb", "call"). The linker must define it for every instantiation — even when no handlers are configured, or instantiation fails on an unresolved import. Per-eval state (the /work tmpdir, the handler map and the call transcript) travels through Store data and reaches the closure via caller.store_data, so a single definition is safe to share across evaluations, threads and Ractor workers.

Protocol (guest side: lib/security_box/guest/rpc.rb):

guest writes /work/rpc_req.json  {"name", "args"}
guest calls SBExt.call           (blocks inside the import)
host executes handlers[name], writes /work/rpc_resp.json
host returns 0 (any non-zero value is a transport failure)

Nothing escapes the closure: a raising handler (or any failure in the bridge) becomes an false, "error": {"class", "message"} response the guest can rescue. Messages never include host details (no backtraces, no paths). Handler results are JSON round-tripped, like guest return values — never Marshal.

Constant Summary collapse

IMPORT_MODULE =
"sb"
IMPORT_NAME =
"call"
REQUEST_FILE =
"rpc_req.json"
RESPONSE_FILE =
"rpc_resp.json"
MAX_CALLS =
1_000
RESULT_LIMIT =
1 << 20

Class Method Summary collapse

Class Method Details

.define_import(linker) ⇒ Object

Defines the import on linker (once per linker; state is per-Store). The closure captures nothing — Ractor-safe.



38
39
40
41
42
43
# File 'lib/security_box/guest_rpc.rb', line 38

def define_import(linker)
  linker.func_new(IMPORT_MODULE, IMPORT_NAME, [], [:i32]) do |caller|
    serve(caller)
  end
  nil
end

.store_data(workdir, handlers) ⇒ Object

Per-eval Store data. handlers is the configuration's name => callable map, or nil when no rpcs are configured (guest calls still get a clean, rescuable error).



48
49
50
51
52
53
# File 'lib/security_box/guest_rpc.rb', line 48

def store_data(workdir, handlers)
  {
    workdir: workdir,
    rpc: handlers.nil? ? nil : { handlers: handlers, calls: [] }
  }
end