kobako 0.20.0-aarch64-linux → 0.21.0-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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +107 -0
  4. data/README.md +96 -44
  5. data/ROADMAP.md +1 -1
  6. data/crates/kobako-runtime/CHANGELOG.md +37 -0
  7. data/crates/kobako-runtime/README.md +1 -1
  8. data/crates/kobako-transport/CHANGELOG.md +31 -0
  9. data/crates/kobako-transport/README.md +43 -0
  10. data/crates/kobako-wasmtime/CHANGELOG.md +23 -0
  11. data/crates/kobako-wasmtime/README.md +1 -1
  12. data/data/kobako.wasm +0 -0
  13. data/lib/kobako/3.3/kobako.so +0 -0
  14. data/lib/kobako/3.4/kobako.so +0 -0
  15. data/lib/kobako/4.0/kobako.so +0 -0
  16. data/lib/kobako/catalog/services.rb +13 -21
  17. data/lib/kobako/catalog/snippets.rb +16 -34
  18. data/lib/kobako/codec/decoder.rb +10 -13
  19. data/lib/kobako/codec/encoder.rb +7 -7
  20. data/lib/kobako/codec/error.rb +6 -6
  21. data/lib/kobako/codec/ext_types.rb +12 -72
  22. data/lib/kobako/codec/handle_walk.rb +6 -6
  23. data/lib/kobako/codec/state.rb +4 -56
  24. data/lib/kobako/codec/utils.rb +4 -4
  25. data/lib/kobako/codec.rb +11 -21
  26. data/lib/kobako/context.rb +23 -17
  27. data/lib/kobako/errors.rb +32 -11
  28. data/lib/kobako/outcome.rb +71 -128
  29. data/lib/kobako/payload/arguments.rb +59 -0
  30. data/lib/kobako/payload.rb +19 -0
  31. data/lib/kobako/sandbox.rb +12 -13
  32. data/lib/kobako/sandbox_options.rb +1 -1
  33. data/lib/kobako/snippet/binary.rb +3 -4
  34. data/lib/kobako/snippet/source.rb +5 -6
  35. data/lib/kobako/snippet.rb +2 -2
  36. data/lib/kobako/transport/call.rb +21 -0
  37. data/lib/kobako/transport/dispatcher.rb +86 -135
  38. data/lib/kobako/transport/error.rb +21 -5
  39. data/lib/kobako/transport/reflection.rb +82 -0
  40. data/lib/kobako/transport/run.rb +25 -33
  41. data/lib/kobako/transport/yielder.rb +27 -36
  42. data/lib/kobako/transport.rb +11 -8
  43. data/lib/kobako/usage.rb +1 -1
  44. data/lib/kobako/version.rb +1 -1
  45. data/release-please-config.json +47 -2
  46. data/sig/kobako/catalog/services.rbs +1 -1
  47. data/sig/kobako/catalog/snippets.rbs +4 -4
  48. data/sig/kobako/codec/error.rbs +4 -4
  49. data/sig/kobako/codec/ext_types.rbs +0 -7
  50. data/sig/kobako/codec/state.rbs +0 -4
  51. data/sig/kobako/codec.rbs +4 -5
  52. data/sig/kobako/context.rbs +4 -4
  53. data/sig/kobako/errors.rbs +15 -3
  54. data/sig/kobako/outcome.rbs +12 -13
  55. data/sig/kobako/payload/arguments.rbs +26 -0
  56. data/sig/kobako/payload.rbs +4 -0
  57. data/sig/kobako/pool.rbs +0 -2
  58. data/sig/kobako/runtime.rbs +15 -13
  59. data/sig/kobako/snippet/binary.rbs +1 -1
  60. data/sig/kobako/snippet/source.rbs +1 -1
  61. data/sig/kobako/transport/call.rbs +21 -0
  62. data/sig/kobako/transport/dispatcher.rbs +8 -10
  63. data/sig/kobako/transport/error.rbs +11 -0
  64. data/sig/kobako/transport/reflection.rbs +15 -0
  65. data/sig/kobako/transport/run.rbs +1 -1
  66. data/sig/kobako/transport/yielder.rbs +2 -2
  67. data/sig/kobako/transport.rbs +8 -2
  68. metadata +12 -12
  69. data/lib/kobako/fault.rb +0 -38
  70. data/lib/kobako/outcome/panic.rb +0 -42
  71. data/lib/kobako/transport/request.rb +0 -76
  72. data/lib/kobako/transport/response.rb +0 -83
  73. data/lib/kobako/transport/yield.rb +0 -94
  74. data/sig/kobako/fault.rbs +0 -19
  75. data/sig/kobako/outcome/panic.rbs +0 -34
  76. data/sig/kobako/transport/request.rbs +0 -29
  77. data/sig/kobako/transport/response.rbs +0 -33
  78. data/sig/kobako/transport/yield.rbs +0 -34
