kobako 0.12.1-aarch64-linux → 0.13.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +22 -0
  4. data/README.md +3 -1
  5. data/crates/kobako-runtime/CHANGELOG.md +16 -0
  6. data/crates/kobako-runtime/README.md +34 -0
  7. data/crates/kobako-wasmtime/CHANGELOG.md +16 -0
  8. data/crates/kobako-wasmtime/README.md +32 -0
  9. data/data/kobako.wasm +0 -0
  10. data/lib/kobako/3.3/kobako.so +0 -0
  11. data/lib/kobako/3.4/kobako.so +0 -0
  12. data/lib/kobako/4.0/kobako.so +0 -0
  13. data/lib/kobako/catalog/handles.rb +3 -3
  14. data/lib/kobako/catalog/namespaces.rb +4 -0
  15. data/lib/kobako/catalog/snippets.rb +4 -0
  16. data/lib/kobako/codec/encoder.rb +5 -1
  17. data/lib/kobako/codec/factory.rb +41 -13
  18. data/lib/kobako/codec/handle_walk.rb +4 -0
  19. data/lib/kobako/codec.rb +1 -1
  20. data/lib/kobako/errors.rb +18 -16
  21. data/lib/kobako/sandbox.rb +71 -39
  22. data/lib/kobako/sandbox_options.rb +71 -13
  23. data/lib/kobako/transport/dispatcher.rb +2 -2
  24. data/lib/kobako/transport/response.rb +14 -14
  25. data/lib/kobako/transport/run.rb +2 -6
  26. data/lib/kobako/transport/yield.rb +1 -1
  27. data/lib/kobako/transport/yielder.rb +2 -2
  28. data/lib/kobako/version.rb +1 -1
  29. data/lib/kobako.rb +0 -1
  30. data/release-please-config.json +78 -3
  31. data/sig/kobako/codec/factory.rbs +3 -0
  32. data/sig/kobako/errors.rbs +7 -14
  33. data/sig/kobako/runtime.rbs +16 -6
  34. data/sig/kobako/sandbox.rbs +11 -7
  35. data/sig/kobako/sandbox_options.rbs +15 -4
  36. data/sig/kobako/transport/dispatcher.rbs +1 -1
  37. data/sig/kobako/transport/run.rbs +2 -2
  38. data/sig/kobako/transport/yielder.rbs +2 -2
  39. data/sig/kobako/transport.rbs +8 -0
  40. metadata +6 -4
  41. data/lib/kobako/snapshot.rb +0 -38
  42. data/sig/kobako/snapshot.rbs +0 -15
@@ -1,17 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "errors"
4
+
3
5
  module Kobako
4
6
  # Kobako::SandboxOptions — immutable Value Object holding the four
5
- # per-Sandbox configuration caps. Built on the +class X <
6
- # Data.define(...)+ subclass form (the Steep-friendly shape — see
7
- # +lib/kobako/outcome/panic.rb+).
7
+ # per-Sandbox configuration caps and the requested isolation profile.
8
+ # Built on the +class X < Data.define(...)+ subclass form (the
9
+ # Steep-friendly shape — see +lib/kobako/outcome/panic.rb+).
8
10
  #
9
- # The +initialize+ method does double duty: it applies DEFAULT fallback
10
- # for absent values and normalises (timeout to Float seconds,
11
- # memory_limit to positive Integer bytes) before delegating to Data's
12
- # +super+. Anything that survives +SandboxOptions.new+ is a wire-ready
13
- # cap bundle the +Kobako::Runtime+ constructor consumes as-is.
14
- class SandboxOptions < Data.define(:timeout, :memory_limit, :stdout_limit, :stderr_limit)
11
+ # The +initialize+ normalises every option before delegating to Data's
12
+ # +super+: +timeout+ to Float seconds, +memory_limit+ / +stdout_limit+ /
13
+ # +stderr_limit+ to positive Integer bytes. Each cap is +nil+-disablable
14
+ # (an absent argument takes its DEFAULT; an explicit +nil+ leaves the
15
+ # bound off), so all four behave uniformly. +profile+ is the one
16
+ # non-cap: a Symbol on the PROFILES ladder naming the posture the
17
+ # runtime builds, which is also the weakest the Host App accepts —
18
+ # +nil+ is rejected because the weakest posture is requested as an
19
+ # explicit +:permissive+. Anything that survives +SandboxOptions.new+
20
+ # is a wire-ready bundle the +Kobako::Runtime+ constructor consumes
21
+ # as-is. The options also own the ladder comparison
22
+ # (+#enforce_floor!+) that +Kobako::Sandbox+ delegates its
23
+ # construction floor check to.
24
+ class SandboxOptions < Data.define(:timeout, :memory_limit, :stdout_limit, :stderr_limit, :profile)
15
25
  # Default wall-clock timeout for a single invocation: 60 seconds.
