Documentation
¶
Index ¶
- Constants
- Variables
- func DefaultAgentPrompt() string
- func DefaultTOML() []byte
- func Expand(s string, vars TemplateVars) (string, error)
- func ExpandPath(p string) string
- func ExpandPathRelative(p, baseDir string) string
- func ExpandSlice(ss []string, vars TemplateVars) ([]string, error)
- func IncludeEnvVarName(repoBasename string) string
- func LegacyRuntimeDirs() []string
- func ParseDurationWithDays(s string) (time.Duration, error)
- func ResolveConfigPath(explicit string) (path string, exists bool, err error)
- func ResolvePath(p string) string
- func ResolveProfile() (profile string, appName string, err error)
- type Agent
- type Approvals
- type ApprovalsBuiltin
- type Config
- type Delete
- type GitPullConfig
- type InputConfig
- type Keybindings
- type MCPServerConfig
- type Messages
- type Notifications
- type OrchestratorConfig
- type OrchestratorSandboxConfig
- type Overlay
- type PRWatchConfig
- type PairRate
- type Paths
- type RemoteConfig
- type RepoConfig
- type SandboxConfig
- type SandboxNetworkConfig
- type StatusBar
- type StatusConfig
- type TemplateVars
- type UnknownKey
- type Watcher
Constants ¶
const DefaultDeleteRetention = 24 * time.Hour
DefaultDeleteRetention is the soft-delete retention window used when [delete] retention is unset.
Variables ¶
var SandboxSignalModes = []string{"isolated", "allow_same_sandbox", "allow_all"}
SandboxSignalModes are the accepted values for [sandbox] signal_mode. They mirror nono v0.66.0's security.signal_mode enum. Empty is also valid (inherit nono's base-profile default).
Functions ¶
func DefaultAgentPrompt ¶ added in v0.35.0
func DefaultAgentPrompt() string
func DefaultTOML ¶ added in v0.19.0
func DefaultTOML() []byte
func ExpandPath ¶ added in v0.11.0
func ExpandPathRelative ¶ added in v0.66.0
ExpandPathRelative resolves a configured path deterministically: it expands a leading ~/, and resolves a still-relative path against baseDir (the directory holding the config file) rather than the process working directory, then cleans the result. This keeps a value like [approvals.builtin] config resolving to the same absolute path regardless of which directory the daemon or CLI happens to run from. An empty (or whitespace-only) path stays empty so callers can distinguish "unset" from a resolved path.
func ExpandSlice ¶
func ExpandSlice(ss []string, vars TemplateVars) ([]string, error)
func IncludeEnvVarName ¶ added in v0.19.0
func LegacyRuntimeDirs ¶ added in v0.11.0
func LegacyRuntimeDirs() []string
LegacyRuntimeDirs returns paths where older versions stored the socket and PID file (TMPDIR or /tmp fallbacks). Used during startup to detect and clean up an orphaned daemon after the socket location changed.
func ParseDurationWithDays ¶ added in v0.3.0
func ResolveConfigPath ¶ added in v0.66.0
ResolveConfigPath returns the config file that LoadOrDefault(explicit) would read and whether that file exists on disk. When explicit is set it is used verbatim. When empty, resolution mirrors LoadOrDefault: the profile/XDG path, falling back to the legacy macOS path only when the XDG file is absent and no profile is active. Diagnostics (e.g. gr doctor) use this so the reported and inspected file is the same one the CLI/daemon actually load.
func ResolvePath ¶ added in v0.16.5
func ResolveProfile ¶ added in v0.18.0
Types ¶
type Agent ¶
type Agent struct {
Command string `json:"command" toml:"command"`
Args []string `json:"args,omitempty" toml:"args"`
ResumeArgs []string `json:"resume_args,omitempty" toml:"resume_args"`
ForkArgs []string `json:"fork_args,omitempty" toml:"fork_args"`
Env map[string]string `json:"env,omitempty" toml:"env"`
IdleTimeout string `json:"idle_timeout,omitempty" toml:"idle_timeout"`
InjectPrompt *bool `json:"inject_prompt,omitempty" toml:"inject_prompt"`
PreTrustWorkspace *bool `json:"pre_trust_workspace,omitempty" toml:"pre_trust_workspace"`
Sandbox SandboxConfig `json:"sandbox" toml:"sandbox"`
MCPServers map[string]MCPServerConfig `json:"mcp_servers,omitempty" toml:"mcp_servers"`
ValidateModel string `json:"validate_model,omitempty" toml:"validate_model"`
// InterruptCount is how many times the interrupt byte (Ctrl-C, 0x03) is sent
// to interrupt this agent, and InterruptDelayMs is the pause in milliseconds
// between successive sends. Some agent TUIs ignore a single Ctrl-C and need
// two rapid presses to actually interrupt (Claude's TUI wants ~200ms apart),
// so both are configurable per agent. Unset means the built-in defaults
// (count 1, delay 0). See issue #620.
InterruptCount *int `json:"interrupt_count,omitempty" toml:"interrupt_count"`
InterruptDelayMs *int `json:"interrupt_delay_ms,omitempty" toml:"interrupt_delay_ms"`
}
func (Agent) IdleTimeoutDuration ¶
func (Agent) InterruptCountValue ¶ added in v0.66.2
InterruptCountValue returns how many times the interrupt byte (Ctrl-C, 0x03) should be sent to interrupt this agent. Defaults to 1 when unset; a value below 1 is clamped to 1 so an interrupt always sends at least once.
func (Agent) InterruptDelay ¶ added in v0.66.2
InterruptDelay returns the pause between successive interrupt bytes. Defaults to 0 (send back-to-back) when unset; a negative value is treated as 0.
func (Agent) PreTrustWorkspaceEnabled ¶ added in v0.48.0
func (Agent) PromptInjectionEnabled ¶ added in v0.32.0
type Approvals ¶ added in v0.13.0
type Approvals struct {
// Enabled controls whether the PreToolUse approve-request gating hook is
// installed. nil (unset) means disabled: the status/lifecycle hooks are
// still installed but the approval gate is not, because unattended agents
// otherwise see their own tool calls as human-rejected and the OS sandbox
// is the intended guardrail. Set to true to opt back into human approval
// gating.
Enabled *bool `toml:"enabled"`
// Backend selects who makes the automated decision: "" (none — always
// prompt the human), "command"/"external" (delegate to a command over
// graith's JSON contract), "localmost" (the real localmost binary over its
// native protocol), or "builtin" (graith's built-in localmost-compatible
// engine). It is the canonical selector; Mode is the deprecated predecessor.
Backend string `toml:"backend"`
Mode string `toml:"mode"`
AutoPop bool `toml:"auto_pop"`
Timeout string `toml:"timeout"`
Command string `toml:"command"`
Builtin ApprovalsBuiltin `toml:"builtin"`
}
func (Approvals) HookEnabled ¶ added in v0.65.0
HookEnabled reports whether the approve-request PreToolUse hook should be installed. Defaults to false when unset — approval gating is opt-in.
func (Approvals) ResolveBackend ¶ added in v0.64.4
ResolveBackend resolves the effective approvals backend, applying back-compat for the deprecated Mode field. It returns the backend name, a non-empty deprecation message when a legacy Mode value was used (callers log it once), and an error for an unknown backend or a conflicting Mode+Backend pair.
Resolution order:
- If Backend is set, use it. If a legacy Mode is ALSO set and maps to a different backend, that is a hard error (refuse to guess intent).
- Else if Mode is one of command/external/localmost, map it to the "command" backend (historical behaviour) and return a deprecation message. A Mode with no Backend is always a warning, never an error.
- Else, the "prompt" backend (no automation).
func (Approvals) TimeoutDuration ¶ added in v0.13.0
func (Approvals) Validate ¶ added in v0.64.5
Validate checks the [approvals] config for static contradictions that would otherwise only surface as an opaque fail-closed session crash at create time (see #740). It rejects an unknown or conflicting backend/mode (via ResolveBackend) and a command key set for a resolved backend that ignores it. Backend *availability* (command present, localmost binary on PATH, builtin config loadable) is still deferred to session-create by the daemon.
type ApprovalsBuiltin ¶ added in v0.64.4
type ApprovalsBuiltin struct {
// Config is the path to a localmost-format config.json (allow/deny rules).
Config string `toml:"config"`
// Allow and Deny are the inline allow/deny rulesets. Each element is either
// a bare rule string ("@arg @*") or a table with per-rule keys
// (rule/unless/redirect/pipe). They are decoded as []any so both TOML forms
// — an array of strings and an array of tables ([[approvals.builtin.allow]])
// — are accepted, then converted to the localmost schema (see InlineJSON).
Allow []any `toml:"allow"`
Deny []any `toml:"deny"`
// AllowSafeXargs and AskNoninteractive mirror the localmost top-level flags.
// nil means unset (the engine's default of true applies).
AllowSafeXargs *bool `toml:"allowSafeXargs"`
AskNoninteractive *bool `toml:"askNoninteractive"`
}
ApprovalsBuiltin configures the built-in localmost-compatible engine. Rules can be supplied either as a path to an external localmost-format config.json (Config), or inline in config.toml via Allow/Deny/AllowSafeXargs/ AskNoninteractive. The two forms are mutually exclusive (see Approvals.Validate).
func (ApprovalsBuiltin) HasInline ¶ added in v0.66.0
func (b ApprovalsBuiltin) HasInline() bool
HasInline reports whether any inline ruleset field is set. When true, the rules are read from config.toml rather than an external Config file. An empty array (allow = []) defines no rules and does not count as inline, so it does not spuriously conflict with an external Config path.
func (ApprovalsBuiltin) InlineJSON ¶ added in v0.66.0
func (b ApprovalsBuiltin) InlineJSON() ([]byte, error)
InlineJSON renders the inline ruleset as localmost-format config.json bytes, so the existing (tested) localmost parser can compile it. The TOML keys map 1:1 to the localmost JSON schema (allow/deny/allowSafeXargs/askNoninteractive, and per-rule rule/unless/redirect/pipe), so a plain JSON re-encode suffices.
type Config ¶
type Config struct {
DefaultAgent string `toml:"default_agent"`
GitHubUsername string `toml:"github_username"`
BranchPrefix string `toml:"branch_prefix"`
DataDir string `toml:"data_dir"`
FetchOnCreate bool `toml:"fetch_on_create"`
AgentPrompt string `toml:"agent_prompt"`
AllowedRepoPaths []string `toml:"allowed_repo_paths"`
Repos []RepoConfig `toml:"repos"`
StatusBar StatusBar `toml:"status_bar"`
Keybindings Keybindings `toml:"keybindings"`
Notifications Notifications `toml:"notifications"`
Messages Messages `toml:"messages"`
Delete Delete `toml:"delete"`
Sandbox SandboxConfig `toml:"sandbox"`
Approvals Approvals `toml:"approvals"`
Status StatusConfig `toml:"status"`
GitPull GitPullConfig `toml:"git_pull"`
PRWatch PRWatchConfig `toml:"pr_watch"`
MCPServers []MCPServerConfig `toml:"mcp_servers"`
Overlay Overlay `toml:"overlay"`
Orchestrator OrchestratorConfig `toml:"orchestrator"`
Remote RemoteConfig `toml:"remote"`
Input InputConfig `toml:"input"`
Agents map[string]Agent `toml:"agents"`
}
func LoadOrDefault ¶
func (*Config) AvailableRepoPaths ¶ added in v0.66.13
AvailableRepoPaths returns the repo paths the orchestrator may use, combining the allowed_repo_paths list and the [[repos]] entries with ~ expanded, in config order and de-duplicated. It returns nil when none are configured.
func (*Config) FindRepo ¶ added in v0.18.0
func (c *Config) FindRepo(repoPath string) (RepoConfig, bool)
func (*Config) OrchestratorSandboxMerged ¶ added in v0.46.0
func (c *Config) OrchestratorSandboxMerged(agentName string) SandboxConfig
func (*Config) RepoPathAllowed ¶ added in v0.11.0
type Delete ¶ added in v0.66.16
type Delete struct {
Retention string `toml:"retention"`
}
Delete configures the soft-delete behaviour of `gr delete`. When retention is a positive duration, `gr delete` marks a session deleted and keeps its worktree/state for the window; the daemon purges it after the window elapses. A retention of "0" disables soft delete: `gr delete` is then rejected (with a message pointing at `gr purge`), since delete must never destroy — `gr purge` remains the way to hard-delete immediately.
func (Delete) RetentionDuration ¶ added in v0.66.16
RetentionDuration resolves the configured soft-delete retention window. An unset value defaults to DefaultDeleteRetention (24h); "0" (or any zero duration) disables soft delete. An unparseable value falls back to the default so a typo never silently turns off recovery.
type GitPullConfig ¶ added in v0.42.0
func (GitPullConfig) IntervalDuration ¶ added in v0.42.0
func (g GitPullConfig) IntervalDuration() time.Duration
type InputConfig ¶ added in v0.66.16
type InputConfig struct {
// DragArrowKeys enables touch/hold-and-drag arrow keys:
// press-and-hold the left mouse button then drag to emit discrete arrow-key
// presses to the focused pane. Off by default because it repurposes
// left-drag (which terminals otherwise use for text selection). Mouse-wheel
// scrolling is always passed through unchanged.
DragArrowKeys bool `toml:"drag_arrow_keys"`
// DragArrowThreshold is the number of cells of drag movement that produces
// one arrow-key press. Values below 1 fall back to the default.
DragArrowThreshold int `toml:"drag_arrow_threshold"`
}
InputConfig is the optional [input] block controlling terminal input gestures in the attach passthrough loop.
type Keybindings ¶
type Keybindings struct {
Prefix string `toml:"prefix"`
NewSession string `toml:"new_session"`
ForkSession string `toml:"fork_session"`
DeleteSession string `toml:"delete_session"`
Detach string `toml:"detach"`
SessionList string `toml:"session_list"`
NextSession string `toml:"next_session"`
PrevSession string `toml:"prev_session"`
LastSession string `toml:"last_session"`
ResumeSession string `toml:"resume_session"`
RenameSession string `toml:"rename_session"`
Search string `toml:"search"`
ScrollMode string `toml:"scroll_mode"`
Shell string `toml:"shell"`
OrchestratorSession string `toml:"orchestrator_session"`
}
type MCPServerConfig ¶ added in v0.22.0
type MCPServerConfig struct {
Name string `json:"-" toml:"name"`
Command string `json:"command" toml:"command"`
Args []string `json:"args,omitempty" toml:"args,omitempty"`
Env map[string]string `json:"env,omitempty" toml:"env,omitempty"`
Disabled bool `json:"-" toml:"disabled,omitempty"`
Sandbox *bool `json:"-" toml:"sandbox,omitempty"`
SandboxConfig *SandboxConfig `json:"-" toml:"sandbox_config,omitempty"`
}
func MergeMCPServers ¶ added in v0.22.0
func MergeMCPServers(global []MCPServerConfig, overrides map[string]MCPServerConfig) []MCPServerConfig
type Messages ¶ added in v0.3.0
func (Messages) MaxAgeDuration ¶ added in v0.3.0
type Notifications ¶ added in v0.2.0
type OrchestratorConfig ¶ added in v0.42.0
type OrchestratorConfig struct {
Enabled bool `toml:"enabled"`
Agent string `toml:"agent"`
Model string `toml:"model"`
IdleTimeout string `toml:"idle_timeout"`
Prompt string `toml:"prompt"`
PromptFile string `toml:"prompt_file"`
Sandbox OrchestratorSandboxConfig `toml:"sandbox"`
}
func (OrchestratorConfig) AgentName ¶ added in v0.42.0
func (o OrchestratorConfig) AgentName() string
func (OrchestratorConfig) IdleTimeoutDuration ¶ added in v0.42.0
func (o OrchestratorConfig) IdleTimeoutDuration() time.Duration
type OrchestratorSandboxConfig ¶ added in v0.46.0
type PRWatchConfig ¶ added in v0.59.0
type PRWatchConfig struct {
Enabled bool `toml:"enabled"`
NotifyCIFailures bool `toml:"notify_ci_failures"`
NotifyMergeConflicts bool `toml:"notify_merge_conflicts"`
NotifyReviewComments bool `toml:"notify_review_comments"`
NotifyPRComments bool `toml:"notify_pr_comments"`
NotifyReviewDecisions bool `toml:"notify_review_decisions"`
NotifyPRLifecycle bool `toml:"notify_pr_lifecycle"`
NotifyCIRecovery bool `toml:"notify_ci_recovery"`
PollPending string `toml:"poll_pending"`
PollTerminal string `toml:"poll_terminal"`
PollMerged string `toml:"poll_merged"`
MaxNotificationsPerPR int `toml:"max_notifications_per_pr"`
Debounce string `toml:"debounce"`
}
PRWatchConfig controls the PR & CI awareness loop, which resolves each session's GitHub PR via the gh CLI, polls its CI checks and review comments, and notifies the owning session's inbox on meaningful transitions.
Every notify_* sub-option defaults on: enabling pr_watch is meant to be a single switch (enabled = true) that turns on all notifications, and users selectively disable the classes they don't want. The classes are still gated separately because they carry different authority — a CI failure is a machine verdict (safe to act on), while a review comment or decision is human intent that may not be actionable — so each can be turned off independently.
Comments come in two distinct kinds, each with its own gate:
- NotifyReviewComments covers inline code-review comments (the pulls/{n}/comments surface) — feedback anchored to a file and line.
- NotifyPRComments covers regular conversation comments on the PR thread (the issues/{n}/comments surface) — issue-style comments not tied to a line of code.
They are separate signals: a reviewer leaving inline nits and someone dropping a "ship it" on the conversation thread differ, and a user may want one without the other.
For backward compatibility, notify_pr_comments used to be folded into notify_review_comments; see applyPRWatchCommentCompat, which keeps an older config that only set notify_review_comments delivering conversation comments.
func (PRWatchConfig) DebounceDuration ¶ added in v0.59.0
func (p PRWatchConfig) DebounceDuration() time.Duration
DebounceDuration is the minimum cooldown between notifications to one session.
func (PRWatchConfig) MaxNotifications ¶ added in v0.59.0
func (p PRWatchConfig) MaxNotifications() int
MaxNotifications returns the per-head-SHA notification cap, defaulting to 10.
func (PRWatchConfig) PollMergedDuration ¶ added in v0.59.0
func (p PRWatchConfig) PollMergedDuration() time.Duration
PollMergedDuration is the sweep interval for merged/closed PRs.
func (PRWatchConfig) PollPendingDuration ¶ added in v0.59.0
func (p PRWatchConfig) PollPendingDuration() time.Duration
PollPendingDuration is the poll interval while a PR has pending/in-progress checks.
func (PRWatchConfig) PollTerminalDuration ¶ added in v0.59.0
func (p PRWatchConfig) PollTerminalDuration() time.Duration
PollTerminalDuration is the poll interval once all checks are terminal (PR still open).
type PairRate ¶ added in v0.66.3
PairRate is a parsed pair_request_rate: Count events per Per duration.
func ParsePairRequestRate ¶ added in v0.66.3
ParsePairRequestRate parses a "<n>/<unit>" rate such as "5/min". The unit is one of sec/min/hour (with the aliases second/minute/hour). The count must be a positive integer. Any other shape is a hard error (fail-closed).
type Paths ¶
type Paths struct {
Profile string
AppName string
ConfigFile string
DataDir string
RuntimeDir string
SocketPath string
PIDFile string
StateFile string
LogDir string
DaemonLog string
MessagesDB string
TmpDir string
}
func ResolvePaths ¶
func (Paths) EnsureDirs ¶
func (Paths) WithDataDir ¶ added in v0.21.0
type RemoteConfig ¶ added in v0.66.3
type RemoteConfig struct {
// Enabled turns the remote listener on. Off by default; when false the rest
// of the block is not validated so a disabled block never blocks startup.
Enabled bool `toml:"enabled"`
// Mode selects the transport: "tsnet" (embedded Tailscale via tsnet) or
// "interface" (bind the host's existing tailnet interface IP).
Mode string `toml:"mode"`
// Hostname is the tsnet node name / MagicDNS label (tsnet mode).
Hostname string `toml:"hostname"`
// Port is the TCP port the listener binds.
Port int `toml:"port"`
// AuthKeyFile is the path to a tsnet auth key (tsnet mode only).
AuthKeyFile string `toml:"auth_key_file"`
// Tags are the tsnet ACL tags applied to the node (tsnet mode only).
Tags []string `toml:"tags"`
// AllowTailnetUsers is the WhoIs allowlist (Gate 1). Entries are either a
// tailnet user email or a "tag:"-prefixed tag. A bare "tag:" entry opts
// tagged nodes in; with no tag entry, tagged nodes are disallowed.
AllowTailnetUsers []string `toml:"allow_tailnet_users"`
// RequirePairing requires per-device pairing (Gate 2) for human-level
// rights. Defaults to true; false is UNSAFE (trusts the tailnet identity
// alone) and is restricted to read-only access — see the design doc §B.2.
RequirePairing bool `toml:"require_pairing"`
// PairRequestRate is the anti-flood limit on pending pair requests, written
// "<n>/<unit>" (e.g. "5/min"); units are sec, min, or hour. Empty means no
// configured limit here (the daemon applies its own default).
PairRequestRate string `toml:"pair_request_rate"`
}
RemoteConfig is the optional, off-by-default [remote] block that exposes a tailnet-facing control listener (see the native-app design doc §A.4/§B). It is fail-closed: when Enabled, an invalid block is a hard config-load error (static validation only — runtime listener provisioning failures, e.g. a missing tailnet IP or cert, are handled by the remote listener, not here).
func (RemoteConfig) AllowsTaggedNodes ¶ added in v0.66.3
func (r RemoteConfig) AllowsTaggedNodes() bool
AllowsTaggedNodes reports whether any allow_tailnet_users entry opts tagged nodes in (a "tag:"-prefixed entry). With no such entry, tagged nodes — which WhoIs resolves with no user — are disallowed by default.
func (RemoteConfig) Validate ¶ added in v0.66.3
func (r RemoteConfig) Validate() error
Validate checks the [remote] block for static contradictions. Rules are only enforced when Enabled — a disabled block (even with otherwise-invalid values) always loads. It is fail-closed: an invalid enabled block is a hard error.
type RepoConfig ¶ added in v0.18.0
type RepoConfig struct {
Path string `toml:"path"`
AllowConcurrent bool `toml:"allow_concurrent"`
Singleton bool `toml:"singleton"`
Includes []string `toml:"includes"`
}
func (RepoConfig) Validate ¶ added in v0.19.0
func (rc RepoConfig) Validate() error
type SandboxConfig ¶ added in v0.11.0
type SandboxConfig struct {
Enabled bool `json:"enabled" toml:"enabled"`
Disabled *bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
// Backend selects the sandbox backend: "safehouse" (macOS only) or "nono"
// (Linux + macOS). It has NO default — when the sandbox is enabled and
// Backend is unset the daemon fails closed with an actionable error. This
// is a deliberate pre-1.0 behaviour change (see the nono sandbox design doc).
Backend string `json:"backend,omitempty" toml:"backend"`
Command string `json:"command,omitempty" toml:"command"`
// Profile (nono only) is the base profile graith's generated profile
// extends. Empty means nono's built-in "default" (its audited deny groups +
// base system paths). Set it to a maintained registry profile — e.g.
// "always-further/claude" — to inherit that agent's upstream file grants
// (its ~/.claude, ~/.claude.json, versioned binary dir, …) instead of
// hand-listing them via write_files.
//
// nono resolves "extends" by MERGING the base profile with graith's
// generated one. Collection fields (filesystem.allow/read,
// environment.allow_vars, network.allow_domain, …) are UNIONED (append +
// dedup) — graith's grants are added to, not substituted for, the base's;
// only scalar fields (e.g. workdir.access, security.signal_mode) are
// child-overridden. So graith's filesystem grants are always present, but
// graith's env allowlist can only WIDEN the base profile's, it cannot narrow
// it. A base profile that allows extra env vars, network
// domains, set_vars, command policies, or session hooks (which run outside
// the sandbox) therefore relaxes graith's baseline — so a custom profile is
// only as tight as the operator has audited it to be. Choose a trusted,
// least-privilege profile. nono's audited deny groups (deny_credentials, …)
// are marked required and merged into every resolved profile regardless of
// this field, so a custom base cannot silently drop the credential-deny
// baseline. The safehouse backend has no profile concept and ignores it.
Profile string `json:"profile,omitempty" toml:"profile"`
Features []string `json:"features,omitempty" toml:"features"`
ReadDirs []string `json:"read_dirs,omitempty" toml:"read_dirs"`
WriteDirs []string `json:"write_dirs,omitempty" toml:"write_dirs"`
// ReadFiles / WriteFiles grant access to individual files rather than whole
// directories. They exist for paths that can't be expressed as a directory
// grant without over-sharing — most importantly single files that live
// directly in $HOME (e.g. an agent's ~/.claude.json login file), where
// granting the parent directory would expose unrelated secrets (.env, ssh
// keys, tfvars). ReadFiles is read-only; WriteFiles is read+write, mirroring
// the read_dirs / write_dirs convention (where "write" means read+write, not
// nono's write-only mode). They map to the nono profile's
// filesystem.read_file / filesystem.allow_file; the safehouse backend folds
// them into its read-only / read-write path lists.
ReadFiles []string `json:"read_files,omitempty" toml:"read_files"`
WriteFiles []string `json:"write_files,omitempty" toml:"write_files"`
// SignalMode controls whether the sandboxed process may signal other
// processes. It maps to nono's security.signal_mode ("isolated",
// "allow_same_sandbox", "allow_all"). Empty inherits nono's base-profile
// default (allow_same_sandbox). safehouse ignores it. Setting "isolated"
// makes graith's `process-control` semantics meaningful under nono (Phase 1
// left it a no-op). See the nono sandbox design doc §C5.
SignalMode string `json:"signal_mode,omitempty" toml:"signal_mode"`
// Network is an optional egress policy. It maps to the nono profile's
// network section (network.block / network.allow_domain). safehouse has no
// network primitive and only warns. A network policy also raises the
// enforcement floor: nono needs Landlock ABI v4 (kernel 6.7+) to filter
// network, so a requested policy on an older kernel fails closed.
Network *SandboxNetworkConfig `json:"network,omitempty" toml:"network"`
}
func (SandboxConfig) Merge ¶ added in v0.11.0
func (s SandboxConfig) Merge(agent SandboxConfig) SandboxConfig
type SandboxNetworkConfig ¶ added in v0.64.0
type SandboxNetworkConfig struct {
// Block denies all outbound network access (nono is network-allowed by
// default). Maps to network.block = true.
Block bool `json:"block,omitempty" toml:"block"`
// AllowDomains is the proxy allowlist. Maps to network.allow_domain. When
// set, nono runs its L7 filtering proxy and only these domains are
// reachable. Entries are plain hostnames or URL globs.
AllowDomains []string `json:"allow_domains,omitempty" toml:"allow_domains"`
}
SandboxNetworkConfig is graith's egress policy. It maps directly onto nono v0.66.0's profile network section: Block -> network.block, AllowDomains -> network.allow_domain (an L7 proxy allowlist; a plain hostname allows the host, a URL glob restricts to matching endpoints).
func (*SandboxNetworkConfig) IsSet ¶ added in v0.64.0
func (n *SandboxNetworkConfig) IsSet() bool
IsSet reports whether this network policy requests any egress restriction. A nil or empty config requests nothing (matches nono's allow-by-default).
type StatusConfig ¶ added in v0.32.0
type StatusConfig struct {
TTL string `toml:"ttl"`
}
func (StatusConfig) TTLDuration ¶ added in v0.32.0
func (s StatusConfig) TTLDuration() time.Duration
type TemplateVars ¶
type UnknownKey ¶ added in v0.66.0
type UnknownKey struct {
// Table is the dotted parent-table path, e.g. "agents.claude.sandbox".
// Empty for top-level keys.
Table string
// Name is the unrecognised leaf key, e.g. "read_dir".
Name string
// Suggestion is the closest known key in the same table, or "" if none is
// close enough to be worth a "did you mean".
Suggestion string
}
UnknownKey is a config key that graith's schema does not recognise. It is a diagnostic aid (surfaced by `gr doctor`), not a load error: the runtime load stays lenient so an older daemon won't refuse a config written for a newer graith, and a typo silently drops the key rather than bricking startup. See issue #720.
func UnknownKeys ¶ added in v0.66.0
func UnknownKeys(path string) ([]UnknownKey, error)
UnknownKeys parses the TOML at path and reports keys that don't map to any field in the Config schema — typos (read_dir vs read_dirs), keys under the wrong table, or options from a newer graith than this binary. Unknown keys are never returned as an error; the returned error is only for a missing, unreadable, or unparseable file.
func (UnknownKey) FullKey ¶ added in v0.66.0
func (u UnknownKey) FullKey() string
FullKey renders the key with its table prefix, e.g. "sandbox.read_dir".