Module: SecurityBox

Defined in:
lib/security_box.rb,
lib/security_box/pool.rb,
lib/security_box/errors.rb,
lib/security_box/result.rb,
lib/security_box/runtime.rb,
lib/security_box/sandbox.rb,
lib/security_box/version.rb,
lib/security_box/envelope.rb,
lib/security_box/eval_run.rb,
lib/security_box/registry.rb,
lib/security_box/guest_rpc.rb,
lib/security_box/ractor_pool.rb,
lib/security_box/module_cache.rb,
lib/security_box/configuration.rb

Defined Under Namespace

Modules: Envelope, EvalRun, GuestRpc, ModuleCache, Mounts, Registry, Rpcs, Runtime Classes: Configuration, Error, ImageMissing, InvalidConfiguration, Pool, PoolClosed, RactorPool, Result, Sandbox

Constant Summary collapse

VERSION =
"0.6.0"

Class Method Summary collapse

Class Method Details

.eval(code, **overrides) ⇒ Object

Convenience shortcut:

SecurityBox.eval("1 + 1")            # => Result
SecurityBox.eval("1", fuel: 100)     # per-call overrides


21
22
23
# File 'lib/security_box.rb', line 21

def self.eval(code, **overrides)
  Sandbox.new.eval(code, **overrides)
end

.pool(profile = nil, size: 4, **overrides) ⇒ Object

Builds a Pool of :oneshot sandboxes (bounded concurrency on threads). NOTE: evals serialize on the GVL — see RactorPool for parallelism. SecurityBox.pool(:lean, size: 4).eval("1 + 1")



55
56
57
# File 'lib/security_box.rb', line 55

def self.pool(profile = nil, size: 4, **overrides)
  Pool.new(profile, size: size, **overrides)
end

.ractor_pool(profile = nil, size: 4, **overrides) ⇒ Object

Builds a RactorPool (real parallelism: worker Ractors sharing one Engine+Module). Costs one module deserialize at creation (~0.5s via the disk cache); each eval still pays the guest boot (~240ms). SecurityBox.ractor_pool(:lean, size: 4).eval("1 + 1")



63
64
65
# File 'lib/security_box.rb', line 63

def self.ractor_pool(profile = nil, size: 4, **overrides)
  RactorPool.new(profile, size: size, **overrides)
end

.register(name, from: nil, &block) ⇒ Object

Registers a named, reusable profile (see Registry). SecurityBox.register(:lean, from: :default) { |c| c.fuel 5_000_000 }



27
28
29
# File 'lib/security_box.rb', line 27

def self.register(name, from: nil, &block)
  Registry.register(name, from: from, &block)
end

.spawn(profile = nil, **overrides) ⇒ Object

Builds a Sandbox from a registered profile, a Configuration, or the defaults:

SecurityBox.spawn(:lean)                  # named profile
SecurityBox.spawn(:lean, fuel: 1_000)     # profile + per-call overrides
SecurityBox.spawn(my_config)              # explicit configuration
SecurityBox.spawn                         # default configuration


37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/security_box.rb', line 37

def self.spawn(profile = nil, **overrides)
  config = case profile
           when nil then Configuration.build(**overrides)
           when Symbol, String
             resolved = Registry.resolve(profile)
             overrides.empty? ? resolved : resolved.with(**overrides)
           when Configuration
             overrides.empty? ? profile : profile.with(**overrides)
           else
             raise ArgumentError,
                   "profile must be a registered name or a Configuration, got #{profile.class}"
           end
  Sandbox.new(config)
end

.warmup(**options) ⇒ Object

Prepares the shared runtime artifacts (Engine + compiled Module) ahead of time so the first #eval skips the cold module compilation (~15s per process). #eval warms these caches lazily on first use, so calling warmup is a pure optimization: behavior and results are identical without it. Accepts the same options as Configuration.build (only image_path and epoch_interval_ms affect what gets warmed). Returns the warmed Sandbox; safe to call multiple times.



74
75
76
# File 'lib/security_box.rb', line 74

def self.warmup(**options)
  Sandbox.new(Configuration.build(**options))
end