16
26
  DEFAULT_TIMEOUT_SECONDS = 60.0
17
27
 
@@ -23,17 +33,41 @@ module Kobako
23
33
  # Default per-channel capture ceiling: 1 MiB.
24
34
  DEFAULT_OUTPUT_LIMIT = 1 << 20
25
35
 
36
+ # The isolation ladder, weakest first — index order is rank order,
37
+ # so a floor check is an index comparison.
38
+ PROFILES = %i[permissive hermetic].freeze
39
+
40
+ # Default isolation profile: the strictest rung — opting down to
41
+ # +:permissive+ is the Host App's explicit trade.
42
+ DEFAULT_PROFILE = :hermetic
43
+
26
44
  def initialize(timeout: DEFAULT_TIMEOUT_SECONDS,
27
45
  memory_limit: DEFAULT_MEMORY_LIMIT,
28
- stdout_limit: nil,
29
- stderr_limit: nil)
46
+ stdout_limit: DEFAULT_OUTPUT_LIMIT,
47
+ stderr_limit: DEFAULT_OUTPUT_LIMIT,
48
+ profile: DEFAULT_PROFILE)
30
49
  timeout = normalize_timeout(timeout)
31
50
  memory_limit = normalize_memory_limit(memory_limit)
32
- stdout_limit ||= DEFAULT_OUTPUT_LIMIT
33
- stderr_limit ||= DEFAULT_OUTPUT_LIMIT
51
+ stdout_limit = normalize_output_limit(stdout_limit, "stdout_limit")
52
+ stderr_limit = normalize_output_limit(stderr_limit, "stderr_limit")
53
+ profile = normalize_profile(profile)
34
54
  super
35
55
  end
36
56
 
57
+ # Enforce the requested +profile+ as the floor against +declared+ —
58
+ # the posture a runtime reports having built — so a runtime that
59
+ # cannot honor the request fails construction with
60
+ # +Kobako::SetupError+ instead of weakening the posture silently.
61
+ # Both fallbacks fail closed: a declaration off the PROFILES ladder
62
+ # ranks below every request, and a request off the ladder
63
+ # (unreachable past +initialize+) refuses every declaration.
64
+ def enforce_floor!(declared)
65
+ return if (PROFILES.index(declared) || -1) >= (PROFILES.index(profile) || PROFILES.size)
66
+
67
+ raise Kobako::SetupError, "runtime declares isolation profile #{declared.inspect}, " \
68
+ "below the requested floor #{profile.inspect}"
69
+ end
70
+
37
71
  private
38
72
 
39
73
  # Coerce +timeout+ into the Float seconds the ext expects, or +nil+
@@ -62,5 +96,29 @@ module Kobako
62
96
 
63
97
  memory_limit
64
98
  end
