Class: Kobako::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/kobako/registry.rb,
lib/kobako/registry/dispatcher.rb,
lib/kobako/registry/handle_table.rb,
lib/kobako/registry/service_group.rb

Overview

Kobako::Registry — per-Sandbox container of Service Groups and Handle state. Manages capability injection and guest-initiated RPC dispatch (SPEC.md B-07..B-21).

Public API:

registry = Kobako::Registry.new
group = registry.define(:MyService)    # => ServiceGroup
group.bind(:KV, kv_object)             # => group (chainable)
registry.to_preamble                   # => array for Frame 1
registry.dispatch(request_bytes)       # => msgpack bytes (delegated to Dispatcher)

Service Groups are defined in Kobako::Registry::ServiceGroup (lib/kobako/registry/service_group.rb). The opaque Handle allocator lives in Kobako::Registry::HandleTable (lib/kobako/registry/handle_table.rb). Dispatch helpers live in Kobako::Registry::Dispatcher (lib/kobako/registry/dispatcher.rb).

Defined Under Namespace

Modules: Dispatcher Classes: HandleTable, ServiceGroup

Constant Summary collapse

NAME_PATTERN =

Ruby constant-name pattern shared by Group and Member names (SPEC.md B-07/B-08 Notes). Referenced by both #define here and ServiceGroup#bind — single source of truth so the validation rule cannot drift between the two boundaries.

/\A[A-Z]\w*\z/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(handle_table: HandleTable.new) ⇒ Registry

Build a fresh Registry. handle_table is an internal seam that injects a pre-configured HandleTable; tests pass one whose next_id is pinned near MAX_ID to exercise the B-21 cap-exhaustion path without 2³¹ allocations. Production callers leave it at the default.



39
40
41
42
43
# File 'lib/kobako/registry.rb', line 39

def initialize(handle_table: HandleTable.new)
  @groups = {}
  @handle_table = handle_table
  @sealed = false
end

Instance Attribute Details

#handle_tableObject (readonly)

Expose the Kobako::Registry::HandleTable for tests and wire-layer Handle wrapping.



147
148
149
# File 'lib/kobako/registry.rb', line 147

def handle_table
  @handle_table
end

Instance Method Details

#bound?(target) ⇒ Boolean

Returns true when target (a “GroupName::MemberName” path) resolves to a bound member, false otherwise.

Returns:

  • (Boolean)


77
78
79
80
# File 'lib/kobako/registry.rb', line 77

def bound?(target)
  group, member_name, = resolve_pair(target)
  !group.nil? && !member_name.nil? && !group[member_name].nil?
end

#define(name) ⇒ Object

Declare or retrieve the Group named name (idempotent — SPEC.md B-10). name is a constant-form name as a Symbol or String (must satisfy NAME_PATTERN). Returns the Kobako::Registry::ServiceGroup for that name, creating it if it does not exist. Raises ArgumentError when name is malformed, or when called after the owning Sandbox has been sealed by #run.

Raises:

  • (ArgumentError)


51
52
53
54
55
56
57
58
59
60
61
# File 'lib/kobako/registry.rb', line 51

def define(name)
  raise ArgumentError, "cannot define after Sandbox#run has been invoked" if @sealed

  name_str = name.to_s
  unless NAME_PATTERN.match?(name_str)
    raise ArgumentError,
          "GroupName must match #{NAME_PATTERN.inspect} (got #{name.inspect})"
  end

  @groups[name_str] ||= ServiceGroup.new(name_str)
end

#dispatch(request_bytes) ⇒ Object

Dispatch a single RPC request and return the encoded response bytes (SPEC.md B-12). request_bytes is a msgpack-encoded Request envelope. Called by the Rust ext from inside __kobako_rpc_call. Always returns a binary String — never raises. Delegates to Dispatcher.dispatch which reifies any failure as a Response.err envelope so the guest sees the failure as a normal RPC error rather than a wasm trap.



141
142
143
# File 'lib/kobako/registry.rb', line 141

def dispatch(request_bytes)
  Dispatcher.dispatch(request_bytes, self)
end

#empty?Boolean

Returns true when no groups have been declared, false otherwise.

Returns:

  • (Boolean)


94
95
96
# File 'lib/kobako/registry.rb', line 94

def empty?
  @groups.empty?
end

#groupsObject

Returns all declared Kobako::Registry::ServiceGroup instances as an Array.



84
85
86
# File 'lib/kobako/registry.rb', line 84

def groups
  @groups.values
end

#guest_preambleObject

Encode the preamble as msgpack bytes for stdin Frame 1 delivery (SPEC.md B-02). Uses plain MessagePack (no kobako ext types) because the preamble contains only strings — no Handles or Exception envelopes. Structure: [[“GroupName”, [“MemberA”, “MemberB”]], …]. Returns a binary String of msgpack bytes.



112
113
114
# File 'lib/kobako/registry.rb', line 112

def guest_preamble
  MessagePack.pack(to_preamble)
end

#lookup(target) ⇒ Object

Resolve a target path of the form “GroupName::MemberName” to the bound Host object. target is a two-level path using the :: separator. Returns the bound Host object. Raises KeyError when the group or the member is not bound.

Raises:

  • (KeyError)


67
68
69
70
71
72
73
# File 'lib/kobako/registry.rb', line 67

def lookup(target)
  group, member_name, group_name = resolve_pair(target)
  raise KeyError, "no service group named #{group_name.inspect}" if group.nil?
  raise KeyError, "no member #{target.inspect} bound in registry" unless member_name

  group.fetch(member_name)
end

#reset_handles!Object

Reset the HandleTable for a new #run boundary. Called by Sandbox#run before each invocation (SPEC.md B-19).



130
131
132
# File 'lib/kobako/registry.rb', line 130

def reset_handles!
  @handle_table.reset!
end

#seal!Object

Mark the Registry as sealed. Called by ‘Sandbox#run` on first run. After sealing, #define raises ArgumentError. Idempotent.



118
119
120
121
# File 'lib/kobako/registry.rb', line 118

def seal!
  @sealed = true
  self
end

#sealed?Boolean

Returns true when #seal! has been called, false otherwise.

Returns:

  • (Boolean)


124
125
126
# File 'lib/kobako/registry.rb', line 124

def sealed?
  @sealed
end

#sizeObject

Returns the number of declared groups as an Integer.



89
90
91
# File 'lib/kobako/registry.rb', line 89

def size
  @groups.size
end

#to_preambleObject

Structured Frame 1 description. Called by Sandbox#run when assembling stdin Frame 1 (SPEC.md B-02). Returns an unencoded preamble array — an Array of two-element [name, members] arrays, one per declared group.



102
103
104
# File 'lib/kobako/registry.rb', line 102

def to_preamble
  @groups.values.map(&:to_preamble)
end