@@ -0,0 +1,43 @@
1
+ # kobako-transport
2
+
3
+ The fixed tier of the [kobako](https://github.com/elct9620/kobako) wire —
4
+ an in-process Wasm sandbox for running untrusted mruby scripts.
5
+
6
+ kobako is assembled from three parts chosen independently: a host, a
7
+ payload codec, and a guest. That works only because two things are the
8
+ same in every assembly. This crate is both of them, and nothing else:
9
+
10
+ - `envelope` — the **core envelope**, the outer frame each message rides
11
+ in: `Call` / `Reply` / `YieldReply` for a dispatch round-trip,
12
+ `Outcome` / `Panic` for how an invocation ended, `Run` / `Preamble` /
13
+ `Snippets` for what an invocation is handed, and the `ErrorRecord` a
14
+ guest failure carries
15
+ - `abi` — the values a host and a guest must already agree on to
16
+ exchange a byte: the version, the packed `(ptr, len)` return layout,
17
+ the invocation-channel frame prefix, and the message size cap
18
+
19
+ The envelope reads a message's routing fields and its ok-versus-fault
20
+ tag without decoding a payload byte; everything the resolved method
21
+ consumes rides through as an opaque `payload` this layer never reads.
22
+ A decoded envelope borrows the buffer it came from, so that payload
23
+ reaches its reader as a view rather than a copy.
24
+
25
+ This crate depends on no other, and every kobako tier depends on it:
26
+ `kobako-runtime` and `kobako-wasmtime` on the host, `kobako-core` and
27
+ `kobako-mruby` in the guest, and any third-party engine, codec, or
28
+ guest that composes against them.
29
+
30
+ The byte layout is specified in
31
+ [`docs/wire/envelope.md`](https://github.com/elct9620/kobako/blob/main/docs/wire/envelope.md);
32
+ the golden vectors in this crate are derived from that document.
33
+
34
+ ## Usage
35
+
36
+ ```toml
37
+ [dependencies]
38
+ kobako-transport = "0.13.0" # x-release-please-version
39
+ ```
40
+
41
+ ## License
42
+
43
+ Apache-2.0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.12.0...kobako-wasmtime-v0.13.0) (2026-07-29)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **wasmtime:** put every cap in one struct, named the way the field is read
9
+ * **runtime:** give the engine contract names an implementer can write
10
+ * **transport:** name the core envelope's types after what they carry
11
+ * **wire:** route every tier through the one envelope
12
+ * **wire:** carry dispatch over the core envelope with an opaque payload
13
+
14
+ ### Features
15
+
16
+ * **wire:** carry dispatch over the core envelope with an opaque payload ([556104b](https://github.com/elct9620/kobako/commit/556104bf86fdf481b5368b70af83eb0add4b2708))
17
+
18
+
19
+ ### Code Refactoring
20
+
21
+ * **runtime:** give the engine contract names an implementer can write ([0a41491](https://github.com/elct9620/kobako/commit/0a41491360cf17def8245c0758679e92572b990f))
22
+ * **transport:** name the core envelope's types after what they carry ([b44de65](https://github.com/elct9620/kobako/commit/b44de6502953826c567d4ef8a594561479f50c1d))
23
+ * **wasmtime:** put every cap in one struct, named the way the field is read ([3f57860](https://github.com/elct9620/kobako/commit/3f578608b044d1ade0ae35231266cf3c9e517c02))
24
+ * **wire:** route every tier through the one envelope ([c5cd33a](https://github.com/elct9620/kobako/commit/c5cd33a5346f49857fa6e1f45c9cf9b9bea0ff77))
25
+
3
26
  ## [0.12.0](https://github.com/elct9620/kobako/compare/kobako-wasmtime-v0.11.0...kobako-wasmtime-v0.12.0) (2026-07-24)
4
27
 
5
28
 
@@ -24,7 +24,7 @@ SDK consumes the same surface.
24
24
 
25
25
  ```toml
26
26
  [dependencies]
27
- kobako-wasmtime = "0.12.0" # x-release-please-version
27
+ kobako-wasmtime = "0.13.0" # x-release-please-version
28
28
  ```
29
29
 
30
30
  ## License
data/data/kobako.wasm CHANGED
Binary file
Binary file
Binary file
Binary file
@@ -1,19 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../codec"
4
3
  require_relative "../errors"
5
4
 
6
5
  module Kobako
7
6
  module Catalog
8
7
  # Kobako::Catalog::Services — per-Sandbox registry of Service
9
8
  # bindings keyed by their constant-path name. Holds the flat
10
- # path→object table and the preamble emitted on Frame 1.
9
+ # path→object table and the declared path set every invocation
10
+ # announces on Frame 1.
11
11
  #
12
12
  # Public API:
13
13
  #
14
14
  # services = Kobako::Catalog::Services.new
15
15
  # services.bind("MyService::KV", kv_object) # => services (chainable)
16
- # services.encode # => msgpack bytes for Frame 1
16
+ # services.paths # => ["MyService::KV"]
17
17
  # services.lookup("MyService::KV") # => kv_object
18
18
  #
19
19
  # Per-dispatch routing is +Kobako::Transport::Dispatcher+'s
@@ -31,7 +31,6 @@ module Kobako
31
31
  def initialize
32
32
  @bindings = {} # : Hash[String, untyped]
33
33
  @sealed = false
34
- @encoded = nil # : String?
35
34
  end
36
35
 
37
36
  # Bind +object+ as the Service reachable at +path+ — a +Symbol+ or
@@ -66,27 +65,20 @@ module Kobako
66
65
  @bindings[target_str]
67
66
  end
68
67
 
69
- # Encode the preamble as msgpack bytes for stdin Frame 1 delivery —
70
- # a flat array of the bound constant paths, in bind order:
71
- # +["MyService::KV", "File"]+. Routes through Kobako::Codec::Encoder
72
- # like every other host-side wire encode; the preamble carries only
73
- # Strings, so none of the kobako ext types fire. Returns a binary
74
- # +String+ of msgpack bytes.
75
- #
76
- # The bytes are pinned eagerly by #seal! at the first invocation, so
77
- # every subsequent call is a pure read of the frozen preamble — a bind
78
- # can never reach the registry after the seal to alter Frame 1.
79
- def encode
80
- @encoded || Codec::Encoder.encode(@bindings.keys).freeze
68
+ # The bound constant paths in bind order — +["MyService::KV",
69
+ # "File"]+ — which +Runtime#eval+ / +#run+ frame into the Frame 1
70
+ # preamble. The registry holds the bindings; the wire layout is the
71
+ # native side's.
72
+ def paths
73
+ @bindings.keys
81
74
  end
82
75
 
83
- # Mark the registry as sealed and pin the Frame 1 preamble bytes so
84
- # registration can never alter them afterwards. Called by +Sandbox+ on
85
- # the first invocation; afterwards #bind raises ArgumentError.
86
- # Idempotent; returns +self+.
76
+ # Mark the registry as sealed so registration can never alter the
77
+ # declared path set afterwards. Called by +Sandbox+ on the first
78
+ # invocation; afterwards #bind raises ArgumentError. Idempotent;
79
+ # returns +self+.
87
80
  def seal!
88
81
  @sealed = true
89
- @encoded ||= Codec::Encoder.encode(@bindings.keys).freeze
90
82
  self
91
83
  end
92
84
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../codec"
4
3
  require_relative "../snippet"
5
4
 
6
5
  module Kobako
@@ -28,32 +27,16 @@ module Kobako
28
27
 
29
28
  def initialize
30
29
  @entries = [] # : Array[Kobako::Snippet::Source | Kobako::Snippet::Binary]
31
- @encoded = nil # : String?
32
30
  end
33
31
 
34
- # Serialize the registered snippets to wire bytes. Each entry
35
- # contributes a msgpack map shape; the collection rides as a single
36
- # msgpack array. An empty registry serializes to an empty array, never
37
- # absent. The wire codec is an implementation detail — callers
38
- # receive a binary +String+ that the +Kobako::Runtime+ layer ships
39
- # through the invocation channel. The entry value objects stay pure
40
- # carriers — this collection-tier method reads their attributes
41
- # externally via +entry_payload+ rather than asking each entry to
42
- # self-encode.
43
- #
44
- # Before the first invocation this recomputes from the still-open
45
- # table; #seal! then pins the bytes, so every later call is a pure
46
- # read of the frozen memo.
47
- def encode
48
- @encoded || Codec::Encoder.encode(@entries.map { |entry| entry_payload(entry) }).freeze
49
- end
50
-
51
- # Freeze the Frame 3 table bytes at the first invocation so the
52
- # replayed snippet set can never change afterwards. Called by +Sandbox+
53
- # alongside the Service / Extension seals. Idempotent; returns +self+.
54
- def seal!
55
- @encoded ||= Codec::Encoder.encode(@entries.map { |entry| entry_payload(entry) }).freeze
56
- self
32
+ # The registered snippets in insertion order, each projected as the
33
+ # +[kind, name, body]+ triple +Runtime#eval+ / +#run+ frame into
34
+ # Frame 3 — +kind+ names the form as a Symbol, +name+ is +nil+ for
35
+ # the bytecode form. The entry value objects stay pure carriers, so
36
+ # this collection-tier method reads their attributes externally
37
+ # rather than asking each entry to project itself.
38
+ def entries
39
+ @entries.map { |entry| entry_tuple(entry) }
57
40
  end
58
41
 
59
42
  # Register one preloaded snippet in either of two forms.
@@ -124,17 +107,16 @@ module Kobako
124
107
  nil
125
108
  end
126
109
 
127
- # Build the msgpack-ready Hash for one entry. Source entries
128
- # contribute their host-side +name+; Binary entries omit it
129
- # because the canonical name lives in the bytecode's embedded
130
- # +debug_info+ and is read by the guest at load time
131
- # ({docs/wire-codec.md Invocation channels}[link:../../../docs/wire-codec.md]).
132
- def entry_payload(entry)
110
+ # Project one entry as its +[kind, name, body]+ triple. Source
111
+ # entries contribute their host-side +name+; Binary entries carry
112
+ # +nil+ because the canonical name lives in the bytecode's embedded
113
+ # +debug_info+ and is read by the guest at load time.
114
+ def entry_tuple(entry)
133
115
  case entry
134
116
  when Snippet::Source
135
- { "name" => entry.name.to_s, "kind" => Snippet::Source::KIND, "body" => entry.body }
136
- when Snippet::Binary
137
- { "kind" => Snippet::Binary::KIND, "body" => entry.body }
117
+ [Snippet::Source::KIND, entry.name.to_s, entry.body]
118
+ else
119
+ [Snippet::Binary::KIND, nil, entry.body]
138
120
  end
139
121
  end
140
122
 
@@ -9,10 +9,10 @@ require_relative "utils"
9
9
  module Kobako
10
10
  module Codec
11
11
  # Module-level entry point for the host side of the kobako wire
12
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type Mapping).
12
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type Mapping).
13
13
  #
14
14
  # Translates msgpack gem exceptions into the kobako error taxonomy
15
- # (Truncated, InvalidType, InvalidEncoding, UnsupportedType) so
15
+ # (TruncatedInputError, InvalidTypeError, InvalidEncodingError, UnsupportedTypeError) so
16
16
  # callers can pattern-match on the SPEC's wire-violation categories
17
17
  # without leaking the gem's internal exception classes.
18
18
  #
@@ -21,14 +21,14 @@ module Kobako
21
21
  # because callers always decode exactly one wire value at a time.
22
22
  module Decoder
23
23
  # Decode +bytes+ into one Ruby value and validate transitively
24
- # against the SPEC type mapping. Raises Truncated, InvalidType,
25
- # or InvalidEncoding on wire violations.
24
+ # against the SPEC type mapping. Raises TruncatedInputError, InvalidTypeError,
25
+ # or InvalidEncodingError on wire violations.
26
26
  #
27
27
  # When a block is given, the decoded value is yielded and the block's
28
28
  # result is returned — wire Value Objects use this to build themselves
29
29
  # from the decoded payload. The block runs inside this method's
30
30
  # rescue, so a Value Object's +ArgumentError+ invariant failure
31
- # surfaces as InvalidType without a separate Utils.with_boundary
31
+ # surfaces as InvalidTypeError without a separate Utils.with_boundary
32
32
  # wrapper at the call site.
33
33
  def self.decode(bytes)
34
34
  value = FACTORY.load(bytes.b)
@@ -37,26 +37,23 @@ module Kobako
37
37
  # msgpack gem raises the format/type errors below; +ArgumentError+
38
38
  # comes from our ext-type validators (Handle id range, Exception type
39
39
  # whitelist) and from a yielded block's Value Object invariants — both
40
- # are wire violations, so both map to InvalidType.
40
+ # are wire violations, so both map to InvalidTypeError.
41
41
  rescue ::MessagePack::UnknownExtTypeError, ::MessagePack::MalformedFormatError,
42
42
  ::MessagePack::StackError, ::ArgumentError => e
43
- raise InvalidType, e.message
43
+ raise InvalidTypeError, e.message
44
44
  # +UnpackError+ is the gem's umbrella class for short-read /
45
45
  # incomplete-buffer faults; +EOFError+ covers underflow at the
46
46
  # buffer edge.
47
47
  rescue ::MessagePack::UnpackError, ::EOFError => e
48
- raise Truncated, e.message
48
+ raise TruncatedInputError, e.message
49
49
  end
50
50
 
51
51
  # SPEC pins +str+ family payloads to UTF-8
52
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § str/bin
52
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § str/bin
53
53
  # Encoding Rules). The msgpack gem returns UTF-8-tagged Strings for
54
54
  # str family but does not validate the bytes; +bin+ family decodes
55
55
  # to ASCII-8BIT. Walk the tree once and reject invalid UTF-8 in any
56
- # str-typed leaf via Utils.assert_utf8!. Kobako::Fault
57
- # payloads are validated transitively: +ExtTypes#unpack_fault+
58
- # feeds the inner ext-0x02 bytes back through this Decoder, so their
59
- # +str+ fields are already covered by the time control returns here.
56
+ # str-typed leaf via Utils.assert_utf8!.
60
57
  class << self
61
58
  private
62
59
 
@@ -8,13 +8,13 @@ require_relative "ext_types"
8
8
  module Kobako
9
9
  module Codec
10
10
  # Module-level entry point for the host side of the kobako wire
11
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type Mapping).
11
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type Mapping).
12
12
  #
13
13
  # The codec backbone is the official +msgpack+ gem: integers, floats,
14
14
  # strings, arrays, and maps go through the gem's narrowest-encoding
15
- # logic; the three kobako-specific ext types (0x00 Symbol, 0x01
16
- # Capability Handle, 0x02 Exception envelope) are registered by
17
- # ExtTypes on the process-wide factory.
15
+ # logic; the two kobako-specific ext types (0x00 Symbol, 0x01
16
+ # Capability Handle) are registered by ExtTypes on the process-wide
17
+ # factory.
18
18
  #
19
19
  # Public API is a single function — +.encode+. The codec is stateless;
20
20
  # there is no buffer accumulator and no streaming write API. Callers
@@ -22,8 +22,8 @@ module Kobako
22
22
  # themselves.
23
23
  module Encoder
24
24
  # Encode +value+ to wire bytes (binary-encoded String).
25
- # SPEC's 12-entry type mapping is a closed set: a value outside it is
26
- # rejected as +UnsupportedType+ by the factory's +BasicObject+ guard
25
+ # SPEC's 11-entry type mapping is a closed set: a value outside it is
26
+ # rejected as +UnsupportedTypeError+ by the factory's +BasicObject+ guard
27
27
  # (ExtTypes#register_unrepresentable), which raises before the msgpack
28
28
  # gem can route the value through +to_msgpack+ — so a permissive
29
29
  # +method_missing+ object cannot answer that probe and mis-encode. The
@@ -33,7 +33,7 @@ module Kobako
33
33
  def self.encode(value)
34
34
  FACTORY.dump(value)
35
35
  rescue ::RangeError, ::NoMethodError => e
36
- raise UnsupportedType, e.message
36
+ raise UnsupportedTypeError, e.message
37
37
  end
38
38
  end
39
39
  end
@@ -5,7 +5,7 @@ module Kobako
5
5
  # Base class for all wire-codec faults raised by the pure-Ruby host codec.
6
6
  #
7
7
  # The wire codec implements the binary contract pinned in
8
- # {docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type Mapping.
8
+ # {docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type Mapping.
9
9
  # Every wire violation surfaces as a
10
10
  # subclass of Error so callers can pattern-match on the specific
11
11
  # fault while still rescuing all codec faults via this base class.
@@ -15,20 +15,20 @@ module Kobako
15
15
  class Error < StandardError; end
16
16
 
17
17
  # Input ended before the type prefix or payload was fully consumed.
18
- class Truncated < Error; end
18
+ class TruncatedInputError < Error; end
19
19
 
20
- # The type byte at the current position is not in the 12-entry kobako
20
+ # The type byte at the current position is not in the 11-entry kobako
21
21
  # type mapping (e.g. an unknown ext code, or a reserved msgpack tag).
22
- class InvalidType < Error; end
22
+ class InvalidTypeError < Error; end
23
23
 
24
24
  # A msgpack +str+ payload was not valid UTF-8, or an ext 0x00 Symbol
25
25
  # payload was not valid UTF-8 — both are wire violations per SPEC.
26
- class InvalidEncoding < Error; end
26
+ class InvalidEncodingError < Error; end
27
27
 
28
28
  # The encoder was handed a Ruby object whose type has no wire
29
29
  # representation (e.g. Range, Time). Higher layers may catch this
30
30
  # and re-route the value through Handle allocation, but at the
31
31
  # codec level it is a hard error.
32
- class UnsupportedType < Error; end
32
+ class UnsupportedTypeError < Error; end
33
33
  end
34
34
  end
@@ -6,45 +6,39 @@ require_relative "error"
6
6
  require_relative "utils"
7
7
  require_relative "state"
8
8
  require_relative "../handle"
9
- require_relative "../fault"
10
9
 
11
10
  module Kobako
12
11
  module Codec
13
12
  # The kobako wire ext-type conversions
14
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types)
13
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Ext Types)
15
14
  # as pure functions: per-operation decode state is threaded in as an
16
15
  # argument, so the module itself holds nothing. #build_factory assembles
17
16
  # the one +MessagePack::Factory+ these conversions are registered on.
18
17
  module ExtTypes
19
18
  # MessagePack ext type code reserved for Symbol
20
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
19
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Ext Types
21
20
  # → ext 0x00). Module-private — mirrors +codec::EXT_SYMBOL+ on the
22
21
  # Rust side.
23
22
  EXT_SYMBOL = 0x00
24
23
  # MessagePack ext type code reserved for Capability Handle
25
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
24
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Ext Types
26
25
  # → ext 0x01). Module-private — mirrors +codec::EXT_HANDLE+ on the
27
26
  # Rust side.
28
27
  EXT_HANDLE = 0x01
29
- # MessagePack ext type code reserved for Exception envelope
30
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Ext Types
31
- # → ext 0x02). Module-private — mirrors +codec::EXT_ERRENV+ on the
32
- # Rust side.
33
- EXT_ERRENV = 0x02
34
- private_constant :EXT_SYMBOL, :EXT_HANDLE, :EXT_ERRENV
28
+ private_constant :EXT_SYMBOL, :EXT_HANDLE
35
29
 
36
30
  # Inert ext id the unrepresentable-value guard registers under. It is
37
31
  # never emitted (the guard's packer always raises) and never decoded
38
32
  # (no unpacker is registered, so the id stays an UnknownExtTypeError on
39
33
  # the wire), so it is not a wire ext type: deliberately not named
40
- # +EXT_*+ like the three real ext codes, since it has no Rust-side mirror
34
+ # +EXT_*+ like the two real ext codes, since it has no Rust-side mirror
41
35
  # and must stay outside the wire-symmetry inventory.
42
36
  UNREPRESENTABLE_GUARD_ID = 0x7F
43
37
  private_constant :UNREPRESENTABLE_GUARD_ID
44
38
 
45
39
  module_function
46
40
 
47
- # Assemble a +MessagePack::Factory+ with the three kobako ext types plus
41
+ # Assemble a +MessagePack::Factory+ with the two kobako ext types plus
48
42
  # the unrepresentable-value guard registered, frozen because
49
43
  # registration is its only mutation and happens exactly once. The
50
44
  # stateful conversions resolve their per-operation state at call time,
@@ -53,7 +47,6 @@ module Kobako
53
47
  factory = MessagePack::Factory.new
54
48
  register_symbol(factory)
55
49
  register_handle(factory)
56
- register_fault(factory)
57
50
  register_unrepresentable(factory)
58
51
  factory.freeze
59
52
  end
@@ -64,7 +57,7 @@ module Kobako
64
57
  end
65
58
 
66
59
  # Validate the ext-0x00 payload as UTF-8 and intern. Raises
67
- # InvalidEncoding on invalid bytes — SPEC forbids the
60
+ # InvalidEncodingError on invalid bytes — SPEC forbids the
68
61
  # binary-encoding fallback that msgpack-gem's default unpacker
69
62
  # would otherwise apply. The re-tag step lives here because the
70
63
  # msgpack ext-type unpacker hands us binary bytes; the assertion
@@ -86,64 +79,19 @@ module Kobako
86
79
  # Peel off the fixext-4 frame, hand the bytes to the
87
80
  # Host-Gem-internal +Kobako::Handle.restore+ factory, and
88
81
  # translate the +ArgumentError+ raised by Handle's invariants
89
- # into a wire-layer +InvalidType+ via Codec::Utils.with_boundary.
82
+ # into a wire-layer +InvalidTypeError+ via Codec::Utils.with_boundary.
90
83
  # The Value Object owns the id-range contract; this method only
91
84
  # owns the frame shape. Records the Handle sighting on +state+ so a
92
85
  # Handle-free decode can skip the downstream resolution walk.
93
86
  def unpack_handle(payload, state)
94
87
  state.record_handle!
95
88
  bytes = payload.b
96
- raise InvalidType, "Handle payload must be 4 bytes, got #{bytes.bytesize}" unless bytes.bytesize == 4
89
+ raise InvalidTypeError, "Handle payload must be 4 bytes, got #{bytes.bytesize}" unless bytes.bytesize == 4
97
90
 
98
91
  id = bytes.unpack1("N") # : Integer
99
92
  Codec::Utils.with_boundary { Kobako::Handle.restore(id) }
100
93
  end
101
94
 
102
- # Encode the inner ext-0x02 map via Encoder (not the raw factory) so
103
- # the embedded payload flows through the same boundary as a top-level
104
- # encode — nested kobako values (Handle, nested Fault) reach the
105
- # registered ext-type packers. A +details+ chain nested past the
106
- # +state+ depth cap has no wire representation and surfaces as
107
- # +UnsupportedType+. In a payload position (+state+ inside a
108
- # forbid_faults bracket) the envelope has no wire representation at
109
- # all, so the refusal routes the value into the position's
110
- # non-representable handling — the Dispatcher's auto-wrap rescue,
111
- # or a raise at the yield site.
112
- def pack_fault(fault, state)
113
- if state.faults_forbidden?
114
- raise UnsupportedType, "Kobako::Fault has no wire representation in a payload position"
115
- end
116
-
117
- state.within_ext_frame(UnsupportedType) do
118
- Encoder.encode("type" => fault.type, "message" => fault.message, "details" => fault.details)
119
- end
120
- end
121
-
122
- # Peel the embedded msgpack map and hand it to +Kobako::Fault.new+
123
- # inside Decoder.decode's block form, so the value-object's
124
- # +ArgumentError+ invariants surface as +InvalidType+ through the
125
- # decoder boundary. Inner decode goes through Decoder (not the raw
126
- # factory) so the embedded +str+ payloads flow through the same
127
- # UTF-8 validation as a top-level decode. A nested ext 0x02 in
128
- # +details+ re-enters this method, so the +state+ ext-frame guard
129
- # bounds the chain depth to keep it from exhausting the native stack.
130
- # In a payload position (+state+ inside a forbid_faults bracket) the
131
- # envelope is a wire violation outright — its sole legal position is
132
- # the Response fault field.
133
- def unpack_fault(payload, state)
134
- if state.faults_forbidden?
135
- raise InvalidType, "Fault envelope (ext 0x02) is not a legal value in a payload position"
136
- end
137
-
138
- state.within_ext_frame(InvalidType) do
139
- Decoder.decode(payload) do |map|
140
- raise InvalidType, "Fault payload must be a map" unless map.is_a?(Hash)
141
-
142
- Kobako::Fault.new(type: map["type"], message: map["message"], details: map["details"])
143
- end
144
- end
145
- end
146
-
147
95
  def register_symbol(factory)
148
96
  factory.register_type(
149
97
  EXT_SYMBOL, Symbol,
@@ -160,17 +108,9 @@ module Kobako
160
108
  )
161
109
  end
162
110
 
163
- def register_fault(factory)
164
- factory.register_type(
165
- EXT_ERRENV, Kobako::Fault,
166
- packer: ->(fault) { ExtTypes.pack_fault(fault, State.current) },
167
- unpacker: ->(payload) { ExtTypes.unpack_fault(payload, State.current) }
168
- )
169
- end
170
-
171
111
  # A catch-all packer that rejects any value with no wire representation
172
- # as +UnsupportedType+. Registered on +BasicObject+ so it also covers
173
- # BasicObject-based proxies; the narrower Symbol / Handle / Fault
112
+ # as +UnsupportedTypeError+. Registered on +BasicObject+ so it also covers
113
+ # BasicObject-based proxies; the narrower Symbol / Handle
174
114
  # registrations still win by most-specific match, and native types never
175
115
  # reach it. Packer-only: the guard never writes bytes, so its id is inert
176
116
  # and the decode surface stays fail-closed.
@@ -184,7 +124,7 @@ module Kobako
184
124
  def register_unrepresentable(factory)
185
125
  factory.register_type(
186
126
  UNREPRESENTABLE_GUARD_ID, BasicObject,
187
- packer: ->(_value) { raise UnsupportedType, "value has no wire representation" }
127
+ packer: ->(_value) { raise UnsupportedTypeError, "value has no wire representation" }
188
128
  )
189
129
  end
190
130
  end
@@ -11,8 +11,8 @@ module Kobako
11
11
  # path; #deep_restore resolves each wire-decoded Handle back to its
12
12
  # host object on every guest→host value path. #representable? is the
13
13
  # by-value codec-type predicate that decides which leaves #deep_wrap
14
- # must wrap: the closed 12-entry wire type set
15
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type
14
+ # must wrap: the closed 11-entry wire type set
15
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type
16
16
  # Mapping).
17
17
  #
18
18
  # All helpers are pure except #deep_wrap, whose only side effect is
@@ -23,7 +23,7 @@ module Kobako
23
23
  # Inclusive Integer range the msgpack gem encodes without raising
24
24
  # +RangeError+ at encode time — signed +int 64+ minimum through
25
25
  # unsigned +uint 64+ maximum
26
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type
26
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type
27
27
  # Mapping #3, the +fixint+ / +int 8..64+ / +uint 8..64+ union).
28
28
  # Anchored as a +Range+ so #primitive_type? stays a single
29
29
  # dispatch line. This is the codec's encode domain — not to
@@ -33,16 +33,16 @@ module Kobako
33
33
  MSGPACK_INT_RANGE = (-(2**63)..((2**64) - 1))
34
34
 
35
35
  # Codec-type predicate
36
- # ({docs/wire-codec.md}[link:../../../docs/wire-codec.md] § Type
36
+ # ({docs/wire/payload-msgpack.md}[link:../../../docs/wire/payload-msgpack.md] § Type
37
37
  # Mapping). Returns +true+ when +value+ belongs to the closed
38
- # 12-entry codec type set — +nil+, +TrueClass+, +FalseClass+,
38
+ # 11-entry codec type set — +nil+, +TrueClass+, +FalseClass+,
39
39
  # +Integer+ (in the +i64..u64+ value domain), +Float+, +String+,
40
40
  # +Symbol+, +Kobako::Handle+, +Array+ whose every element is itself
41
41
  # representable, or +Hash+ whose every key and value are
42
42
  # representable. Integers outside the codec's signed-64 /
43
43
  # unsigned-64 union are rejected so the predicate agrees with the
44
44
  # msgpack gem's encode-time +RangeError+ behaviour the codec
45
- # already surfaces as UnsupportedType.
45
+ # already surfaces as UnsupportedTypeError.
46
46
  #
47
47
  # The optional +depth+ bounds the recursive descent at the wire's
48
48
  # structural nesting cap so a self-referential container — reachable