99
+
100
+ # Coerce a per-channel output cap (+stdout_limit+ / +stderr_limit+)
101
+ # into the byte cap the ext expects, or +nil+ to leave the channel
102
+ # uncapped. Same shape as +normalize_memory_limit+: a positive Integer
103
+ # when set, Float / zero / negative rejected. +name+ tags the
104
+ # +ArgumentError+ with the offending keyword.
105
+ def normalize_output_limit(limit, name)
106
+ return nil if limit.nil?
107
+ unless limit.is_a?(Integer) && limit.positive?
108
+ raise ArgumentError, "#{name} must be a positive Integer or nil, got #{limit.inspect}"
109
+ end
110
+
111
+ limit
112
+ end
113
+
114
+ # Validate +profile+ against the PROFILES ladder. Unlike the caps
115
+ # there is no +nil+ form: the weakest posture is requested as an
116
+ # explicit +:permissive+, so anything off the ladder — +nil+
117
+ # included — is rejected.
118
+ def normalize_profile(profile)
119
+ return profile if PROFILES.include?(profile)
120
+
121
+ raise ArgumentError, "profile must be one of #{PROFILES.map(&:inspect).join(", ")}, got #{profile.inspect}"
122
+ end
65
123
  end
66
124
  end
@@ -72,8 +72,8 @@ module Kobako
72
72
  # closure so the Dispatcher stays stateless and the registry doesn't
73
73
  # need to publish accessors for the Sandbox-owned +Catalog::Handles+
74
74
  # or +Runtime+. +yield_to_guest+ is a +String → String+ callable
75
- # (typically +Runtime#yield_to_active_invocation+ bound as a lambda)
76
- # used only when the Request carries +block_given: true+. Always
75
+ # (the ext's per-dispatch +Kobako::Runtime::GuestYielder+) used only
76
+ # when the Request carries +block_given: true+. Always
77
77
  # returns a binary String — every failure path is reified as a
78
78
  # Response.error envelope so the guest sees a transport error rather
79
79
  # than a wasm trap.
@@ -38,20 +38,6 @@ module Kobako
38
38
  new(status: STATUS_ERROR, payload: fault)
39
39
  end
40
40
 
41
- # Decode +bytes+ into a {Response}. Raises +Codec::InvalidType+ when the
42
- # envelope is not the expected 2-element msgpack array, or when the
43
- # Value Object's construction invariants reject the decoded fields.
44
- def self.decode(bytes)
45
- Codec::Decoder.decode(bytes) do |arr|
46
- unless arr.is_a?(Array) && arr.length == 2
47
- raise Codec::InvalidType, "Response envelope is malformed (expected a 2-element array)"
48
- end
49
-
50
- status, payload = arr
51
- new(status: status, payload: payload)
52
- end
53
- end
54
-
55
41
  def initialize(status:, payload:)
56
42
  unless [STATUS_OK, STATUS_ERROR].include?(status)
57
43
  raise ArgumentError, "Response status must be 0 (ok) or 1 (error), got #{status.inspect}"
@@ -71,6 +57,20 @@ module Kobako
71
57
  def encode
72
58
  Codec::Encoder.encode([status, payload])
73
59
  end
60
+
61
+ # Decode +bytes+ into a {Response}. Raises +Codec::InvalidType+ when the
62
+ # envelope is not the expected 2-element msgpack array, or when the
63
+ # Value Object's construction invariants reject the decoded fields.
64
+ def self.decode(bytes)
65
+ Codec::Decoder.decode(bytes) do |arr|
66
+ unless arr.is_a?(Array) && arr.length == 2
67
+ raise Codec::InvalidType, "Response envelope is malformed (expected a 2-element array)"
68
+ end
69
+
70
+ status, payload = arr
71
+ new(status: status, payload: payload)
72
+ end
73
+ end
74
74
  end
75
75
  end
76
76
  end
@@ -43,8 +43,8 @@ module Kobako
43
43
 
44
44
  def initialize(entrypoint:, args: [], kwargs: {})
45
45
  entrypoint = normalize_entrypoint(entrypoint)
46
- args = validate_args!(args)
47
- kwargs = validate_kwargs!(kwargs)
46
+ validate_args!(args)
47
+ validate_kwargs!(kwargs)
48
48
  super
49
49
  end
50
50
 
@@ -100,8 +100,6 @@ module Kobako
100
100
  def validate_args!(args)
101
101
  raise ArgumentError, "arguments must be an Array" unless args.is_a?(Array)
