models

package
v0.7.23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// MaxCustomDomainsPerSandbox bounds the host matcher fan-out per route.
	// 25 keeps the Caddy host-list small and matches the value documented in
	// plans/custom-domains.md.
	MaxCustomDomainsPerSandbox = 25
	// MaxCustomDomainsPerCreateRequest bounds how many hostnames a single
	// CreateSandbox call can attach. Lower than the per-sandbox cap so an
	// accidental array doesn't burn the ACME budget all at once.
	MaxCustomDomainsPerCreateRequest = 5
)

Custom-domain caps. Exported so internal/config can read SB_ env overrides against these defaults and the service layer can reference the canonical numbers from one place.

View Source
const (
	IngressTargetSourceHostname = "hostname"
	IngressTargetSourceIPs      = "ips"
	IngressTargetSourceMixed    = "mixed"
	IngressTargetSourceUnknown  = "unknown"
)

IngressTarget Source values. Constants exported so cluster and service layers can build targets without stringly-typed errors.

View Source
const (
	DNSRecordTypeCNAME = "CNAME"
	DNSRecordTypeA     = "A"
	DNSRecordTypeAAAA  = "AAAA"
	DNSRecordTypeANAME = "ANAME"
	DNSRecordTypeALIAS = "ALIAS"
)

DNS record types we emit. CNAME / A / AAAA are the routing records on-demand ACME + HTTP-01 require; TXT (composed inline) proves ownership. ANAME / ALIAS are apex-flattening alternatives to CNAME: a bare CNAME is illegal at a zone root (RFC 1034 §3.6.2), so providers expose flattening under different record types (Cloudflare: CNAME, dnsmadeeasy/EasyDNS: ANAME, Route 53/DNSimple: ALIAS). For an apex on a hostname ingress we emit all three as mutually-exclusive candidates — the caller adds the one their provider supports.

View Source
const (
	ContainerEngineDocker     = "docker"
	ContainerEngineContainerd = "containerd"
)

Host-level container engine identifiers. Distinct from Sandbox.Runtime (OCI runtime type: docker/gvisor/firecracker/wasm). Engine selects which daemon on the host realizes OCI sandboxes (dockerd vs native containerd).

View Source
const (
	MaxCredentialKeys  = 32
	MaxCredentialBytes = 4096
)

MaxCredentialKeys / MaxCredentialBytes bound credential payload size so a malicious request can't blow up daemon memory.

View Source
const (
	RuntimeDocker = "docker" // Docker's standard runc-backed runtime.
	RuntimeGvisor = "gvisor" // gVisor (runsc). User-space kernel for untrusted workloads.
	RuntimeKata   = "kata"   // Reserved: Kata Containers. Not yet implemented; rejected at create time.
	// RuntimeFirecracker selects the native Firecracker microVM runtime
	// (no Docker in the path). Plumbed through ValidRuntime so the API
	// accepts the identifier ahead of the implementation; CreateSandbox
	// rejects with ErrRuntimeNotImplemented until the per-host operator
	// opts in via SB_ENABLE_FIRECRACKER=true AND the driver has actually
	// landed. See plans/snapshot-clone-fast-boot.md.
	//
	// Distinct from RuntimeKata, which stays reserved for Kata Containers
	// (a different shape — Kata-with-Firecracker abstracts the snapshot
	// API away and is not what this constant refers to).
	RuntimeFirecracker = "firecracker"
	// RuntimeWasm selects the WASM/WASI runtime (plans/wasm-runtime.md).
	// ValidRuntime accepts the identifier ahead of the implementation; create
	// rejects with ErrRuntimeNotImplemented until SB_ENABLE_WASM lands.
	RuntimeWasm = "wasm"
	// RuntimeIsolate selects the V8-isolate (Workers-model) runtime
	// (plans/isolate-runtime.md). The name is deliberately engine-neutral —
	// "isolate" describes the model (isolates + fetch handler + capability
	// grants), not the workerd engine behind it. ValidRuntime accepts the
	// identifier ahead of the implementation; create rejects with
	// ErrRuntimeNotImplemented until the operator flips SB_ENABLE_ISOLATE
	// AND the driver has landed (same gate shape as firecracker/wasm).
	RuntimeIsolate = "isolate"
)

User-facing runtime identifiers. These are the values the API, SDK, and stored sandbox row carry — chosen to match what an operator searching for "Docker" or "gVisor isolation" would type. The pkg/docker layer translates each one to the underlying OCI runtime binary name (runc / runsc / ...) when shaping the daemon request.

The empty string is reserved for legacy rows that pre-date the runtime field — those resolve to the host default at start time.

View Source
const (
	DurabilityEphemeral    = "ephemeral"
	DurabilityPassivatable = "passivatable"
	DurabilityDurable      = "durable"
)

Durability class for sandbox restart/survival semantics (plans/wasm-runtime.md §4.2).

View Source
const (
	DefaultCPU      float64 = 1
	DefaultMemoryMB int     = 1024
	DefaultDiskGB   int     = 10
)

DefaultCPU, DefaultMemoryMB, DefaultDiskGB are the values normalizeCreateRequest substitutes when the caller leaves them at zero. Exported so any code path that has to reason about an un-normalized CreateSandboxRequest (placement scoring on the way IN, failover-recreate target selection on the way OUT) uses the same numbers — drift here causes placement to score against ghost capacity that doesn't match the eventual admission reservation.

View Source
const (
	ImageDistributionExternalRegistry = "external_registry"
	ImageDistributionAOCR             = "aocr"
	ImageDistributionLocalOnly        = "local_only"
	// ImageDistributionAOCRImported is the post-auto-import distribution mode:
	// the bytes have been re-mounted under the cluster's own AOCR namespace
	// (`cluster/<id>/_imported/...`) and subsequent pulls use the cluster PAT,
	// not the user's upstream credentials. The user-visible Image field is
	// preserved; only the RegistryRef and mode flip. See F21 of the
	// cluster-mirror-and-snapshot-distribution plan for the full rationale —
	// the short version is that this is what makes `failover.policy: recreate`
	// survive upstream credential rotation.
	ImageDistributionAOCRImported = "aocr_imported"
)
View Source
const (
	SnapshotPushStateActive  = "active"
	SnapshotPushStatePending = "pending"
	SnapshotPushStatePushing = "pushing"
	SnapshotPushStateError   = "error"
)

SnapshotPushState tracks the lifecycle of the optional background push of a local snapshot to the AOCR registry. The two terminal states a polling SDK cares about are "active" (snapshot is ready everywhere it can be) and "error" (push failed; the snapshot is still usable on the originating node and the reconciler will retry). "pending" / "pushing" are transient.

When SB_SNAPSHOT_PUSH_ENABLED is false, or when the snapshot's image already lives in a remote registry, new rows are written with state "active" directly — making the response shape identical to today's behavior.

View Source
const (
	FailoverPolicyNone     = "none"
	FailoverPolicyRecreate = "recreate"
)
View Source
const (
	ExposedPortProtocolHTTP = "http"
	ExposedPortProtocolTCP  = "tcp"
	ExposedPortProtocolTLS  = "tls"
)

Exposed port protocols. http is the original behavior (Caddy HTTP reverse proxy); tcp and tls are the caddy-l4 paths added with the L4 work.

View Source
const (
	FacadeDaytona = "daytona"
	FacadeE2B     = "e2b"
)

Facade names used by sandbox_compat_state, snapshot_aliases, and request_idempotency. The string is the only thing persisted, so renaming a facade later would require a one-shot UPDATE.

View Source
const (
	RequestStatePending = "pending"
	RequestStateReady   = "ready"
)

Idempotent-request states for request_idempotency.state. Pending means a write is in flight and holds the row's lock; Ready means the write completed and retries within ReplayUntil should replay TargetID instead of running again.

View Source
const (
	TemplatePushStateActive  = "active"
	TemplatePushStatePending = "pending"
	TemplatePushStatePushing = "pushing"
	TemplatePushStateError   = "error"
)

TemplatePushState tracks the lifecycle of the optional background push of a Firecracker template's artifacts (rootfs.ext4 + snapshot.memory + snapshot.state + manifest.json) to the AOCR registry under `cluster/<id>/templates/<tid>:latest`. Mirrors SnapshotPushState* — operators see one push-state vocabulary across snapshots and templates.

"active" is the terminal value (push succeeded OR push is disabled / not applicable on this node). New rows on a daemon with push disabled stay "active" forever; the reconciler only picks "pending" and "error".

View Source
const (
	HeaderJSBundleReplicated = "X-Cluster-Bundle-Replicated"
	HeaderJSBundleOwner      = "X-Cluster-Bundle-Owner"
)

JS-bundle cluster-replication headers. Isolate's bundle store is per-node, so the node that receives an upload fans the bundle out to its peers (otherwise an isolate create placed on a different node fails "bundle not found"). The replicated POST carries these so the receiver stores the bundle under the ORIGINAL owner (not the replication PAT's scope) and does NOT fan out again (loop guard). Defined here because both pkg/api/v1 (reader) and internal/cluster (writer) import models but not each other.

View Source
const MaxLifecycleDuration = 30 * 24 * time.Hour

MaxLifecycleDuration caps each Lifecycle field. 30 days is generous for any reasonable workload while still rejecting typos like "87600h" that would otherwise sit in the DB pretending to be useful.

View Source
const MaxMountsPerSandbox = 8

MaxMountsPerSandbox caps fan-out so a malicious request can't make sandboxd build an arbitrarily large container spec. Platform volumes count against the same cap as external mounts (a single shared budget).

Variables

