Documentation
¶
Index ¶
- Constants
- Variables
- func CurrentSchemaVersion() int
- func DisplayName(key string) string
- type HostFile
- type HostShare
- type Namespace
- type NetworkBlock
- type NetworkPolicy
- type Phase
- type PhaseStatus
- type Policy
- type PolicyCache
- type PortForward
- type Provider
- type Sandbox
- type StopReason
- type Store
- func (s *Store) CacheDir() string
- func (s *Store) Delete(name string) error
- func (s *Store) DeletePolicy(name string) error
- func (s *Store) Exists(name string) bool
- func (s *Store) HistoryDir() string
- func (s *Store) List() ([]Sandbox, error)
- func (s *Store) ListNamespaces() ([]Namespace, error)
- func (s *Store) ListPolicies() ([]*Policy, error)
- func (s *Store) Load(name string) (*Sandbox, error)
- func (s *Store) LoadNamespace(name string) (*Namespace, error)
- func (s *Store) LoadPolicy(name string) (*Policy, error)
- func (s *Store) LoadPolicyCache(name string) (*PolicyCache, error)
- func (s *Store) NamespaceConfigExists(name string) bool
- func (s *Store) NamespaceConfigPath(name string) string
- func (s *Store) RootDir() string
- func (s *Store) RunMigrations(w io.Writer) error
- func (s *Store) Save(sb *Sandbox) error
- func (s *Store) SaveNamespace(ns *Namespace) error
- func (s *Store) SavePolicy(p *Policy) error
- func (s *Store) SavePolicyCache(name string, c *PolicyCache) error
- func (s *Store) SchemaVersion() int
- func (s *Store) StateDir(sandboxName string) string
- func (s *Store) VMDir(sandboxName string) string
- func (s *Store) WorktreeDir(sandboxName string) string
- type VMState
Constants ¶
const ( BlockOriginNamespace = "namespace" BlockOriginMod = "mod" BlockOriginCustom = "custom" )
Block origins stored on sandbox records, lowest to highest precedence. Frozen (see VMState) — and doubly so here: blockOriginRank sends unknown strings to the lowest precedence, so a renamed origin would silently invert which rules win.
const DefaultNamespace = "default"
DefaultNamespace is the grouping a sandbox belongs to when none is set.
const DefaultPolicyName = "default"
DefaultPolicyName is reserved for the built-in dev allowlist; it can be loaded but never saved or deleted, and it is the chain a sandbox gets when no `use` list was ever written.
const RecordSchemaVersion = 1
RecordSchemaVersion is the current shape of a sandbox record's JSON, stamped onto Sandbox.RecordSchema at every Save. Bump only when a record written by this clawk would be misread by the previous one — additive omitempty fields don't count.
Variables ¶
var DefaultAllowedDomains = []string{}/* 137 elements not displayed */
DefaultAllowedDomains are domains allowed by default for development. Originally seeded from Andrew Lock's microVM sandbox allow list: https://andrewlock.net/running-ai-agents-safely-in-a-microvm-using-docker-sandbox/
Every entry here is a contract in both directions: removing one after release breaks users who rely on it, and each one is a destination the agent can reach without asking — i.e. a potential exfiltration endpoint. Additions must be (a) operated by the organization they claim to serve and (b) needed by a mainstream development workflow, not one project. When in doubt, leave it out — users can `clawk network allow` per sandbox or per namespace.
var ErrPolicyNotFound = errors.New("policy not found")
ErrPolicyNotFound is returned by LoadPolicy for a name with no record on disk (and no builtin).
Functions ¶
func CurrentSchemaVersion ¶
func CurrentSchemaVersion() int
CurrentSchemaVersion is the schema version this build targets.
func DisplayName ¶
DisplayName is the human-facing form of a store key. It currently returns the key unchanged — anchored sandboxes are keyed by a clean `<base>` and carry their binding in Sandbox.Anchor, so no key needs rewriting for display. Retained as the single seam every surface (`clawk list`/`status`, the workspace CLAUDE.md, the menubar) routes through, should display names ever need to diverge from keys again.
Types ¶
type HostFile ¶
type HostFile struct {
HostPath string `json:"host_path"`
GuestPath string `json:"guest_path"`
Mode uint32 `json:"mode,omitempty"`
}
HostFile is a snapshot-on-up file pushed from host into guest. Sourced from clawk.mod `files (...)` and refreshed on every `clawk up` — edits on the host are NOT live (use HostShare for that). HostPath is tilde- and env-expanded at compose time; Mode == 0 preserves the host file's permissions.
type HostShare ¶
type HostShare struct {
}
HostShare is a virtio-fs live mount from host directory into guest. Sourced from clawk.mod `shares (...)`. Reflects on the host immediately — credentials that rotate underneath (~/.aws after `aws sts assume-role`, ~/.config/gcloud) stay in sync without a clawk-up cycle. Defaults to ReadOnly=true at parse time so an accidental in-VM write can't clobber the host credential.
type Namespace ¶
type Namespace struct {
Name string `json:"name"`
AllowedDomains []string `json:"allowed_domains,omitempty"`
AllowedIPs []string `json:"allowed_ips,omitempty"`
// DeniedDomains are blocked outright (a block overrides any allow). They
// come from inline `deny` entries; `deny source "<url>"` blocklists are
// registered as source policies referenced via Use instead.
DeniedDomains []string `json:"denied_domains,omitempty"`
// DeniedIPs are the IP/CIDR counterparts of DeniedDomains, from
// `deny ip <addr>` entries in a namespace manifest.
DeniedIPs []string `json:"denied_ips,omitempty"`
// Use names the policies forming the base of every member sandbox's
// chain, in increasing precedence. nil means unspecified (the built-in
// "default" applies unless a sandbox writes its own list); a non-nil
// list is complete. Together with the inline allow/deny entries above,
// these resolve live at up/reload — they are never copied into
// sandbox records, so a namespace edit propagates to existing members.
Use []string `json:"use,omitempty"`
Files []HostFile `json:"files,omitempty"`
Env []string `json:"env,omitempty"`
// Instructions and Memory seed every sandbox in the namespace: extra
// CLAUDE.md guidance and baseline auto-memory respectively. They merge
// with a repo's clawk.mod equivalents — namespace first, as the broader
// scope — in applyNamespaceDefaults.
Instructions []string `json:"instructions,omitempty"`
Memory string `json:"memory,omitempty"`
}
Namespace holds per-namespace defaults merged into every sandbox created in it: a shared network allowlist plus files/shares/env injected into each sandbox (e.g. context markdown). Stored at namespaces/<name>/namespace.json, co-located with that namespace's sandboxes — so a namespace's whole footprint (config + sandboxes) is one directory. An empty namespace is fine: it's a pure grouping until you give it defaults.
type NetworkBlock ¶
type NetworkBlock struct {
Origin string `json:"origin"`
Name string `json:"name,omitempty"`
AllowDomains []string `json:"allow_domains,omitempty"`
AllowIPs []string `json:"allow_ips,omitempty"`
DenyDomains []string `json:"deny_domains,omitempty"`
DenyIPs []string `json:"deny_ips,omitempty"`
}
NetworkBlock is one origin-labeled layer of a sandbox's network policy. Origins mirror the chain in the design doc: "namespace" and "mod" carry file-composed entries, "custom" carries CLI edits and persisted interactive grants. Policy-referenced layers ("default", "policy", "source") are never stored on the sandbox — they resolve from Use at up/reload so a policy edit propagates without rewriting sandbox records.
type NetworkPolicy ¶
type NetworkPolicy struct {
// AllowedDomains, AllowedIPs and DeniedDomains are the legacy flat
// policy of pre-block sandbox records. Normalize folds them into the
// "custom" block on load; new records are written block-shaped and
// leave these empty.
AllowedDomains []string `json:"allowed_domains,omitempty"`
AllowedIPs []string `json:"allowed_ips,omitempty"`
// DeniedDomains are blocked outright — the domain and every subdomain.
// A block overrides any allow and suppresses the interactive prompt, so
// the agent is refused immediately without asking again. Entries are
// registrable (root) domains: "telemetry.example.com" is blocked by an
// entry of "example.com".
DeniedDomains []string `json:"denied_domains,omitempty"`
// Use names the policies whose blocks form the base of this sandbox's
// chain, in increasing precedence. nil means the chain was never made
// explicit and resolves to ["default"]; a non-nil list is complete —
// include "default" where wanted.
Use []string `json:"use,omitempty"`
// Blocks are the sandbox's own policy layers, lowest precedence first.
// They sit above every Use-referenced policy; the "custom" block (CLI
// edits and persisted interactive grants) stays last, above the "mod"
// block (entries composed from clawk.mod).
Blocks []NetworkBlock `json:"blocks,omitempty"`
}
NetworkPolicy controls outbound network access from the sandbox.
AllowedDomains support wildcards like "*.example.com" and are matched at DNS-resolution time. AllowedIPs accept plain addresses or CIDR ranges like "10.0.0.0/24" and are checked on every TCP SYN, catching direct-IP connections (useful for provisioning new servers that don't have DNS yet).
func (*NetworkPolicy) Block ¶
func (n *NetworkPolicy) Block(origin string) *NetworkBlock
Block returns the policy's layer with the given origin, appending an empty one if absent. The returned pointer is valid until Blocks next grows — mutate it before any further Block call.
func (*NetworkPolicy) Normalize ¶
func (n *NetworkPolicy) Normalize()
Normalize migrates a legacy flat record into block form and restores the block-order invariant. Idempotent; the store applies it on every load.
type Phase ¶
type Phase struct {
Repo string `json:"repo"`
Branch string `json:"branch"`
Status PhaseStatus `json:"status"`
Order int `json:"order"`
Worktree string `json:"worktree,omitempty"` // path to the git worktree on host
Setup []string `json:"setup,omitempty"` // commands to run in the VM after every boot (template `on up`)
// OnCreate is the list of commands to run once after the very first
// boot of the sandbox, before the runner attaches. Sourced from the
// repo Clawkfile's `on create` block. Hard-fails the up — see
// Sandbox.CreatePending.
OnCreate []string `json:"on_create,omitempty"`
// OnCreateAt records the wall-clock time `on create` last completed
// successfully for this phase. Zero means it has not run yet (or the
// sandbox is in the create-pending state). Used by up.go to decide
// whether to run `on create` again after a failure.
OnCreateAt time.Time `json:"on_create_at,omitempty"`
// InPlace, if true, means Worktree points at the user's actual
// directory rather than a dedicated git worktree. Set by
// `clawk here`. Destroy skips `git worktree remove` for these.
InPlace bool `json:"in_place,omitempty"`
}
type PhaseStatus ¶
type PhaseStatus string
const ( PhaseStatusPending PhaseStatus = "pending" PhaseStatusActive PhaseStatus = "active" PhaseStatusMerged PhaseStatus = "merged" )
type Policy ¶
type Policy struct {
Name string `json:"name"`
AllowDomains []string `json:"allow_domains,omitempty"`
AllowIPs []string `json:"allow_ips,omitempty"`
DenyDomains []string `json:"deny_domains,omitempty"`
DenyIPs []string `json:"deny_ips,omitempty"`
// Source is an external blocklist URL (hosts/Adblock/plain formats).
// Fetched entries live in the sibling cache file, not here.
Source string `json:"source,omitempty"`
// Refresh bounds cache staleness, as a Go duration string ("24h").
Refresh string `json:"refresh,omitempty"`
}
Policy is a named, reusable block of network rules, referenced from sandboxes via `use <name>`. Stored at policies/<name>/policy.json.
func BuiltinDefaultPolicy ¶
func BuiltinDefaultPolicy() *Policy
BuiltinDefaultPolicy is the "default" chain block: the dev allowlist. It is never stored on disk — LoadPolicy synthesizes it so a `use default` entry always resolves.
type PolicyCache ¶
type PolicyCache struct {
FetchedAt time.Time `json:"fetched_at"`
ETag string `json:"etag,omitempty"`
DenyDomains []string `json:"deny_domains"`
AllowDomains []string `json:"allow_domains,omitempty"` // @@ exceptions
}
PolicyCache holds entries fetched from Policy.Source. Regenerable; stored at policies/<name>/cache.json.
type PortForward ¶
PortForward maps a host port to a guest port so services running in the VM are reachable from the host (e.g., dev servers). Applied at VM start time; changes require an `up` cycle to take effect.
func (PortForward) String ¶
func (p PortForward) String() string
type Provider ¶
type Provider string
Provider identifies which VM backend runs a sandbox. Values are persisted in sandbox records and clawk.mod files — frozen (see VMState); legacyProviderVFKit below is what a rename costs.
const ( // ProviderVZ runs sandboxes on macOS via Apple's // Virtualization.framework (no external VMM binary) with gvproxy for // userspace networking and ACL enforcement. The default on macOS. ProviderVZ Provider = "vz" // ProviderFirecracker runs sandboxes on Linux via Firecracker. // Networking is TAP-on-bridge with no host-side filtering. ProviderFirecracker Provider = "firecracker" )
type Sandbox ¶
type Sandbox struct {
Name string `json:"name"`
Provider Provider `json:"provider"`
Profile string `json:"profile,omitempty"` // active overlay profile (if any)
// Namespace groups the sandbox for organization and (Phase 2)
// per-namespace defaults. Empty is treated as DefaultNamespace; use
// NamespaceName for the resolved value.
Namespace string `json:"namespace,omitempty"`
// Anchor is the directory this sandbox is bound to, set for sandboxes
// created by the bare `clawk` invocation (addressed by being in the
// directory rather than by a typed name). Empty means the sandbox is
// explicitly named. Its presence is the cwd-vs-ticket discriminator.
Anchor string `json:"anchor,omitempty"`
// DesiredState is the lifecycle state the user wants: VMStateRunning after
// `clawk up`, VMStateStopped after `clawk down`. Empty means no explicit
// intent yet. It's *spec* (desired), distinct from VMState below (*status*,
// observed) — and it's what a future server-side reconciler converges to,
// so the imperative up/down commands are already declarative edits.
DesiredState VMState `json:"desired_state,omitempty"`
// ResourceVersion increments on every spec write. Groundwork for optimistic
// concurrency once there are multiple writers (the cloud control plane);
// today it's just a monotonic "this record changed" counter.
ResourceVersion int `json:"resource_version,omitempty"`
// RecordSchema is the schema version of this record's JSON shape,
// stamped by Store.Save on every write (RecordSchemaVersion). Distinct
// from the store-wide version in meta.json: that one drives directory
// migrations, this one says which clawk shape wrote THIS record, so a
// selective per-record migration is possible without guesswork. Zero
// means the record predates the field: schema 1.
RecordSchema int `json:"record_schema,omitempty"`
Phases []Phase `json:"phases"`
Network NetworkPolicy `json:"network"`
Forwards []PortForward `json:"forwards,omitempty"`
// Files is the list of host->guest file copies refreshed on every
// `clawk up`. See HostFile. Empty = no snapshots.
Files []HostFile `json:"files,omitempty"`
// HostShare. Empty = no live mounts.
//
// Changes to this list require `clawk down && clawk up` to re-emit
// the provider's device list, but the disk image survives.
Shares []HostShare `json:"shares,omitempty"`
// Instructions are extra persistent-guidance blocks rendered into the
// generated workspace CLAUDE.md (see sandbox.WorkspaceDocFile), sourced
// from the namespace and a repo's clawk.mod. Each entry is a markdown
// block read on every boot — the place for "always ask before X" or
// project conventions that must survive a throwaway VM.
Instructions []string `json:"instructions,omitempty"`
// Memory is seed content for the agent's auto-memory MEMORY.md, sourced
// from the namespace and clawk.mod. Written into the memory dir once on
// first boot and never afterward (see sandbox.SeedClaudeMemory), so a
// fresh sandbox starts with baseline knowledge without clobbering memory
// the agent has since accumulated.
Memory string `json:"memory,omitempty"`
// Image is the OCI image reference this sandbox boots as its root
// filesystem (clawk.mod `vm ( image <ref> )`). The provider builds an
// ext4 rootfs from the image (with clawk-init and the pty-agent
// injected), boots it via direct-kernel, and the sandbox runs
// sshd-free — all host access goes over the vsock agent.
Image string `json:"image,omitempty"`
// Kernel overrides the guest kernel the vz provider direct-boots: a
// local vmlinux path or an http(s) URL. Empty = the default Kata
// kernel. Declared via clawk.mod `vm ( kernel <path|url> )` or the
// --kernel flag. The main use is supplying a KVM-enabled kernel for
// nested virtualization (the stock Kata kernel has KVM disabled, so
// the guest has no /dev/kvm).
Kernel string `json:"kernel,omitempty"`
// GuestABI records the guest-contract version (the clawk-init boot
// manifest schema + the pty-agent vsock protocol; see
// sandbox.CurrentGuestABI) baked into this sandbox's disk at create.
// Guest binaries are never updated in place, so this is the record a
// later host consults to fail readably ("recreate this sandbox")
// instead of hitting an in-guest version error mid-boot. Zero means
// the record predates the field: ABI 1.
GuestABI int `json:"guest_abi,omitempty"`
// RequiredEnv holds the names (not values) of host env vars this
// sandbox wants exported inside the VM. Declared in clawk.mod via
// `env ( NAME ... )`. Values are read from the host shell at
// sandbox-create time and written to /etc/profile.d/99-clawk-env.sh
// in the guest — never persisted to disk on the host alongside the
// name so we don't check secrets into the sandbox state file.
RequiredEnv []string `json:"required_env,omitempty"`
// NestedVirt opts the VM into hardware-assisted nested
// virtualization so the guest can run its own VMs (Docker with KVM,
// Firecracker, etc.). Requires macOS 15+ and M3-or-newer Apple
// Silicon. Enabled per-sandbox at create time; changes require a
// destroy+recreate to take effect.
NestedVirt bool `json:"nested_virt,omitempty"`
// CPU is the vCPU count exposed to the guest. Zero means the provider
// picks a default. Not a reservation — KVM and VZ don't charge host CPU
// time for idle vCPUs.
CPU uint `json:"cpu,omitempty"`
// MemoryMiB is the baseline memory target in mebibytes. When
// MemoryMaxMiB > MemoryMiB, providers that support virtio-balloon
// reclaim (max - baseline) back to the host at boot and let the guest
// grow on demand via deflate_on_oom. Zero = no ballooning.
MemoryMiB uint64 `json:"memory_mib,omitempty"`
// MemoryMaxMiB is the guest-visible hard cap on memory in mebibytes —
// the amount allocated at boot. Zero = provider default.
MemoryMaxMiB uint64 `json:"memory_max_mib,omitempty"`
// IdleTimeoutSec is how long the sandbox may sit idle (no attached
// session, quiescent guest) before its VM daemon stops it to reclaim
// host memory. Zero = the built-in default; negative = never stop.
// Declared via clawk.mod `vm ( idle_timeout <dur|off> )`. An idle stop
// is a park, not a `clawk down`: DesiredState stays running and any
// attach boots the VM back.
IdleTimeoutSec int64 `json:"idle_timeout_sec,omitempty"`
// --- Status: observed runtime state, NOT user-authoritative ---------------
// A reconciled cache; the provider/OS is the source of truth. Reconcile via
// observe() before trusting these for a decision (clawk list/status/migrate
// already do).
VMState VMState `json:"vm_state"`
// StopReason records why the VM last left the running state, when the
// stop wasn't an explicit user verb. Today the only value is
// StopReasonIdle (the daemon parked an idle VM). Cleared on every boot
// and on explicit `clawk down`, so a bare "stopped" always means the
// user asked for it.
StopReason StopReason `json:"stop_reason,omitempty"`
VMPid int `json:"vm_pid,omitempty"`
GuestIP string `json:"guest_ip,omitempty"`
GatewayIP string `json:"gateway_ip,omitempty"`
MACAddress string `json:"mac_address,omitempty"`
// CreatePending is set when one of the phase `on create` commands has
// failed at least once. The VM is left running so the user can shell in
// and investigate; runner attach is refused with an actionable message;
// the next `clawk up` re-runs `on create` from scratch. `clawk destroy`
// is the explicit reset.
CreatePending bool `json:"create_pending,omitempty"`
// CreatePendingReason carries the human-readable failure (phase name +
// failing command + provider error) so `clawk status` can surface it
// without re-running anything. Cleared on a successful `on create`.
CreatePendingReason string `json:"create_pending_reason,omitempty"`
// SessionProject is the stable identifier of the session-history repo
// this sandbox's Claude Code conversations belong to (see
// internal/sessions.ProjectID). Sandboxes that work on the same repo set
// share one history repo — each on its own branch — so a fresh sandbox
// for the same project boots with prior transcripts and memory. Computed
// and persisted on first bring-up; empty on sandboxes created before the
// feature, in which case it is derived again from the phases. Currently
// populated by the vz provider only.
SessionProject string `json:"session_project,omitempty"`
CreatedAt time.Time `json:"created_at"`
// PRRefreshedAt is the wall-clock time of the last successful
// PR-state refresh via `gh`. v2 derives Phase.Status from PR
// state; this timestamp gates a 60-second cache so `clawk
// status` doesn't shell out on every invocation.
PRRefreshedAt time.Time `json:"pr_refreshed_at,omitempty"`
}
func (*Sandbox) DisplayName ¶
DisplayName returns the sandbox's human-facing name. See DisplayName.
func (*Sandbox) Key ¶
Key is the sandbox's store key, "<namespace>/<name>" — the identity the store resolves to an on-disk location. Bare names passed to the store (e.g. from CLI args) resolve to the default namespace; this is the explicit form for callers that hold the sandbox.
func (*Sandbox) NamespaceName ¶
NamespaceName is the sandbox's namespace, resolving the empty zero value to DefaultNamespace so callers never have to special-case it.
type StopReason ¶
type StopReason string
StopReason qualifies a VMStateStopped that the user didn't ask for. Values are persisted in sandbox records — frozen (see VMState).
const StopReasonIdle StopReason = "idle"
StopReasonIdle marks a VM the daemon stopped after the sandbox sat idle past its idle timeout. Distinct from an explicit `clawk down` so the CLI can render "stopped (idle)" and the attach path knows a mid-attach shutdown was a park it should transparently boot through.
const StopReasonSuspended StopReason = "suspended"
StopReasonSuspended marks a VM stopped by `clawk snapshot`: its memory + device state sits in a suspend file next to the VM, and the next boot (resume, up, or any attach verb) restores it exactly where it left off instead of cold-booting. Rendered as "stopped (suspended)".
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func NewStoreAt ¶
NewStoreAt creates a Store rooted at an explicit directory, skipping the home-dir resolution and legacy migration NewStore does. Used by `clawk image gc` (which targets a caller-supplied clawk root) and by tests.
func (*Store) DeletePolicy ¶
DeletePolicy removes a policy's whole directory — record and cache both.
func (*Store) HistoryDir ¶
HistoryDir returns the root holding per-project session-history repos — the bare git repos that version Claude Code conversations across sandboxes (see internal/sessions). Lives outside per-sandbox dirs so destroying one sandbox never touches the shared history.
<rootDir>/history/<projectID>.git
func (*Store) ListNamespaces ¶
ListNamespaces returns every namespace present on disk — those with a namespace.json and those that merely hold sandboxes.
func (*Store) ListPolicies ¶
ListPolicies returns every policy record on disk, sorted by name. The builtin "default" is synthesized, not stored, so it never appears here.
func (*Store) LoadNamespace ¶
LoadNamespace returns a namespace's config. A namespace directory with no namespace.json yet (e.g. one that only holds sandboxes) returns an empty config, so "no record" reads as "no defaults" rather than an error.
func (*Store) LoadPolicy ¶
LoadPolicy returns a policy by name. The reserved name "default" resolves to BuiltinDefaultPolicy; an absent record returns ErrPolicyNotFound.
func (*Store) LoadPolicyCache ¶
func (s *Store) LoadPolicyCache(name string) (*PolicyCache, error)
LoadPolicyCache returns a policy's fetched-entry cache. An absent cache reads as an empty one (zero FetchedAt → always stale), so "never fetched" needs no special casing at call sites.
func (*Store) NamespaceConfigExists ¶
NamespaceConfigExists reports whether a namespace has a config record (vs. being a bare grouping that only holds sandboxes).
func (*Store) NamespaceConfigPath ¶
NamespaceConfigPath is the on-disk path of a namespace's config record (for `clawk namespace edit`).
func (*Store) RootDir ¶
RootDir returns the top-level clawk directory (~/.clawk on a real host, or a temp dir for tests via NewStoreAt). Callers that need host-scoped artifacts living outside the per-sandbox subdirectories — provision.sh overrides, the long-lived OAuth token, image cache roots — anchor on this path.
func (*Store) RunMigrations ¶
RunMigrations replays every pending migration step in order, advancing the recorded version after each. Idempotent: steps already applied are skipped, and a step is safe to re-run. A disruptive step that defers running sandboxes does not advance the version (so it retries) and, by strict ordering, blocks later steps until it fully applies.
func (*Store) SaveNamespace ¶
func (*Store) SavePolicy ¶
SavePolicy writes a policy record, validating its name first (the builtin "default" is reserved and cannot be overwritten).
func (*Store) SavePolicyCache ¶
func (s *Store) SavePolicyCache(name string, c *PolicyCache) error
SavePolicyCache writes the fetched-entry cache for a policy. The cache is regenerable, so it lives beside the record rather than inside it — refresh is a one-file rewrite picked up by every referencing sandbox.
func (*Store) SchemaVersion ¶
SchemaVersion is the store's recorded on-disk schema version (0 if unset).
func (*Store) StateDir ¶
StateDir returns the per-sandbox persistent state directory — host storage that lives OUTSIDE the sandbox's VMDir and therefore survives destroy + recreate cycles. Used to mount Claude Code's conversation and memory directories from a stable location per sandbox name.
Layout:
<rootDir>/state/<sandboxName>/
claude/
projects/ # mounted as /home/agent/.claude/projects/
memory/ # mounted as /home/agent/.claude/memory/
The directory is created lazily by callers that need it.
func (*Store) WorktreeDir ¶
WorktreeDir returns the worktree directory for a sandbox.
type VMState ¶
type VMState string
VMState values are persisted in sandbox records: frozen — never rename an existing value, only add. Same rule for StopReason, Provider and the BlockOrigin* constants below; the vfkit→vz Provider rename left Normalize() carrying migration code forever, which is the tax this rule avoids.