102
102
  raise ArgumentError, forged_handle_message("arguments") if args.any?(Kobako::Handle)
103
-
104
- args
105
103
  end
106
104
 
107
105
  # Reject a non-Symbol kwargs key, and a +Kobako::Handle+ arriving
@@ -117,8 +115,6 @@ module Kobako
117
115
  "keyword argument keys must be Symbols (got #{bad_keys.inspect})"
118
116
  end
119
117
  raise ArgumentError, forged_handle_message("keyword argument values") if kwargs.each_value.any?(Kobako::Handle)
120
-
121
- kwargs
122
118
  end
123
119
 
124
120
  # Single source of truth for the forged-Handle reject message so the
@@ -67,7 +67,7 @@ module Kobako
67
67
  raise Codec::InvalidType, "YieldResponse must carry at least one byte" if bytes.empty?
68
68
 
69
69
  tag = bytes.getbyte(0) # : Integer
70
- body = bytes.byteslice(1, bytes.bytesize - 1) || +""
70
+ body = bytes.byteslice(1, bytes.bytesize - 1) # : String
71
71
 
72
72
  reject_dead_tag!(tag)
73
73
  new(tag: tag, value: Codec::Decoder.decode(body))
@@ -28,8 +28,8 @@ module Kobako
28
28
  # dispatch completes; any later call to a stashed Yielder then raises
29
29
  # +LocalJumpError+ — the observable shape of an escaped Yielder.
30
30
  class Yielder
31
- # +yield_to_guest+ is a +String → String+ callable (typically
32
- # +Runtime#yield_to_active_invocation+ bound through a lambda) that
31
+ # +yield_to_guest+ is a +String → String+ callable (the ext's
32
+ # per-dispatch +Kobako::Runtime::GuestYielder+) that
33
33
  # {#yield} invokes to re-enter the guest; +break_tag+ is the +catch+
34
34
  # throw tag the Dispatcher matches against to unwind the Service on
35
35
  # +tag 0x02+. +handler+ is the Sandbox's +Kobako::Catalog::Handles+,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- VERSION = "0.12.1"
4
+ VERSION = "0.13.0"
5
5
  end
data/lib/kobako.rb CHANGED
@@ -13,6 +13,5 @@ require_relative "kobako/errors"
13
13
  require_relative "kobako/transport"
14
14
  require_relative "kobako/catalog"
15
15
  require_relative "kobako/runtime"
16
- require_relative "kobako/snapshot"
17
16
  require_relative "kobako/sandbox"
18
17
  require_relative "kobako/pool"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3
3
  "release-type": "ruby",
4
- "last-release-sha": "5694da60b08931ea260e13025689b8d8c47d767a",
4
+ "last-release-sha": "98509af508988f708bf0d7a76a718bb0428a177e",
5
5
  "packages": {
6
6
  ".": {
7
7
  "component": "kobako",
@@ -18,6 +18,11 @@
18
18
  "path": "/wasm/Cargo.lock",
19
19
  "jsonpath": "$.package[?(@.name=='kobako-core')].version"
20
20
  },
21
+ {
22
+ "type": "toml",
23
+ "path": "/wasm/kobako-core/Cargo.toml",
24
+ "jsonpath": "$.dependencies['kobako-codec'].version"
25
+ },
21
26
  {
22
27
  "type": "generic",
23
28
  "path": "/wasm/kobako-core/README.md"
@@ -38,6 +43,11 @@
38
43
  "path": "/wasm/kobako/Cargo.toml",
39
44
  "jsonpath": "$.dependencies['kobako-core'].version"
40
45
  },
46
+ {
47
+ "type": "toml",
48
+ "path": "/wasm/kobako/Cargo.toml",
49
+ "jsonpath": "$.dependencies['kobako-codec'].version"
50
+ },
41
51
  {
42
52
  "type": "generic",
43
53
  "path": "/wasm/kobako/README.md"
@@ -103,13 +113,78 @@
103
113
  "path": "/wasm/kobako-baker/README.md"
104
114
  }
105
115
  ]
