kobako 0.28.0-aarch64-linux → 0.29.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +170 -0
- data/README.md +154 -36
- data/ROADMAP.md +5 -0
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +86 -0
- data/crates/kobako-runtime/README.md +13 -8
- data/crates/kobako-transport/CHANGELOG.md +17 -0
- data/crates/kobako-transport/README.md +1 -1
- data/crates/kobako-wasmtime/CHANGELOG.md +119 -0
- data/crates/kobako-wasmtime/README.md +5 -3
- data/data/kobako.wasm +0 -0
- data/lib/kobako/3.3/kobako.so +0 -0
- data/lib/kobako/3.4/kobako.so +0 -0
- data/lib/kobako/4.0/kobako.so +0 -0
- data/lib/kobako/catalog/flags.rb +2 -14
- data/lib/kobako/catalog/grants.rb +30 -0
- data/lib/kobako/catalog/mount.rb +27 -0
- data/lib/kobako/catalog/services.rb +11 -0
- data/lib/kobako/catalog/snippets.rb +5 -9
- data/lib/kobako/catalog.rb +11 -0
- data/lib/kobako/codec/decoder.rb +1 -0
- data/lib/kobako/codec/ext_types.rb +1 -1
- data/lib/kobako/codec/state.rb +19 -3
- data/lib/kobako/codec/value_budget.rb +86 -0
- data/lib/kobako/codec.rb +7 -0
- data/lib/kobako/context.rb +38 -11
- data/lib/kobako/errors.rb +15 -14
- data/lib/kobako/filesystem.rb +38 -0
- data/lib/kobako/pool.rb +2 -2
- data/lib/kobako/sandbox.rb +40 -12
- data/lib/kobako/sandbox_options.rb +39 -12
- data/lib/kobako/snippet/{binary.rb → bytecode.rb} +1 -1
- data/lib/kobako/snippet.rb +1 -1
- data/lib/kobako/transport/block_failure.rb +35 -0
- data/lib/kobako/transport/dispatcher.rb +30 -26
- data/lib/kobako/transport/exposure.rb +5 -7
- data/lib/kobako/transport/reflection.rb +79 -23
- data/lib/kobako/transport/yielder.rb +8 -30
- data/lib/kobako/transport.rb +1 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/sig/kobako/catalog/flags.rbs +0 -4
- data/sig/kobako/catalog/grants.rbs +13 -0
- data/sig/kobako/catalog/mount.rbs +28 -0
- data/sig/kobako/catalog/services.rbs +4 -0
- data/sig/kobako/catalog/snippets.rbs +1 -1
- data/sig/kobako/catalog.rbs +1 -0
- data/sig/kobako/codec/state.rbs +7 -1
- data/sig/kobako/codec/value_budget.rbs +23 -0
- data/sig/kobako/codec.rbs +2 -0
- data/sig/kobako/context.rbs +14 -6
- data/sig/kobako/errors.rbs +4 -4
- data/sig/kobako/filesystem.rbs +88 -0
- data/sig/kobako/guest_yielder.rbs +9 -0
- data/sig/kobako/pool.rbs +1 -1
- data/sig/kobako/runtime.rbs +21 -49
- data/sig/kobako/sandbox.rbs +7 -1
- data/sig/kobako/sandbox_options.rbs +24 -2
- data/sig/kobako/snapshot.rbs +20 -0
- data/sig/kobako/snippet/bytecode.rbs +12 -0
- data/sig/kobako/transport/block_failure.rbs +13 -0
- data/sig/kobako/transport/dispatcher.rbs +3 -3
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +14 -1
- data/sig/kobako/transport/yielder.rbs +3 -7
- data/sig/kobako/transport.rbs +2 -2
- metadata +26 -8
- data/sig/kobako/snippet/binary.rbs +0 -12
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
require_relative "../codec"
|
|
4
4
|
require_relative "../errors"
|
|
5
5
|
require_relative "../payload"
|
|
6
|
+
require_relative "block_failure"
|
|
6
7
|
require_relative "call"
|
|
7
8
|
require_relative "reflection"
|
|
8
9
|
require_relative "yielder"
|
|
9
10
|
|
|
10
11
|
module Kobako
|
|
11
12
|
module Transport
|
|
12
|
-
# Answers a routed Call with +[
|
|
13
|
-
# +[
|
|
14
|
-
#
|
|
13
|
+
# Answers a routed Call with one arm per Reply variant: +[:ok, body, nil]+,
|
|
14
|
+
# +[:fault, message, fault_type]+, or +[:break, "", nil]+ — a block
|
|
15
|
+
# exit the guest kept carries nothing. It never raises, so every
|
|
16
|
+
# failure reaches the guest as a fault.
|
|
15
17
|
module Dispatcher # :nodoc:
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
+
# Thrown rather than raised, so a Service unwound by a block exit runs
|
|
19
|
+
# its +ensure+ and cannot +rescue+ the exit. +private_constant+ is a
|
|
18
20
|
# convention boundary — not a defence.
|
|
19
21
|
BREAK_THROW = :__kobako_break__
|
|
20
22
|
private_constant :BREAK_THROW
|
|
@@ -23,15 +25,18 @@ module Kobako
|
|
|
23
25
|
|
|
24
26
|
class UnreadableRequestError < Kobako::Codec::Error; end # :nodoc:
|
|
25
27
|
|
|
26
|
-
# The category
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
28
|
+
# The category each failure answering with its message alone takes,
|
|
29
|
+
# keyed by the class raised and ordered most specific first. kobako's
|
|
30
|
+
# own refusals are worded so none reads as a Service's exception, and
|
|
31
|
+
# +ArgumentError+, raised by a Service and by a call that will not
|
|
32
|
+
# bind alike, is named by +argument+ already. A class absent here is
|
|
33
|
+
# the Service's own exception, which answers under +runtime+ wearing
|
|
34
|
+
# the +<class>: <message>+ shape that names it.
|
|
30
35
|
#
|
|
31
36
|
# +Kobako::Codec::Error+ is the floor rather than a path of its own:
|
|
32
37
|
# a codec fault reaching the boundary unnamed is the exchange failing,
|
|
33
38
|
# and must not be dressed as something a Service raised.
|
|
34
|
-
|
|
39
|
+
BARE_FAULTS = {
|
|
35
40
|
HandleExhaustedError => "internal",
|
|
36
41
|
UndefinedTargetError => "undefined",
|
|
37
42
|
ArgumentError => "argument",
|
|
@@ -40,23 +45,24 @@ module Kobako
|
|
|
40
45
|
Kobako::Codec::Error => "internal",
|
|
41
46
|
Kobako::SandboxError => "runtime"
|
|
42
47
|
}.freeze
|
|
43
|
-
private_constant :
|
|
48
|
+
private_constant :BARE_FAULTS
|
|
44
49
|
|
|
45
50
|
# The per-invocation state arrives as arguments so the Dispatcher
|
|
46
51
|
# stays stateless and neither the resolver nor the Context publishes
|
|
47
52
|
# accessors for it.
|
|
48
|
-
def self.dispatch(call, resolver, handles, guest_yielder)
|
|
49
|
-
yielder = Yielder.new(guest_yielder, BREAK_THROW, handles) if call.block_given
|
|
50
|
-
[
|
|
53
|
+
def self.dispatch(call, resolver, handles, failure, guest_yielder)
|
|
54
|
+
yielder = Yielder.new(guest_yielder, BREAK_THROW, handles, failure) if call.block_given
|
|
55
|
+
catch(BREAK_THROW) { return [:ok, encode_ok(run(call, resolver, handles, yielder), handles), nil] }
|
|
56
|
+
[:break, "", nil] # : [Symbol, String, String?]
|
|
51
57
|
# StandardError is the boundary by intent: a Service method's
|
|
52
58
|
# application fault folds into a guest-rescuable fault, while a
|
|
53
59
|
# host-process failure (NoMemoryError, SignalException, a bare Exception)
|
|
54
60
|
# stays uncaught and traps the invocation rather than being masked as a
|
|
55
61
|
# rescuable fault.
|
|
56
62
|
rescue StandardError => e
|
|
57
|
-
[
|
|
63
|
+
[:fault, *caught_fault(e, failure)] # : [Symbol, String, String?]
|
|
58
64
|
ensure
|
|
59
|
-
yielder&.invalidate
|
|
65
|
+
yielder&.invalidate
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
class << self
|
|
@@ -66,7 +72,7 @@ module Kobako
|
|
|
66
72
|
arguments, carried_handle = decode_arguments(call.payload)
|
|
67
73
|
exposure = resolve_target(call.target, resolver, handles)
|
|
68
74
|
args, kwargs = resolve_call_args(arguments, handles, carried_handle)
|
|
69
|
-
|
|
75
|
+
invoke(exposure, call.method_name, args, kwargs, yielder)
|
|
70
76
|
end
|
|
71
77
|
|
|
72
78
|
# A codec fault here is a request that never became a call, restated
|
|
@@ -84,21 +90,19 @@ module Kobako
|
|
|
84
90
|
arguments.kwargs.transform_values { |v| resolve_arg(v, handles) }]
|
|
85
91
|
end
|
|
86
92
|
|
|
87
|
-
# The class prefix marks a Service's own exception
|
|
88
|
-
# it is the +<class>: <message>+ shape a Host App is
|
|
89
|
-
# secrets out of, so wearing it says the Service raised.
|
|
90
|
-
# refusals answer under their own wording instead of borrowing that
|
|
91
|
-
# shape.
|
|
93
|
+
# The class prefix marks a Service's own exception that its category
|
|
94
|
+
# does not name: it is the +<class>: <message>+ shape a Host App is
|
|
95
|
+
# told to keep secrets out of, so wearing it says the Service raised.
|
|
92
96
|
#
|
|
93
97
|
# The guest's own block failing is not the Service's to report at
|
|
94
98
|
# all, so the Yielder that raised it is asked first — it recognises
|
|
95
99
|
# its own by identity and words the failure the guest's way.
|
|
96
|
-
def caught_fault(error,
|
|
97
|
-
block_failure =
|
|
100
|
+
def caught_fault(error, failure)
|
|
101
|
+
block_failure = failure.fault_text(error)
|
|
98
102
|
return fault("block", block_failure) if block_failure
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
return fault(
|
|
104
|
+
bare = BARE_FAULTS.find { |klass, _| error.is_a?(klass) }
|
|
105
|
+
return fault(bare.last, error.message) if bare
|
|
102
106
|
|
|
103
107
|
fault("runtime", "#{error.class}: #{error.message}")
|
|
104
108
|
end
|
|
@@ -68,7 +68,7 @@ module Kobako
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def class_names(object, klass)
|
|
71
|
-
names =
|
|
71
|
+
names = authored_names(klass)
|
|
72
72
|
case object
|
|
73
73
|
when Struct, Data then names.concat(object.members)
|
|
74
74
|
end
|
|
@@ -78,15 +78,13 @@ module Kobako
|
|
|
78
78
|
def with_singleton(object, names)
|
|
79
79
|
return names if SINGLETON_METHODS.bind_call(object, false).empty?
|
|
80
80
|
|
|
81
|
-
names.union(
|
|
81
|
+
names.union(authored_names(SINGLETON_CLASS_OF.bind_call(object))).freeze
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
# The public methods +owner+ itself defines in source.
|
|
85
|
-
|
|
86
|
-
def authored(owner)
|
|
84
|
+
# The public methods +owner+ itself defines in Ruby source.
|
|
85
|
+
def authored_names(owner)
|
|
87
86
|
owner.public_instance_methods(false).select do |name|
|
|
88
|
-
|
|
89
|
-
file && !file.start_with?("<internal:")
|
|
87
|
+
Reflection.authored?(owner.instance_method(name))
|
|
90
88
|
end
|
|
91
89
|
end
|
|
92
90
|
end
|
|
@@ -3,17 +3,6 @@
|
|
|
3
3
|
module Kobako
|
|
4
4
|
module Transport
|
|
5
5
|
module Reflection # :nodoc:
|
|
6
|
-
# Modules whose instance methods are ambient Ruby reflection /
|
|
7
|
-
# metaprogramming surface (+send+, +public_send+, +instance_eval+,
|
|
8
|
-
# +method+, +tap+, +instance_variable_get+, ...) rather than Service
|
|
9
|
-
# behaviour. A guest-supplied method name resolving to one of these is
|
|
10
|
-
# rejected: only methods the bound object itself exposes as Service
|
|
11
|
-
# behaviour are reachable, and +public_send(:send, ...)+ would
|
|
12
|
-
# otherwise let a guest pivot through +send+ into the private
|
|
13
|
-
# +Kernel#eval+ / +#system+ surface (host RCE).
|
|
14
|
-
META_OWNERS = [BasicObject, Kernel, Object, Module, Class].freeze
|
|
15
|
-
private_constant :META_OWNERS
|
|
16
|
-
|
|
17
6
|
# Callable gadget types whose own public methods are reflection surface
|
|
18
7
|
# (+Proc#binding+ reaches +Binding#eval+, +Method#receiver+ / +#unbind+
|
|
19
8
|
# hand back the underlying object) rather than Service behaviour. Only
|
|
@@ -37,11 +26,33 @@ module Kobako
|
|
|
37
26
|
(exposure.exposes?(name) ? nil : "method #{name.inspect} is not exposed to the guest")
|
|
38
27
|
end
|
|
39
28
|
|
|
29
|
+
# Whether +method+ is written in Ruby rather than built into the
|
|
30
|
+
# platform or copied from the surface every object shares. A native
|
|
31
|
+
# method has no source, and a built-in one names an +<internal:>+ file,
|
|
32
|
+
# so a copy, alias or rebinding of either keeps that mark wherever it
|
|
33
|
+
# is reached from.
|
|
34
|
+
def self.authored?(method)
|
|
35
|
+
file, = method.source_location
|
|
36
|
+
!file.nil? && !file.start_with?("<internal:") && !ambient_copy?(method)
|
|
37
|
+
end
|
|
38
|
+
|
|
40
39
|
class << self
|
|
41
40
|
private
|
|
42
41
|
|
|
42
|
+
# Whether +method+ is a copy, alias or rebinding of a Ruby method
|
|
43
|
+
# +Class+ inherits, such as the +name!+ alias OpenStruct makes of a
|
|
44
|
+
# mixed-in +try+. The copy is owned by the class that made it, but
|
|
45
|
+
# keeps the original's name and source.
|
|
46
|
+
def ambient_copy?(method)
|
|
47
|
+
name = method.original_name
|
|
48
|
+
Class.ancestors.any? do |owner|
|
|
49
|
+
(owner.method_defined?(name, false) || owner.private_method_defined?(name, false)) &&
|
|
50
|
+
owner.instance_method(name).source_location == method.source_location
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
43
54
|
# Guard against ambient reflection methods. A public method whose
|
|
44
|
-
# owner
|
|
55
|
+
# owner +Class+ inherits or a GADGET_OWNERS module — or a singleton
|
|
45
56
|
# class, the owner of every class-level method (+File.popen+ /
|
|
46
57
|
# +Kernel.system+, unreachable via any fixed core-module list) — is
|
|
47
58
|
# rejected, except CALLABLE_ALLOW on a gadget target (a bound lambda
|
|
@@ -55,26 +66,71 @@ module Kobako
|
|
|
55
66
|
# +Delegator+) binds and calls the private method the guest passes as
|
|
56
67
|
# its first argument (+Kernel#system+). A dynamic +method_missing+
|
|
57
68
|
# Service is untouched — the guest reaches it by the virtual name,
|
|
58
|
-
# which resolves through the +
|
|
59
|
-
#
|
|
69
|
+
# which resolves through the +respond_to?+ opt-in, never by naming
|
|
70
|
+
# +method_missing+.
|
|
71
|
+
#
|
|
72
|
+
# A public method must also be authored, so a Service narrowing itself
|
|
73
|
+
# never reaches a built-in or ambient method by a copy, alias or
|
|
74
|
+
# rebinding an owner check cannot see; a non-public one is never
|
|
75
|
+
# reached through a dynamic opt-in either.
|
|
76
|
+
#
|
|
77
|
+
# A name with no method never reaches +instance_method+, which would
|
|
78
|
+
# keep a guest-chosen name as a Symbol for the life of the process.
|
|
60
79
|
def ambient_refusal(target, name)
|
|
61
|
-
return
|
|
80
|
+
return refused(name) if name == :method_missing
|
|
81
|
+
|
|
82
|
+
klass = lookup_class(target)
|
|
83
|
+
return defined_refusal(target, name, klass) if klass.public_method_defined?(name)
|
|
84
|
+
return "no public method #{name.inspect} on target" unless target.respond_to?(name)
|
|
85
|
+
return refused(name) if klass.method_defined?(name) || klass.private_method_defined?(name)
|
|
62
86
|
|
|
63
|
-
|
|
87
|
+
owner_refusal(target, name, klass)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def defined_refusal(target, name, klass)
|
|
91
|
+
method = klass.instance_method(name)
|
|
92
|
+
owner_refusal(target, name, method.owner) ||
|
|
93
|
+
(authored_for?(target, name, method, klass) ? nil : refused(name))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A bound callable keeps its callable names and a record its member
|
|
97
|
+
# readers, though the platform defines both.
|
|
98
|
+
def authored_for?(target, name, method, klass)
|
|
99
|
+
return true if Reflection.authored?(method)
|
|
100
|
+
return true if GADGET_OWNERS.include?(method.owner) && CALLABLE_ALLOW.include?(name)
|
|
101
|
+
|
|
102
|
+
(klass <= Struct || klass <= Data) && target.members.include?(name)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def refused(name)
|
|
106
|
+
"method #{name.inspect} is not a Service method"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def owner_refusal(target, name, owner)
|
|
64
110
|
return nil unless ambient_owner?(owner, target)
|
|
65
111
|
return nil if GADGET_OWNERS.include?(owner) && CALLABLE_ALLOW.include?(name)
|
|
66
112
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
refused(name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The class a method lookup on +target+ starts at. A plain object's
|
|
117
|
+
# singleton class is taken only when it already holds a public
|
|
118
|
+
# method, since asking for it otherwise would create one.
|
|
119
|
+
def lookup_class(target)
|
|
120
|
+
return target.singleton_class if target.is_a?(Module) || target.singleton_methods.any?
|
|
70
121
|
|
|
71
|
-
|
|
122
|
+
target.class
|
|
72
123
|
end
|
|
73
124
|
|
|
74
|
-
#
|
|
75
|
-
#
|
|
125
|
+
# Whatever +Class+ inherits is surface every object shares rather
|
|
126
|
+
# than Service behaviour: the reflection +BasicObject+ through +Class+
|
|
127
|
+
# define (+send+ pivots into +Kernel#eval+, a host RCE), and every
|
|
128
|
+
# module a library mixes into them, such as a +try+ dispatching by
|
|
129
|
+
# name or a +to_json+ dumping instance variables. A plain object's
|
|
130
|
+
# own singleton method (+def obj.x+) stays reachable, since only a
|
|
131
|
+
# Module target counts its singleton class as ambient.
|
|
76
132
|
def ambient_owner?(owner, target)
|
|
77
|
-
|
|
133
|
+
owner >= Class ||
|
|
78
134
|
GADGET_OWNERS.include?(owner) ||
|
|
79
135
|
(target.is_a?(Module) && owner.singleton_class?)
|
|
80
136
|
end
|
|
@@ -6,50 +6,34 @@ require_relative "../errors"
|
|
|
6
6
|
module Kobako
|
|
7
7
|
module Transport
|
|
8
8
|
class Yielder # :nodoc:
|
|
9
|
-
def initialize(guest_yielder, break_tag, handles)
|
|
9
|
+
def initialize(guest_yielder, break_tag, handles, failure)
|
|
10
10
|
@guest_yielder = guest_yielder
|
|
11
11
|
@break_tag = break_tag
|
|
12
12
|
@handles = handles
|
|
13
|
+
@failure = failure
|
|
13
14
|
@active = true
|
|
14
|
-
@raised = nil
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Identity rather than class: a Service that rescued the block's
|
|
18
|
-
# failure and raised its own has reported something else, and the
|
|
19
|
-
# guest must hear about that instead.
|
|
20
|
-
#
|
|
21
|
-
# The class travels with the message because not every block failure
|
|
22
|
-
# is an exception the guest holds — a block value the guest refused
|
|
23
|
-
# has a class to raise under and no object to continue.
|
|
24
|
-
def fault_text(error)
|
|
25
|
-
raised = @raised
|
|
26
|
-
return if raised.nil? || !error.equal?(raised)
|
|
27
|
-
|
|
28
|
-
"#{raised.klass}: #{raised.message}"
|
|
29
15
|
end
|
|
30
16
|
|
|
31
17
|
# The ok value is consumed by the host Service, so a Capability Handle
|
|
32
|
-
# in it is restored.
|
|
33
|
-
#
|
|
34
|
-
# churning a new one.
|
|
18
|
+
# in it is restored. A block exit stays in the guest, so the Service
|
|
19
|
+
# is only unwound.
|
|
35
20
|
def yield(*args)
|
|
36
21
|
raise LocalJumpError, "guest block invoked after host dispatch frame returned" unless @active
|
|
37
22
|
|
|
38
23
|
arm, body, klass = @guest_yielder.call(encode_args(args))
|
|
39
24
|
raise UnsupportedError, "the Sandbox runtime does not support yielding to a block" if arm == :unsupported
|
|
40
|
-
raise remember(BlockError.new(body, klass: klass)) if arm == :error
|
|
25
|
+
raise @failure.remember(BlockError.new(body, klass: klass)) if arm == :error
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
throw @break_tag, value if arm == :break
|
|
27
|
+
throw @break_tag if arm == :break
|
|
44
28
|
|
|
45
|
-
restore(
|
|
29
|
+
restore(*decode_body(body))
|
|
46
30
|
end
|
|
47
31
|
|
|
48
32
|
def to_proc
|
|
49
33
|
method(:yield).to_proc
|
|
50
34
|
end
|
|
51
35
|
|
|
52
|
-
def invalidate
|
|
36
|
+
def invalidate
|
|
53
37
|
@active = false
|
|
54
38
|
end
|
|
55
39
|
|
|
@@ -64,12 +48,6 @@ module Kobako
|
|
|
64
48
|
raise YieldValueError, "Service yielded a value the block cannot receive: #{e.message}"
|
|
65
49
|
end
|
|
66
50
|
|
|
67
|
-
# Only the newest is kept: a Service that rescued an earlier one and
|
|
68
|
-
# yielded again has already handled it.
|
|
69
|
-
def remember(error)
|
|
70
|
-
@raised = error
|
|
71
|
-
end
|
|
72
|
-
|
|
73
51
|
# The tracking bracket opens only around this decode: the guest
|
|
74
52
|
# re-entry may run nested dispatches whose own brackets would
|
|
75
53
|
# otherwise pollute the signal.
|
data/lib/kobako/transport.rb
CHANGED
data/lib/kobako/version.rb
CHANGED
data/lib/kobako.rb
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Kobako
|
|
2
|
+
module Catalog
|
|
3
|
+
class Mount < Data
|
|
4
|
+
type access = Kobako::Filesystem::access
|
|
5
|
+
|
|
6
|
+
type policy = ^(String path, access access) -> boolish
|
|
7
|
+
|
|
8
|
+
type tuple = [String, access, Kobako::Filesystem::mountable, policy?]
|
|
9
|
+
|
|
10
|
+
attr_reader at: String
|
|
11
|
+
attr_reader access: access
|
|
12
|
+
attr_reader filesystem: Kobako::Filesystem::mountable
|
|
13
|
+
attr_reader policy: policy?
|
|
14
|
+
|
|
15
|
+
def self.new: (at: untyped, access: untyped, filesystem: untyped, policy: policy?) -> Mount
|
|
16
|
+
|
|
17
|
+
def key: () -> String
|
|
18
|
+
|
|
19
|
+
def conflict: (Mount other) -> String?
|
|
20
|
+
|
|
21
|
+
def to_native: () -> tuple
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def nested?: (String other) -> bool
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Kobako
|
|
2
2
|
module Catalog
|
|
3
3
|
class Services
|
|
4
|
+
RESERVED_NAMESPACE: String
|
|
5
|
+
|
|
4
6
|
def initialize: () -> void
|
|
5
7
|
|
|
6
8
|
def bind: (Symbol | String path, untyped object) -> self
|
|
@@ -15,6 +17,8 @@ module Kobako
|
|
|
15
17
|
|
|
16
18
|
def validate_path!: (Symbol | String path) -> String
|
|
17
19
|
|
|
20
|
+
def reserved?: (String path) -> bool
|
|
21
|
+
|
|
18
22
|
def collision?: (String path) -> bool
|
|
19
23
|
end
|
|
20
24
|
end
|
data/sig/kobako/catalog.rbs
CHANGED
data/sig/kobako/codec/state.rbs
CHANGED
|
@@ -5,8 +5,14 @@ module Kobako
|
|
|
5
5
|
|
|
6
6
|
def self.current: () -> State
|
|
7
7
|
|
|
8
|
+
@carried_handle: bool
|
|
9
|
+
@value_budget: Integer?
|
|
10
|
+
|
|
11
|
+
attr_reader value_budget: Integer?
|
|
12
|
+
|
|
8
13
|
def track_handles: [T] { () -> T } -> [T, bool]
|
|
9
|
-
def record_handle
|
|
14
|
+
def record_handle: () -> void
|
|
15
|
+
def within_value_budget: [T] (Integer? budget) { () -> T } -> T
|
|
10
16
|
|
|
11
17
|
private
|
|
12
18
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Kobako
|
|
2
|
+
module Codec
|
|
3
|
+
module ValueBudget
|
|
4
|
+
SCALARS: Hash[Integer, Integer]
|
|
5
|
+
|
|
6
|
+
COUNTS: Hash[Integer, [Integer, Integer]]
|
|
7
|
+
|
|
8
|
+
LENGTHS: Hash[Integer, [Integer, Integer]]
|
|
9
|
+
|
|
10
|
+
UINT: Hash[Integer, String]
|
|
11
|
+
|
|
12
|
+
def self.assert_within!: (String bytes, Integer? budget) -> void
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def self.step: (String bytes, Integer pos) -> [Integer?, Integer]
|
|
17
|
+
|
|
18
|
+
def self.step_framed:(String bytes, Integer pos, Integer marker) -> [Integer?, Integer]
|
|
19
|
+
|
|
20
|
+
def self.uint: (String bytes, Integer pos, Integer width) -> Integer
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/sig/kobako/codec.rbs
CHANGED
|
@@ -14,6 +14,8 @@ module Kobako
|
|
|
14
14
|
|
|
15
15
|
def self.track_handles: [T] { () -> T } -> [T, bool]
|
|
16
16
|
|
|
17
|
+
def self.within_value_budget: [T] (Integer? budget) { () -> T } -> T
|
|
18
|
+
|
|
17
19
|
# The Handle allocator/resolver a wire walk works against: alloc
|
|
18
20
|
# registers a non-wire-representable object and returns its Handle,
|
|
19
21
|
# fetch resolves a wire Handle id back to the live object. The
|
data/sig/kobako/context.rbs
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
module Kobako
|
|
2
2
|
class Context
|
|
3
|
+
type preamble = [Array[String], Array[Kobako::Catalog::Snippets::tuple], Array[Kobako::Catalog::Flags::tuple]]
|
|
4
|
+
|
|
5
|
+
@mounts: Hash[String, Kobako::Catalog::Mount]?
|
|
6
|
+
|
|
3
7
|
def initialize: (
|
|
4
8
|
runtime: Kobako::Runtime,
|
|
5
9
|
services: Kobako::Catalog::Services,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
extensions: Kobako::Catalog::Extensions,
|
|
11
|
+
grants: Kobako::Catalog::Grants,
|
|
12
|
+
preamble: preamble
|
|
9
13
|
) -> void
|
|
10
14
|
|
|
11
15
|
def bind: (Symbol | String path, untyped object) -> self
|
|
12
16
|
|
|
17
|
+
def mount: (String at, Kobako::Filesystem::mountable filesystem, ?access: Kobako::Filesystem::access) ?{ (String path, Kobako::Filesystem::access access) -> boolish } -> self
|
|
18
|
+
|
|
13
19
|
def lookup: (Symbol | String path) -> Kobako::Transport::Exposure
|
|
14
20
|
|
|
15
21
|
def eval: (String code) ?{ (Kobako::Context) -> void } -> Kobako::Execution
|
|
@@ -18,18 +24,20 @@ module Kobako
|
|
|
18
24
|
|
|
19
25
|
private
|
|
20
26
|
|
|
27
|
+
def setup: () -> Kobako::Runtime::setup
|
|
28
|
+
|
|
21
29
|
def collect_overrides: () { (Kobako::Context) -> void } -> void
|
|
22
30
|
|
|
23
31
|
def dispatch_handler: () -> Kobako::Transport::dispatch_proc
|
|
24
32
|
|
|
25
|
-
def populate_observability
|
|
33
|
+
def populate_observability:(Kobako::Snapshot snapshot) -> void
|
|
26
34
|
|
|
27
35
|
def with_verb: (Kobako::TrapError err, Symbol verb) -> Kobako::TrapError
|
|
28
36
|
|
|
29
37
|
def build_execution: (untyped value, failed: bool) -> Kobako::Execution
|
|
30
38
|
|
|
31
|
-
def settle_outcome: (Kobako::
|
|
39
|
+
def settle_outcome: (Kobako::Snapshot snapshot, Symbol verb, Symbol? entrypoint) -> Kobako::Execution
|
|
32
40
|
|
|
33
|
-
def invoke!: (Symbol verb, ?entrypoint: Symbol?) { () -> Kobako::
|
|
41
|
+
def invoke!: (Symbol verb, ?entrypoint: Symbol?) { () -> Kobako::Snapshot } -> Kobako::Execution
|
|
34
42
|
end
|
|
35
43
|
end
|
data/sig/kobako/errors.rbs
CHANGED
|
@@ -2,14 +2,14 @@ module Kobako
|
|
|
2
2
|
class Error < StandardError
|
|
3
3
|
end
|
|
4
4
|
|
|
5
|
-
module
|
|
5
|
+
module Recorded
|
|
6
6
|
attr_reader execution: Kobako::Execution?
|
|
7
7
|
|
|
8
8
|
def with_execution: (Kobako::Execution execution) -> self
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
class TrapError < Error
|
|
12
|
-
include
|
|
12
|
+
include Recorded
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
class TimeoutError < TrapError
|
|
@@ -39,12 +39,12 @@ module Kobako
|
|
|
39
39
|
|
|
40
40
|
class SandboxError < Error
|
|
41
41
|
include Diagnosable
|
|
42
|
-
include
|
|
42
|
+
include Recorded
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
class ServiceError < Error
|
|
46
46
|
include Diagnosable
|
|
47
|
-
include
|
|
47
|
+
include Recorded
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
class NoServiceError < ServiceError
|