Class: SecurityBox::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/security_box/configuration.rb

Overview

Immutable sandbox configuration. Use .build to create and #with to derive.

Defined Under Namespace

Classes: Builder

Constant Summary collapse

IMAGE_ENV_VAR =
"SECURITY_BOX_IMAGE"
IMAGE_ASSET_RELATIVE =
"assets/security_box.wasm"
IMAGE_DEV_RELATIVE =
"../../build/security_box.wasm"
DEFAULTS =
{
  image_path: nil, # resolved dynamically (project's build/security_box.wasm)
  fuel: 10_000_000_000,
  fuel_ms: nil, # rate-based fuel ergonomics (see FUEL_PER_MS); nil = use :fuel
  timeout_ms: 2_000,
  memory_size: 512 * 1024 * 1024,
  stdout_limit: 1 << 20,
  stderr_limit: 1 << 16,
  epoch_interval_ms: 25,
  env: {}.freeze,
  mounts: [].freeze,
  rpcs: {}.freeze
}.freeze
FUEL_PER_MS =

Fuel-per-ms conversion for #fuel_ms, from the stage-3 calibration table (docs/plan/stages/stage_3.md): compute workloads burn 3.2e9–8.4e9 fuel/s; 4e9/s is the conservative floor. The guest boot baseline (~9.1e8 fuel) is added on top so the budget covers boot even for trivial evals.

4_000_000
BOOT_FUEL_ALLOWANCE =
1_000_000_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(image_path: nil, fuel:, fuel_ms:, timeout_ms:, memory_size:, stdout_limit:, stderr_limit:, epoch_interval_ms:, env:, mounts:, rpcs:) ⇒ Configuration

Returns a new instance of Configuration.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/security_box/configuration.rb', line 42

def initialize(image_path: nil, fuel:, fuel_ms:, timeout_ms:, memory_size:,
               stdout_limit:, stderr_limit:, epoch_interval_ms:, env:, mounts:,
               rpcs:)
  @image_path = image_path || default_image_path
  @fuel = Integer(fuel)
  @fuel_ms = fuel_ms.nil? ? nil : Integer(fuel_ms)
  @timeout_ms = Integer(timeout_ms)
  @memory_size = Integer(memory_size)
  @stdout_limit = Integer(stdout_limit)
  @stderr_limit = Integer(stderr_limit)
  @epoch_interval_ms = Integer(epoch_interval_ms)
  @env = env.freeze
  @mounts = Mounts.normalize(mounts)
  @rpcs = Rpcs.normalize(rpcs)
  freeze
end

Instance Attribute Details

#env ⇒ Object (readonly)

Returns the value of attribute env.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def env
  @env
end

#epoch_interval_ms ⇒ Object (readonly)

Returns the value of attribute epoch_interval_ms.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def epoch_interval_ms
  @epoch_interval_ms
end

#fuel ⇒ Object (readonly)

Returns the value of attribute fuel.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def fuel
  @fuel
end

#fuel_ms ⇒ Object (readonly)

Returns the value of attribute fuel_ms.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def fuel_ms
  @fuel_ms
end

#image_path ⇒ Object (readonly)

Returns the value of attribute image_path.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def image_path
  @image_path
end

#memory_size ⇒ Object (readonly)

Returns the value of attribute memory_size.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def memory_size
  @memory_size
end

#mounts ⇒ Object (readonly)

Returns the value of attribute mounts.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def mounts
  @mounts
end

#rpcs ⇒ Object (readonly)

Returns the value of attribute rpcs.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def rpcs
  @rpcs
end

#stderr_limit ⇒ Object (readonly)

Returns the value of attribute stderr_limit.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def stderr_limit
  @stderr_limit
end

#stdout_limit ⇒ Object (readonly)

Returns the value of attribute stdout_limit.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def stdout_limit
  @stdout_limit
end

#timeout_ms ⇒ Object (readonly)

Returns the value of attribute timeout_ms.



34
35
36
# File 'lib/security_box/configuration.rb', line 34

def timeout_ms
  @timeout_ms
end

Class Method Details

.build(**options) ⇒ Object



38
39
40
# File 'lib/security_box/configuration.rb', line 38

def self.build(**options)
  new(**DEFAULTS.merge(options)).freeze
end

Instance Method Details

#canonical ⇒ Object



115
116
117
# File 'lib/security_box/configuration.rb', line 115

def canonical
  to_h.except(:rpcs).merge(env: @env.sort.to_h)
end

#effective_fuel ⇒ Object

The fuel budget actually applied to each evaluation. When :fuel_ms is set it takes precedence: an approximate millisecond-based budget (fuel_ms × FUEL_PER_MS + boot allowance) instead of a raw fuel count. The epoch timeout remains the mandatory wall-clock backstop either way.



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

def effective_fuel
  return fuel unless fuel_ms

  fuel_ms * FUEL_PER_MS + BOOT_FUEL_ALLOWANCE
end

#fingerprint ⇒ Object

Stable identity of the configuration values (SHA-256 of the normalized hash). Two configurations with equal settings — regardless of how they were built — share the same fingerprint; any #with change produces a different one. Used to key profiles and, later, cached artifacts.

RPC handlers are deliberately excluded: they are host-side callables (Procs) with no stable serialized identity, and including them would make the fingerprint depend on object addresses.



111
112
113
# File 'lib/security_box/configuration.rb', line 111

def fingerprint
  Digest::SHA256.hexdigest(JSON.generate(canonical))
end

#to_h ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/security_box/configuration.rb', line 87

def to_h
  {
    image_path: @image_path,
    fuel: @fuel,
    fuel_ms: @fuel_ms,
    timeout_ms: @timeout_ms,
    memory_size: @memory_size,
    stdout_limit: @stdout_limit,
    stderr_limit: @stderr_limit,
    epoch_interval_ms: @epoch_interval_ms,
    env: @env,
    mounts: @mounts,
    rpcs: @rpcs
  }
end

#with(**changes) ⇒ Object

Derives a copy with changes applied (never mutates the receiver). Passing both :fuel and a non-nil :fuel_ms is ambiguous and rejected; overriding :fuel on a configuration that uses :fuel_ms is rejected too (pass fuel_ms: nil first to switch to a raw fuel budget).



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/security_box/configuration.rb', line 73

def with(**changes)
  if changes.key?(:fuel_ms)
    if changes[:fuel_ms] && changes.key?(:fuel)
      raise InvalidConfiguration,
            "fuel and fuel_ms are mutually exclusive overrides"
    end
  elsif changes.key?(:fuel) && @fuel_ms
    raise InvalidConfiguration,
          "configuration uses fuel_ms (#{fuel_ms}); override fuel_ms or clear it with fuel_ms: nil instead of fuel"
  end

  self.class.build(**to_h.merge(changes))
end