116
+ },
117
+ "crates/kobako-codec": {
118
+ "component": "kobako-codec",
119
+ "release-type": "rust",
120
+ "extra-files": [
121
+ {
122
+ "type": "toml",
123
+ "path": "/crates/Cargo.lock",
124
+ "jsonpath": "$.package[?(@.name=='kobako-codec')].version"
125
+ },
126
+ {
127
+ "type": "toml",
128
+ "path": "/wasm/Cargo.lock",
129
+ "jsonpath": "$.package[?(@.name=='kobako-codec')].version"
130
+ },
131
+ {
132
+ "type": "generic",
133
+ "path": "/crates/kobako-codec/README.md"
134
+ }
135
+ ]
136
+ },
137
+ "crates/kobako-runtime": {
138
+ "component": "kobako-runtime",
139
+ "release-type": "rust",
140
+ "extra-files": [
141
+ {
142
+ "type": "toml",
143
+ "path": "/crates/Cargo.lock",
144
+ "jsonpath": "$.package[?(@.name=='kobako-runtime')].version"
145
+ },
146
+ {
147
+ "type": "toml",
148
+ "path": "/Cargo.lock",
149
+ "jsonpath": "$.package[?(@.name=='kobako-runtime')].version"
150
+ },
151
+ {
152
+ "type": "generic",
153
+ "path": "/crates/kobako-runtime/README.md"
154
+ }
155
+ ]
156
+ },
157
+ "crates/kobako-wasmtime": {
158
+ "component": "kobako-wasmtime",
159
+ "release-type": "rust",
160
+ "extra-files": [
161
+ {
162
+ "type": "toml",
163
+ "path": "/crates/Cargo.lock",
164
+ "jsonpath": "$.package[?(@.name=='kobako-wasmtime')].version"
165
+ },
166
+ {
167
+ "type": "toml",
168
+ "path": "/Cargo.lock",
169
+ "jsonpath": "$.package[?(@.name=='kobako-wasmtime')].version"
170
+ },
171
+ {
172
+ "type": "toml",
173
+ "path": "/crates/kobako-wasmtime/Cargo.toml",
174
+ "jsonpath": "$.dependencies['kobako-runtime'].version"
175
+ },
176
+ {
177
+ "type": "generic",
178
+ "path": "/crates/kobako-wasmtime/README.md"
179
+ }
180
+ ]
106
181
  }
107
182
  },
108
183
  "plugins": [
109
184
  {
110
185
  "type": "linked-versions",
111
- "groupName": "kobako guest crates",
112
- "components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-json", "kobako-regexp", "kobako-baker"]
186
+ "groupName": "kobako crates",
187
+ "components": ["kobako-codec", "kobako-core", "kobako-rs", "kobako-io", "kobako-json", "kobako-regexp", "kobako-baker", "kobako-runtime", "kobako-wasmtime"]
113
188
  }
114
189
  ],
115
190
  "extra-files": [
@@ -8,6 +8,8 @@ module Kobako
8
8
  EXT_SYMBOL: Integer
9
9
  EXT_HANDLE: Integer
10
10
  EXT_ERRENV: Integer
11
+ MAX_EXT_DEPTH: Integer
12
+ EXT_DEPTH_KEY: Symbol
11
13
 
12
14
  def dump: (untyped value) -> String
13
15
  def load: (String bytes) -> untyped
@@ -26,6 +28,7 @@ module Kobako
26
28
  def unpack_handle: (String payload) -> Kobako::Handle
27
29
  def pack_fault: (Kobako::Fault fault) -> String
28
30
  def unpack_fault: (String payload) -> Kobako::Fault
31
+ def within_ext_frame: [T] (singleton(Kobako::Codec::Error) over_limit) { () -> T } -> T
29
32
  end
30
33
  end
31
34
  end
@@ -17,7 +17,7 @@ module Kobako
17
17
  class ModuleNotBuiltError < SetupError
18
18
  end
19
19
 
20
- class SandboxError < Error
20
+ module StructuredError : Error
21
21
  attr_reader origin: String?
22
22
  attr_reader klass: String?
23
23
  attr_reader backtrace_lines: Array[String]?
@@ -32,22 +32,15 @@ module Kobako
32
32
  ) -> void