View Source
var (
	// ErrCustomDomainInvalid is the validation error returned by
	// NormalizeCustomDomain. Wrapped with context (the offending hostname /
	// reason) so the API surfaces something actionable.
	ErrCustomDomainInvalid = errors.New("invalid custom domain")
	// ErrCustomDomainPerRequestCap is returned by ValidateCustomDomainList
	// when the caller passes too many hostnames in a single create request.
	ErrCustomDomainPerRequestCap = fmt.Errorf("at most %d custom_domains per create request", MaxCustomDomainsPerCreateRequest)
	// ErrCustomDomainNotSupported is returned when a deployment cannot accept
	// custom domains at all: the feature flag is off, or the daemon is running
	// in IP mode (no SB_DOMAIN). Surfaced as HTTP 412 Precondition Failed so
	// clients can distinguish "this cluster does not do custom domains" from
	// "your input is malformed" (which is 400).
	ErrCustomDomainNotSupported = errors.New("custom domains are not enabled on this deployment")
	// ErrCustomDomainProtocolConflict is the IRON RULE violation: a sandbox
	// with custom domains attached must not also expose protocol=tcp/tls
	// ports, because the L4 listener has no way to honor a host-based match
	// — the SNI would route to the wrong sandbox. Surfaced as 409 Conflict.
	ErrCustomDomainProtocolConflict = errors.New("custom domains cannot coexist with tcp/tls exposed ports on the same sandbox")
	// ErrCustomDomainPerSandboxCap is returned by AddCustomDomain when the
	// sandbox already holds MaxCustomDomainsPerSandbox rows. Surfaced as 409.
	ErrCustomDomainPerSandboxCap = fmt.Errorf("at most %d custom domains per sandbox", MaxCustomDomainsPerSandbox)
	// ErrCustomDomainVerificationFailed is returned when the DNS TXT record check fails.
	ErrCustomDomainVerificationFailed = errors.New("custom domain TXT record verification failed")
	// ErrCustomDomainInvalidTargetPort is returned when AddCustomDomainRequest
	// carries a target_port outside [0, 65535]. Zero is the toolbox-default
	// sentinel; anything else must be a real TCP port number. Surfaced as 400.
	ErrCustomDomainInvalidTargetPort = errors.New("invalid custom domain target_port")
	// ErrCustomDomainPortMismatch is returned when an idempotent re-add of
	// an already-attached hostname carries a different target_port than the
	// existing row. We do not silently change the dial target — that would
	// redirect live traffic without the caller knowing. Surfaced as 409 so
	// the caller can detach + re-add deliberately.
	ErrCustomDomainPortMismatch = errors.New("custom domain target_port mismatch on re-add")
)
View Source
var (
	// ErrPlatformVolumesDisabled is returned when a request references platform
	// volumes but the operator has not enabled them. Facades map this to 412
	// Precondition Failed.
	ErrPlatformVolumesDisabled = errors.New("platform volumes are not enabled on this deployment")
	// ErrPlatformVolumesUnsupportedRuntime is returned when platform volumes are
	// requested on a runtime that cannot bind-mount host paths. Firecracker
	// (ext4 block-device rootfs) and wasm (host-mediated, no container FS) both
	// silently drop binds, so they are rejected up front.
	ErrPlatformVolumesUnsupportedRuntime = errors.New("platform volumes are not supported on the firecracker or wasm runtime")
	// ErrPlatformVolumeQuota is returned when a tenant would exceed its
	// configured volume-count cap.
	ErrPlatformVolumeQuota = errors.New("tenant platform-volume quota reached")

	// ErrPlatformVolumeInUse is returned when a delete is attempted while one or
	// more live sandboxes still have the volume attached. Facades map this to
	// 409 Conflict.
	ErrPlatformVolumeInUse = errors.New("volume is still attached to one or more sandboxes")
)
View Source
var ErrBuildKitUnavailable = errors.New("image build on containerd requires BuildKit; buildkitd is not configured on this daemon")

ErrBuildKitUnavailable is returned by image-build endpoints when the host engine is containerd but buildkitd is not wired yet (Phase 3). Clear error, never a hang — see plans/containerd-engine.md §7.4.

View Source
var ErrContainerEngineNotRegistered = errors.New("container engine driver not registered on this node")

ErrContainerEngineNotRegistered is returned when a sandbox row points at an engine driver that was not wired at daemon boot (e.g. containerd row on a docker-only node).

View Source
var ErrRuntimeNotImplemented = errors.New("runtime not yet implemented on this build")

ErrRuntimeNotImplemented is returned when a runtime is recognized as a valid identifier but the implementation has not been wired up yet. Today only "kata" hits this path. Surfaced through the API as a 4xx so operators get an actionable error instead of a generic 500.

View Source
var ErrSandboxExists = errors.New("sandbox already exists")

ErrSandboxExists is returned when a sandbox row with the same primary key already exists. Distinct from name conflicts (store.ErrSandboxNameConflict).

View Source
var ErrSnapshotCorrupt = errors.New("snapshot integrity verification failed")

ErrSnapshotCorrupt is returned when a runtime snapshot fails checksum verification at load time — the on-disk artifact does not match the checksum stamped at capture. Firecracker templates and WASM boundary checkpoints both surface this; the service layer intercepts with errors.Is to mark the backing artifact unhealthy and kick rebuild.

View Source
var ErrSnapshotFenced = errors.New("snapshot clone generation fenced")

ErrSnapshotFenced is returned when a snapshot's clone_generation is older than the store row's current token (§4.8 zombie-write guard).

View Source
var ErrTemplateNotRebuildable = errors.New("template not eligible for rebuild")

ErrTemplateNotRebuildable is returned by RequestTemplateRebuild when the row is in a state where re-running the snapshot phase is unsafe or unsupported:

  • pending / building_rootfs / snapshotting: the initial build is in flight; the goroutine owns the row's state machine and a concurrent rebuild would race the goroutine's status writes.
  • ready_no_snapshot: the snapshot phase failed during initial build and the row has no usable snapshot artifact to re-derive from. A full from-scratch rebuild requires source-image access (deferred — see internal/service/template_health.go comments).
  • failed: terminal state from a failed initial build; operator must delete + recreate.

apihttp.WriteStoreAwareError maps this to 412 Precondition Failed so the operator's tooling can distinguish "row not in a rebuildable state" from "row missing" (404) and "invalid request" (400).

Functions

func DefaultDurabilityForRuntime

func DefaultDurabilityForRuntime(runtime string) string

DefaultDurabilityForRuntime returns the API default when the caller omits durability on create. WASM and isolate default to ephemeral; container/VM runtimes default to passivatable (they survive restarts natively).

func DiskGBEnforced

func DiskGBEnforced(engine, runtime string) bool

DiskGBEnforced reports whether the host engine actually applies CreateSandbox DiskGB as a storage quota. dockerd may (xfs+pquota StorageOpt); containerd overlayfs and gVisor do not today — callers should warn, not fail.

func ModuleRefForCreate

func ModuleRefForCreate(req CreateSandboxRequest) string

ModuleRefForCreate returns the WASM module reference from a create request.

func NormalizeCreateDurability

func NormalizeCreateDurability(value, runtime string) (string, error)

NormalizeCreateDurability validates req.Durability, applies the runtime default when empty, and rejects classes the chosen runtime cannot honor yet.

func NormalizeCustomDomain

func NormalizeCustomDomain(host, baseDomain string) (string, error)

NormalizeCustomDomain lowercases, strips a trailing dot, and validates the shape of host. baseDomain is the operator's SB_DOMAIN — empty means the daemon is in IP mode, in which case the caller is expected to reject the request at the 412 layer before reaching validation. We still validate the hostname shape so callers cannot rely on the IP-mode short-circuit.

Returns the canonical (lowercased, dotted-trimmed) hostname on success.

Rejection rules (each maps to a test in types_test.go):

  • empty or whitespace-only
  • any label > 63 chars
  • total length > 253 chars
  • non-RFC1035 label charset (letters, digits, hyphen; no leading/trailing hyphen on a label)
  • fewer than two labels (single-label is not a public hostname)
  • IP literal (v4 or v6)
  • "localhost" or anything ending in ".local"
  • anything under baseDomain — wildcard already covers it; allowing a custom-domain row would let one tenant steal another sandbox's URL

func NormalizeFailoverPolicy

func NormalizeFailoverPolicy(policy string) (string, error)

func NormalizeImageDistributionMode

func NormalizeImageDistributionMode(mode string) (string, error)

func ResolveContainerEngine

func ResolveContainerEngine(value string) (string, error)

ResolveContainerEngine normalizes SB_CONTAINER_ENGINE. Empty or unknown values default to docker so pre-migration hosts behave identically.

func ResolveContainerEngineOrDocker

func ResolveContainerEngineOrDocker(value string) string

ResolveContainerEngineOrDocker is ResolveContainerEngine that never errors — unknown/empty → docker (boot-path safe).

func ResolveOCIRuntime

func ResolveOCIRuntime(value string) (string, error)

ResolveOCIRuntime maps a user-facing runtime identifier to the OCI runtime binary name to set on Docker's HostConfig.Runtime. Returns the binary name and true on success, or ErrRuntimeNotImplemented if the identifier is valid but the build does not implement it yet (today: kata).

The empty string is treated as "no override" — the caller leaves HostConfig.Runtime unset and Docker uses its compiled-in default. The "docker" identifier maps to "" for the same reason: avoiding an explicit "runc" entry means the daemon is happy even when /etc/docker/daemon.json has no runtimes.runc map entry, which is the common case.

func SandboxEngine

func SandboxEngine(sandbox *Sandbox) string

SandboxEngine returns the engine that owns this sandbox's container lifecycle. Legacy rows with an empty engine column resolve to docker.

func ValidDurability

func ValidDurability(value string) (string, error)

ValidDurability normalizes and validates a durability class. Empty input passes through so the caller can apply a runtime-specific default.

func ValidExposedPortProtocol

func ValidExposedPortProtocol(value string) (string, error)

ValidExposedPortProtocol normalizes "" to http (the historical default) and rejects unknown values. Any caller that surfaces user input must run it through this before persistence.

func ValidRuntime

func ValidRuntime(value string) (string, error)

ValidRuntime normalizes and validates a user-facing runtime identifier. Empty input passes through unchanged so the caller can substitute the host default; any other value must be one of the recognized names. The intent here is "fail fast at the API boundary" — by the time a request reaches the runtime layer, we should already know the value is one we can act on.

func ValidateCustomDomainList

func ValidateCustomDomainList(hosts []string, baseDomain string) ([]string, error)

ValidateCustomDomainList normalizes every entry in hosts against baseDomain and enforces the per-create-request cap and intra-list uniqueness. Returns the canonical slice or the first error. Empty input returns nil, nil — the caller treats "no custom domains" the same as omitting the field.

func ValidateCustomDomainTargetPort

func ValidateCustomDomainTargetPort(port int) error

