Class: Hermetic::Request
- Inherits:
-
Object
- Object
- Hermetic::Request
- Defined in:
- lib/hermetic/request.rb
Overview
The normalized form of one run call. Backends never see raw kwargs —
they see a Request whose invariants already hold: shell-vs-argv resolved,
workdir-relative files only, clean default-deny env, integer host-enforced
timeout, limits merged over box defaults.
Constant Summary collapse
- SHELL =
[ "/bin/sh", "-c" ].freeze
Instance Attribute Summary collapse
-
#env_allowlist ⇒ Object
readonly
Returns the value of attribute env_allowlist.
-
#files ⇒ Object
readonly
Returns the value of attribute files.
-
#limits ⇒ Object
readonly
Returns the value of attribute limits.
-
#network ⇒ Object
readonly
Returns the value of attribute network.
-
#stdin ⇒ Object
readonly
Returns the value of attribute stdin.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
Class Method Summary collapse
Instance Method Summary collapse
-
#clean_env ⇒ Object
The guest environment: host env is default-deny.
- #credential_names ⇒ Object
-
#exec_argv ⇒ Object
Full argv to exec.
-
#exec_string ⇒ Object
The command as a single shell string, for hosted APIs that take one.
-
#idempotency_key ⇒ Object
Deterministic across retries of the same logical run — the executor-side dedup key for the remote in-doubt window (spec §7 risk 2).
-
#initialize(command, files: {}, stdin: nil, env: {}, network: :none, timeout: 30, limits: nil, default_limits: Limits.new, credentials: nil, env_allowlist: [], host_env: ENV) ⇒ Request
constructor
A new instance of Request.
-
#inspect ⇒ Object
(also: #to_s)
Values are redacted; keys/command are the model-visible surface anyway.
- #shell? ⇒ Boolean
Constructor Details
#initialize(command, files: {}, stdin: nil, env: {}, network: :none, timeout: 30, limits: nil, default_limits: Limits.new, credentials: nil, env_allowlist: [], host_env: ENV) ⇒ Request
Returns a new instance of Request.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/hermetic/request.rb', line 17 def initialize(command, files: {}, stdin: nil, env: {}, network: :none, timeout: 30, limits: nil, default_limits: Limits.new, credentials: nil, env_allowlist: [], host_env: ENV) @command = normalize_command(command) @files = normalize_files(files) @stdin = stdin&.to_s @explicit_env = normalize_env(env) @network = NetworkPolicy.from(network) @timeout = normalize_timeout(timeout) @limits = default_limits.merge(limits) @credentials = Credentials.build(credentials) @env_allowlist = Array(env_allowlist).map(&:to_s).freeze @host_env = host_env end |
Instance Attribute Details
#env_allowlist ⇒ Object (readonly)
Returns the value of attribute env_allowlist.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def env_allowlist @env_allowlist end |
#files ⇒ Object (readonly)
Returns the value of attribute files.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def files @files end |
#limits ⇒ Object (readonly)
Returns the value of attribute limits.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def limits @limits end |
#network ⇒ Object (readonly)
Returns the value of attribute network.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def network @network end |
#stdin ⇒ Object (readonly)
Returns the value of attribute stdin.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def stdin @stdin end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
13 14 15 |
# File 'lib/hermetic/request.rb', line 13 def timeout @timeout end |
Class Method Details
.build(command, **kwargs) ⇒ Object
15 |
# File 'lib/hermetic/request.rb', line 15 def self.build(command, **kwargs) = new(command, **kwargs) |
Instance Method Details
#clean_env ⇒ Object
The guest environment: host env is default-deny. Precedence (low→high): allowlisted host vars < resolved credentials < explicit env:. Credentials resolve lazily, per call — never cached on the request.
44 45 46 47 48 49 |
# File 'lib/hermetic/request.rb', line 44 def clean_env allowlisted = @env_allowlist.each_with_object({}) do |name, acc| acc[name] = @host_env[name].to_s if @host_env[name] end allowlisted.merge(@credentials.resolve).merge(@explicit_env) end |
#credential_names ⇒ Object
51 |
# File 'lib/hermetic/request.rb', line 51 def credential_names = @credentials.names |
#exec_argv ⇒ Object
Full argv to exec. A String command runs under /bin/sh -c (matches Silas); an Array execs directly — no shell, no injection surface.
36 |
# File 'lib/hermetic/request.rb', line 36 def exec_argv = shell? ? SHELL + [ @command ] : @command |
#exec_string ⇒ Object
The command as a single shell string, for hosted APIs that take one.
39 |
# File 'lib/hermetic/request.rb', line 39 def exec_string = shell? ? @command : Shellwords.join(@command) |
#idempotency_key ⇒ Object
Deterministic across retries of the same logical run — the executor-side dedup key for the remote in-doubt window (spec §7 risk 2). Credential and host-env VALUES are excluded: short-lived tokens must not change the key (and must never be digested at all).
57 58 59 |
# File 'lib/hermetic/request.rb', line 57 def idempotency_key @idempotency_key ||= Digest::SHA256.hexdigest(JSON.generate(canonical))[0, 32] end |
#inspect ⇒ Object Also known as: to_s
Values are redacted; keys/command are the model-visible surface anyway.
62 63 64 65 66 |
# File 'lib/hermetic/request.rb', line 62 def inspect "#<Hermetic::Request cmd=#{exec_argv.inspect} files=#{files.keys.inspect} " \ "env=#{(@explicit_env.keys + credential_names).sort.inspect} " \ "network=#{@network} timeout=#{@timeout} [values redacted]>" end |
#shell? ⇒ Boolean
32 |
# File 'lib/hermetic/request.rb', line 32 def shell? = @command.is_a?(String) |