33
33
  end
34
34
 
35
- class ServiceError < Error
36
- attr_reader origin: String?
37
- attr_reader klass: String?
38
- attr_reader backtrace_lines: Array[String]?
39
- attr_reader details: untyped
35
+ class SandboxError < Error
36
+ include StructuredError
37
+ end
40
38
 
41
- def initialize: (
42
- String message,
43
- ?origin: String?,
44
- ?klass: String?,
45
- ?backtrace_lines: Array[String]?,
46
- ?details: untyped
47
- ) -> void
39
+ class ServiceError < Error
40
+ include StructuredError
48
41
  end
49
42
 
50
- class HandlerExhaustedError < SandboxError
43
+ class HandleExhaustedError < SandboxError
51
44
  end
52
45
 
53
46
  class BytecodeError < SandboxError
@@ -7,17 +7,27 @@ module Kobako
7
7
  (Float | Integer)? timeout_seconds,
8
8
  Integer? memory_limit,
9
9
  Integer? stdout_limit_bytes,
10
- Integer? stderr_limit_bytes
10
+ Integer? stderr_limit_bytes,
11
+ Symbol profile
11
12
  ) -> Runtime
12
13
 
13
- def on_dispatch=: (^(String) -> String dispatch_proc) -> void
14
+ def on_dispatch=: (^(String, Kobako::Transport::_GuestYielder) -> String dispatch_proc) -> void
14
15
 
15
- def yield_to_active_invocation: (String args_bytes) -> String
16
+ def eval: (String preamble, String source, String snippets) -> String
16
17
 
17
- def eval: (String preamble, String source, String snippets) -> Kobako::Snapshot
18
-
19
- def run: (String preamble, String snippets, String envelope) -> Kobako::Snapshot
18
+ def run: (String preamble, String snippets, String envelope) -> String
20
19
 
21
20
  def usage: () -> [Float, Integer]
21
+
22
+ def captures: () -> [String, bool, String, bool]
23
+
24
+ def profile: () -> Symbol
25
+
26
+ # Frame-scoped guest re-entry handle the ext hands the dispatch Proc as
27
+ # its second argument; stands in as the +String -> String+ callable the
28
+ # host +Transport::Yielder+ invokes for a block yield.
29
+ class GuestYielder
30
+ def call: (String args_bytes) -> String
31
+ end
22
32
  end
23
33
  end
@@ -8,15 +8,15 @@ module Kobako
8
8
  # Forwarded to @options via Forwardable#def_delegators.
9
9
  def timeout: () -> Float?
10
10
  def memory_limit: () -> Integer?
11
- def stdout_limit: () -> Integer
12
- def stderr_limit: () -> Integer
11
+ def stdout_limit: () -> Integer?
12
+ def stderr_limit: () -> Integer?
13
+ def profile: () -> Symbol
13
14
 
15
+ # Every keyword beyond wasm_path forwards verbatim to
16
+ # SandboxOptions.new (the four caps + the profile floor).
14
17
  def initialize: (
15
18
  ?wasm_path: String?,
16
- ?stdout_limit: Integer?,
17
- ?stderr_limit: Integer?,
18
- ?timeout: (Float | Integer)?,
19
- ?memory_limit: Integer?
19
+ **untyped options
20
20
  ) -> void
21
21
 
22
22
  def stdout: () -> String
@@ -42,14 +42,18 @@ module Kobako
42
42
 
43
43
  private
44
44
 
45
+ def build_runtime!: () -> Kobako::Runtime
46
+
45
47
  def install_dispatch_proc!: () -> void
46
48
 
47
49
  def begin_invocation!: () -> void
48
50
 
49
51
  def read_usage!: () -> void
50
52
 