ValidateCustomDomainTargetPort returns nil for 0 (the toolbox sentinel) and any 1..65535. Anything else is ErrCustomDomainInvalidTargetPort. Kept alongside the other custom-domain validators so the API + service layers reference one canonical bound.

func ValidateRuntimeRequest

func ValidateRuntimeRequest(req CreateSandboxRequest, effectiveRuntime string, privileged bool, logf func(string, ...any)) error

ValidateRuntimeRequest enforces runtime policy shared by dockerd and containerd drivers. Privileged mode, GPU vendor, and disk quota warnings must not fork per engine.

Types

type AddCustomDomainRequest

type AddCustomDomainRequest struct {
	Hostname   string `json:"hostname"`
	TargetPort int    `json:"target_port,omitempty"`
}

AddCustomDomainRequest is the body for POST /v1/sandboxes/{id}/custom-domains. Omitting target_port (or sending 0) routes the hostname to the toolbox agent, preserving pre-v2 behavior.

type CreateJSBundleRequest

type CreateJSBundleRequest struct {
	Name              string            `json:"name,omitempty"`
	MainModule        string            `json:"main_module,omitempty"`
	Source            string            `json:"source,omitempty"`
	Modules           map[string]string `json:"modules,omitempty"`
	CompatibilityDate string            `json:"compatibility_date,omitempty"`
}

CreateJSBundleRequest is the body for POST /v1/js-bundles — the "no image, no registry" upload path for the isolate runtime (plans/isolate-runtime.md §8). The bundle is the JS/TS a workerd isolate runs. A one-file bundle sets Source (+ optional MainModule name); a multi-module bundle sets Modules directly. Name is an optional human alias the caller can reference on create instead of the digest; it is scoped to the caller's identity. The API is EXPERIMENTAL until the §10.1 demand checkpoint passes.

type CreateSandboxRequest

type CreateSandboxRequest struct {
	Image string `json:"image"`
	// ImageDistributionMode classifies whether Image can be pulled on any
	// worker (external_registry/aocr) or is pinned to the local node
	// (local_only). Empty is resolved by the service from snapshot metadata
	// or by the default image distribution provider.
	ImageDistributionMode string     `json:"image_distribution_mode,omitempty"`
	ImageDigest           string     `json:"image_digest,omitempty"`
	ImageRegistryRef      string     `json:"image_registry_ref,omitempty"`
	ImageVerifiedAt       *time.Time `json:"image_verified_at,omitempty"`
	// CPU is the number of CPU cores to allocate. Fractional values are
	// supported (e.g. 0.5 = half a core, 1.5 = one and a half cores).
	// Translates to Docker's CpuQuota at 100ms periods.
	CPU              float64           `json:"cpu"`
	MemoryMB         int               `json:"memory_mb"`
	DiskGB           int               `json:"disk_gb"`
	Env              map[string]string `json:"env"`
	OSUser           string            `json:"os_user"`
	NetworkBlockAll  bool              `json:"network_block_all"`
	Registry         *RegistryAuth     `json:"registry,omitempty"`
	ContainerCommand []string          `json:"container_command,omitempty"`
	Mounts           []MountSpec       `json:"mounts,omitempty"`
	// PlatformVolumes references named, operator-backed persistent volumes by
	// name only — the user never supplies storage coordinates or credentials.
	// The service translates each entry into a synthesized, tenant-scoped
	// MountSpec against the operator's shared backend (see
	// plans/e2b-volume-mounts.md). All facades (E2B volumeMounts, Daytona
	// volumes) and the native SDKs populate this single neutral field so the
	// translation lives in one version-agnostic place.
	PlatformVolumes []PlatformVolumeMount `json:"platform_volumes,omitempty"`
	Lifecycle       *Lifecycle            `json:"lifecycle,omitempty"`
	Failover        *Failover             `json:"failover,omitempty"`
	// Name is an optional human-readable identifier. When set, it must be
	// unique across all sandboxes — the store enforces this with a partial
	// unique index. Empty means no name; the sandbox can only be referenced
	// by ID. The Daytona facade requires names; the native /v1 API and other
	// facades may set or omit it.
	//
	// Duplicate names are rejected with HTTP 409 Conflict. Use a unique name
	// per sandbox or omit this field to let the daemon assign an ID instead.
	Name string `json:"name,omitempty"`
	// Tags is an optional free-form key/value map associated with the
	// sandbox. Used by facades that expose label-style metadata (Daytona
	// labels, E2B metadata).
	Tags map[string]string `json:"tags,omitempty"`
	// Runtime selects the container runtime for this sandbox. Empty falls back
	// to the host default (SB_CONTAINER_RUNTIME). Allowed values: "docker"
	// (standard runc-backed Docker runtime, default), "gvisor" (runsc-backed
	// userspace kernel — use for untrusted workloads), or "kata" (reserved,
	// not yet implemented).
	Runtime string `json:"runtime,omitempty"`
	// GPUs attaches GPU resources to the sandbox. Nil means no GPU. GPU access
	// is not supported with the gVisor runtime — the API returns an error if
	// both GPUs and runtime="gvisor" are set.
	GPUs *GPURequest `json:"gpus,omitempty"`
	// NetworkBytesInLimit caps lifetime ingress bytes for the sandbox. Zero
	// means unlimited. When the cumulative counter crosses the limit, the
	// reconcile loop installs an ingress DROP rule. The "we already paid for
	// the bytes you see in the meter" caveat applies — host-side ingress is
	// counted after the NIC has accepted the packet.
	NetworkBytesInLimit int64 `json:"network_bytes_in_limit,omitempty"`
	// NetworkBytesOutLimit caps lifetime egress bytes. Zero means unlimited.
	// Crossing the limit installs an egress DROP rule via the same primitive
	// NetworkBlockAll uses.
	NetworkBytesOutLimit int64 `json:"network_bytes_out_limit,omitempty"`
	// NetworkAllowOut is an egress allowlist of CIDRs: when non-empty the
	// sandbox may reach only these destinations and everything else is
	// dropped. Mutually exclusive with NetworkDenyOut. Enforced by the host
	// firewall (EnableNetworkRules) and a no-op when network rules are off.
	NetworkAllowOut []string `json:"network_allow_out,omitempty"`
	// NetworkDenyOut is an egress blocklist of CIDRs: the sandbox may reach
	// anything except these destinations. Mutually exclusive with
	// NetworkAllowOut. Blocking the entire space is expressed as
	// NetworkBlockAll, not a denyOut of 0.0.0.0/0.
	NetworkDenyOut []string `json:"network_deny_out,omitempty"`
	// AllowPublicTraffic controls whether the sandbox may be exposed to the
	// public internet. On create, omitted (nil) defaults to private — no
	// <id>.<domain> ingress route and empty public_url. Pass an explicit true
	// to opt in at create time, or call expose_port later (which flips the
	// sandbox public as the opt-in lever). False always refuses exposure.
	// The sandbox stays reachable via the platform's own paths (toolbox
	// exec/file proxy, SSH gateway), which do not route through the public
	// ingress.
	AllowPublicTraffic *bool `json:"allow_public_traffic,omitempty"`
	// MaskRequestHost, when non-empty, rewrites the upstream Host header on
	// ingress to exposed HTTP ports to this value. Dev servers and frameworks
	// that validate the Host header (Vite allowedHosts, webpack-dev-server,
	// Django ALLOWED_HOSTS, Rails host authorization) reject requests whose
	// Host is the per-sandbox public hostname; masking it to a value the app
	// accepts (e.g. "localhost") unblocks them. Empty = pass through whatever
	// the route would otherwise send (today's behavior). HTTP-only; TCP/TLS
	// exposures ignore it (no Host header in raw passthrough).
	MaskRequestHost string `json:"mask_request_host,omitempty"`
	// CustomDomains attaches operator-provided public hostnames to the
	// sandbox at create time. Each hostname must resolve (via DNS) to the
	// cluster's ingress host before HTTPS can serve traffic — see
	// plans/custom-domains.md. Bare strings on the wire; the response shape
	// (Sandbox.CustomDomains) carries per-hostname status. Capped by
	// MaxCustomDomainsPerCreateRequest.
	CustomDomains []string `json:"custom_domains,omitempty"`
	// TemplateID, when set alongside Runtime="firecracker", skips the
	// per-create OCI→ext4 build and reuses a previously prepared
	// rootfs.ext4 from a Firecracker template (POST /v1/templates). The
	// template must be in status="ready" or Create rejects with a
	// not-ready error. Ignored on the Docker path — templates are a
	// Firecracker-only concept (see plans/snapshot-clone-fast-boot.md
	// Phase 2). Pre-snapshot phase: the rootfs is the only artifact a
	// template provides; Phase 3 layers snapshot.memory/state on top.
	TemplateID string `json:"template_id,omitempty"`
	// OverlaySizeGB, when > 0 and Runtime="firecracker", attaches a
	// per-sandbox writable virtio-blk device (drive_id="overlay") of
	// this size at /dev/vdb. On the snapshot-load fast-boot path the
	// device must exist in the template snapshot's virtio-blk state —
	// templates built before Phase 3 PR-B (has_overlay=false) reject
	// this field with an error pointing at POST /v1/templates for a
	// rebuild. The host allocates a sparse file; the guest is
	// responsible for mkfs and mount (or set SB_FIRECRACKER_OVERLAY_MKFS
	// on the daemon to have the host mkfs.ext4 the file at create time).
	// Ignored on the Docker path. Capped at 1024 GiB by request
	// validation; the daemon's admission control may reject smaller.
	OverlaySizeGB int `json:"overlay_size_gb,omitempty"`
	// Durability selects the sandbox survival class across restarts (§4.2 in
	// plans/wasm-runtime.md). Empty defaults per runtime: passivatable for
	// docker/firecracker/gvisor; ephemeral for wasm. "durable" is WASM-only
	// and not yet implemented on any runtime.
	Durability string `json:"durability,omitempty"`
	// ModuleRef selects the WASM module for runtime=wasm. When set, Image may
	// be omitted; when empty, Image is treated as the module reference.
	// runtime=isolate reuses this field for the JS/TS bundle reference
	// (plans/isolate-runtime.md §9) — no separate bundle_ref field until the
	// demand checkpoint validates the tier.
	ModuleRef string `json:"module_ref,omitempty"`
	// TenantID is the isolate-group key for runtime=isolate: sandboxes with
	// the same tenant share one workerd process; the OS-process boundary
	// between tenants is the REQUIRED cross-tenant isolation boundary
	// (plans/isolate-runtime.md §2.1). SECURITY: this value is
	// server-authorized, never a free-form client value — a caller who could
	// choose an arbitrary group key could force co-residency inside another
	// tenant's process. The service layer rejects values the authenticated
	// identity is not authorized to use; when empty, the group key falls back
	// to the authenticated API identity. Ignored by other runtimes today.
	TenantID string `json:"tenant_id,omitempty"`
}

