Documentation
¶
Overview ¶
Package nono provides Go bindings for the nono capability-based security sandbox.
nono uses Linux Landlock and macOS Seatbelt to restrict what resources a process can access. The three primary types are:
- CapabilitySet — builds the set of allowed filesystem paths and network modes.
- QueryContext — checks whether a given path or network operation would be permitted.
- SandboxState — serialises and deserialises a CapabilitySet as JSON.
Typical usage:
caps := nono.New()
defer caps.Close()
if err := caps.AllowPath("/data", nono.AccessRead); err != nil {
log.Fatal(err)
}
if err := caps.SetNetworkMode(nono.NetworkBlocked); err != nil {
log.Fatal(err)
}
if err := nono.Apply(caps); err != nil { // irreversible
log.Fatal(err)
}
Example (ErrorHandling) ¶
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/nolabs-ai/nono-go"
)
func main() {
dir, err := os.MkdirTemp("", "nono-example-*")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
caps := nono.New()
defer caps.Close()
err = caps.AllowPath(filepath.Join(dir, "missing"), nono.AccessRead)
fmt.Println(errors.Is(err, nono.ErrPathNotFound))
}
Output: true
Index ¶
- Variables
- func Apply(caps *CapabilitySet) error
- func IsSupported() bool
- func Version() string
- type AccessMode
- type CapabilitySet
- func (cs *CapabilitySet) AddPlatformRule(rule string) error
- func (cs *CapabilitySet) AllowCommand(cmd string) error
- func (cs *CapabilitySet) AllowFile(path string, mode AccessMode) error
- func (cs *CapabilitySet) AllowPath(path string, mode AccessMode) error
- func (cs *CapabilitySet) BlockCommand(cmd string) error
- func (cs *CapabilitySet) Close() error
- func (cs *CapabilitySet) Deduplicate() error
- func (cs *CapabilitySet) FSCapabilities() []FSCapability
- func (cs *CapabilitySet) IsNetworkBlocked() bool
- func (cs *CapabilitySet) NetworkMode() NetworkMode
- func (cs *CapabilitySet) PathCovered(path string) (bool, error)
- func (cs *CapabilitySet) ProxyPort() uint16
- func (cs *CapabilitySet) SetNetworkBlocked(blocked bool) errordeprecated
- func (cs *CapabilitySet) SetNetworkMode(mode NetworkMode) error
- func (cs *CapabilitySet) SetProxyPort(port uint16) error
- func (cs *CapabilitySet) Summary() string
- type CapabilitySourceTag
- type Error
- type ErrorCode
- type FSCapability
- type NetworkMode
- type PlatformInfo
- type QueryContext
- type QueryReason
- type QueryResult
- type QueryStatus
- type SandboxState
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrPathNotFound error = &Error{code: ErrCodePathNotFound, message: "path not found"} ErrExpectedDirectory error = &Error{code: ErrCodeExpectedDirectory, message: "expected directory"} ErrExpectedFile error = &Error{code: ErrCodeExpectedFile, message: "expected file"} ErrPathCanonicalization error = &Error{code: ErrCodePathCanonicalization, message: "path canonicalization failed"} ErrNoCapabilities error = &Error{code: ErrCodeNoCapabilities, message: "no capabilities"} ErrSandboxInit error = &Error{code: ErrCodeSandboxInit, message: "sandbox init failed"} ErrUnsupportedPlatform error = &Error{code: ErrCodeUnsupportedPlatform, message: "unsupported platform"} ErrBlockedCommand error = &Error{code: ErrCodeBlockedCommand, message: "blocked command"} ErrConfigParse error = &Error{code: ErrCodeConfigParse, message: "config parse error"} ErrProfileParse error = &Error{code: ErrCodeProfileParse, message: "profile parse error"} ErrIO error = &Error{code: ErrCodeIO, message: "i/o error"} ErrInvalidArg error = &Error{code: ErrCodeInvalidArg, message: "invalid argument"} ErrTrustVerification error = &Error{code: ErrCodeTrustVerification, message: "trust verification failed"} ErrUnknown error = &Error{code: ErrCodeUnknown, message: "unknown error"} )
Sentinel errors for use with errors.Is. Example:
if errors.Is(err, nono.ErrPathNotFound) { ... }
The Error.Is method matches by code only, so the message from the C library does not need to match the sentinel's message. Both fields of Error are unexported, so callers cannot mutate sentinel values through an errors.As pointer — the error codes are stable for the lifetime of the process.
Functions ¶
func Apply ¶
func Apply(caps *CapabilitySet) error
Apply activates the sandbox for the current process using the given capabilities. This is irreversible — once applied, the process and all children can only access resources allowed by caps.
On success caps is closed: the capability set is consumed by the kernel and any subsequent mutations would have no effect on the active sandbox.
func IsSupported ¶
func IsSupported() bool
IsSupported reports whether sandboxing is available on the current platform.
Types ¶
type AccessMode ¶
type AccessMode uint32
AccessMode controls the type of filesystem access granted. uint32 matches the C parameter type (uint32_t) used in the FFI functions.
const ( AccessRead AccessMode = C.NONO_ACCESS_MODE_READ AccessWrite AccessMode = C.NONO_ACCESS_MODE_WRITE AccessReadWrite AccessMode = C.NONO_ACCESS_MODE_READ_WRITE )
func (AccessMode) String ¶
func (m AccessMode) String() string
String returns a human-readable representation of the access mode.
type CapabilitySet ¶
type CapabilitySet struct {
// contains filtered or unexported fields
}
CapabilitySet holds a set of permissions to be applied to the sandbox. Use New to create one, and Apply to activate it. Call CapabilitySet.Close when done if you want immediate cleanup; otherwise the finalizer will release memory when GC runs.
CapabilitySet is safe for concurrent use. Close may be called concurrently with other methods; subsequent calls on a closed set return an error. Read-only methods (NetworkMode, ProxyPort, IsNetworkBlocked, Summary, PathCovered, FSCapabilities) may execute concurrently with each other.
func New ¶
func New() *CapabilitySet
New creates a new empty CapabilitySet. It panics on allocation failure (equivalent to the runtime panicking on out-of-memory).
func (*CapabilitySet) AddPlatformRule ¶
func (cs *CapabilitySet) AddPlatformRule(rule string) error
AddPlatformRule adds a raw platform-specific sandbox rule. On macOS this is a Seatbelt S-expression; on Linux it is ignored.
func (*CapabilitySet) AllowCommand ¶
func (cs *CapabilitySet) AllowCommand(cmd string) error
AllowCommand adds a command to the allow list (overrides block lists).
func (*CapabilitySet) AllowFile ¶
func (cs *CapabilitySet) AllowFile(path string, mode AccessMode) error
AllowFile grants single-file access at the given path with the specified mode.
func (*CapabilitySet) AllowPath ¶
func (cs *CapabilitySet) AllowPath(path string, mode AccessMode) error
AllowPath grants directory access at the given path with the specified mode.
func (*CapabilitySet) BlockCommand ¶
func (cs *CapabilitySet) BlockCommand(cmd string) error
BlockCommand adds a command to the block list.
func (*CapabilitySet) Close ¶
func (cs *CapabilitySet) Close() error
Close frees the underlying C capability set immediately. It is safe to call Close multiple times; subsequent calls are no-ops. Close always returns nil; the error return satisfies io.Closer.
func (*CapabilitySet) Deduplicate ¶
func (cs *CapabilitySet) Deduplicate() error
Deduplicate removes redundant filesystem capabilities, keeping the highest access level for overlapping paths. Returns an error only if the set is closed; for open sets, the operation always succeeds (the underlying C function returns void and cannot fail).
func (*CapabilitySet) FSCapabilities ¶
func (cs *CapabilitySet) FSCapabilities() []FSCapability
FSCapabilities returns all filesystem capabilities in the set. Returns nil if the set is closed. Entries whose access mode is reported as invalid by the C library (NONO_ACCESS_MODE_INVALID) are skipped.
Warning: this method holds the internal read lock across O(N × fields) sequential CGo calls — one per field per entry — because the C API has no bulk accessor. All write operations (AllowPath, AllowFile, Close, etc.) are blocked for the full duration of the iteration. On large capability sets this may cause write starvation; call CapabilitySet.Deduplicate beforehand to minimize N.
TODO(perf): replace with a single CGo call when a bulk C accessor is available upstream.
func (*CapabilitySet) IsNetworkBlocked ¶
func (cs *CapabilitySet) IsNetworkBlocked() bool
IsNetworkBlocked reports whether outbound network access is blocked. Returns false if the set is closed.
Note: although [SetNetworkBlocked] is deprecated in favour of [SetNetworkMode], this read-only accessor is retained because NetworkMode() == NetworkBlocked is less readable in simple boolean contexts.
func (*CapabilitySet) NetworkMode ¶
func (cs *CapabilitySet) NetworkMode() NetworkMode
NetworkMode returns the current network mode. Returns NetworkBlocked if the set is closed.
func (*CapabilitySet) PathCovered ¶
func (cs *CapabilitySet) PathCovered(path string) (bool, error)
PathCovered reports whether path is covered by an existing directory capability. Returns (false, ErrInvalidArg) if path contains a NUL byte (which would be silently truncated by C.CString, making the check meaningless). Returns (false, nil) if the set is closed.
func (*CapabilitySet) ProxyPort ¶
func (cs *CapabilitySet) ProxyPort() uint16
ProxyPort returns the proxy port (meaningful only in NetworkProxyOnly mode). Returns 0 if the set is closed.
func (*CapabilitySet) SetNetworkBlocked
deprecated
func (cs *CapabilitySet) SetNetworkBlocked(blocked bool) error
SetNetworkBlocked sets whether outbound network access is blocked.
Deprecated: use [SetNetworkMode] instead, which provides access to all network modes. SetNetworkBlocked(true) is equivalent to SetNetworkMode(NetworkBlocked); SetNetworkBlocked(false) is equivalent to SetNetworkMode(NetworkAllowAll). The last call between SetNetworkBlocked and SetNetworkMode wins.
func (*CapabilitySet) SetNetworkMode ¶
func (cs *CapabilitySet) SetNetworkMode(mode NetworkMode) error
SetNetworkMode sets the network mode. Use [SetProxyPort] to configure the port when using NetworkProxyOnly. SetNetworkMode and the deprecated [SetNetworkBlocked] both control the same underlying network mode field; the last call wins.
func (*CapabilitySet) SetProxyPort ¶
func (cs *CapabilitySet) SetProxyPort(port uint16) error
SetProxyPort sets the proxy port for NetworkProxyOnly mode.
func (*CapabilitySet) Summary ¶
func (cs *CapabilitySet) Summary() string
Summary returns a plain-text summary of the capability set. Returns an empty string if the set is closed.
type CapabilitySourceTag ¶
type CapabilitySourceTag int
CapabilitySourceTag identifies where a capability came from. int reflects the default C enum signedness; values are always non-negative.
const ( SourceUser CapabilitySourceTag = C.NONO_CAPABILITY_SOURCE_TAG_USER SourceGroup CapabilitySourceTag = C.NONO_CAPABILITY_SOURCE_TAG_GROUP SourceSystem CapabilitySourceTag = C.NONO_CAPABILITY_SOURCE_TAG_SYSTEM SourceProfile CapabilitySourceTag = C.NONO_CAPABILITY_SOURCE_TAG_PROFILE )
func (CapabilitySourceTag) String ¶
func (s CapabilitySourceTag) String() string
String returns a human-readable representation of the capability source tag.
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error is returned by nono FFI calls that fail. Use Error.Code to identify the failure kind; compare against the ErrCode* constants or use errors.Is with the named Err* sentinels. Use Error.Message to obtain the human-readable description.
Both fields are unexported to prevent callers from mutating sentinel error values through an errors.As pointer, which would corrupt future errors.Is comparisons. Use the Code() and Message() accessor methods to read them.
func (*Error) Error ¶
Error implements the error interface. The string is the C library's description. When no message is available the code is included instead.
type ErrorCode ¶
type ErrorCode int
ErrorCode is the numeric code returned by a failing nono FFI call. All failure codes are negative; zero indicates success (NONO_ERROR_CODE_OK). Codes are not guaranteed to be contiguous — use errors.Is with the named Err* sentinels or switch on ErrCode* constants rather than range-checking.
const ( ErrCodePathNotFound ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_PATH_NOT_FOUND) ErrCodeExpectedDirectory ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_EXPECTED_DIRECTORY) ErrCodeExpectedFile ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_EXPECTED_FILE) ErrCodePathCanonicalization ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_PATH_CANONICALIZATION) ErrCodeNoCapabilities ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_NO_CAPABILITIES) ErrCodeSandboxInit ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_SANDBOX_INIT) ErrCodeUnsupportedPlatform ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_UNSUPPORTED_PLATFORM) ErrCodeBlockedCommand ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_BLOCKED_COMMAND) ErrCodeConfigParse ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_CONFIG_PARSE) ErrCodeProfileParse ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_PROFILE_PARSE) ErrCodeIO ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_IO) ErrCodeInvalidArg ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_INVALID_ARG) ErrCodeTrustVerification ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_TRUST_VERIFICATION) ErrCodeUnknown ErrorCode = ErrorCode(C.NONO_ERROR_CODE_ERR_UNKNOWN) )
Error code constants corresponding to the C library's NonoErrorCode enum. Values are sourced directly from the C enum to prevent silent divergence if the C library renumbers any code.
type FSCapability ¶
type FSCapability struct {
OriginalPath string
ResolvedPath string
Access AccessMode
IsFile bool
Source CapabilitySourceTag
GroupName string // non-empty only when Source == SourceGroup
}
FSCapability describes a single filesystem capability in a CapabilitySet.
type NetworkMode ¶
type NetworkMode uint32
NetworkMode controls outbound network access. uint32 matches the C parameter type (uint32_t) used in the FFI functions.
const ( NetworkBlocked NetworkMode = C.NONO_NETWORK_MODE_BLOCKED NetworkAllowAll NetworkMode = C.NONO_NETWORK_MODE_ALLOW_ALL NetworkProxyOnly NetworkMode = C.NONO_NETWORK_MODE_PROXY_ONLY )
func (NetworkMode) String ¶
func (m NetworkMode) String() string
String returns a human-readable representation of the network mode.
type PlatformInfo ¶
PlatformInfo describes sandboxing support on the current platform.
func SupportInfo ¶
func SupportInfo() PlatformInfo
SupportInfo returns detailed platform sandboxing support information. When only a boolean is needed, prefer the cheaper IsSupported function, which avoids the two C-to-Go string copies (platform, details) that SupportInfo performs.
type QueryContext ¶
type QueryContext struct {
// contains filtered or unexported fields
}
QueryContext evaluates permission queries against a snapshot of a CapabilitySet. The capability set is cloned internally when creating a QueryContext, so subsequent modifications to the source CapabilitySet do not affect it.
QueryContext is safe for concurrent use. Multiple goroutines may call QueryPath and QueryNetwork simultaneously; only Close requires exclusive access.
Example ¶
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/nolabs-ai/nono-go"
)
func main() {
dir, err := os.MkdirTemp("", "nono-example-*")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
caps := nono.New()
defer caps.Close()
if err := caps.AllowPath(dir, nono.AccessRead); err != nil {
panic(err)
}
if err := caps.SetNetworkMode(nono.NetworkAllowAll); err != nil {
panic(err)
}
qc, err := nono.NewQueryContext(caps)
if err != nil {
panic(err)
}
defer qc.Close()
pathResult, err := qc.QueryPath(filepath.Join(dir, "file.txt"), nono.AccessRead)
if err != nil {
panic(err)
}
networkResult, err := qc.QueryNetwork()
if err != nil {
panic(err)
}
fmt.Println(pathResult.Status)
fmt.Println(networkResult.Status)
}
Output: allowed allowed
func NewQueryContext ¶
func NewQueryContext(caps *CapabilitySet) (*QueryContext, error)
NewQueryContext creates a QueryContext from a CapabilitySet.
func (*QueryContext) Close ¶
func (qc *QueryContext) Close() error
Close frees the underlying C query context immediately. It is safe to call Close multiple times; subsequent calls are no-ops. Close always returns nil; the error return satisfies io.Closer.
func (*QueryContext) QueryNetwork ¶
func (qc *QueryContext) QueryNetwork() (QueryResult, error)
QueryNetwork checks whether outbound network access is permitted.
func (*QueryContext) QueryPath ¶
func (qc *QueryContext) QueryPath(path string, mode AccessMode) (QueryResult, error)
QueryPath checks whether path access with the given mode is permitted.
type QueryReason ¶
type QueryReason int
QueryReason explains why a query was allowed or denied.
const ( ReasonGrantedPath QueryReason = C.NONO_QUERY_REASON_GRANTED_PATH ReasonNetworkAllowed QueryReason = C.NONO_QUERY_REASON_NETWORK_ALLOWED ReasonPathNotGranted QueryReason = C.NONO_QUERY_REASON_PATH_NOT_GRANTED ReasonInsufficientAccess QueryReason = C.NONO_QUERY_REASON_INSUFFICIENT_ACCESS ReasonNetworkBlocked QueryReason = C.NONO_QUERY_REASON_NETWORK_BLOCKED )
func (QueryReason) String ¶
func (r QueryReason) String() string
String returns a human-readable representation of the query reason.
type QueryResult ¶
type QueryResult struct {
// Status is whether the operation is allowed or denied.
Status QueryStatus
// Reason is the specific reason for the status.
Reason QueryReason
// GrantedPath is the capability path that grants access.
// Non-empty only when Reason == ReasonGrantedPath.
GrantedPath string
// GrantedAccess is the access mode string of the granting capability.
// Non-empty only when Reason == ReasonGrantedPath.
// Typed as string because the C library returns a human-readable mode name.
GrantedAccess string
// ActualAccess is the access mode that was actually granted.
// Non-empty only when Reason == ReasonInsufficientAccess.
ActualAccess string
// RequestedAccess is the access mode that was requested but not granted.
// Non-empty only when Reason == ReasonInsufficientAccess.
RequestedAccess string
}
QueryResult holds the outcome of a permission query. The C NonoQueryResult struct uses abbreviated field names that differ from the Go field names; see extractQueryResult in query.go for the mapping.
type QueryStatus ¶
type QueryStatus int
QueryStatus indicates whether a queried operation is allowed or denied.
const ( QueryAllowed QueryStatus = C.NONO_QUERY_STATUS_ALLOWED QueryDenied QueryStatus = C.NONO_QUERY_STATUS_DENIED )
func (QueryStatus) String ¶
func (s QueryStatus) String() string
String returns a human-readable representation of the query status.
type SandboxState ¶
type SandboxState struct {
// contains filtered or unexported fields
}
SandboxState is a serializable snapshot of a CapabilitySet. Use StateFromCaps or StateFromJSON to create one.
SandboxState is safe for concurrent use. Multiple goroutines may call JSON concurrently; only Close requires exclusive access.
Example ¶
package main
import (
"encoding/json"
"fmt"
"os"
"github.com/nolabs-ai/nono-go"
)
func main() {
dir, err := os.MkdirTemp("", "nono-example-*")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
caps := nono.New()
defer caps.Close()
if err := caps.AllowPath(dir, nono.AccessReadWrite); err != nil {
panic(err)
}
if err := caps.SetNetworkMode(nono.NetworkBlocked); err != nil {
panic(err)
}
state, err := nono.StateFromCaps(caps)
if err != nil {
panic(err)
}
defer state.Close()
jsonStr, err := state.JSON()
if err != nil {
panic(err)
}
restored, err := nono.StateFromJSON(jsonStr)
if err != nil {
panic(err)
}
defer restored.Close()
restoredCaps, err := restored.Caps()
if err != nil {
panic(err)
}
defer restoredCaps.Close()
fmt.Println(json.Valid([]byte(jsonStr)))
fmt.Println(restoredCaps.NetworkMode())
}
Output: true blocked
func StateFromCaps ¶
func StateFromCaps(caps *CapabilitySet) (*SandboxState, error)
StateFromCaps creates a SandboxState snapshot from a CapabilitySet.
func StateFromJSON ¶
func StateFromJSON(data string) (*SandboxState, error)
StateFromJSON deserializes a SandboxState from a JSON string. On failure the returned error contains the C library's description. The error code is always ErrCodeUnknown because the C function does not return a numeric code; use errors.Is(err, nono.ErrUnknown) to match the error, and err.(*nono.Error).Message() for the human-readable reason.
The parameter type is string (not []byte) to match the C FFI signature, which accepts a null-terminated C string.
func (*SandboxState) Caps ¶
func (ss *SandboxState) Caps() (*CapabilitySet, error)
Caps converts the state back to a CapabilitySet. On failure, error codes are:
- ErrInvalidArg if the state has been closed.
- ErrUnknown for C-originated failures (the C function returns only a pointer; use err.(*Error).Message() for the human-readable reason).
func (*SandboxState) Close ¶
func (ss *SandboxState) Close() error
Close frees the underlying C sandbox state immediately. It is safe to call Close multiple times; subsequent calls are no-ops. Close always returns nil; the error return satisfies io.Closer.
func (*SandboxState) JSON ¶
func (ss *SandboxState) JSON() (string, error)
JSON serializes the state to a JSON string. The return type is string (not []byte) to match the C FFI, which returns a null-terminated string that is copied into Go memory by goString. On failure, error codes are:
- ErrInvalidArg if the state has been closed.
- ErrUnknown for C-originated failures (the C function returns only a pointer; use err.(*Error).Message() for the human-readable reason).