53
+ def read_captures!: () -> void
54
+
51
55
  def trap_class_for: (Kobako::TrapError err) -> singleton(Kobako::TrapError)
52
56
 
53
- def invoke!: (Symbol verb) { () -> Kobako::Snapshot } -> untyped
57
+ def invoke!: (Symbol verb) { () -> String } -> untyped
54
58
  end
55
59
  end
@@ -3,30 +3,41 @@ module Kobako
3
3
  DEFAULT_TIMEOUT_SECONDS: Float
4
4
  DEFAULT_MEMORY_LIMIT: Integer
5
5
  DEFAULT_OUTPUT_LIMIT: Integer
6
+ DEFAULT_PROFILE: Symbol
7
+ PROFILES: Array[Symbol]
6
8
 
7
9
  attr_reader timeout: Float?
8
10
  attr_reader memory_limit: Integer?
9
- attr_reader stdout_limit: Integer
10
- attr_reader stderr_limit: Integer
11
+ attr_reader stdout_limit: Integer?
12
+ attr_reader stderr_limit: Integer?
13
+ attr_reader profile: Symbol
11
14
 
12
15
  def self.new: (
13
16
  ?timeout: (Float | Integer)?,
14
17
  ?memory_limit: Integer?,
15
18
  ?stdout_limit: Integer?,
16
- ?stderr_limit: Integer?
19
+ ?stderr_limit: Integer?,
20
+ ?profile: Symbol
17
21
  ) -> SandboxOptions
18
22
 
19
23
  def initialize: (
20
24
  ?timeout: (Float | Integer)?,
21
25
  ?memory_limit: Integer?,
22
26
  ?stdout_limit: Integer?,
23
- ?stderr_limit: Integer?
27
+ ?stderr_limit: Integer?,
28
+ ?profile: Symbol
24
29
  ) -> void
25
30
 
31
+ def enforce_floor!: (Symbol declared) -> void
32
+
26
33
  private
27
34
 
28
35
  def normalize_timeout: ((Float | Integer)? timeout) -> Float?
29
36
 
30
37
  def normalize_memory_limit: (Integer? memory_limit) -> Integer?
38
+
39
+ def normalize_output_limit: (Integer? limit, String name) -> Integer?
40
+
41
+ def normalize_profile: (untyped profile) -> Symbol
31
42
  end
32
43
  end
@@ -12,7 +12,7 @@ module Kobako
12
12
 
13
13
  CALLABLE_ALLOW: Array[Symbol]
14
14
 
15
- def self?.dispatch: (String request_bytes, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler, ^(String) -> String yield_to_guest) -> String
15
+ def self?.dispatch: (String request_bytes, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler, Kobako::Transport::_GuestYielder yield_to_guest) -> String
16
16
 
17
17
  def self?.resolve_call_args: (Kobako::Transport::Request request, Kobako::Catalog::Handles handler) -> [Array[untyped], Hash[Symbol, untyped]]
18
18
 
@@ -17,9 +17,9 @@ module Kobako
17
17
 
18
18
  def normalize_entrypoint: (Symbol | String target) -> Symbol
19
19
 
20
- def validate_args!: (Array[untyped] args) -> Array[untyped]
20
+ def validate_args!: (Array[untyped] args) -> void
21
21
 
22
- def validate_kwargs!: (Hash[untyped, untyped] kwargs) -> Hash[Symbol, untyped]
22
+ def validate_kwargs!: (Hash[untyped, untyped] kwargs) -> void
23
23
 
24
24
  def forged_handle_message: (String slot) -> String
25
25
  end
@@ -1,12 +1,12 @@
1
1
  module Kobako
2
2
  module Transport
3
3
  class Yielder
4
- @yield_to_guest: ^(String) -> String
4
+ @yield_to_guest: Kobako::Transport::_GuestYielder
5
5
  @break_tag: Symbol
6
6
  @handler: Kobako::Catalog::Handles
7
7
  @active: bool
8
8
 