func (*CreateSandboxRequest) ApplyImageDistribution

func (r *CreateSandboxRequest) ApplyImageDistribution(meta ImageDistributionMetadata)

func (CreateSandboxRequest) ImageDistribution

func (r CreateSandboxRequest) ImageDistribution() ImageDistributionMetadata

func (CreateSandboxRequest) ShouldRecreateOnFailover

func (r CreateSandboxRequest) ShouldRecreateOnFailover() bool

type CreateSandboxResponse

type CreateSandboxResponse struct {
	Sandbox
	SSHPrivateKey string `json:"ssh_private_key,omitempty"`
}

CreateSandboxResponse is what the API returns from POST /v1/sandboxes. SSHPrivateKey is generated server-side per sandbox and returned exactly once — it is never persisted and never returned again. The corresponding public key is stored on the sandbox record and is the only key authorized to SSH into that sandbox.

type CreateSandboxSnapshotRequest

type CreateSandboxSnapshotRequest struct {
	Name string `json:"name"`
}

CreateSandboxSnapshotRequest creates a reusable local image snapshot from an existing sandbox container. Name is the image reference callers can later pass back into CreateSandboxRequest.Image.

type CreateSessionRequest

type CreateSessionRequest struct {
	// Name is the human-friendly session label. Two callers asking for the
	// same Name see the same session (idempotent attach). Default "default".
	Name    string            `json:"name,omitempty"`
	Argv    []string          `json:"argv,omitempty"`
	Command string            `json:"command,omitempty"`
	WorkDir string            `json:"workdir,omitempty"`
	Env     map[string]string `json:"env,omitempty"`
	PTY     bool              `json:"pty,omitempty"`
	Cols    int               `json:"cols,omitempty"`
	Rows    int               `json:"rows,omitempty"`
}

CreateSessionRequest is the body of POST /sessions on toolboxd (and the shape the sandboxd proxy forwards). Either Argv or Command may be supplied; Command is run via `sh -c` for convenience.

type CreateTemplateRequest

type CreateTemplateRequest struct {
	ID         string `json:"id,omitempty"`
	Image      string `json:"image"`
	MinSizeMiB int    `json:"min_size_mib,omitempty"`
}

CreateTemplateRequest is the body for POST /v1/templates. ID is optional — empty means "service generates one"; supplying an explicit ID lets a deploy pipeline assert idempotency across retries (the store rejects a duplicate ID with ErrTemplateIDConflict → API 409). Image is the skopeo-style ref ("docker://python:3.11", "oci-archive:/path.tar", etc.) passed through verbatim to pkg/oci.Builder. MinSizeMiB is an optional floor for the ext4 image — useful when the unpacked rootfs is small but the operator expects guests to write substantially more.

type CreateWasmModuleRequest

type CreateWasmModuleRequest struct {
	ID         string `json:"id,omitempty"`
	ModuleRef  string `json:"module_ref"`
	Entrypoint string `json:"entrypoint,omitempty"`
}

CreateWasmModuleRequest is the body for POST /v1/wasm-modules. ID is optional — empty means the service uses the module digest as the catalogue id. ModuleRef is resolved synchronously on this host.

type CustomDomain

