std/sandbox/lib
std/sandbox/src/lib.trb
Loads .trb files as sandboxed receiver closures - the mechanism behind project.trb and configuration scripts
(see "Receiver Scripts and the Sandbox" in CONCEPT.md, and docs/design/SCRIPTS.md).
A script runs in the VM, always: the step and time limits and the panic that does not end the caller are properties
of an interpreter (docs/design/SCRIPTS.md section 1). A program torb run runs in the VM loads one, from a path it
was compiled with or from any other, and so does a native binary torb build --embed-vm built, which embeds the VM.
A native binary torb build compiled loads one through the front end and the VM it embeds (slice 8): the script
is checked against Value and run in that VM, and the value crosses as text in both directions, through the
derived Encode and Decode of Value, every field included - so a receiver of such a program is
Encode & Decode, which a type of plain settings is by derivation.
type SandboxError
type SandboxError with Show, Error
What went wrong loading a script: a syntax or type error against the receiver type, or a capability it does not
have. line is 0 when the problem is not about one place in the script (e.g. the file could not be read).
type SandboxCapabilities
type SandboxCapabilities
What a script may do, granted at the call site of Sandbox.load - never in the script or its project file
(a script that could grant itself capabilities would not be a sandbox). Left at the defaults, a script has no
IO, no network, no clock, no environment and no foreign functions.
field moduleNames
protected var moduleNames: List<String> = []
The modules SandboxCapabilities.modules granted, which the script's imports are held against.
field readOnlyRoots
protected var readOnlyRoots: List<String> = []
The roots files granted for reading only, in the order they were granted.
field readWriteRoots
protected var readWriteRoots: List<String> = []
The roots files granted for reading and writing, in the order they were granted.
field variablePatterns
protected var variablePatterns: List<String> = []
The environment variable name patterns environment granted.
field connectPatterns
protected var connectPatterns: List<String> = []
The host:port patterns network granted to resolve and connect to, written as connect lines of the grant.
field listenPatterns
protected var listenPatterns: List<String> = []
The address:port patterns network granted to listen on and bind, written as listen lines of the grant.
field stepLimit
protected var stepLimit: Int = 1_000_000
The most steps the script may run before it is stopped; limits sets it.
field memoryLimit
protected var memoryLimit: Int = 64_000_000
In bytes, which is how the grant carries it.
field timeLimit
protected var timeLimit: Int = 2000
Milliseconds: a Duration is no constant a default could be.
field workerLimit
protected var workerLimit: Int = 1
The most workers the script's tasks may run on at once; limits sets it.
fn modules
var fn modules(...names: String)
Additional parts of the standard library the script may use, e.g. "std/text".
fn files
var fn files(readOnly: String = "", readWrite: String = "")
File system roots the script may reach. A side that is left out stays closed.
fn network
var fn network(connect: String = "", listen: String = "")
Network patterns the script may reach, each host:port (docs/design/NETWORK.md section 10): connect what it may
resolve and connect to - "api.example.test:443", "*.example.test:*", "[::1]:8080" - and listen the
address it may listen on or bind, "127.0.0.1:8080". The port is a number or *, the host a name, *. and a
name for every name below it, * for every host, or an address. A side that is left out stays closed, and
calling it again adds patterns. The script also needs modules "std/network".
fn environment
var fn environment(...patterns: String)
Environment variable name patterns the script may read, e.g. "APP_*".
fn limits
var fn limits(steps: Int = 1_000_000, memory: ByteSize = 64.megabytes(), time: Duration = 2.seconds(), workers: Int = 1)
Guards against runaway scripts (loop {}). workers is the most workers the script's tasks may run on at once
(docs/design/CONCURRENCY.md section 3): a script that could fan out over the host's cores would be a denial of
service its grant does not show. The VM runs the tasks of a script on the thread of its sandbox, so a script never
uses more than one whatever the number says.
type Script
type Script<Value>
A loaded script, ready to run against a Value of the caller's own. It is not a closure, because the body of the
script runs inside the sandbox and can still fail there: a step, memory or time limit, or a panic - the only panic
in the language that is recoverable, because the VM is interpreting it and the script has a heap of its own.
fn apply
fn apply(var value: Value): Result<Void, SandboxError>
Runs the body of the script against value, configuring it in place.
type Sandbox
native type Sandbox
Sandboxes and type checks .trb files against a receiver type.
fn load
static fn load<Value>(path: String, capabilities: (var self: SandboxCapabilities) => Void = {}): Result<Script<Value>, SandboxError>
Type checks path against Value and returns it as a Script, ready to run against a Value of the caller's
own. What is wrong with the file (syntax, types, a module the script may not import) is reported here; what goes
wrong while it runs is reported by Script.apply instead. The trailing block grants capabilities beyond the
defaults.
A path the program was compiled with - a literal of a Sandbox.load - names a script the program carries, which
torb check checked with it. Any other path is read, checked against Value and lowered when the program asks for
it, relative to the working directory (docs/design/SCRIPTS.md slice 7); Value is then a type without type
arguments.
Examples
type ServerConfig {
var port: Int = 8080
}
const loaded = Sandbox.load<ServerConfig>("./config.trb") {
files readOnly: "./config"
limits steps: 1_000_000, memory: 16.megabytes()
}
print loaded.isOk()
fn loadText
static fn loadText<Value>(text: String, name: String, capabilities: (var self: SandboxCapabilities) => Void = {}): Result<Script<Value>, SandboxError>
Sandbox.load for a script the program holds as text, which it fetched through a Storage or built itself, and
not as a file: text is type checked against Value and returned as a Script, with the same checks, grants
and limits as load and the same errors. name is what the script is called in a SandboxError, and
SandboxError.line counts the lines of text. It is no path and is read against nothing; the same text loaded
twice is the same script, whatever it is called. As for a path only known while the program runs, Value is a
type without type arguments, and the program writes nothing to a file to load the script.
std/sandbox knows no Storage: the program fetches the text and hands it over, so what the script may reach
is what the caller grants and not what the program's storage reaches.
Examples
type ServerConfig {
var port: Int = 8080
}
// the text came from a `Storage.readText`, a request or a database
const text = "port = 9100"
var config = ServerConfig()
if const Ok(script) = Sandbox.loadText<ServerConfig>(text, "server.trb") {
const _ = script.apply config
}
print config.port