9
- def initialize: (^(String) -> String yield_to_guest, Symbol break_tag, Kobako::Catalog::Handles handler) -> void
9
+ def initialize: (Kobako::Transport::_GuestYielder yield_to_guest, Symbol break_tag, Kobako::Catalog::Handles handler) -> void
10
10
 
11
11
  def yield: (*untyped args) -> untyped
12
12
 
@@ -1,4 +1,12 @@
1
1
  module Kobako
2
2
  module Transport
3
+ # The guest re-entry callable a dispatch hands to a Yielder: invoked
4
+ # with the encoded yield args, it returns the encoded YieldResponse.
5
+ # The ext's Kobako::Runtime::GuestYielder is the production conformer;
6
+ # modelled structurally so the Transport layer needs no upward
7
+ # dependency on Runtime.
8
+ interface _GuestYielder
9
+ def call: (String) -> String
10
+ end
3
11
  end
4
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobako
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Aotokitsuruya
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-27 00:00:00.000000000 Z
11
+ date: 2026-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -38,6 +38,10 @@ files:
38
38
  - LICENSE
39
39
  - README.md
40
40
  - SECURITY.md
41
+ - crates/kobako-runtime/CHANGELOG.md
42
+ - crates/kobako-runtime/README.md
43
+ - crates/kobako-wasmtime/CHANGELOG.md
44
+ - crates/kobako-wasmtime/README.md
41
45
  - data/kobako.wasm
42
46
  - lib/kobako.rb
43
47
  - lib/kobako/3.3/kobako.so
@@ -65,7 +69,6 @@ files:
65
69
  - lib/kobako/runtime.rb
66
70
  - lib/kobako/sandbox.rb
67
71
  - lib/kobako/sandbox_options.rb
68
- - lib/kobako/snapshot.rb
69
72
  - lib/kobako/snippet.rb
70
73
  - lib/kobako/snippet/binary.rb
71
74
  - lib/kobako/snippet/source.rb
@@ -104,7 +107,6 @@ files:
104
107
  - sig/kobako/runtime.rbs
105
108
  - sig/kobako/sandbox.rbs
106
109
  - sig/kobako/sandbox_options.rbs
107
- - sig/kobako/snapshot.rbs
108
110
  - sig/kobako/snippet.rbs
109
111
  - sig/kobako/snippet/binary.rbs
110
112
  - sig/kobako/snippet/source.rbs
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "capture"
4
- require_relative "usage"
5
-
6
- module Kobako
7
- # Kobako::Snapshot — per-invocation observable bundle returned from
8
- # +Kobako::Runtime#eval+ and +#run+.
9
- #
10
- # The magnus class (see ext/kobako/src/snapshot.rs) carries seven raw
11
- # readers: +return_bytes+, +stdout_bytes+, +stdout_truncated+,
12
- # +stderr_bytes+, +stderr_truncated+, +wall_time+, +memory_peak+. This
13
- # file reopens the class to add the Ruby-side helpers that pack those
14
- # raw fields into the user-facing value objects +Kobako::Capture+ and
15
- # +Kobako::Usage+ — the same shape +Kobako::Sandbox+ exposes to the
16
- # Host App.
17
- class Snapshot
18
- # Wrap the stdout capture pair (+stdout_bytes+, +stdout_truncated+)
19
- # as a +Kobako::Capture+ value object. The byte content never carries
20
- # a truncation sentinel; +#truncated?+ is the only way to observe
21
- # that the cap was hit.
22
- def stdout
23
- Capture.new(bytes: stdout_bytes, truncated: stdout_truncated)
24
- end
25
-
26
- # Wrap the stderr capture pair as a +Kobako::Capture+ value object.
27
- # Mirror of +#stdout+.
28
- def stderr
29
- Capture.new(bytes: stderr_bytes, truncated: stderr_truncated)
30
- end
31
-
32
- # Wrap the per-last-invocation usage pair (+wall_time+,
33
- # +memory_peak+) as a +Kobako::Usage+ value object.
34
- def usage
35
- Usage.new(wall_time: wall_time, memory_peak: memory_peak)
36
- end
37
- end
38
- end