Documentation / guides
Profiles and Access Dimensions
Define filesystem, network, command, home, and isolation authority in an immutable sandbox Profile.
sandbox.Profile is the normalized authority description. Construct it with sandbox.NewProfile(ProfileConfig), not by trying to fill internal fields. Construction canonicalizes roots, rejects contradictory additional roots, validates every enum, derives required guarantees, and computes a deterministic fingerprint. The returned profile is immutable.
The dimensions
| Field | Values | Meaning |
|---|---|---|
WorkspaceRead, WorkspaceWrite | Deny, Gated, Allow | Authority under WorkspaceRoot. Read and write are independent axes. |
HostRead, HostWrite | Deny, Gated, Allow | The rest of the host filesystem, subject to platform compilation. |
Network | Deny, Gated, Allow | Egress authority. Target-scoped grants can narrow a gated request. |
Command | Deny, Gated, Allow | Whether command admission is refused, requires a grant, or can proceed. |
AdditionalRoots | []RootAccess | Extra canonical roots with their own read/write pair. |
Home | IsolatedHome, RealHome | The child HOME. Isolated homes are owned below the ExecutorSet scratch child. |
Isolation | Sandboxed, Unconfined | Whether the OS boundary is required. Unconfined needs an explicit acknowledgement. |
The access enum is deliberately ordered as Deny < Gated < Allow. That ordering is used by restriction to take the narrower value. It is not a claim that Gated itself is an operating-system restriction. It is an application admission state.
Construct a profile
package example
import (
"fmt"
"github.com/looprig/sandbox"
)
func profileFor(workspace, cache string) (*sandbox.Profile, error) {
profile, err := sandbox.NewProfile(sandbox.ProfileConfig{
WorkspaceRoot: workspace,
WorkspaceRead: sandbox.Allow,
WorkspaceWrite: sandbox.Gated,
HostRead: sandbox.Deny,
HostWrite: sandbox.Deny,
Network: sandbox.Gated,
Command: sandbox.Gated,
Home: sandbox.IsolatedHome,
Isolation: sandbox.Sandboxed,
AdditionalRoots: []sandbox.RootAccess{
{Path: cache, Read: sandbox.Allow, Write: sandbox.Deny},
},
})
if err != nil {
return nil, fmt.Errorf("profile: %w", err)
}
read, err := profile.AccessFor("filesystem.read", workspace)
if err != nil {
return nil, err
}
fmt.Println("workspace read authority", read == uint8(sandbox.Allow))
return profile, nil
}
AccessFor accepts the stable access vocabulary command.execute, network, filesystem.read, and filesystem.write. Filesystem scopes are absolute paths, tree:<configured-root> for a configured recursive root, or host:* for the host axis. A tree scope that is not a configured root returns Deny; malformed scopes return ErrInvalidProfile.
Unconfined is explicit
An Unconfined profile is not a fallback when a host lacks a backend. It requires AckUnconfined: true, Allow on filesystem and network axes, and Allow for every additional root. The null backend is selected only for that acknowledged shape. A Sandboxed profile remains a required OS boundary and fails with ErrSandboxUnavailable or another typed setup error if the host cannot compile it.
The Home choice is independent from the authority axes. RealHome gives the child the real user home, while IsolatedHome gives each Executor key a separate directory and adds it to the compiled writable policy. See filesystem and environment for the practical implications.