kobako 0.10.0-aarch64-linux → 0.11.1-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57c31ede9c0c253bd6186a0c950286aa4af392b81befdec4453f97bbc5a84eda
4
- data.tar.gz: 28b31fd5e6ff25ba026e8c13d160165e5dd0539242841190601e72c09df820f6
3
+ metadata.gz: ea2898e0ad1df3bcda9f6b71f7c811890f248c8bf8203c8114a271aa97dd234b
4
+ data.tar.gz: 59faa2b92a0f751b7fe13c339c1bf47502d1560577a0d74ea77cfb72d68eb0aa
5
5
  SHA512:
6
- metadata.gz: 5e80224cc7d153c4980d3eeaea0b974c8a12e65d9fb280c5dec1e2dc057a012bd9407f47a5f37447df66e12c7ea00cf00cb248e6a115cd6ad6c76b627dda5be6
7
- data.tar.gz: d7727d01b800a4f8e877c9925fc2619919e7c262e986617cb903e6282bad6c79456ce7d292b40db993574912ad9ce598b3a9c41f5603ad8b1e26d538d291ceb3
6
+ metadata.gz: f125e8a66ce7b03dc9608f30cc10b6ea3dd6d41115104c39852725c09e2fd0f0f3df2c07c8b4801f4f57f27e80485548d591281f972f30151a9ac8d7001b5856
7
+ data.tar.gz: 35ae1f7b98347be2a7b7f9e28173d8ffad8ea0129784c507530c11227a29585805a774b541be6e5e59d501304ee6325a3cf9206b48318e643eb3e0edc800f483
@@ -1 +1 @@
1
- {".":"0.10.0","wasm/kobako-core":"0.5.0","wasm/kobako":"0.5.0","wasm/kobako-io":"0.5.0","wasm/kobako-regexp":"0.5.0","wasm/kobako-baker":"0.5.0"}
1
+ {".":"0.11.1","wasm/kobako-core":"0.5.1","wasm/kobako":"0.5.1","wasm/kobako-io":"0.5.1","wasm/kobako-regexp":"0.5.1","wasm/kobako-baker":"0.5.1"}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.1](https://github.com/elct9620/kobako/compare/v0.11.0...v0.11.1) (2026-06-14)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **guest:** adopt beni 0.7.0 protected dispatch (B-51) ([c61655b](https://github.com/elct9620/kobako/commit/c61655bcead336d32a4b6ff7ff1b34c21cdfccd9))
9
+
10
+ ## [0.11.0](https://github.com/elct9620/kobako/compare/v0.10.0...v0.11.0) (2026-06-13)
11
+
12
+
13
+ ### Features
14
+
15
+ * **transport:** narrow guest-reachable methods via respond_to_guest? ([7a25fe3](https://github.com/elct9620/kobako/commit/7a25fe3b440b523b8a692b7601d08877b1305b0d))
16
+
3
17
  ## [0.10.0](https://github.com/elct9620/kobako/compare/v0.9.2...v0.10.0) (2026-06-12)
4
18
 
5
19
 
data/README.md CHANGED
@@ -69,7 +69,7 @@ The script executes inside the Wasm guest. It cannot read your filesystem, open
69
69
 
70
70
  ### Services
71
71
 
72
- Declare a Namespace, then `bind` any Ruby object as a Member; the guest reaches it as a `<Namespace>::<Member>` proxy and invokes its public methods through the Transport wire. See [`docs/behavior.md`](docs/behavior.md) B-07..B-12.
72
+ Declare a Namespace, then `bind` any Ruby object as a Member; the guest reaches it as a `<Namespace>::<Member>` proxy and invokes its public methods through the Transport wire. See [`docs/behavior/registration.md`](docs/behavior/registration.md) B-07..B-12.
73
73
 
74
74
  ```ruby
75
75
  class User
@@ -93,7 +93,7 @@ Names must match `/\A[A-Z]\w*\z/`. Symbol kwargs travel transparently to the hos
93
93
 
94
94
  ### Output Capture
95
95
 
96
- Guest writes through `puts` / `print` / `p` / `$stdout` / `$stderr` are buffered per-channel and exposed independently of the return value ([`docs/behavior.md`](docs/behavior.md) B-04). Buffers clear at the start of each invocation; overflow is clipped at the cap and flagged by `#stdout_truncated?` / `#stderr_truncated?`.
96
+ Guest writes through `puts` / `print` / `p` / `$stdout` / `$stderr` are buffered per-channel and exposed independently of the return value ([`docs/behavior/lifecycle.md`](docs/behavior/lifecycle.md) B-04). Buffers clear at the start of each invocation; overflow is clipped at the cap and flagged by `#stdout_truncated?` / `#stderr_truncated?`.
97
97
 
98
98
  ```ruby
99
99
  result = sandbox.eval(<<~RUBY)
@@ -134,7 +134,7 @@ end
134
134
 
135
135
  ### Resource Limits
136
136
 
137
- Each invocation enforces a wall-clock `timeout` and a per-invocation linear-memory `memory_limit`; exhaustion raises a `TrapError` subclass. Pass `nil` to `timeout` / `memory_limit` to disable that cap. Read [`Sandbox#usage`](lib/kobako/sandbox.rb) after the call — populated on every outcome including traps — for actual consumption ([`docs/behavior.md`](docs/behavior.md) B-35).
137
+ Each invocation enforces a wall-clock `timeout` and a per-invocation linear-memory `memory_limit`; exhaustion raises a `TrapError` subclass. Pass `nil` to `timeout` / `memory_limit` to disable that cap. Read [`Sandbox#usage`](lib/kobako/sandbox.rb) after the call — populated on every outcome including traps — for actual consumption ([`docs/behavior/lifecycle.md`](docs/behavior/lifecycle.md) B-35).
138
138
 
139
139
  ```ruby
140
140
  sandbox = Kobako::Sandbox.new(
@@ -202,7 +202,9 @@ For workloads that must be isolated from each other (one Sandbox per tenant, per
202
202
 
203
203
  ### Pooling
204
204
 
205
- For hosts that serve many short invocations, `Kobako::Pool` keeps a bounded set of warm, identically set-up Sandboxes and hands each one to a single exclusive holder at a time ([`docs/behavior.md`](docs/behavior.md) B-46..B-48). Construction forwards every `Sandbox.new` keyword verbatim; the optional block is the per-Sandbox setup window and runs exactly once per constructed Sandbox.
205
+ For hosts that serve many short invocations, `Kobako::Pool` keeps a bounded set of warm, identically set-up Sandboxes and hands each one to a single exclusive holder at a time ([`docs/behavior/runtime.md`](docs/behavior/runtime.md) B-46..B-48). Construction forwards every `Sandbox.new` keyword verbatim; the optional block is the per-Sandbox setup window and runs exactly once per constructed Sandbox.
206
+
207
+ `Kobako::Pool` is experimental today and is best treated as a convenience for warm, pre-configured reuse rather than a throughput optimisation. B-49 bakes the shared boot state into the artifact and every dynamic script still compiles and runs per invocation, so all a pool actually saves is the ~30 µs host-side `Sandbox.new`. For the workload kobako is built for — many small, short-lived Sandboxes running dynamic scripts — that is not a significant gain (~4-5% in the [serverless example](examples/serverless/README.md), and proportionally less once the script itself does real work).
206
208
 
207
209
  ```ruby
208
210
  pool = Kobako::Pool.new(slots: 4) do |sandbox|
@@ -221,7 +223,7 @@ Sandboxes construct lazily on first demand. `#with` yields a Sandbox with empty
221
223
 
222
224
  ### Service Blocks
223
225
 
224
- A Service method can accept a guest-supplied block via `&blk` and `yield` into it. The block body runs inside the Wasm guest; `break` / `next` / exceptions follow normal Ruby semantics, scoped to the single dispatch. See [`docs/behavior.md`](docs/behavior.md) B-23..B-30.
226
+ A Service method can accept a guest-supplied block via `&blk` and `yield` into it. The block body runs inside the Wasm guest; `break` / `next` / exceptions follow normal Ruby semantics, scoped to the single dispatch. See [`docs/behavior/yield.md`](docs/behavior/yield.md) B-23..B-30.
225
227
 
226
228
  ```ruby
227
229
  sandbox.define(:Seq).bind(:Map, ->(items, &blk) { items.map(&blk) })
@@ -232,7 +234,7 @@ sandbox.eval('Seq::Map.call([1, 2, 3]) { |x| x * 2 }')
232
234
 
233
235
  ### Handle Management
234
236
 
235
- A non-wire-representable host object — returned from a Service (B-14), passed to `#run` (B-34), or handed back from the guest (B-37) — crosses the boundary as an opaque `Kobako::Handle` proxy and is restored to the original object before host code sees it; any other unrepresentable value raises `Kobako::SandboxError`. Handles are scoped to a single invocation ([`docs/behavior.md`](docs/behavior.md) B-13..B-21, B-34, B-37).
237
+ A non-wire-representable host object — returned from a Service (B-14), passed to `#run` (B-34), or handed back from the guest (B-37) — crosses the boundary as an opaque `Kobako::Handle` proxy and is restored to the original object before host code sees it; any other unrepresentable value raises `Kobako::SandboxError`. Handles are scoped to a single invocation ([`docs/behavior/dispatch.md`](docs/behavior/dispatch.md) B-13..B-21, B-34, B-37).
236
238
 
237
239
  ```ruby
238
240
  class Greeter
@@ -268,7 +270,7 @@ This is deliberate, not a leak. Handle IDs run to 2³¹ − 1 per invocation and
268
270
 
269
271
  ### Snippets & Entrypoints
270
272
 
271
- `Sandbox#preload` registers named mruby snippets that replay into every invocation's canonical boot state; `Sandbox#run(:Target, *args, **kwargs)` dispatches into a top-level `Object` constant defined by those snippets ([`docs/behavior.md`](docs/behavior.md) B-31..B-33).
273
+ `Sandbox#preload` registers named mruby snippets that replay into every invocation's canonical boot state; `Sandbox#run(:Target, *args, **kwargs)` dispatches into a top-level `Object` constant defined by those snippets ([`docs/behavior/invocation.md`](docs/behavior/invocation.md) B-31..B-33).
272
274
 
273
275
  ```ruby
274
276
  sandbox = Kobako::Sandbox.new
@@ -320,9 +322,14 @@ sandbox.define(:Cfg).bind(:Settings, ThemeReader.new) # not: bind(:Settings, Ap
320
322
  sandbox.eval('Cfg::Settings.color') # => "#3366ff" — every other method raises NoMethodError
321
323
  ```
322
324
 
325
+ When a purpose-built wrapper is more than you need, an object can gate its own surface in
326
+ place: a private `respond_to_guest?(name)` answers, per method, whether the guest may call
327
+ it. Returning `false` for every name makes the object opaque — a credential the guest
328
+ forwards to another Service but never reads — while a named subset becomes an allow-list.
329
+
323
330
  Guest code can name any `<Namespace>::<Member>` path, but a forged name only resolves to
324
331
  something you bound — the real authorization gate is this host-side allowlist. Give each
325
- trust context its own Sandbox, and see [`docs/security.md`](docs/security.md) for the rest
332
+ trust context its own Sandbox, and see [`docs/security-model.md`](docs/security-model.md) for the rest
326
333
  as security-design concerns: validating untrusted input, default-deny external effects,
327
334
  and controlling the return surface.
328
335
 
data/SECURITY.md ADDED
@@ -0,0 +1,35 @@
1
+ # Security Policy
2
+
3
+ kobako runs untrusted guest code inside an in-process Wasm sandbox, so a break in
4
+ its isolation boundary is treated as a security issue. This file is about **reporting
5
+ such an issue**; for how the boundary is meant to work and where your
6
+ responsibilities as a host begin, see [`docs/security-model.md`](docs/security-model.md).
7
+
8
+ ## Supported versions
9
+
10
+ kobako is pre-1.0. Security fixes land on the latest released `0.x` version only;
11
+ upgrade to it before reporting.
12
+
13
+ ## Reporting a vulnerability
14
+
15
+ Report privately through GitHub's **[Report a vulnerability](https://github.com/elct9620/kobako/security/advisories/new)**
16
+ flow — please do not open a public issue or pull request for a suspected vulnerability.
17
+
18
+ Include the affected version, a minimal guest script or host setup that reproduces the
19
+ issue, and what boundary you expected to hold. You can expect an initial acknowledgement
20
+ within a few days; once a fix or mitigation is agreed, disclosure is coordinated through
21
+ a GitHub Security Advisory. Reporters are credited in the published advisory unless you
22
+ ask to stay anonymous.
23
+
24
+ ## Scope
25
+
26
+ In scope is anything that lets guest code cross the isolation boundary it should not:
27
+ reaching host memory, the filesystem, the network, or `ENV`; obtaining ambient time or
28
+ entropy the host froze; reaching a `Namespace::Member` you never bound; or a
29
+ memory-safety fault in the host codec or wasmtime driver.
30
+
31
+ Out of scope is what a bound Service is *designed* to expose: if guest code reaches a
32
+ method because you bound an object carrying it, that is a host-side authorization
33
+ choice, not a sandbox escape — narrow the bound surface as described in the security
34
+ model. Resource exhaustion that stays within the limits you configured is likewise
35
+ expected behaviour, not a vulnerability.
data/data/kobako.wasm CHANGED
Binary file
Binary file
Binary file
Binary file
@@ -4,7 +4,7 @@ module Kobako
4
4
  # Host-side captured prefix of guest stdout / stderr produced during a
5
5
  # single +Kobako::Sandbox+ invocation, paired with the truncation flag
6
6
  # the WASI pipe sets when the guest wrote past the configured per-channel
7
- # cap ({docs/behavior.md B-04}[link:../../docs/behavior.md]).
7
+ # cap.
8
8
  #
9
9
  # Immutable value object: the captured bytes and the truncation flag
10
10
  # always travel together and the instance is frozen on construction.
@@ -30,14 +30,12 @@ module Kobako
30
30
  end
31
31
 
32
32
  # Returns +true+ iff the underlying capture channel exceeded its
33
- # configured cap during the originating +Sandbox+ invocation
34
- # ({docs/behavior.md B-04}[link:../../docs/behavior.md]).
33
+ # configured cap during the originating +Sandbox+ invocation.
35
34
  def truncated? = @truncated
36
35
 
37
- # Pre-invocation sentinel ({docs/behavior.md B-05}[link:../../docs/behavior.md]).
38
- # Empty UTF-8 bytes and +truncated? == false+; reused by every fresh
39
- # +Sandbox+ and by +Sandbox+ between invocations to denote "no capture
40
- # yet".
36
+ # Pre-invocation sentinel. Empty UTF-8 bytes and +truncated? == false+;
37
+ # reused by every fresh +Sandbox+ and by +Sandbox+ between invocations
38
+ # to denote "no capture yet".
41
39
  EMPTY = new(bytes: "", truncated: false)
42
40
  end
43
41
  end
@@ -5,34 +5,29 @@ require_relative "../handle"
5
5
  module Kobako
6
6
  module Catalog
7
7
  # Host-side mapping from opaque integer Handle IDs to Ruby objects.
8
- # The table is owned by +Kobako::Sandbox+
9
- # ({docs/behavior.md B-19}[link:../../../docs/behavior.md]) and injected
8
+ # The table is owned by +Kobako::Sandbox+ and injected
10
9
  # into the per-Sandbox +Kobako::Catalog::Namespaces+ so guest→host dispatch
11
10
  # resolves Handle targets and arguments against the same table that
12
- # host→guest wire encoding allocates into
13
- # ({docs/behavior.md B-14, B-34}[link:../../../docs/behavior.md]).
11
+ # host→guest wire encoding allocates into.
14
12
  #
15
- # Lifecycle invariants ({docs/behavior.md}[link:../../../docs/behavior.md]):
13
+ # Lifecycle invariants:
16
14
  #
17
- # - {docs/behavior.md B-15}[link:../../../docs/behavior.md] Handle IDs
18
- # are allocated by a monotonically increasing counter scoped to a
19
- # single invocation. The first ID issued in an invocation is 1; ID 0
20
- # is reserved as the invalid sentinel and is never returned by
21
- # +#alloc+.
15
+ # - Handle IDs are allocated by a monotonically increasing counter
16
+ # scoped to a single invocation. The first ID issued in an
17
+ # invocation is 1; ID 0 is reserved as the invalid sentinel and is
18
+ # never returned by +#alloc+.
22
19
  #
23
- # - {docs/behavior.md B-19}[link:../../../docs/behavior.md] At every
24
- # invocation boundary (via +#reset!+), every Handle issued under the
25
- # old state becomes invalid. Reset applies uniformly regardless of
26
- # allocation source (B-14 Service return or B-34 host-injected
20
+ # - At every invocation boundary (via +#reset!+), every Handle issued
21
+ # under the old state becomes invalid. Reset applies uniformly
22
+ # regardless of allocation source (Service return or host-injected
27
23
  # argument).
28
24
  #
29
- # - {docs/behavior.md B-21}[link:../../../docs/behavior.md] The cap is
30
- # +0x7fff_ffff+ (2³¹ 1). Allocation beyond the cap raises
31
- # immediately — no silent truncation, no wrap, no ID reuse.
25
+ # - The cap is +0x7fff_ffff+ (2³¹ − 1). Allocation beyond the cap
26
+ # raises immediately no silent truncation, no wrap, no ID reuse.
32
27
  class Handles
33
28
  # Build a fresh, empty table. +next_id+ is an internal seam that
34
- # sets the starting value of the monotonic counter (defaults to 1 per
35
- # B-15); tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
29
+ # sets the starting value of the monotonic counter (defaults to 1);
30
+ # tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
36
31
  # the cap-exhaustion path without 2³¹ allocations.
37
32
  def initialize(next_id: 1)
38
33
  @entries = {} # : Hash[Integer, untyped]
@@ -45,8 +40,7 @@ module Kobako
45
40
  # +[Kobako::Handle::MIN_ID, Kobako::Handle::MAX_ID]+. Raises
46
41
  # +Kobako::HandlerExhaustedError+ if the next ID would exceed the
47
42
  # cap. The cap is anchored on +Kobako::Handle+ — the wire codec
48
- # and the allocator share the same invariant
49
- # ({docs/behavior.md B-21}[link:../../../docs/behavior.md]).
43
+ # and the allocator share the same invariant.
50
44
  #
51
45
  # Returning a Handle (rather than a bare Integer id) keeps the
52
46
  # allocator's output a domain entity; +Kobako::Handle.restore+
@@ -69,9 +63,8 @@ module Kobako
69
63
  @entries[id]
70
64
  end
71
65
 
72
- # Clear all entries AND reset the counter to 1. Called at the per-invocation
73
- # boundary by +Kobako::Sandbox+ see
74
- # {docs/behavior.md B-19}[link:../../../docs/behavior.md]. Returns +self+.
66
+ # Clear all entries AND reset the counter to 1. Called at the
67
+ # per-invocation boundary by +Kobako::Sandbox+. Returns +self+.
75
68
  def reset!
76
69
  @entries.clear
77
70
  @next_id = 1
@@ -89,12 +82,12 @@ module Kobako
89
82
 
90
83
  private
91
84
 
92
- # Refuse to mint a Capability Handle for a reflective gadget
93
- # ({docs/behavior.md B-43}[link:../../../docs/behavior.md]): a +Binding+ /
94
- # +Method+ / +UnboundMethod+ would hand the guest a callable proxy onto
95
- # host reflection (a returned +Binding+ reaches +Binding#eval+). Raising
96
- # here keeps the rule at the single mint point, so it holds on both the
97
- # Service-return (B-14) and the +#run+ host→guest auto-wrap (B-34) paths.
85
+ # Refuse to mint a Capability Handle for a reflective gadget:
86
+ # a +Binding+ / +Method+ / +UnboundMethod+ would hand the guest a
87
+ # callable proxy onto host reflection (a returned +Binding+ reaches
88
+ # +Binding#eval+). Raising here keeps the rule at the single mint
89
+ # point, so it holds on both the Service-return and the +#run+
90
+ # host→guest auto-wrap paths.
98
91
  def reject_unwrappable!(object)
99
92
  case object
100
93
  when Binding, Method, UnboundMethod
@@ -102,7 +95,7 @@ module Kobako
102
95
  end
103
96
  end
104
97
 
105
- # Guard {#alloc} against issuing an ID past the B-21 cap. Returns +nil+
98
+ # Guard {#alloc} against issuing an ID past the cap. Returns +nil+
106
99
  # on success; raises +Kobako::HandlerExhaustedError+ at exhaustion.
107
100
  def ensure_capacity!
108
101
  cap = Kobako::Handle::MAX_ID
@@ -9,8 +9,7 @@ module Kobako
9
9
  module Catalog
10
10
  # Kobako::Catalog::Namespaces — per-Sandbox registry of
11
11
  # +Kobako::Namespace+ entities. Holds the Namespace / Member bindings
12
- # and the preamble emitted on Frame 1
13
- # ({docs/behavior.md B-07..B-11}[link:../../../docs/behavior.md]).
12
+ # and the preamble emitted on Frame 1.
14
13
  #
15
14
  # Public API:
16
15
  #
@@ -24,14 +23,13 @@ module Kobako
24
23
  # +Kobako::Transport::Dispatcher+'s responsibility — the Dispatcher
25
24
  # receives this registry and the +Catalog::Handles+ as arguments from
26
25
  # the +Runtime#on_dispatch+ Proc that +Kobako::Sandbox#initialize+
27
- # installs ({docs/behavior.md B-12}[link:../../../docs/behavior.md]).
28
- # The registry holds an injected +Catalog::Handles+ reference so
26
+ # installs. The registry holds an injected +Catalog::Handles+ reference so
29
27
  # dispatch target resolution and host→guest auto-wrap share the same
30
- # Sandbox-owned allocator ({docs/behavior.md B-19}[link:../../../docs/behavior.md]).
28
+ # Sandbox-owned allocator.
31
29
  class Namespaces
32
30
  # Build a fresh registry. +handler+ is an internal seam that injects
33
31
  # a pre-configured +Catalog::Handles+; tests pass one whose +next_id+
34
- # is pinned near +MAX_ID+ to exercise the B-21 cap-exhaustion path
32
+ # is pinned near +MAX_ID+ to exercise the cap-exhaustion path
35
33
  # without 2³¹ allocations. Production callers leave it at the default.
36
34
  def initialize(handler: Catalog::Handles.new)
37
35
  @namespaces = {} # : Hash[String, Kobako::Namespace]
@@ -40,14 +38,12 @@ module Kobako
40
38
  @encoded = nil # : String?
41
39
  end
42
40
 
43
- # Declare or retrieve the Namespace named +name+ (idempotent
44
- # {docs/behavior.md B-10}[link:../../../docs/behavior.md]).
41
+ # Declare or retrieve the Namespace named +name+ (idempotent).
45
42
  # +name+ is a constant-form name as a +Symbol+ or +String+ (must satisfy
46
43
  # +Namespace::NAME_PATTERN+). Returns the +Kobako::Namespace+ for that
47
44
  # name, creating it if it does not exist. Raises +ArgumentError+ when
48
45
  # +name+ is malformed, or when called after the owning Sandbox has been
49
- # sealed by its first invocation
50
- # ({docs/behavior.md B-07}[link:../../../docs/behavior.md]).
46
+ # sealed by its first invocation.
51
47
  def define(name)
52
48
  raise ArgumentError, "cannot define after first Sandbox invocation" if @sealed
53
49
 
@@ -74,20 +70,18 @@ module Kobako
74
70
  namespace.fetch(member_name)
75
71
  end
76
72
 
77
- # Encode the preamble as msgpack bytes for stdin Frame 1 delivery
78
- # ({docs/behavior.md B-02}[link:../../../docs/behavior.md]). Routes through
79
- # {Kobako::Codec::Encoder} like every other host-side wire encode so
80
- # there is a single codec path; the preamble carries only Strings and
81
- # Arrays, so none of the kobako ext types actually fire. Structure:
82
- # +[["Namespace", ["MemberA", "MemberB"]], ...]+. Returns a binary
83
- # +String+ of msgpack bytes.
73
+ # Encode the preamble as msgpack bytes for stdin Frame 1 delivery.
74
+ # Routes through {Kobako::Codec::Encoder} like every other host-side
75
+ # wire encode so there is a single codec path; the preamble carries
76
+ # only Strings and Arrays, so none of the kobako ext types actually
77
+ # fire. Structure: +[["Namespace", ["MemberA", "MemberB"]], ...]+.
78
+ # Returns a binary +String+ of msgpack bytes.
84
79
  #
85
80
  # Once sealed, the bytes are computed once and reused for every
86
- # subsequent invocation: B-33 seals Service registration (B-07 /
87
- # B-08) at the first invocation, so the preamble is exactly the
88
- # bindings that existed at that moment — a bind reaching a
89
- # +Kobako::Namespace+ after the seal raises +ArgumentError+ (E-45)
90
- # and never alters Frame 1.
81
+ # subsequent invocation: sealing freezes Service registration at the
82
+ # first invocation, so the preamble is exactly the bindings that
83
+ # existed at that moment — a bind reaching a +Kobako::Namespace+
84
+ # after the seal raises +ArgumentError+ and never alters Frame 1.
91
85
  def encode
92
86
  return @encoded if @encoded
93
87
 
@@ -97,11 +91,9 @@ module Kobako
97
91
  end
98
92
 
99
93
  # Mark the registry as sealed and propagate the seal to every
100
- # declared +Kobako::Namespace+
101
- # ({docs/behavior.md B-33}[link:../../../docs/behavior.md]). Called
102
- # by +Sandbox+ on the first invocation. After sealing, #define
103
- # raises ArgumentError (E-18) and +Namespace#bind+ raises
104
- # ArgumentError (E-45). Idempotent.
94
+ # declared +Kobako::Namespace+. Called by +Sandbox+ on the first
95
+ # invocation. After sealing, both #define and +Namespace#bind+
96
+ # raise ArgumentError. Idempotent.
105
97
  def seal!
106
98
  return self if @sealed
107
99
 
@@ -6,8 +6,7 @@ require_relative "../snippet"
6
6
  module Kobako
7
7
  module Catalog
8
8
  # Kobako::Catalog::Snippets — per-Sandbox insertion-ordered registry
9
- # of preloaded snippets
10
- # ({docs/behavior.md B-32 / B-33}[link:../../../docs/behavior.md]).
9
+ # of preloaded snippets.
11
10
  #
12
11
  # Entries replay against the fresh +mrb_state+ before per-invocation
13
12
  # source / entrypoint resolution. Each +Snippet::Source+ entry's +name+
@@ -15,18 +14,16 @@ module Kobako
15
14
  # +debug_info+ that surfaces in every backtrace frame originating from
16
15
  # the snippet as +(snippet:Name):line+. Duplicate names within the
17
16
  # +code:+ form would produce ambiguous attribution and are rejected at
18
- # registration time
19
- # ({docs/behavior.md E-33}[link:../../../docs/behavior.md]).
17
+ # registration time.
20
18
  # +Snippet::Binary+ entries carry no host-side name — their canonical
21
19
  # name lives in the bytecode's +debug_info+ and is read by the guest at
22
20
  # load time; the host does not extract it.
23
21
  #
24
- # Sealing (B-33) is governed by the owning Sandbox — the registry itself
22
+ # Sealing is governed by the owning Sandbox — the registry itself
25
23
  # is append-only and exposes no mutation API beyond +#register+; the
26
24
  # Sandbox guards +#register+ behind the seal check before delegating.
27
25
  class Snippets
28
- # Ruby constant-name pattern enforced on snippet names
29
- # ({docs/behavior.md E-34}[link:../../../docs/behavior.md]).
26
+ # Ruby constant-name pattern enforced on snippet names.
30
27
  NAME_PATTERN = /\A[A-Z]\w*\z/
31
28
 
32
29
  def initialize
@@ -45,7 +42,7 @@ module Kobako
45
42
  # self-encode.
46
43
  #
47
44
  # The bytes are memoized — the table is replayed verbatim on every
48
- # invocation after B-33 seals it, so Frame 3 never changes between
45
+ # invocation after sealing, so Frame 3 never changes between
49
46
  # encodes; {#register} drops the memo while the table is still open.
50
47
  def encode
51
48
  return @encoded if @encoded
@@ -53,8 +50,7 @@ module Kobako
53
50
  @encoded = Codec::Encoder.encode(@entries.map { |entry| entry_payload(entry) }).freeze
54
51
  end
55
52
 
56
- # Register one preloaded snippet in either of two forms
57
- # ({docs/behavior.md B-32}[link:../../../docs/behavior.md]).
53
+ # Register one preloaded snippet in either of two forms.
58
54
  #
59
55
  # * Source form +register(code: src, name: Name)+ — +src+ is the
60
56
  # mruby source as a String; the bytes are re-encoded as UTF-8
@@ -65,15 +61,13 @@ module Kobako
65
61
  # precompiled RITE bytecode as a String, duplicated and forced
66
62
  # to ASCII-8BIT so msgpack-ruby ships it as +bin+. Returns
67
63
  # +nil+ — bytecode entries are anonymous on the host side; any
68
- # structural validation
69
- # ({docs/behavior.md E-37 / E-38}[link:../../../docs/behavior.md])
70
- # is deferred to the guest at first replay.
64
+ # structural validation is deferred to the guest at first replay.
71
65
  #
72
66
  # The two forms are mutually exclusive: shape validation lives
73
67
  # here so callers (chiefly +Kobako::Sandbox#preload+) collapse to
74
68
  # a single delegation. Raises +ArgumentError+ on mixed forms,
75
- # missing keywords, wrong types, malformed +name+ (E-34), or
76
- # duplicate +code:+ +name+ (E-33).
69
+ # missing keywords, wrong types, malformed +name+, or
70
+ # duplicate +code:+ +name+.
77
71
  def register(code: nil, name: nil, binary: nil)
78
72
  @encoded = nil
79
73
  if binary
@@ -89,7 +83,7 @@ module Kobako
89
83
 
90
84
  # Source-form register path. Delegates argument-shape checks to
91
85
  # +ensure_source_args!+ (which returns the narrowed +[code, name]+
92
- # pair), normalises +name+ to a Symbol, rejects duplicates (E-33),
86
+ # pair), normalises +name+ to a Symbol, rejects duplicates,
93
87
  # and appends the Source entry.
94
88
  def register_source!(code, name)
95
89
  code, name = ensure_source_args!(code, name)
@@ -19,8 +19,7 @@ module Kobako
19
19
  # - Representability predicate ({representable?}) and the symmetric
20
20
  # host→guest +#run+ argument walk ({deep_wrap}) used by
21
21
  # +Kobako::Transport::Run#encode+ to route non-representable leaves
22
- # through the Sandbox's +Kobako::Catalog::Handles+
23
- # ({docs/behavior.md B-34}[link:../../../docs/behavior.md]).
22
+ # through the Sandbox's +Kobako::Catalog::Handles+.
24
23
  #
25
24
  # All helpers are pure — they only inspect inputs, never mutate
26
25
  # them — except {deep_wrap}, whose only side effect is allocating
@@ -84,8 +83,7 @@ module Kobako
84
83
 
85
84
  # Deep-walk Array / Hash containers in +value+ and replace every
86
85
  # leaf that fails {representable?} with a +Kobako::Handle+
87
- # allocated from +handler+
88
- # ({docs/behavior.md B-34}[link:../../../docs/behavior.md]). The
86
+ # allocated from +handler+. The
89
87
  # walk only descends through representable container shapes
90
88
  # (Array, Hash) one structural level at a time; a non-representable
91
89
  # leaf is wrapped as-is without inspecting its internal structure.
@@ -116,8 +114,7 @@ module Kobako
116
114
 
117
115
  # Deep-walk Array / Hash containers in +value+ and replace every
118
116
  # +Kobako::Handle+ leaf with the host-side object +handler+ resolves
119
- # it to ({docs/behavior.md B-37}[link:../../../docs/behavior.md]).
120
- # The symmetric inverse of {deep_wrap}: that walk allocates objects
117
+ # it to. The symmetric inverse of {deep_wrap}: that walk allocates objects
121
118
  # into Handles on the host→guest argument path; this walk resolves
122
119
  # Handles back to their objects on every guest→host value path — the
123
120
  # +#eval+ / +#run+ result and the yield-block result alike. The walk
@@ -126,11 +123,11 @@ module Kobako
126
123
  # unchanged.
127
124
  #
128
125
  # +value+ is a decoded Ruby value (a Handle here is a wire-decoded
129
- # +Kobako::Handle+, never a guest-forged one — B-20); +handler+ must
126
+ # +Kobako::Handle+, never a guest-forged one); +handler+ must
130
127
  # respond to +#fetch(id) -> object+ (a host-side
131
128
  # +Kobako::Catalog::Handles+). +handler.fetch+ raises
132
- # +Kobako::SandboxError+ for an id with no live binding, which is the
133
- # corrupted-runtime fallback B-37 specifies.
129
+ # +Kobako::SandboxError+ for an id with no live binding, the
130
+ # corrupted-runtime fallback.
134
131
  def deep_restore(value, handler)
135
132
  case value
136
133
  when ::Array then value.map { |element| Utils.deep_restore(element, handler) }