type CustomDomain struct {
	Hostname string             `json:"hostname"`
	Status   CustomDomainStatus `json:"status"`
	// TargetPort is the in-container TCP port traffic for this hostname
	// reverse-proxies to. Zero means "use the toolbox port" (the legacy
	// behavior before per-domain target ports existed). Non-zero values
	// dial straight to the user's app — e.g. an HTTP server on 3333.
	// Set once at attach time; changing it requires detach + re-add so
	// in-flight traffic can't silently redirect.
	TargetPort int       `json:"target_port,omitempty"`
	LastError  string    `json:"last_error,omitempty"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
}

CustomDomain is the per-domain row returned in Sandbox.CustomDomains.

type CustomDomainDNSRecords

type CustomDomainDNSRecords struct {
	Records []DNSRecord   `json:"records"`
	Target  IngressTarget `json:"target"`
}

CustomDomainDNSRecords is the response body for GET /v1/sandboxes/{id}/custom-domains/dns. Records is the flat ready-to-paste list (one row per custom domain × per ingress address); Target is the raw aggregation the records were composed from, included so callers can render their own UI without re-fetching.

type CustomDomainStatus

type CustomDomainStatus string

CustomDomainStatus is the per-domain lifecycle state surfaced through the API + SDK so callers can render "DNS not pointed yet" vs "issuing cert" vs "ready" without polling Caddy. Driven entirely server-side; clients read.

const (
	// CustomDomainPendingDNS — row exists, Caddy has not yet asked for the
	// hostname. This is the state after AddCustomDomain returns and before
	// the first inbound HTTPS connection.
	CustomDomainPendingDNS CustomDomainStatus = "pending_dns"
	// CustomDomainIssuing — first ask hit, ACME flow started. Cleared on the
	// next ask that sees the cert in storage.
	CustomDomainIssuing CustomDomainStatus = "issuing"
	// CustomDomainReady — cert in shared storage, serving connections.
	CustomDomainReady CustomDomainStatus = "ready"
	// CustomDomainFailed — Caddy gave up on ACME for this host (LastError
	// carries the surfaced reason). No automatic retry beyond Caddy's own
	// backoff; the next ask will flip it back to issuing.
	CustomDomainFailed CustomDomainStatus = "failed"
)

type DNSRecord

type DNSRecord struct {
	Hostname string `json:"hostname"`
	Type     string `json:"type"`
	Name     string `json:"name"`
	Value    string `json:"value"`
	Notes    string `json:"notes,omitempty"`
}

DNSRecord is one DNS row a user must add at their provider to make a custom domain reach the cluster. Hostname is the full custom hostname (api.acme.com); Name is the leftmost label (or "@" for apex) the way DNS UIs accept it; Value is the right-hand side (target hostname or IP).

Notes carries provider-specific gotchas the SDK pre-renders so callers don't have to embed the documentation themselves. Currently only used to surface the Cloudflare "DNS only, gray cloud" warning — without it the proxy terminates TLS and on-demand ACME never fires.

func ComposeDNSRecords

func ComposeDNSRecords(hostnames []string, target IngressTarget, txtNamePrefix, txtValuePrefix string) []DNSRecord

ComposeDNSRecords expands every hostname against an IngressTarget into the set of DNS records the user must create at their provider. Shape rules:

  • Subdomains with target.Hostname available → one CNAME (smallest, survives IP changes, works at every provider).
  • Apex hostnames with target.IPs available → one A (and AAAA for IPv6) per IP. Apex prefers IPs over CNAME because CNAME-at-apex requires provider-specific flattening (Cloudflare, Route 53 alias, DNSimple ALIAS, etc.) and many providers reject it.
  • Apex hostnames with only target.Hostname (no IPs) → three mutually- exclusive flattening candidates (CNAME, ANAME, ALIAS), all at "@" with the same value. A bare CNAME is illegal at a zone root, and providers name their flattening record differently, so we emit all three and the note tells the caller to add the one their provider supports.
  • Subdomains with only target.IPs → one A/AAAA per IP.
  • target empty / Source=unknown → returns nil. Callers render an operator-must-configure-ingress error rather than fake records.

One strategy per hostname is required: a CNAME owner name cannot coexist with A/AAAA records at the same name (RFC 1034 §3.6.2). DNS providers reject the second row, so emitting both as "ready-to-paste" would just trip the user up.

The TXT verification record proves zone control, not sandbox binding: value is `<txtValuePrefix><hostname>`, so the same record stays valid across sandbox recreates and can be provisioned BEFORE a sandbox exists. Cross-sandbox hijack is still blocked by the cluster-wide uniqueness gate on hostname insert. `txtNamePrefix` and `txtValuePrefix` allow customizing the verification TXT records.

type ErrorResponse

type ErrorResponse struct {
	Error string `json:"error"`
}

type ExecRequest

type ExecRequest struct {
	Command        string            `json:"command"`
	WorkDir        string            `json:"workdir,omitempty"`
	Env            map[string]string `json:"env,omitempty"`
	TimeoutSeconds int               `json:"timeout_seconds,omitempty"`
}

type ExecResult

type ExecResult struct {
	Stdout     string `json:"stdout"`
	Stderr     string `json:"stderr"`
	ExitCode   int    `json:"exit_code"`
	DurationMS int64  `json:"duration_ms"`
}

type ExposePortRequest

type ExposePortRequest struct {
	Protocol string `json:"protocol,omitempty"`
}

ExposePortRequest is the optional JSON body for POST /v1/sandboxes/{id}/ports/{port}. Empty body or empty Protocol falls back to "http" — the historical default — so old SDK callers keep working unchanged.

type ExposePortResponse

type ExposePortResponse struct {
	Protocol  string `json:"protocol"`
	PublicURL string `json:"public_url"`
	Host      string `json:"host,omitempty"`
	HostPort  int    `json:"host_port,omitempty"`
}

ExposePortResponse is the JSON body returned by POST /v1/sandboxes/{id}/ports/{port}. Protocol is the canonical value the daemon picked ("http", "tcp", or "tls"), PublicURL is the dialable URL for the exposure, and Host/HostPort are populated only on the raw-TCP path so SDKs can hand them to native protocol clients without parsing tcp://host:port out of PublicURL.

type ExposedPort

type ExposedPort struct {
	SandboxID string `json:"sandbox_id"`
	Port      int    `json:"port"`
	// Protocol is one of "http" (default — Caddy HTTP reverse proxy), "tcp"
	// (caddy-l4 listener bound to HostPort, raw TCP forward to the container),
	// or "tls" (caddy-l4 SNI route on the shared TLS listener). Pre-migration
	// rows carry "http" via the column default.
	Protocol string `json:"protocol"`
	// HostPort is the parent-host TCP listener allocated for protocol="tcp"
	// from the configured pool (default [22000, 23000]). Zero for http/tls
	// modes, which don't reserve a per-exposure host port.
	HostPort  int       `json:"host_port,omitempty"`
	PublicURL string    `json:"public_url"`
	CreatedAt time.Time `json:"created_at"`
}

type Failover

type Failover struct {
	Policy string `json:"policy,omitempty"`
}

Failover controls what the cluster should do if the sandbox's owner node is declared dead. Empty or omitted means "none": orphan the placement and return 410 Gone. "recreate" opts the sandbox into best-effort recreation from its replicated create spec on another worker.

func (Failover) NormalizedPolicy

func (f Failover) NormalizedPolicy() string

func (Failover) ShouldRecreate

func (f Failover) ShouldRecreate() bool

func (Failover) Validate

func (f Failover) Validate() error

type GPURequest

type GPURequest struct {
	// Vendor is required. Allowed values: "nvidia", "amd", "apple".
	Vendor GPUVendor `json:"vendor"`
	// Count is the number of GPUs to allocate. Use -1 to request all GPUs on
	// the host. Zero is treated as 1 (default). Ignored for AMD (all AMD GPUs
	// on the host are exposed via /dev/kfd and /dev/dri).
	Count int `json:"count,omitempty"`
	// DeviceIDs pins the sandbox to specific GPU device indices or UUIDs.
	// For NVIDIA: GPU indices ("0", "1") or UUIDs ("GPU-abc123...").
	// For AMD and Apple: ignored.
	DeviceIDs []string `json:"device_ids,omitempty"`
}

GPURequest describes the GPU resources to attach to a sandbox. The parent CreateSandboxRequest.GPUs field is a pointer, so omitting it entirely means no GPU — this struct only appears when the caller explicitly opts in.

func (*GPURequest) Validate

func (g *GPURequest) Validate() error

Validate checks GPURequest fields for consistency.

type GPUVendor

type GPUVendor string

GPUVendor identifies the GPU hardware vendor for sandbox GPU allocation.

const (
	// GPUVendorNVIDIA selects NVIDIA GPUs via nvidia-container-runtime.
	// Requires nvidia-container-toolkit installed on the host.
	GPUVendorNVIDIA GPUVendor = "nvidia"
	// GPUVendorAMD selects AMD GPUs via ROCm device bind-mounts (/dev/kfd,
	// /dev/dri). Requires ROCm drivers on the host.
	GPUVendorAMD GPUVendor = "amd"
	// GPUVendorApple selects Apple Silicon GPU via Docker Desktop's
	// experimental Metal support. Only functional on macOS with Docker Desktop;
	// Linux hosts will receive a Docker daemon error at container creation.
	GPUVendorApple GPUVendor = "apple"
)

type HealthStatus

type HealthStatus struct {
	Status          string `json:"status"`
	Sandboxes       int    `json:"sandboxes"`
	Docker          string `json:"docker"`
	Caddy           string `json:"caddy"`
	Firecracker     string `json:"firecracker,omitempty"`
	Wasm            string `json:"wasm,omitempty"`
	Isolate         string `json:"isolate,omitempty"`
	SSHGateway      string `json:"ssh_gateway"`
	ClusterTopology string `json:"cluster_topology,omitempty"`
	ClusterNodes    int    `json:"cluster_nodes,omitempty"`
	Version         string `json:"version"`
}

type IdempotentRequestRecord

type IdempotentRequestRecord struct {
	Scope       string
	Fingerprint string
	TargetID    string
	State       string
	LockedUntil time.Time
	ReplayUntil time.Time
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

IdempotentRequestRecord is the row shape for request_idempotency. Scope is a facade-defined namespace string (e.g. "e2b.create") so the same fingerprint hash can be reused across facades without collision. The generic store helpers stay facade-agnostic — only the scope string says which caller owns the row.

type ImageDistributionMetadata

type ImageDistributionMetadata struct {
	Mode        string     `json:"mode,omitempty"`
	Digest      string     `json:"digest,omitempty"`
	RegistryRef string     `json:"registry_ref,omitempty"`
	VerifiedAt  *time.Time `json:"verified_at,omitempty"`
}

ImageDistributionMetadata describes whether an image can be materialized on an arbitrary worker. It is deliberately metadata only: registries, AOCR, and cache services remain pluggable deployment choices outside the core daemon.

func (ImageDistributionMetadata) IsZero

func (m ImageDistributionMetadata) IsZero() bool

type IngressTarget

type IngressTarget struct {
	Hostname string   `json:"hostname,omitempty"`
	IPs      []string `json:"ips,omitempty"`
	Source   string   `json:"source"`
}

IngressTarget is the cluster-published address(es) that DNS for a custom domain must point at. The shape depends on how the operator deployed AerolVM:

  • Source="hostname" — the cluster advertises a stable hostname (e.g. ingress.example.com that itself resolves to a load balancer). DNS for custom domains is a CNAME to this hostname.
  • Source="ips" — the cluster advertises one or more raw IPs. DNS for custom domains is one A (and AAAA for IPv6) record per IP.
  • Source="mixed" — some ingress nodes gossip a hostname, others gossip raw IPs. Callers should prefer the hostname (smaller record set, survives IP changes) but render both so apex domains without CNAME-flattening can still be configured.
  • Source="unknown" — no ingress node has gossiped a public address yet. This is the boot-time / mis-configured case; callers should treat the target as unusable rather than guessing.

The struct mirrors what GET /v1/ingress/dns returns. SDK helpers wrap it in language-idiomatic types but the JSON shape is shared.

type JSBundle

type JSBundle struct {
	Digest     string `json:"digest"`
	ModuleRef  string `json:"module_ref"`
	Name       string `json:"name,omitempty"`
	MainModule string `json:"main_module"`
	SizeBytes  int64  `json:"size_bytes"`
}

JSBundle is the catalogue view of a stored bundle (POST/GET /v1/js-bundles). ModuleRef is the "sha256:<digest>" form a create request passes as module_ref; Name, when set, is the alias that also resolves on create.

type Lifecycle

type Lifecycle struct {
	StopIfIdleFor    time.Duration `json:"stop_if_idle_for,omitempty"`
	DestroyIfIdleFor time.Duration `json:"destroy_if_idle_for,omitempty"`
	StopAtAge        time.Duration `json:"stop_at_age,omitempty"`
	DestroyAtAge     time.Duration `json:"destroy_at_age,omitempty"`
	// Serverless opts the sandbox into HTTP-wake behavior: stops driven by
	// lifecycle (idle timer / age) or involuntary exits arm the sandbox to
	// be transparently restarted on the next inbound HTTP request. Manual
	// StopSandbox calls clear the arming so the sandbox stays down.
	// Requires StopIfIdleFor to be set explicitly; there is no implicit
	// default — see Validate.
	Serverless bool `json:"serverless,omitempty"`
}

Lifecycle declares automatic stop/destroy timers for a sandbox. Each field is a duration; zero means "disabled" for that axis. Idle triggers measure time since LastActiveAt (i.e. since the last toolbox/exec/SSH activity). Age triggers measure time since CreatedAt — they are absolute deadlines that do not reset on Stop+Start, so a "destroy at 24h" sandbox stays destroyable even if the user restarts it minutes before the deadline.

Multiple fields can be set; whichever timer fires first wins. Destroy supersedes Stop for the same trigger axis. The lifecycle sweep runs every minute, so deadlines are honored to roughly that resolution.

func (Lifecycle) IsZero

func (l Lifecycle) IsZero() bool

IsZero reports whether all four timers are disabled. Useful for skipping Lifecycle inspection in the sweep when there's nothing to evaluate.

func (Lifecycle) Validate

func (l Lifecycle) Validate() error

Validate enforces the invariants we rely on at sweep time:

  • no negative durations (zero means disabled, negative is meaningless),
  • each field <= MaxLifecycleDuration,
  • if both stop and destroy of the same trigger are set, destroy must not fire before stop. Without this we could surprise a user who set "stop at 2h, destroy at 1h" by skipping the stop entirely.

func (Lifecycle) ValidateWithBypassFloor

func (l Lifecycle) ValidateWithBypassFloor(pollInterval, reconcileInterval time.Duration) error

ValidateWithBypassFloor runs Validate then additionally enforces the activity-floor constraint that HTTP-wake direct-route bypass imposes on serverless sandboxes: the netstats poller is the per-sandbox activity signal in the bypass shape, and the idle sweep needs at least one full netstats tick + one reconcile pass to observe "no traffic" without false-stopping a busy sandbox. The floor is 2 × pollInterval + reconcileInterval; sub-floor StopIfIdleFor values would let the sweep fire between netstats ticks and stop a sandbox that was actively serving the whole time.

Callers in the wake-aware (non-bypass) shape must use Validate() directly — the floor does not apply when every request still bumps LastActiveAt through sandboxd's ingress proxy. See plans/warm-direct-route-bypass.md D4.

type MountSpec

type MountSpec struct {
	Type        MountType         `json:"type"`
	Target      string            `json:"target"`
	Source      string            `json:"source"`
	Options     map[string]string `json:"options,omitempty"`
	Credentials map[string]string `json:"credentials,omitempty"`
	ReadOnly    bool              `json:"read_only,omitempty"`
}

MountSpec describes a single external-storage mount the user wants to be available inside their sandbox. Credentials are encrypted at rest in the daemon's database, materialized only on the host as the FUSE process needs them, and never returned by any read API.

func (*MountSpec) Redact

func (m *MountSpec) Redact() MountSpecRedacted

Redact strips credentials, returning the user-safe view.

func (*MountSpec) Validate

func (m *MountSpec) Validate(toolboxMountPath string) error

Validate checks the spec against the daemon's mount policy. toolboxMountPath is the path the toolbox binary is bind-mounted to inside the container; we refuse to let the user shadow it.

type MountSpecFile

type MountSpecFile struct {
	Mounts []MountSpec `json:"mounts"`
}

MountSpecFile is the JSON envelope used to (de)serialize the encrypted blob of a sandbox's mount specs in the daemon's database. Kept as a struct rather than a bare slice so future fields (version, key id) can be added without a migration.

type MountSpecRedacted

type MountSpecRedacted struct {
	Type           MountType         `json:"type"`
	Target         string            `json:"target"`
	Source         string            `json:"source"`
	Options        map[string]string `json:"options,omitempty"`
	ReadOnly       bool              `json:"read_only,omitempty"`
	HasCredentials bool              `json:"has_credentials"`
}

MountSpecRedacted is the read-only view returned by the API. It mirrors MountSpec but strips credentials.

func RedactMounts

func RedactMounts(mounts []MountSpec) []MountSpecRedacted

RedactMounts returns the read-only API view for a slice of mounts.

type MountType

type MountType string

MountType identifies the storage backend the user wants mounted inside their container. The daemon runs the mount tool on the host (in a per-sandbox directory) and bind-mounts that directory into the container, so credentials never enter the container and the user's image needs no mount tooling.

const (
	MountTypeS3     MountType = "s3"
	MountTypeNFS    MountType = "nfs"
	MountTypeSSHFS  MountType = "sshfs"
	MountTypeRclone MountType = "rclone"
)

type NetworkUsage

type NetworkUsage struct {
	SandboxID       string     `json:"sandbox_id"`
	BytesIn         int64      `json:"bytes_in"`
	BytesOut        int64      `json:"bytes_out"`
	BytesInLimit    int64      `json:"bytes_in_limit"`
	BytesOutLimit   int64      `json:"bytes_out_limit"`
	QuotaExceeded   bool       `json:"quota_exceeded"`
	QuotaExceededAt *time.Time `json:"quota_exceeded_at,omitempty"`
	// LastSampledAt is omitted until the netstats poller has produced at
	// least one sample. Pointer + omitempty so we don't serialize the zero
	// time as "0001-01-01T00:00:00Z" before the first tick.
	LastSampledAt *time.Time `json:"last_sampled_at,omitempty"`
}

NetworkUsage is the response shape for GET /v1/sandboxes/{id}/network/usage. BytesInLimit / BytesOutLimit zero means unlimited.

type PendingVolumeDeletion

type PendingVolumeDeletion struct {
	VolumeID  string    `json:"volume_id"`
	Tenant    string    `json:"tenant"`
	Name      string    `json:"name"`
	Backend   string    `json:"backend"`
	Source    string    `json:"source"`
	CreatedAt time.Time `json:"created_at"`
}

PendingVolumeDeletion is a durable cleanup ledger row. Daytona delete removes the user-visible metadata row only after this record exists, so a backend cleanup failure never leaves remote data without coordinates to reconcile.

type PlatformVolumeMount

type PlatformVolumeMount struct {
	Name     string `json:"name"`
	Path     string `json:"path"`
	ReadOnly bool   `json:"read_only,omitempty"`
}

PlatformVolumeMount is a request to attach a named, operator-backed persistent volume at Path inside the sandbox. Name is sanitized and scoped to the caller's tenant by the service; the user supplies nothing else (no bucket, no credentials). See plans/e2b-volume-mounts.md.

type PushWasmModuleOptions

type PushWasmModuleOptions struct {
	Name             string
	Tag              string
	Module           []byte
	RegistryUsername string
	RegistryToken    string
}

PushWasmModuleOptions is the SDK-side input for a BYO module upload. The wire form is the raw .wasm body plus name/tag query params and the caller's registry credentials as headers; this struct is the Go SDK's ergonomic shape.

type PushWasmModuleResponse

type PushWasmModuleResponse struct {
	ModuleRef string `json:"module_ref"`
	Digest    string `json:"digest"`
	SizeBytes int64  `json:"size_bytes"`
}

PushWasmModuleResponse is returned by POST /v1/wasm-modules/push after the daemon validates a BYO .wasm upload and forwards it to the registry. The daemon stores nothing; the caller references ModuleRef (an oci:// ref) on a subsequent create.

type RegistryAuth

type RegistryAuth struct {
	Server   string `json:"server"`
	Username string `json:"username"`
	Password string `json:"password"`
}

type ResizeSandboxRequest

type ResizeSandboxRequest struct {
	CPU      float64 `json:"cpu"`
	MemoryMB int     `json:"memory_mb"`
	DiskGB   int     `json:"disk_gb"`
}

type Sandbox

type Sandbox struct {
	ID               string            `json:"id"`
	Image            string            `json:"image"`
	Status           SandboxStatus     `json:"status"`
	PublicURL        string            `json:"public_url"`
	ContainerID      string            `json:"container_id,omitempty"`
	ContainerIP      string            `json:"container_ip,omitempty"`
	CPU              float64           `json:"cpu"`
	MemoryMB         int               `json:"memory_mb"`
	DiskGB           int               `json:"disk_gb"`
	OSUser           string            `json:"os_user"`
	Env              map[string]string `json:"env,omitempty"`
	NetworkBlockAll  bool              `json:"network_block_all"`
	ToolboxEnabled   bool              `json:"toolbox_enabled"`
	ToolboxToken     string            `json:"-"`
	SSHPublicKey     string            `json:"ssh_public_key,omitempty"`
	ExposedPorts     []ExposedPort     `json:"exposed_ports,omitempty"`
	CreatedAt        time.Time         `json:"created_at"`
	UpdatedAt        time.Time         `json:"updated_at"`
	LastActiveAt     time.Time         `json:"last_active_at"`
	LastError        string            `json:"last_error,omitempty"`
	ContainerCommand []string          `json:"container_command,omitempty"`
	Lifecycle        Lifecycle         `json:"lifecycle"`
	Failover         *Failover         `json:"failover,omitempty"`
	// Name is the optional unique identifier set at create time. Empty when
	// the sandbox was created without one (the common path on /v1 today).
	Name string `json:"name,omitempty"`
	// Tags is the optional key/value bag set at create time. Facades use it
	// for label-style metadata (Daytona labels, E2B metadata) but the field
	// is facade-agnostic — anything that wants attribute-tagged sandboxes
	// can write here.
	Tags map[string]string `json:"tags,omitempty"`
	// Runtime is the container runtime this sandbox uses (one of "docker",
	// "gvisor", or "kata"). Pre-migration rows carry "" and resolve to the
	// host default at start time; new sandboxes always store the resolved
	// value so the choice cannot drift across host restarts.
	Runtime string `json:"runtime"`
	// Engine is the host-level container engine that owns this sandbox's
	// lifecycle (dockerd or native containerd). Pre-migration rows carry ""
	// and resolve to docker so flipping SB_CONTAINER_ENGINE does not strand
	// existing sandboxes on the wrong driver.
	Engine string `json:"engine,omitempty"`
	// GPUs is the GPU configuration this sandbox was created with. Nil means
	// no GPU was requested.
	GPUs *GPURequest `json:"gpus,omitempty"`
	// RegistryAuthSealed is the AES-GCM-encrypted JSON of the original
	// RegistryAuth supplied at create time, or nil/empty when the sandbox
	// pulled from a public registry. Persisted so cluster failover can hand
	// the credentials back to the new owner's docker pull. Never serialized
	// over the API — it is internal store ↔ service plumbing.
	RegistryAuthSealed []byte `json:"-"`
	// RegistryAuth is the transient, UNSEALED registry credential. It is never
	// persisted (no column) nor serialized (json:"-"); the service unseals
	// RegistryAuthSealed into it in-memory right before a WASM start/rehydrate
	// so a failover peer can re-pull a private oci:// module under the tenant's
	// identity (codex C4). Nil on the create/public path.
	RegistryAuth *RegistryAuth `json:"-"`
	// NetworkBytesIn / NetworkBytesOut are cumulative byte counters
	// maintained by the netstats poller. They survive container restarts —
	// a new veth resets in-memory baseline math but the persisted total is
	// preserved.
	NetworkBytesIn  int64 `json:"network_bytes_in"`
	NetworkBytesOut int64 `json:"network_bytes_out"`
	// NetworkBytesInLimit / NetworkBytesOutLimit are the caps the sandbox was
	// created or patched with. Zero = unlimited.
	NetworkBytesInLimit  int64 `json:"network_bytes_in_limit"`
	NetworkBytesOutLimit int64 `json:"network_bytes_out_limit"`
	// NetworkAllowOut / NetworkDenyOut are the egress CIDR policy the sandbox
	// was created with, persisted so the start and reconcile paths can
	// reinstall the host-firewall rules after a restart (parity with
	// NetworkBlockAll). Mutually exclusive; at most one is non-empty.
	NetworkAllowOut []string `json:"network_allow_out,omitempty"`
	NetworkDenyOut  []string `json:"network_deny_out,omitempty"`
	// AllowPublicTraffic mirrors the create-time flag. Nil means "not set"
	// (treated as allowed); a non-nil false makes ExposePort refuse to install
	// a public route. Persisted so the gate survives restarts.
	AllowPublicTraffic *bool `json:"allow_public_traffic,omitempty"`
	// MaskRequestHost mirrors the create-time flag: the value the ingress layer
	// rewrites the upstream Host header to for exposed HTTP ports. Persisted so
	// the direct Caddy route (baked at install time) and the serverless wake
	// proxy (read per request) agree on the same value after a restart.
	MaskRequestHost string `json:"mask_request_host,omitempty"`
	// NetworkQuotaExceeded reflects whether either lifetime byte counter has
	// crossed its limit. The reconcile loop installs DROP rules when this is
	// true; clearing requires raising the limit (or zeroing it for unlimited).
	NetworkQuotaExceeded bool `json:"network_quota_exceeded"`
	// NetworkQuotaExceededAt is the wall-clock time the limit was first
	// observed crossed. Nil while under quota.
	NetworkQuotaExceededAt *time.Time `json:"network_quota_exceeded_at,omitempty"`
	// AutoImportPending is set by the post-pull auto-import path when the
	// AOCR ImportAPI call failed. A background reconciler walks rows with
	// this flag and re-tries the import; once the import succeeds the flag
	// is cleared and the create spec's ImageDistributionMode flips to
	// `aocr_imported`. The flag is local-node bookkeeping — it is not
	// replicated via Raft, since each owner gets one opportunistic shot
	// at the import per pull.
	AutoImportPending bool `json:"-"`
	// WakeArmed indicates the sandbox is currently in the stopped state
	// because of a lifecycle-driven idle timeout or an involuntary exit
	// while Lifecycle.Serverless was true. Wake-aware control-plane proxies
	// transparently start the sandbox on the next inbound request when this
	// is set. Cleared on manual StopSandbox and after a successful wake.
	// Internal-only bookkeeping; never exposed over the wire.
	WakeArmed bool `json:"-"`
	// CustomDomains carries the per-hostname rows for any operator-attached
	// public hostnames. Status is server-managed (pending_dns → issuing →
	// ready / failed). Nil when the sandbox has no custom domains.
	CustomDomains []CustomDomain `json:"custom_domains,omitempty"`
	// TemplateID records the Firecracker template this sandbox was created
	// from, when one was supplied. Empty for Docker sandboxes and for
	// Firecracker sandboxes built from an ad-hoc Image. The Phase 2 template
	// GC reads this column via IsTemplateReferenced to decide whether a
	// template is still in use; the firecracker driver also reads it on
	// recreate so failover stays on the same rootfs.
	TemplateID string `json:"template_id,omitempty"`
	// OverlaySizeGB is the size of the per-sandbox writable overlay
	// drive (virtio-blk at /dev/vdb), echoed back from the create
	// request so callers can confirm what was provisioned. Zero means
	// no overlay was attached. Persisted because the runtime cleanup
	// path needs to know whether overlay.ext4 was allocated in the
	// per-sandbox runDir.
	OverlaySizeGB int `json:"overlay_size_gb,omitempty"`
	// OwnerRef is the account key this sandbox is attributed to, resolved by
	// the optional fleet control plane from a user token at create time.
	// Empty means operator/PAT-created (the only possibility on the
	// open-source build). Owner scoping uses it to fence user tokens to their
	// own sandboxes; the PAT bypasses scoping. Never exposed over the wire —
	// it is internal attribution, not caller-facing.
	OwnerRef string `json:"-"`
	// FleetSuspended marks a sandbox that was stopped by a fleet standing
	// directive (suspend), as opposed to a user/operator stop. Recovery
	// restarts exactly the sandboxes carrying this marker, then clears it.
	// Internal-only bookkeeping.
	FleetSuspended bool `json:"-"`
	// Durability is the survival class this sandbox was created with. Empty
	// on pre-migration rows resolves to passivatable at read time.
	Durability string `json:"durability,omitempty"`
	// ModuleRef is the WASM module reference when runtime=wasm.
	ModuleRef string `json:"module_ref,omitempty"`
	// ModuleDigest is the sha256 hex digest of the resolved .wasm bytes.
	ModuleDigest string `json:"module_digest,omitempty"`
	// CheckpointPath points at a local §4.8.1 mem.snap directory when status
	// is passivated. Internal-only; not exposed over the wire.
	CheckpointPath string `json:"-"`
	// CloneGeneration is the §4.8 fencing token for checkpoint/clone writes.
	CloneGeneration string `json:"-"`
	// WasmRegistryRef is the AOCR ref for the last pushed durable checkpoint.
	WasmRegistryRef string `json:"-"`
	// WasmRegistryDigest is the manifest digest from the last AOCR push.
	WasmRegistryDigest string `json:"-"`
	// TenantID is the isolate-group key this sandbox was created under
	// (runtime=isolate only; empty for other runtimes and when the group key
	// fell back to the authenticated identity). Persisted so restart
	// reconcile and cluster failover rebuild the same per-tenant group
	// process the security posture is stated in terms of
	// (plans/isolate-runtime.md §2.1).
	TenantID string `json:"tenant_id,omitempty"`
}

type SandboxCompatState

type SandboxCompatState struct {
	SandboxID string
	Facade    string
	StateJSON string
	CreatedAt time.Time
	UpdatedAt time.Time
}

SandboxCompatState carries facade-private state that has no native meaning on its own — opaque wire-shape sugar like Daytona's `target` or E2B's `template_id`. The store treats StateJSON as a byte string; facades own the schema inside it.

type SandboxRuntimeState

type SandboxRuntimeState struct {
	SandboxID   string
	ContainerID string
	ContainerIP string
	Status      SandboxStatus
	// WASM-only: resolved module metadata returned by the wasm driver on create.
	ModuleRef    string
	ModuleDigest string
	// ModulePath / ModuleSizeBytes are the resolved local artifact location and
	// size. The service persists them onto the catalogue row so the row is
	// resolvable by catalogue id instead of a misleading empty-path "ready" row
	// (codex P1). Empty when resolution did not yield a local path.
	ModulePath      string
	ModuleSizeBytes int64
	// AdoptedParkID is non-empty when Create adopted a warm-pool slot; the
	// service uses it to release the park capacity reservation (§4).
	AdoptedParkID string
}

SandboxRuntimeState is the runtime view of a sandbox returned by the container runtime layer (Docker today, gVisor/native runsc tomorrow). It carries only what the service needs to reconcile and route — anything else belongs on models.Sandbox or stays in the runtime implementation.

type SandboxSnapshot

type SandboxSnapshot struct {
	Name            string    `json:"name"`
	Image           string    `json:"image"`
	ImageID         string    `json:"image_id,omitempty"`
	SourceSandboxID string    `json:"source_sandbox_id"`
	CreatedAt       time.Time `json:"created_at"`
	// Image distribution metadata is the snapshot-level placement contract.
	// Local-only snapshots may be started only on the node whose Docker image
	// store contains Image; external_registry/aocr snapshots may be placed on
	// any worker that can pull RegistryRef/Image.
	ImageDistributionMode string     `json:"image_distribution_mode,omitempty"`
	ImageDigest           string     `json:"image_digest,omitempty"`
	ImageRegistryRef      string     `json:"image_registry_ref,omitempty"`
	ImageVerifiedAt       *time.Time `json:"image_verified_at,omitempty"`

	// Entrypoint is the optional command override the caller wants the
	// runtime to use when starting a sandbox from this snapshot.
	Entrypoint []string `json:"entrypoint,omitempty"`
	// RegionID echoes the region the caller requested when creating the
	// snapshot. The facade persists this verbatim for read-back; region
	// routing itself is not yet wired through the daemon.
	RegionID string `json:"region_id,omitempty"`
	// CPU / MemoryMB / DiskGB / GPU mirror the resource hints the caller
	// supplied on create — surfaced back to clients that poll the snapshot
	// after creation.
	CPU      float64 `json:"cpu,omitempty"`
	MemoryMB int     `json:"memory_mb,omitempty"`
	DiskGB   int     `json:"disk_gb,omitempty"`
	GPU      float64 `json:"gpu,omitempty"`

	// PushState reflects the AOCR-push lifecycle for this snapshot. See the
	// SnapshotPushState* constants above. When push is disabled (or the image
	// already lives in a remote registry) this is "active" from the moment
	// the row is inserted, matching pre-feature behavior. Otherwise it
	// transitions pending → pushing → active|error, and the cluster placement
	// path treats the snapshot as locally-pinned until it hits "active".
	PushState string `json:"push_state,omitempty"`
	// PushError is populated only when PushState is "error" — the last error
	// the reconciler saw, surfaced to the Daytona facade's errorReason field.
	PushError string `json:"push_error,omitempty"`
}

SandboxSnapshot is the persisted metadata for a committed sandbox image. Image is the reusable image reference; ImageID is the content-addressed Docker image ID returned by the runtime after the commit succeeds.

Two creation paths populate this row:

  1. Snapshot-of-a-running-sandbox (legacy) — SourceSandboxID is set, resource fields stay zero, the image is committed by the runtime.
  2. Snapshot-from-image (Daytona facade) — SourceSandboxID is empty, resource fields and Entrypoint reflect the caller's create payload, and Image is either the caller-supplied imageName or a freshly built tag from a buildInfo Dockerfile.

func (*SandboxSnapshot) ApplyImageDistribution

func (s *SandboxSnapshot) ApplyImageDistribution(meta ImageDistributionMetadata)

func (SandboxSnapshot) ImageDistribution

func (s SandboxSnapshot) ImageDistribution() ImageDistributionMetadata

type SandboxStatus

type SandboxStatus string
const (
	SandboxStatusCreating  SandboxStatus = "creating"
	SandboxStatusStarted   SandboxStatus = "started"
	SandboxStatusStopped   SandboxStatus = "stopped"
	SandboxStatusDestroyed SandboxStatus = "destroyed"
	SandboxStatusError     SandboxStatus = "error"
	// SandboxStatusPassivated marks a WASM sandbox checkpointed to disk,
	// awaiting rehydrate after daemon restart (plans/wasm-runtime.md §4.3).
	SandboxStatusPassivated SandboxStatus = "passivated"
	// SandboxStatusAwaitingRuntime marks a passivated/durable WASM row found
	// when EnableWasm=false — reconcile holds the row until WASM is re-enabled.
	SandboxStatusAwaitingRuntime SandboxStatus = "awaiting_runtime"
	// SandboxStatusPassivateFailed marks a WASM sandbox whose drain checkpoint
	// timed out or failed; durability is preserved for operator inspection.
	SandboxStatusPassivateFailed SandboxStatus = "passivate_failed"
)

Sandbox lifecycle states.

SandboxStatusDestroyed marks a sandbox whose container is gone — either because the user called DELETE /sandboxes/{id}, or because the container died out-of-band and the event monitor noticed. The status exists as a transient marker only: the API-driven destroy path deletes the row in the same call, and the reconcile loop deletes any row whose container has gone missing on its next pass. Destroyed rows are not retained — keeping them around would also keep their host_port reservations in exposed_ports, slowly exhausting the L4 TCP allocator pool over the daemon's lifetime.

type Session

type Session struct {
	ID         string        `json:"id"`
	Name       string        `json:"name"`
	Argv       []string      `json:"argv"`
	WorkDir    string        `json:"workdir,omitempty"`
	PTY        bool          `json:"pty"`
	Status     SessionStatus `json:"status"`
	ExitCode   int           `json:"exit_code"`
	ExitSignal string        `json:"exit_signal,omitempty"`
	CreatedAt  time.Time     `json:"created_at"`
	StartedAt  time.Time     `json:"started_at"`
	ExitedAt   time.Time     `json:"exited_at,omitempty"`
	Recording  bool          `json:"recording"`
	Bytes      int64         `json:"bytes"`    // total stdout+stderr bytes produced
	Attached   int           `json:"attached"` // current number of attached clients
}

Session is the metadata view of a session — never includes its output.

type SessionList

type SessionList struct {
	Sessions []Session `json:"sessions"`
}

SessionList is the GET /sessions response shape.

type SessionResizeRequest

type SessionResizeRequest struct {
	Cols int `json:"cols"`
	Rows int `json:"rows"`
}

SessionResizeRequest is the POST /sessions/{id}/resize body.

type SessionSignalRequest

type SessionSignalRequest struct {
	Signal string `json:"signal"`
}

SessionSignalRequest is the POST /sessions/{id}/signal body. Signal is one of INT, TERM, KILL, HUP, QUIT (or the SIG*-prefixed forms).

type SessionStatus

type SessionStatus string

SessionStatus is the lifecycle stage of a long-running command session inside a sandbox container.

const (
	SessionStatusRunning SessionStatus = "running"
	SessionStatusExited  SessionStatus = "exited"
	SessionStatusKilled  SessionStatus = "killed"
	SessionStatusFailed  SessionStatus = "failed" // could not start
)

type SnapshotAlias

type SnapshotAlias struct {
	Alias        string
	SnapshotName string
	Facade       string
	ExtraNames   []string
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

SnapshotAlias maps a facade-shaped alternate identifier (e.g. E2B's base64-encoded `snapshot_*` token) onto a native sandbox_snapshots.name. ExtraNames carries additional facade-visible names that point at the same native snapshot.

type Template

type Template struct {
	ID                 string         `json:"id"`
	Image              string         `json:"image"`
	Status             TemplateStatus `json:"status"`
	RootfsPath         string         `json:"-"`
	RootfsSizeBytes    int64          `json:"rootfs_size_bytes,omitempty"`
	MinSizeMiB         int            `json:"min_size_mib,omitempty"`
	LastError          string         `json:"last_error,omitempty"`
	CreatedAt          time.Time      `json:"created_at"`
	UpdatedAt          time.Time      `json:"updated_at"`
	ReadyAt            *time.Time     `json:"ready_at,omitempty"`
	SnapshotMemoryPath string         `json:"-"`
	SnapshotStatePath  string         `json:"-"`
	SnapshotSizeBytes  int64          `json:"snapshot_size_bytes,omitempty"`
	SnapshotChecksum   string         `json:"-"`
	SnapshotVsockCID   uint32         `json:"-"`
	SnapshotError      string         `json:"snapshot_error,omitempty"`
	HasSnapshot        bool           `json:"has_snapshot"`
	// HasOverlay is the API-facing "this template was built with a
	// per-sandbox overlay drive placeholder baked into its snapshot
	// state" flag. PR-A templates have it false; the snapshot-load path
	// rejects an OverlaySizeGB request against a HasOverlay=false
	// template because Firecracker cannot add a virtio-blk device
	// post-load — only PATCH an existing one's backing path. Operators
	// rebuild the template via POST /v1/templates to upgrade.
	HasOverlay bool `json:"has_overlay"`
	// PushState reflects the AOCR-push lifecycle for this template's
	// artifacts (Phase 6 PR 6-B.1). See the TemplatePushState* constants.
	// When push is disabled (or this is a warm-upgrade row) the value is
	// "active" from the moment the row is inserted, matching pre-feature
	// behaviour. Otherwise it transitions pending → pushing → active|error
	// driven by TemplateArtifactPushReconciler.
	PushState string `json:"push_state,omitempty"`
	// PushError is populated only when PushState is "error" — the last
	// error the reconciler saw, surfaced to operator-facing API responses.
	PushError string `json:"push_error,omitempty"`
	// RegistryRef is the canonical destination ref the push pipeline
	// wrote (`<host>/cluster/<id>/templates/<tid>:latest`). Populated
	// after a successful push; empty until then. Read by consumer-side
	// code in PR 6-B.2 to know where peers can pull the template from.
	RegistryRef string `json:"registry_ref,omitempty"`
	// PushDigest is the manifest digest (`sha256:...`) the registry
	// assigned to the pushed tag, as reported by the Docker push stream's
	// `aux` payload. Empty when the daemon did not surface one — older
	// daemons or registries that don't return a manifest digest leave
	// this blank.
	PushDigest string `json:"push_digest,omitempty"`
}

Template is the persisted record for a Firecracker rootfs template. See plans/snapshot-clone-fast-boot.md Phase 2: an image is converted once into a rootfs.ext4 and many sandboxes can boot from it without re-running the skopeo+umoci+mkfs pipeline.

RootfsPath is the absolute on-disk location of the prepared rootfs.ext4 (`<FirecrackerTemplatesDir>/<id>/rootfs.ext4`). It is json:"-" — the path is a server-internal detail that should not leak to API callers; clients reference templates by ID.

Snapshot* fields are populated by the Phase 3 second build phase. HasSnapshot is the API-facing "this template fast-boots" flag — the path/checksum/CID fields stay json:"-" because they are pure server detail. SnapshotError carries the reason a template ended up ready_no_snapshot (rootfs is usable, snapshot phase failed) so the operator does not have to grep daemon logs.

type TemplateStatus

type TemplateStatus string

TemplateStatus tracks a Firecracker template through its async build. pending is the wire-visible state immediately after POST /v1/templates (matches Phase 2 — clients that simply poll for ready keep working). building_rootfs and snapshotting are intermediate states the Phase 3 two-phase build goroutine walks through. ready means rootfs+snapshot are both on disk; ready_no_snapshot means rootfs is fine but the snapshot phase failed (CID alloc, snapshotter error) — sandboxes can still cold-boot from this template, just without fast-boot. failed means even the rootfs phase did not complete; LastError carries the reason for the operator to inspect. unhealthy is the Phase 6 PR-A terminal state for "snapshot was ready and worked at least once, then a load-time checksum mismatch proved the on-disk artifact is corrupt" — distinct from ready_no_snapshot (which is build-time degradation) so operators can alert on the runtime regression specifically. Cold- boot still works against unhealthy because the rootfs is unaffected; the service layer kicks an async rebuild that transitions back to ready once the snapshot phase succeeds.

const (
	TemplateStatusPending         TemplateStatus = "pending"
	TemplateStatusBuildingRootfs  TemplateStatus = "building_rootfs"
	TemplateStatusSnapshotting    TemplateStatus = "snapshotting"
	TemplateStatusReady           TemplateStatus = "ready"
	TemplateStatusReadyNoSnapshot TemplateStatus = "ready_no_snapshot"
	TemplateStatusFailed          TemplateStatus = "failed"
	TemplateStatusUnhealthy       TemplateStatus = "unhealthy"
)

type UpdateLifecycleRequest

type UpdateLifecycleRequest struct {
	Lifecycle
}

UpdateLifecycleRequest is the body for PUT /v1/sandboxes/{id}/lifecycle. Full-replacement semantics: send all four fields. To clear a field, set it to zero. To preserve a field, send its current value (read it via GET first if the caller doesn't already have it).

type UpdateNetworkLimitsRequest

type UpdateNetworkLimitsRequest struct {
	NetworkBytesInLimit  *int64 `json:"network_bytes_in_limit,omitempty"`
	NetworkBytesOutLimit *int64 `json:"network_bytes_out_limit,omitempty"`
}

UpdateNetworkLimitsRequest is the body for PATCH /v1/sandboxes/{id}/network/limits. Each field is a pointer so the handler can distinguish "leave alone" (nil) from "set to unlimited" (pointer to zero). Negative values are rejected at the service layer.

type Volume

type Volume struct {
	ID      string `json:"id"`
	Tenant  string `json:"tenant"`
	Name    string `json:"name"`
	Backend string `json:"backend"`
	// Source is the backend coordinate (S3 bucket/prefix or NFS host:/path)
	// frozen at creation time. Delete and reconciliation read this stored value
	// rather than recomputing it from mutable operator config, so a volume keeps
	// mounting and reclaiming the exact location it was created against even if
	// the operator later changes the configured bucket/prefix/export. Empty for
	// rows created before this column existed; callers fall back to recompute.
	Source    string    `json:"source"`
	CreatedAt time.Time `json:"created_at"`
}

Volume is a first-class, operator-backed persistent volume object. E2B references volumes by name only (no object), but the Daytona facade exposes full CRUD, so a volume needs a durable row: a stable id, the owning tenant, the user-facing name, and which backend it lives on. The backing storage (S3 prefix / NFS dir) is derived deterministically from (tenant, name) — the row is metadata, not the data itself.

type VolumeAttachment

type VolumeAttachment struct {
	Tenant        string    `json:"tenant"`
	VolumeID      string    `json:"volume_id"`
	SandboxID     string    `json:"sandbox_id"`
	Target        string    `json:"target"`
	Source        string    `json:"source"`
	CreatedAt     time.Time `json:"created_at"`
	CreatedVolume bool      `json:"-"`
}

VolumeAttachment is the indexed store-side reference from a sandbox to a platform volume. It deliberately duplicates the deterministic source so delete and reconciliation paths never need to decrypt sandbox_mounts or rebuild the source from mutable operator config just to answer "is this attached?".

type WasmModule

type WasmModule struct {
	ID              string           `json:"id"`
	ModuleRef       string           `json:"module_ref"`
	Status          WasmModuleStatus `json:"status"`
	ModulePath      string           `json:"-"`
	ModuleSizeBytes int64            `json:"module_size_bytes,omitempty"`
	Digest          string           `json:"digest,omitempty"`
	Entrypoint      string           `json:"entrypoint,omitempty"`
	HasWarm         bool             `json:"has_warm"`
	LastError       string           `json:"last_error,omitempty"`
	CreatedAt       time.Time        `json:"created_at"`
	UpdatedAt       time.Time        `json:"updated_at"`
	ReadyAt         *time.Time       `json:"ready_at,omitempty"`
}

WasmModule is one row in the wasm_modules catalogue (plans/wasm-runtime.md).

type WasmModuleStatus

type WasmModuleStatus string

WasmModuleStatus is the catalogue lifecycle for POST /v1/wasm-modules rows.

const (
	WasmModuleStatusReady  WasmModuleStatus = "ready"
	WasmModuleStatusFailed WasmModuleStatus = "failed"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL