kobako 0.9.1 → 0.9.2

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: f2af9677bcec2d298db05d1259360b9589ac7726d855157dbbfd0be0ca666472
4
- data.tar.gz: 5ed589a7b179274650d04280d9f84456873ec6fbab46166e85974a3d92d9ab9d
3
+ metadata.gz: 3177d01c5cb742a539ac9713226c17c159299911344b1590a680aacd99aa70fd
4
+ data.tar.gz: 6f9a4503a56855076b1f5d83725775669ae4c34283b24bcb8467485509e26c03
5
5
  SHA512:
6
- metadata.gz: 59c775f5aacdf0b81a8f00970385974d2e7173b356d9886bd7e173f006a668449eb1672833b222129a227a5f4a3746b92d1fece7b44265eba44963414406b8d2
7
- data.tar.gz: d902273d0c0df298c41e6be747d692ba8ceec86f564c81cd81347a75bf2a78b2fc35cfd73ccbd44696cabcff01cfdcf76d7260ba99cd2f194a1400cdbe4e88df
6
+ metadata.gz: b8f0f15f5c76c54b785e109ca6f7c10a37ebc7394d49f05637b18ace67a1326f941aa0a03ee99b985d8b09b70c39ba42c8318f9dd4fe885dba530472e5b24c81
7
+ data.tar.gz: 794e0c2f476fceaee007fbeb1c8082accd8d1de2de682aa333606bc0ab6593ed281abda277222ca51d58a88f2663d37220e8068891396565221ecc694bdc2b68
@@ -1 +1 @@
1
- {".":"0.9.1","wasm/kobako-core":"0.4.0","wasm/kobako":"0.4.0","wasm/kobako-io":"0.4.0","wasm/kobako-regexp":"0.4.0"}
1
+ {".":"0.9.2","wasm/kobako-core":"0.4.1","wasm/kobako":"0.4.1","wasm/kobako-io":"0.4.1","wasm/kobako-regexp":"0.4.1"}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.2](https://github.com/elct9620/kobako/compare/v0.9.1...v0.9.2) (2026-06-11)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **catalog:** never mint a Capability Handle for a reflective gadget ([6c2d29d](https://github.com/elct9620/kobako/commit/6c2d29d0fbcced5187df5538c2c6c437705fd6d8))
9
+ * **ext:** cap stdin frames at 16 MiB like the run envelope ([a94099a](https://github.com/elct9620/kobako/commit/a94099a03830929c55fcb266e227073db9c5a624))
10
+ * **ext:** deny guest ambient clock and entropy at the WASI layer ([1275b35](https://github.com/elct9620/kobako/commit/1275b35264813628a2aeb396a3546faf1f6d9d0c))
11
+ * **transport:** reject reflective gadget methods in guest dispatch ([948fb9e](https://github.com/elct9620/kobako/commit/948fb9ea7d6c0d6bd91f6e261d3263743974388b))
12
+ * **wasm:** mirror the reflection rejection in the guest proxy ([f6ead3b](https://github.com/elct9620/kobako/commit/f6ead3b91f1ac92c3c075397d177edb4b82cd15d))
13
+
3
14
  ## [0.9.1](https://github.com/elct9620/kobako/compare/v0.9.0...v0.9.1) (2026-06-11)
4
15
 
5
16
 
data/Cargo.lock CHANGED
@@ -878,7 +878,7 @@ dependencies = [
878
878
 
879
879
  [[package]]
880
880
  name = "kobako"
881
- version = "0.9.1"
881
+ version = "0.9.2"
882
882
  dependencies = [
883
883
  "magnus",
884
884
  "wasmtime",
data/Cargo.toml CHANGED
@@ -9,3 +9,9 @@ members = ["./ext/kobako"]
9
9
  # dependency graphs separate.
10
10
  exclude = ["wasm", "vendor"]
11
11
  resolver = "2"
12
+
13
+ # Strip the local-symbol table from the shipped ext (~22% of the binary).
14
+ # Guest wasm backtraces and Rust panic locations don't depend on it; for
15
+ # native-crash debugging rebuild with `RB_SYS_CARGO_PROFILE=dev`.
16
+ [profile.release]
17
+ strip = true
data/data/kobako.wasm CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "kobako"
3
- version = "0.9.1"
3
+ version = "0.9.2"
4
4
  edition = "2021"
5
5
  authors = ["Aotokitsuruya <contact@aotoki.me>"]
6
6
  license = "Apache-2.0"
@@ -0,0 +1,78 @@
1
+ //! Defense-in-depth denial of guest ambient authority at the WASI layer.
2
+ //!
3
+ //! `wasmtime-wasi`'s `WasiCtxBuilder` defaults the guest's `wasi:clocks` to
4
+ //! the host wall / monotonic clock and `wasi:random` to a fresh per-context
5
+ //! seed. No allowlisted mrbgem reaches these preview1 imports today
6
+ //! (`build_config/wasi.rb`), but a future libc-backed gem would silently
7
+ //! obtain real time and host entropy — a covert timing channel and a
8
+ //! nondeterminism source the sandbox deliberately excludes (docs/security.md).
9
+ //! Pinning the clocks to the Unix epoch and the RNG to a constant stream
10
+ //! makes that denial a property of the host, not merely of the gem allowlist.
11
+ //!
12
+ //! The host wall-clock cap is unaffected: the per-invocation timeout runs on
13
+ //! wasmtime epoch interruption against a host `Instant`, never the guest's
14
+ //! frozen `wasi:clocks/monotonic-clock` (docs/behavior.md B-01).
15
+
16
+ use std::time::Duration;
17
+
18
+ use wasmtime_wasi::random::Deterministic;
19
+ use wasmtime_wasi::{HostMonotonicClock, HostWallClock};
20
+
21
+ /// Wall clock frozen at the Unix epoch — the guest observes no real time.
22
+ pub(super) struct FrozenWallClock;
23
+
24
+ impl HostWallClock for FrozenWallClock {
25
+ fn resolution(&self) -> Duration {
26
+ Duration::from_secs(1)
27
+ }
28
+
29
+ fn now(&self) -> Duration {
30
+ Duration::ZERO
31
+ }
32
+ }
33
+
34
+ /// Monotonic clock frozen at zero — the guest observes no elapsed time.
35
+ pub(super) struct FrozenMonotonicClock;
36
+
37
+ impl HostMonotonicClock for FrozenMonotonicClock {
38
+ fn resolution(&self) -> u64 {
39
+ 1
40
+ }
41
+
42
+ fn now(&self) -> u64 {
43
+ 0
44
+ }
45
+ }
46
+
47
+ /// Constant-stream RNG handed to the guest's `wasi:random`, so a guest that
48
+ /// reaches `random_get` receives no host entropy.
49
+ pub(super) fn deterministic_rng() -> Deterministic {
50
+ Deterministic::new(vec![0])
51
+ }
52
+
53
+ #[cfg(test)]
54
+ mod tests {
55
+ //! Pin the frozen-clock contract: the guest must observe no real wall
56
+ //! or monotonic time through `wasi:clocks`, regardless of the host's
57
+ //! actual clock. A regression that reverts either clock to the WASI
58
+ //! default would re-expose ambient time to a future libc-backed gem.
59
+ use super::*;
60
+
61
+ #[test]
62
+ fn the_guest_wall_clock_reads_the_unix_epoch() {
63
+ assert_eq!(
64
+ FrozenWallClock.now(),
65
+ Duration::ZERO,
66
+ "guest wasi:clocks/wall-clock must read the Unix epoch, not host time"
67
+ );
68
+ }
69
+
70
+ #[test]
71
+ fn the_guest_monotonic_clock_never_advances() {
72
+ assert_eq!(
73
+ FrozenMonotonicClock.now(),
74
+ 0,
75
+ "guest wasi:clocks/monotonic-clock must stay frozen at zero"
76
+ );
77
+ }
78
+ }
@@ -18,7 +18,7 @@
18
18
  //! initial allocation and prior invocations' watermark are outside the
19
19
  //! budget.
20
20
  //!
21
- //! [SPEC.md Single-Invocation Slot]: ../../../SPEC.md
21
+ //! [SPEC.md Single-Invocation Slot]: ../../../../SPEC.md
22
22
 
23
23
  use std::cell::{Ref, RefCell, RefMut};
24
24
  use std::time::{Duration, Instant};
@@ -31,6 +31,7 @@
31
31
  // `timeout_err` / `memory_limit_err` / `setup_err` constructors shared by
32
32
  // every submodule, and the Ruby init() that registers the class.
33
33
 
34
+ mod ambient;
34
35
  mod cache;
35
36
  mod capture;
36
37
  mod config;
@@ -506,7 +507,7 @@ impl Runtime {
506
507
  rstring_to_vec(preamble),
507
508
  rstring_to_vec(source),
508
509
  rstring_to_vec(snippets),
509
- ]);
510
+ ])?;
510
511
  self.call_with_caps(eval, ())
511
512
  .map_err(|e| trap::call_err(&ruby, e))?;
512
513
  self.build_snapshot(&ruby)
@@ -530,7 +531,7 @@ impl Runtime {
530
531
  ) -> Result<Snapshot, MagnusError> {
531
532
  let ruby = Ruby::get().expect("Ruby thread");
532
533
  let run = require_export(&ruby, self.exports.run.as_ref())?;
533
- self.refresh_wasi(&[rstring_to_vec(preamble), rstring_to_vec(snippets)]);
534
+ self.refresh_wasi(&[rstring_to_vec(preamble), rstring_to_vec(snippets)])?;
534
535
  let (env_ptr, env_len) = self.write_envelope(&ruby, envelope)?;
535
536
  self.call_with_caps(run, (env_ptr, env_len))
536
537
  .map_err(|e| trap::call_err(&ruby, e))?;
@@ -735,8 +736,18 @@ impl Runtime {
735
736
  /// `capture::clip_capture` can distinguish "wrote exactly cap
736
737
  /// bytes" from "exceeded cap"; uncapped channels fall back
737
738
  /// to `usize::MAX` and rely on `memory_limit` (docs/behavior.md E-20)
738
- /// for the real ceiling.
739
- fn refresh_wasi(&self, frames: &[Vec<u8>]) {
739
+ /// for the real ceiling. Raises `Kobako::TrapError` when any frame
740
+ /// exceeds the 16 MiB cap that keeps its `u32` length prefix from
741
+ /// wrapping.
742
+ fn refresh_wasi(&self, frames: &[Vec<u8>]) -> Result<(), MagnusError> {
743
+ let ruby = Ruby::get().expect("Ruby thread");
744
+ // Every frame carries the same 16 MiB cap as the `#run` envelope
745
+ // (`write_envelope`): the length prefix is a `u32`, so a frame past
746
+ // the cap would silently wrap and corrupt the stdin frame stream.
747
+ for frame in frames {
748
+ guest_mem::checked_payload_len(frame.len()).map_err(|msg| trap_err(&ruby, msg))?;
749
+ }
750
+
740
751
  let total: usize = frames.iter().map(|f| 4 + f.len()).sum();
741
752
  let mut stdin_content: Vec<u8> = Vec::with_capacity(total);
742
753
  for frame in frames {
@@ -754,12 +765,18 @@ impl Runtime {
754
765
  builder.stdin(stdin_pipe);
755
766
  builder.stdout(stdout_pipe.clone());
756
767
  builder.stderr(stderr_pipe.clone());
768
+ // Deny the preview1 ambient-authority imports the guest never legitimately
769
+ // reaches but the WASI layer would otherwise grant (see `ambient`).
770
+ builder.wall_clock(ambient::FrozenWallClock);
771
+ builder.monotonic_clock(ambient::FrozenMonotonicClock);
772
+ builder.secure_random(ambient::deterministic_rng());
757
773
  let wasi = builder.build_p1();
758
774
 
759
775
  self.store
760
776
  .borrow_mut()
761
777
  .data_mut()
762
778
  .install_wasi(wasi, stdout_pipe, stderr_pipe);
779
+ Ok(())
763
780
  }
764
781
 
765
782
  /// Invoke `__kobako_take_outcome`, decode the packed `(ptr<<32)|len`
@@ -30,6 +30,13 @@ module Kobako
30
30
  # +0x7fff_ffff+ (2³¹ − 1). Allocation beyond the cap raises
31
31
  # immediately — no silent truncation, no wrap, no ID reuse.
32
32
  class Handles
33
+ # Reflective gadget types that are never minted as a Capability Handle
34
+ # ({docs/behavior.md B-43}[link:../../../docs/behavior.md]): wrapping a
35
+ # +Binding+ / +Method+ / +UnboundMethod+ would hand the guest a callable
36
+ # proxy onto host reflection (a returned +Binding+ reaches +Binding#eval+).
37
+ UNWRAPPABLE_TYPES = [Binding, Method, UnboundMethod].freeze
38
+ private_constant :UNWRAPPABLE_TYPES
39
+
33
40
  # Build a fresh, empty table. +next_id+ is an internal seam that
34
41
  # sets the starting value of the monotonic counter (defaults to 1 per
35
42
  # B-15); tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
@@ -53,14 +60,9 @@ module Kobako
53
60
  # is reserved for the codec's wire-decode path, where the id is
54
61
  # the only thing the bytes carry.
55
62
  def alloc(object)
63
+ reject_unwrappable!(object)
64
+ ensure_capacity!
56
65
  id = @next_id
57
- cap = Kobako::Handle::MAX_ID
58
- if id > cap
59
- raise HandlerExhaustedError,
60
- "Out of handle allocations: too many host objects were referenced " \
61
- "in a single invocation (limit #{cap})"
62
- end
63
-
64
66
  @entries[id] = object
65
67
  @next_id = id + 1
66
68
  Kobako::Handle.restore(id)
@@ -94,6 +96,27 @@ module Kobako
94
96
 
95
97
  private
96
98
 
99
+ # Refuse to mint a Capability Handle for a reflective gadget
100
+ # ({UNWRAPPABLE_TYPES}, B-43). Raising here keeps the rule at the single
101
+ # mint point, so it holds on both the Service-return (B-14) and the
102
+ # +#run+ host→guest auto-wrap (B-34) paths.
103
+ def reject_unwrappable!(object)
104
+ return unless UNWRAPPABLE_TYPES.any? { |type| object.is_a?(type) }
105
+
106
+ raise SandboxError, "a #{object.class} cannot cross as a Capability Handle"
107
+ end
108
+
109
+ # Guard {#alloc} against issuing an ID past the B-21 cap. Returns +nil+
110
+ # on success; raises +Kobako::HandlerExhaustedError+ at exhaustion.
111
+ def ensure_capacity!
112
+ cap = Kobako::Handle::MAX_ID
113
+ return unless @next_id > cap
114
+
115
+ raise HandlerExhaustedError,
116
+ "Out of handle allocations: too many host objects were referenced " \
117
+ "in a single invocation (limit #{cap})"
118
+ end
119
+
97
120
  # Single source of truth for the "unknown Handle id" raise used by
98
121
  # {#fetch}. Returns +nil+ on success; raises +Kobako::SandboxError+
99
122
  # when +id+ is not currently bound.
@@ -3,7 +3,6 @@
3
3
  require_relative "handles"
4
4
  require_relative "../codec"
5
5
  require_relative "../errors"
6
- require_relative "../transport/request"
7
6
  require_relative "../namespace"
8
7
 
9
8
  module Kobako
data/lib/kobako/codec.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "codec/error"
4
+ require_relative "codec/utils"
5
+ require_relative "codec/factory"
6
+ require_relative "codec/encoder"
7
+ require_relative "codec/decoder"
4
8
 
5
9
  module Kobako
6
10
  # Host-side MessagePack codec for the kobako wire contract — the
@@ -17,15 +21,10 @@ module Kobako
17
21
  # {Decoder} are thin wrappers that register the three kobako-specific
18
22
  # ext types (0x00 Symbol, 0x01 Capability Handle, 0x02 Exception
19
23
  # envelope) on a single +MessagePack::Factory+ instance. The Rust side
20
- # mirrors this layer as the +codec+ module in the +kobako-wasm+ crate;
24
+ # mirrors this layer as the +codec+ module in the +kobako-core+ crate;
21
25
  # the ext-code constants live as module-private values on {Factory}
22
26
  # alongside +codec::EXT_SYMBOL+ / +codec::EXT_HANDLE+ /
23
27
  # +codec::EXT_ERRENV+ on that side.
24
28
  module Codec
25
29
  end
26
30
  end
27
-
28
- require_relative "codec/utils"
29
- require_relative "codec/factory"
30
- require_relative "codec/encoder"
31
- require_relative "codec/decoder"
@@ -46,13 +46,28 @@ module Kobako
46
46
  # metaprogramming surface (+send+, +public_send+, +instance_eval+,
47
47
  # +method+, +tap+, +instance_variable_get+, ...) rather than Service
48
48
  # behaviour. A guest-supplied method name resolving to one of these is
49
- # rejected: the security contract is that only methods the bound object
50
- # itself defines are reachable, and +public_send(:send, ...)+ would
51
- # otherwise let a guest pivot through +send+ into the private
52
- # +Kernel#eval+ / +#system+ surface (host RCE).
49
+ # rejected ({docs/behavior.md B-42}[link:../../../docs/behavior.md]):
50
+ # only methods the bound object itself exposes as Service behaviour are
51
+ # reachable, and +public_send(:send, ...)+ would otherwise let a guest
52
+ # pivot through +send+ into the private +Kernel#eval+ / +#system+
53
+ # surface (host RCE).
53
54
  META_OWNERS = [BasicObject, Kernel, Object, Module, Class].freeze
54
55
  private_constant :META_OWNERS
55
56
 
57
+ # Callable gadget types whose own public methods are reflection surface
58
+ # (+Proc#binding+ reaches +Binding#eval+, +Method#receiver+ / +#unbind+
59
+ # hand back the underlying object) rather than Service behaviour. Only
60
+ # {CALLABLE_ALLOW} is reachable on a target of these types; a bound
61
+ # lambda stays invocable, its reflective surface does not (B-42).
62
+ GADGET_OWNERS = [Proc, Method, UnboundMethod, Binding].freeze
63
+ private_constant :GADGET_OWNERS
64
+
65
+ # The sole methods reachable on a {GADGET_OWNERS} target: invoking it
66
+ # (+call+ / +[]+ / +yield+) and the harmless +arity+ / +lambda?+
67
+ # describers that aid guest-side debugging.
68
+ CALLABLE_ALLOW = %i[call [] yield arity lambda?].freeze
69
+ private_constant :CALLABLE_ALLOW
70
+
56
71
  # Dispatch a single transport request and return the encoded
57
72
  # Response bytes ({docs/behavior.md B-12}[link:../../../docs/behavior.md]).
58
73
  # Invoked from the +Runtime#on_dispatch+ Proc that
@@ -131,14 +146,18 @@ module Kobako
131
146
  end
132
147
 
133
148
  # Guard the +public_send+ below against ambient reflection methods
134
- # (see {META_OWNERS}). A concretely-defined public method whose owner
135
- # is a meta module is rejected; a name with no concrete public method
136
- # is allowed only when the target opts into it via +respond_to?+
137
- # (dynamic +method_missing+ Services), since the dangerous meta methods
138
- # are all concretely defined and therefore never reach that branch.
149
+ # ({docs/behavior.md B-42}[link:../../../docs/behavior.md]). A public
150
+ # method whose owner is a {META_OWNERS} or {GADGET_OWNERS} module is
151
+ # rejected, except {CALLABLE_ALLOW} on a gadget target (a bound lambda
152
+ # stays invocable). A name with no concrete public method is allowed
153
+ # only when the target opts into it via +respond_to?+ (dynamic
154
+ # +method_missing+ Services), since the dangerous methods are all
155
+ # concretely defined and therefore never reach that branch.
139
156
  def reject_meta_method!(target, name)
140
157
  owner = target.public_method(name).owner
141
- return unless META_OWNERS.include?(owner)
158
+ gadget = GADGET_OWNERS.include?(owner)
159
+ return unless META_OWNERS.include?(owner) || gadget
160
+ return if gadget && CALLABLE_ALLOW.include?(name)
142
161
 
143
162
  raise UndefinedTargetError, "method #{name.inspect} is not a Service method"
144
163
  rescue NameError
@@ -5,16 +5,8 @@ require_relative "../codec"
5
5
 
6
6
  module Kobako
7
7
  # See lib/kobako/transport.rb for the umbrella module doc; this file
8
- # owns the Request value object and its +#encode+ / +.decode+ codec,
9
- # plus the +STATUS_OK+ / +STATUS_ERROR+ constants shared with Response.
8
+ # owns the Request value object and its +#encode+ / +.decode+ codec.
10
9
  module Transport
11
- # ---------------- Response status bytes (docs/wire-contract.md § Response Shape) ---
12
-
13
- # Response variant marker for the success branch.
14
- STATUS_OK = 0
15
- # Response variant marker for the fault branch.
16
- STATUS_ERROR = 1
17
-
18
10
  # Value object for a single guest-initiated Transport Request
19
11
  # ({docs/wire-codec.md Envelope Encoding → Request}[link:../../../docs/wire-codec.md]).
20
12
  #
@@ -2,12 +2,19 @@
2
2
 
3
3
  require_relative "../codec"
4
4
  require_relative "../fault"
5
- require_relative "request"
6
5
 
7
6
  module Kobako
8
7
  # See lib/kobako/transport.rb for the umbrella module doc; this file
9
- # owns the Response value object and its +#encode+ / +.decode+ codec.
8
+ # owns the Response value object and its +#encode+ / +.decode+ codec,
9
+ # plus the +STATUS_OK+ / +STATUS_ERROR+ status bytes.
10
10
  module Transport
11
+ # ---------------- Response status bytes (docs/wire-contract.md § Response Shape) ---
12
+
13
+ # Response variant marker for the success branch.
14
+ STATUS_OK = 0
15
+ # Response variant marker for the fault branch.
16
+ STATUS_ERROR = 1
17
+
11
18
  # Value object for a single host-side Transport Response
12
19
  # ({docs/wire-codec.md Envelope Encoding → Response}[link:../../../docs/wire-codec.md]).
13
20
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- VERSION = "0.9.1"
4
+ VERSION = "0.9.2"
5
5
  end
@@ -7,8 +7,7 @@
7
7
  "component": "kobako",
8
8
  "include-component-in-tag": false,
9
9
  "release-type": "ruby",
10
- "exclude-paths": ["wasm"],
11
- "release-as": "0.9.1"
10
+ "exclude-paths": ["wasm"]
12
11
  },
13
12
  "wasm/kobako-core": {
14
13
  "component": "kobako-core",
@@ -1,6 +1,8 @@
1
1
  module Kobako
2
2
  module Catalog
3
3
  class Handles
4
+ UNWRAPPABLE_TYPES: Array[Module]
5
+
4
6
  def initialize: (?next_id: Integer) -> void
5
7
 
6
8
  def alloc: (untyped object) -> Kobako::Handle
@@ -13,6 +15,10 @@ module Kobako
13
15
 
14
16
  private
15
17
 
18
+ def reject_unwrappable!: (untyped object) -> void
19
+
20
+ def ensure_capacity!: () -> void
21
+
16
22
  def require_bound!: (Integer id) -> void
17
23
  end
18
24
  end
@@ -8,6 +8,10 @@ module Kobako
8
8
 
9
9
  META_OWNERS: Array[Module]
10
10
 
11
+ GADGET_OWNERS: Array[Module]
12
+
13
+ CALLABLE_ALLOW: Array[Symbol]
14
+
11
15
  def self?.dispatch: (String request_bytes, Kobako::Catalog::Namespaces namespaces, Kobako::Catalog::Handles handler, ^(String) -> String yield_to_guest) -> String
12
16
 
13
17
  def self?.resolve_call_args: (Kobako::Transport::Request request, Kobako::Catalog::Handles handler) -> [Array[untyped], Hash[Symbol, untyped]]
@@ -1,8 +1,5 @@
1
1
  module Kobako
2
2
  module Transport
3
- STATUS_OK: Integer
4
- STATUS_ERROR: Integer
5
-
6
3
  class Request < Data
7
4
  attr_reader target: String | Kobako::Handle
8
5
  attr_reader method_name: String
@@ -1,5 +1,8 @@
1
1
  module Kobako
2
2
  module Transport
3
+ STATUS_OK: Integer
4
+ STATUS_ERROR: Integer
5
+
3
6
  class Response < Data
4
7
  attr_reader status: Integer
5
8
  attr_reader payload: untyped
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kobako
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aotokitsuruya
@@ -58,6 +58,7 @@ files:
58
58
  - ext/kobako/extconf.rb
59
59
  - ext/kobako/src/lib.rs
60
60
  - ext/kobako/src/runtime.rs
61
+ - ext/kobako/src/runtime/ambient.rs
61
62
  - ext/kobako/src/runtime/cache.rs
62
63
  - ext/kobako/src/runtime/capture.rs
63
64
  - ext/kobako/src/runtime/config.rs