Documentation
¶
Overview ¶
Package config provides configuration types and loading for shed.
Index ¶
- Constants
- Variables
- func AddKnownHost(host string, port int, hostKey string) error
- func ClientCredentialPaths(name string) (certPath, keyPath string)
- func CredentialMountTag(name string) string
- func CredsStagingRoot() string
- func EnsureConfigDir() error
- func ExpandPath(path string) string
- func ExpandRepoShorthand(repo string) string
- func GetClientConfigDir() string
- func GetClientConfigPath() string
- func GetCredsDir() string
- func GetKnownHostsPath() string
- func GetSyncConfigPath() string
- func GetTunnelLogDir() string
- func GetTunnelLogPath(shedName string) string
- func GetTunnelStatePath() string
- func GetTunnelsConfigPath() string
- func HasWritableHostMount(projectMounts []MountConfig, serverCfg *ServerConfig) bool
- func IsReservedEgressName(name string) bool
- func IsSSHRepoURL(repo string) bool
- func KnownHostLine(host string, port int, hostKey string) string
- func LegacyWireAuthMode(mode string) string
- func LoadClientCredentials(name, certPath, keyPath string) (*tls.Certificate, error)
- func LockServerCredentials(name string) (func(), error)
- func MatchesExcludePatterns(relPath string, patterns []string) bool
- func NormalizeAuthMode(mode string) string
- func ParseUpperSize(s string) (int64, error)
- func ProjectAddDirTargets(mounts []MountConfig, landingDir string) []string
- func ProjectMountBasename(hostDir string) (string, error)
- func ProjectMountSources(mounts []MountConfig) []string
- func ProjectMountTag(basename string) string
- func ProjectMountTagForTarget(target string) string
- func RemoveKnownHost(host string, port int, hostKey string) error
- func RemoveServerCredentials(name string) error
- func RepoDirName(repo string) (string, error)
- func ResolveBackend(backend, goos, goarch string) (string, error)
- func SanitizeRepoURL(repo string) string
- func ServerCredsDir(name string) string
- func ValidGitHubUsername(s string) bool
- func ValidateGitRepoURL(repoURL string) error
- func ValidateMountDir(path string) error
- func ValidateSessionName(name string) error
- func ValidateShedName(name string) error
- func ValidateSnapshotName(name string) error
- func WriteClientCredentials(name string, certPEM, keyPEM []byte) (certPath, keyPath string, err error)
- type APIError
- type APIErrorDetail
- type AuthConfig
- type ClientConfig
- func (c *ClientConfig) AddServer(name string, entry ServerEntry) error
- func (c *ClientConfig) CacheShed(name string, server string, status string)
- func (c *ClientConfig) CacheShedAt(name string, server string, status string, at time.Time)
- func (c *ClientConfig) GetCreateTimeout() time.Duration
- func (c *ClientConfig) GetDefaultServer() (*ServerEntry, string, error)
- func (c *ClientConfig) GetServer(name string) (*ServerEntry, error)
- func (c *ClientConfig) GetShedServer(name string) (string, error)
- func (c *ClientConfig) RemoveServer(name string) error
- func (c *ClientConfig) RemoveShedCache(name string)
- func (c *ClientConfig) Save() error
- func (c *ClientConfig) SaveToPath(path string) error
- func (c *ClientConfig) SetDefaultServer(name string) error
- func (c *ClientConfig) Update(mutate func(*ClientConfig)) error
- func (c *ClientConfig) UpdateChecked(check func(*ClientConfig) error, mutate func(*ClientConfig)) error
- type CreateShedRequest
- type DiskSize
- type DiskUsage
- type DiskUsageOrError
- type DiskUsageTotals
- type Duration
- type EgressConfig
- type EgressProfile
- type EgressProfileInfo
- type EgressSetRequest
- type EgressStatus
- type ExtensionHealthInfo
- type ExtensionsConfig
- type FileEntry
- type FirecrackerConfig
- func (c *FirecrackerConfig) GetDefaultImage() string
- func (c *FirecrackerConfig) GetExtractKernel() bool
- func (c *FirecrackerConfig) GetImageAliases() map[string]string
- func (c *FirecrackerConfig) GetImagesDir() string
- func (c *FirecrackerConfig) GetNeedsInitrd() bool
- func (c *FirecrackerConfig) GetPlatform() string
- func (c *FirecrackerConfig) GetPullConcurrency() int
- func (c *FirecrackerConfig) GetPullPolicy() string
- func (c *FirecrackerConfig) ResolveBaseRootfs() (ResolvedImage, error)
- func (c *FirecrackerConfig) ResolveImage(image string) (ResolvedImage, error)
- func (c *FirecrackerConfig) Validate() error
- type GitConfig
- type ImageDescriptor
- type ImageDiskEntry
- type ImageInfo
- type ImageInspectResponse
- type ImageManifest
- type ImagePullRequest
- type ImagePullResponse
- type ImagePushRequest
- type ImagePushResponse
- type ImageTagRequest
- type ImagesResponse
- type MountConfig
- type PruneImagesResponse
- type PruneReport
- type PruneReportOrError
- type PruneReportTotals
- type PrunedItem
- type ResolvedImage
- type SSHAuthConfig
- type SSHHostKeyResponse
- type ServerConfig
- func (c *ServerConfig) ActiveDefaultImage() string
- func (c *ServerConfig) AuthEnforced() bool
- func (c *ServerConfig) AuthModeValue() string
- func (c *ServerConfig) EffectiveSSHAuth() *SSHAuthConfig
- func (c *ServerConfig) HTTPAuthEnforced() bool
- func (c *ServerConfig) HTTPListenAddr() string
- func (c *ServerConfig) HTTPSEnabled() bool
- func (c *ServerConfig) HTTPSListenAddr() string
- func (c *ServerConfig) MTLSMode() bool
- func (c *ServerConfig) PlainHTTPEnabled() bool
- func (c *ServerConfig) PreflightAuth() error
- func (c *ServerConfig) SSHAuth() *SSHAuthConfig
- func (c *ServerConfig) SSHListenAddr() string
- func (c *ServerConfig) TokenMode() bool
- func (c *ServerConfig) TokenTTL() time.Duration
- func (c *ServerConfig) Validate() error
- func (c *ServerConfig) ValidateNoHostCoupling() error
- type ServerEntry
- type ServerInfo
- type Session
- type SessionRC
- type SessionsResponse
- type Shed
- type ShedCache
- type ShedDiskEntry
- type ShedsResponse
- type SkippedItem
- type Snapshot
- type SnapshotCreateRequest
- type SnapshotCreateResponse
- type SnapshotDiskEntry
- type SnapshotsResponse
- type StagedClientCredentials
- type SystemDFResponse
- type SystemPruneResponse
- type UserProfileStore
- type VZConfig
- func (c *VZConfig) GetDefaultImage() string
- func (c *VZConfig) GetExtractKernel() bool
- func (c *VZConfig) GetImageAliases() map[string]string
- func (c *VZConfig) GetImagesDir() string
- func (c *VZConfig) GetNeedsInitrd() bool
- func (c *VZConfig) GetPlatform() string
- func (c *VZConfig) GetPullConcurrency() int
- func (c *VZConfig) GetPullPolicy() string
- func (c *VZConfig) ResolveBaseRootfs() (ResolvedImage, error)
- func (c *VZConfig) ResolveImage(image string) (ResolvedImage, error)
- func (c *VZConfig) Validate() error
Constants ¶
const ( MaxFirecrackerCPUs = 32 MaxFirecrackerMemoryMB = 256 * 1024 // 256 GB MaxFirecrackerDiskGB = 1024 // 1 TB MaxVsockCID uint32 = 65535 MaxVsockPort uint32 = 65535 MinTimeout = 1 * time.Second MaxTimeout = 30 * time.Minute // Guest-MTU bounds, shared by the auto-detection clamp // (vmutil.ClampGuestMTU) and the guest_mtu override validation on both // backends. Floor is the IPv6 minimum link MTU (RFC 8200) — guaranteed // routable on any path; ceiling is the host vmnet/TAP MTU, above which a // guest packet would itself black-hole. MinGuestMTU = 1280 MaxGuestMTU = 1500 )
Firecracker validation upper bounds.
const ( MaxVZCPUs = 32 MaxVZMemoryMB = 256 * 1024 // 256 GB MaxVZDiskGB = 1024 // 1 TB )
VZ validation upper bounds (decoupled from Firecracker).
const ( MinUpperSizeBytes int64 = 1 * 1024 * 1024 * 1024 MaxUpperSizeBytes int64 = 100 * 1024 * 1024 * 1024 )
UpperSize bounds. The plan caps configurable upper size at 1G-100G; values outside this range almost always indicate a config typo and should fail-fast at validation time.
const ( StatusRunning = "running" StatusStopped = "stopped" StatusStarting = "starting" StatusError = "error" )
Shed status constants.
const ( // DefaultSessionName is the name used when no session is specified. DefaultSessionName = "default" // MaxSessionNameLength is the maximum allowed length for a session name. MaxSessionNameLength = 63 )
Session constants.
const ( ErrShedNotFound = "SHED_NOT_FOUND" ErrShedAlreadyExists = "SHED_ALREADY_EXISTS" ErrShedAlreadyRunning = "SHED_ALREADY_RUNNING" ErrShedAlreadyStopped = "SHED_ALREADY_STOPPED" ErrShedNotStopped = "SHED_NOT_STOPPED" ErrInvalidShedName = "INVALID_SHED_NAME" ErrInvalidRepoURL = "INVALID_REPO_URL" ErrCloneFailed = "CLONE_FAILED" ErrBackendNotEnabled = "BACKEND_NOT_ENABLED" ErrUnknownImage = "UNKNOWN_IMAGE" ErrImageNotFound = "IMAGE_NOT_FOUND" ErrImageInUse = "IMAGE_IN_USE" ErrBackendError = "BACKEND_ERROR" ErrInternalError = "INTERNAL_ERROR" ErrSessionNotFound = "SESSION_NOT_FOUND" ErrInvalidSessionName = "INVALID_SESSION_NAME" ErrTmuxNotAvailable = "TMUX_NOT_AVAILABLE" ErrInvalidLocalDir = "INVALID_LOCAL_DIR" ErrInvalidRequest = "INVALID_REQUEST" ErrSnapshotNotFound = "SNAPSHOT_NOT_FOUND" ErrSnapshotAlreadyExists = "SNAPSHOT_ALREADY_EXISTS" ErrSnapshotSourceRunning = "SNAPSHOT_SOURCE_RUNNING" ErrSnapshotBackendMismatch = "SNAPSHOT_BACKEND_MISMATCH" ErrInvalidSnapshotName = "INVALID_SNAPSHOT_NAME" ErrProfileNotFound = "PROFILE_NOT_FOUND" ErrProfileReserved = "PROFILE_RESERVED" // name collides with a config/reserved profile ErrProfileInUse = "PROFILE_IN_USE" // referenced by one or more sheds )
Error codes for API responses.
const ( BackendFirecracker = "firecracker" BackendVZ = "vz" BackendDetect = "detect" )
Backend type constants for Shed.Backend field.
const ( SSHAuthOff = "off" // accept all keys (legacy default) SSHAuthWarn = "warn" // log would-deny attempts, but still accept SSHAuthEnforce = "enforce" // reject keys not in the allowlist )
SSH auth modes for SSHAuthConfig.Mode.
const ( AuthModeOpen = "open" // default: no enforcement (tailnet/LAN posture) AuthModeToken = "token" // SSH allowlist + HTTP tokens + TLS, all enforced // AuthModeMTLS: the client credential is a short-lived certificate issued // over the SSH bootstrap channel; no bearer tokens exist in this mode. // Shares every other token-mode invariant (SSH allowlist enforce, TLS-only, // https_port default) — see AuthEnforced in server.go. AuthModeMTLS = "mtls" )
Auth modes for AuthConfig.Mode — the secure-by-default switch. "secure" is the pre-rename spelling of AuthModeToken, kept as a deprecated alias that config load normalizes to AuthModeToken (see normalizeAuthMode in server.go) — downstream code only ever observes AuthModeToken.
const DefaultFirecrackerImagesDir = "/var/lib/shed/firecracker/images"
DefaultFirecrackerImagesDir is the default directory for Firecracker rootfs images. This is a subdirectory of /var/lib/shed/firecracker/ to avoid mixing image files with the kernel, instance directories, and sockets that already live there.
const DefaultPullConcurrency = 3
DefaultVZImagesDir is the default directory for VZ rootfs images. DefaultPullConcurrency is the default number of image blobs downloaded in parallel per registry pull (Docker's default). Bounded to avoid tripping registry connection/rate limits.
const DefaultTokenTTL = 24 * time.Hour
DefaultTokenTTL is the lifetime of a bootstrap-minted HTTP token when auth.token_ttl is unset.
const DefaultUpperSize = "5G"
DefaultUpperSize is the fallback upper-layer size when the config omits upper_size_default. Plan-aligned at 5 GB sparse — a working guess for typical "build a project" workloads.
const DefaultVZImagesDir = "~/Library/Application Support/shed/vz"
const HomePath = "/home/shed"
HomePath is the shed user's home directory inside the VM. Repos, --local-dir, and --add-dir mounts all live under here, and interactive logins land here by default (or in a project subdirectory when one is present).
const MaxShedNameLength = 63
MaxShedNameLength is the maximum allowed length for a shed name.
const SnapshotSchemaVersion = 2
SnapshotSchemaVersion is the current snapshot schema version. Bumped from 1 to 2 with the introduction of LowerDigest tracking.
Variables ¶
var ( // ErrShedNotFoundSentinel is returned when a shed does not exist. ErrShedNotFoundSentinel = errors.New("shed not found") // ErrShedAlreadyExistsSentinel is returned when creating a shed that already exists. ErrShedAlreadyExistsSentinel = errors.New("shed already exists") // ErrShedAlreadyRunningSentinel is returned when starting a shed that is already running. ErrShedAlreadyRunningSentinel = errors.New("shed is already running") // ErrSessionNotFoundSentinel is returned when a tmux session does not exist. ErrSessionNotFoundSentinel = errors.New("session not found") // ErrTmuxNotAvailableSentinel is returned when tmux is not installed in the container. ErrTmuxNotAvailableSentinel = errors.New("tmux is not available in this container") // ErrShedNotRunningSentinel is returned when an operation requires a running shed. ErrShedNotRunningSentinel = errors.New("shed is not running") // ErrShedNotStoppedSentinel is returned when an operation requires a // stopped shed (e.g. shed reset, shed snapshot create). ErrShedNotStoppedSentinel = errors.New("shed must be stopped first") // ErrUnknownImageSentinel is returned when a requested image variant does not exist. ErrUnknownImageSentinel = errors.New("unknown image") // ErrImageNotFoundSentinel is returned when a cached image does not exist. ErrImageNotFoundSentinel = errors.New("image not found") // ErrImageInUseSentinel is returned when trying to delete an image referenced by config. ErrImageInUseSentinel = errors.New("image is referenced by config") // ErrNotSupportedSentinel is returned when an operation is not supported by a backend. ErrNotSupportedSentinel = errors.New("not supported by this backend") // ErrSnapshotNotFoundSentinel is returned when a snapshot does not exist. ErrSnapshotNotFoundSentinel = errors.New("snapshot not found") // ErrSnapshotAlreadyExistsSentinel is returned when creating a snapshot that already exists. ErrSnapshotAlreadyExistsSentinel = errors.New("snapshot already exists") // ErrSnapshotSourceRunningSentinel is returned when snapshotting a running shed. ErrSnapshotSourceRunningSentinel = errors.New("source shed is running; stop it before snapshotting") // ErrSnapshotBackendMismatchSentinel is returned when spawning a snapshot on the wrong backend. ErrSnapshotBackendMismatchSentinel = errors.New("snapshot backend does not match target") // ErrInvalidShedRequestSentinel is the catch-all sentinel for CreateShedRequest // field-level validation that maps to HTTP 400 INVALID_REQUEST. Add new // field-conflict cases (e.g., --from-snapshot combined with --image or --repo) // under this sentinel rather than minting per-conflict sentinels. ErrInvalidShedRequestSentinel = errors.New("invalid create-shed request") // ErrStopIncompleteSentinel is returned when StopShed asked the VMM to // terminate but the recorded PID is still alive after the stop // sequence (vm.Stop returned without error). Surfacing this prevents // the metadata from advertising status=stopped while a zombie VMM // still holds the workspace upper / vsock sockets. ErrStopIncompleteSentinel = errors.New("VMM did not exit after stop sequence") // ErrZombiePresentSentinel is returned when StartShed sees a non-zero // PID in metadata whose process is still alive AND looks like the // expected VMM binary (vfkit / firecracker). This protects against // silently spawning a second VMM under the same name when metadata // got out of sync (partial save / external tampering / server crash // between vm.Start and metadata.Save). ErrZombiePresentSentinel = errors.New("recorded VMM pid is still alive; refusing to spawn a second instance") )
Sentinel errors for shed and session operations.
var ErrCredentialPairMismatch = creds.ErrPairMismatch
ErrCredentialPairMismatch reports a stored certificate and private key that do not belong together.
It is a RECOVERABLE state, not a corruption to report to the user: a crash between the two renames of a rotation leaves exactly this, and so does an interrupted restore. Callers treat it as "no credential" and re-enroll — see LoadClientCredentials.
Functions ¶
func AddKnownHost ¶
AddKnownHost adds an SSH host key to the known_hosts file.
func ClientCredentialPaths ¶ added in v0.8.1
ClientCredentialPaths returns the certificate and key paths for a named server, without touching the filesystem.
func CredentialMountTag ¶
CredentialMountTag returns the VirtioFS/9P mount tag for a credential share. Tags use the format "cred-{name}" to avoid collisions with project mounts.
func CredsStagingRoot ¶ added in v0.8.1
func CredsStagingRoot() string
CredsStagingRoot returns the directory holding not-yet-adopted credential material (~/.shed/creds/%staging). Exported so a test can make a staging write fail without reaching into this package's internals.
func EnsureConfigDir ¶
func EnsureConfigDir() error
EnsureConfigDir ensures the config directory exists.
func ExpandPath ¶
ExpandPath expands ~ to the user's home directory.
func ExpandRepoShorthand ¶
ExpandRepoShorthand expands owner/repo shorthand to a full git SSH URL. Full URLs (with scheme or git@ prefix) pass through unchanged. Empty strings pass through unchanged.
func GetClientConfigDir ¶
func GetClientConfigDir() string
GetClientConfigDir returns the path to the shed config directory.
func GetClientConfigPath ¶
func GetClientConfigPath() string
GetClientConfigPath returns the path to the client config file.
func GetCredsDir ¶ added in v0.8.1
func GetCredsDir() string
GetCredsDir returns the root of the client-credential store (~/.shed/creds).
func GetKnownHostsPath ¶
func GetKnownHostsPath() string
GetKnownHostsPath returns the path to the known_hosts file.
func GetSyncConfigPath ¶
func GetSyncConfigPath() string
GetSyncConfigPath returns the path to the sync config file.
func GetTunnelLogDir ¶ added in v0.7.6
func GetTunnelLogDir() string
GetTunnelLogDir returns the directory holding background tunnel daemon logs.
func GetTunnelLogPath ¶ added in v0.7.6
GetTunnelLogPath returns the log path for a shed's background tunnel daemon. shedName is escaped so a name containing path separators can't traverse out of the log directory (the daemon opens this path with O_TRUNC).
func GetTunnelStatePath ¶
func GetTunnelStatePath() string
GetTunnelStatePath returns the path to the tunnel state file.
func GetTunnelsConfigPath ¶
func GetTunnelsConfigPath() string
GetTunnelsConfigPath returns the path to the tunnels config file.
func HasWritableHostMount ¶ added in v0.7.7
func HasWritableHostMount(projectMounts []MountConfig, serverCfg *ServerConfig) bool
HasWritableHostMount reports whether a shed with the given project mounts (--local-dir/--add-dir), running under serverCfg, has any writable host-backed directory. A destroy/delete SIGKILLs the guest without a graceful shutdown; if any such mount exists the guest must be synced first, or unsynced writes to that host data are lost. (The discarded upper needs no sync — only host-backed mounts, which outlive the shed, do.) serverCfg may be nil.
func IsReservedEgressName ¶ added in v0.7.5
IsReservedEgressName reports whether name is reserved (cannot be a user/config profile name): "off", "none", "default" (case-insensitive).
func IsSSHRepoURL ¶
IsSSHRepoURL reports whether a repo URL uses SSH transport. Matches both `git@host:path` (SCP-like) and `ssh://...` schemes. HTTPS, git://, http://, and empty strings return false.
func KnownHostLine ¶ added in v0.8.1
KnownHostLine renders the exact ~/.shed/known_hosts line for an endpoint, in OpenSSH's syntax: a bare hostname on port 22, the bracketed "[host]:port" form otherwise.
It is exported because it is also the IDENTITY of a line: RemoveKnownHost matches on it, and `shed server add` keeps it around so a failed add can undo exactly the line it wrote and nothing else.
func LegacyWireAuthMode ¶ added in v0.8.1
LegacyWireAuthMode maps the canonical "token" spelling back to the legacy "secure" for wire surfaces that released pre-rename clients consume.
The one such surface is /api/info's auth_mode field: an old client decides whether to bootstrap a credential at all by checking the exact string "secure" (cmd/shed/server.go:352 at v0.8.0). Reporting "token" there makes the old client skip the bootstrap and save an entry with no credential, which then 401s on every command — so token mode keeps the legacy spelling on that wire indefinitely, and clients normalize on decode (NormalizeAuthMode). "open" and "mtls" pass through: "open" predates the rename unchanged, and mtls-mode /api/info is client-certificate-gated, so no pre-rename client can ever read it.
func LoadClientCredentials ¶ added in v0.8.1
func LoadClientCredentials(name, certPath, keyPath string) (*tls.Certificate, error)
LoadClientCredentials reads a server's stored certificate + key under that server's credential lock and assembles them for the TLS stack.
It returns (nil, err) for EVERY unusable state — absent files, unreadable files, malformed PEM, and a certificate that does not match the key — because all of them mean the same thing to the caller: there is no credential to present, so re-enroll.
name identifies the lock, not the paths: the paths come from the config entry (which the user may have edited) while the lock is keyed on the entry's name, matching what WriteClientCredentials holds. An empty name skips the lock.
func LockServerCredentials ¶ added in v0.8.1
LockServerCredentials takes the exclusive advisory lock guarding one server's credential pair and returns the release function.
It is exported so a caller can hold that lock across a SEQUENCE the store cannot see as one operation — specifically, the CLI's credential persists, which have to update config.yaml and the credential files together. Writing the files and updating the config are two separately-locked commits, and a token↔mtls flip racing an mtls enrollment can interleave them into "config names a certificate that the other persist just deleted". Holding this lock across both halves is what makes that pair atomic.
LOCK ORDERING (the whole invariant, in one place): this lock is taken BEFORE the client-config file lock, never after, and no path takes it while holding the config lock. Update's mutation closure must therefore never call anything in this file — Load/Write/Remove/Stage/Commit all sit under this lock.
It is NOT re-entrant: flock keys on the open file description, so calling LoadClientCredentials or WriteClientCredentials (which take the same lock internally) while holding this one deadlocks the process against itself. Callers hold it around Remove — which does not lock — and around config updates.
func MatchesExcludePatterns ¶
MatchesExcludePatterns reports whether relPath matches any of the given glob patterns. Patterns like "dir/*" also match the directory itself and deeply nested paths (e.g., "dir/sub/deep/file").
func NormalizeAuthMode ¶ added in v0.8.1
NormalizeAuthMode maps the deprecated "secure" auth.mode spelling to the canonical "token"; every other value passes through unchanged. This is the client-boundary half of the wire-compat contract (see LegacyWireAuthMode): any code that decodes an auth mode string off the wire — /api/info from a released pre-rename server (which reports "secure"), or from a current server in token mode (which deliberately still reports "secure") — must normalize before comparing against the AuthMode* constants.
func ParseUpperSize ¶
ParseUpperSize accepts a human-friendly suffix (G, M, or bare bytes) and returns the size in bytes. Validates the range against MinUpperSizeBytes/MaxUpperSizeBytes.
Pre-checks that n*mul won't overflow int64 — otherwise a value like "10000000000G" would silently wrap to a tiny positive number and slip past the range bounds.
func ProjectAddDirTargets ¶ added in v0.6.6
func ProjectAddDirTargets(mounts []MountConfig, landingDir string) []string
ProjectAddDirTargets returns the guest target paths of a shed's --add-dir mounts: every project mount except the --local-dir mount the shed lands in (identified by landingDir). Order is preserved. Returns nil when there are no add-dir mounts (a bare, --repo, or --local-dir-only shed). Used to expose SHED_ADD_DIRS to sessions and provisioning hooks.
func ProjectMountBasename ¶ added in v0.6.4
ProjectMountBasename validates a host directory destined to be mounted under the shed user's home directory and returns the guest-directory basename it will be mounted at (HomePath/<basename>).
Dotfile-style names are rejected because project mounts become siblings of the home directory's own infrastructure (~/.ssh, ~/.config, ~/.local, ...), all of which are dot-prefixed; refusing leading-dot basenames prevents a mount from shadowing them.
func ProjectMountSources ¶ added in v0.6.4
func ProjectMountSources(mounts []MountConfig) []string
ProjectMountSources returns the host source directories of the given mounts.
func ProjectMountTag ¶ added in v0.6.4
ProjectMountTag returns a stable, unique, virtio-fs-safe mount tag for a project mount (--local-dir / --add-dir) identified by its guest-dir basename. The tag is "proj-<sanitized>-<hash>": the hash keeps tags distinct even when two different basenames sanitize to the same prefix, and the whole tag is capped at maxMountTagLen.
func ProjectMountTagForTarget ¶ added in v0.6.4
ProjectMountTagForTarget returns the VirtioFS/9P mount tag for a project mount given its guest target path (HomePath/<basename>). Guest paths always use '/'.
func RemoveKnownHost ¶ added in v0.8.1
RemoveKnownHost deletes ONE line from ~/.shed/known_hosts: the exact line AddKnownHost would have written for this endpoint and key.
It exists so a `shed server add` that pins a host key and then fails — an unauthorized SSH key, a verification that does not answer, a duplicate name — can leave the file exactly as it found it. Pinning a key for a server that was never added is quiet residue: it silently pre-trusts that endpoint for the next attempt, which is precisely the decision the operator was in the middle of making.
The match is on the whole rendered line, so an entry the user (or an earlier successful add) put there is never touched — not even for the same host with a different key, and not a "@cert-authority"/"@revoked" marked line, whose leading token makes it a different line. Only the LAST occurrence is removed: the line this call is undoing is the one most recently appended.
A missing file, or a file with no such line, is not an error — the caller is undoing something it may never have done.
func RemoveServerCredentials ¶ added in v0.8.1
RemoveServerCredentials deletes a server's credential directory. It is called by `shed server rm`: leaving a private key behind for a server the user has explicitly forgotten is exactly the kind of quiet residue that turns up years later. A missing directory is not an error.
func RepoDirName ¶ added in v0.6.4
RepoDirName returns the directory name `git clone <repo>` would create: the last path segment of the URL with a trailing ".git" removed. It mirrors git's own default-directory logic for the URL forms shed accepts (https://, git://, ssh://, git@host:path, and expanded owner/repo shorthand).
func ResolveBackend ¶
ResolveBackend resolves a backend string to a concrete backend type. When backend is "detect", it selects based on the platform: darwin/arm64 → vz, linux → firecracker.
func SanitizeRepoURL ¶
SanitizeRepoURL returns repo with the password component removed from the URL's userinfo, if any. The username is preserved (it's informational, not a secret). SSH-form URLs (git@host:path) and shorthand pass through unchanged. Use this when logging URLs to avoid leaking embedded credentials from forms like `https://user:password@host/repo.git`.
func ServerCredsDir ¶ added in v0.8.1
ServerCredsDir returns the credential directory for a named server entry.
func ValidGitHubUsername ¶ added in v0.7.0
ValidGitHubUsername reports whether s is a syntactically valid GitHub username (and, by construction, safe to interpolate into the .keys URL).
func ValidateGitRepoURL ¶
ValidateGitRepoURL validates that a git repository URL is well-formed. Accepts https://, git://, ssh://, and git@host:path formats.
func ValidateMountDir ¶ added in v0.6.4
ValidateMountDir checks that a project-mount host directory is usable: an absolute path, comma-free (commas break vfkit VirtioFS device arguments), existing, and a directory. The returned errors are phrased as predicates ("must be an absolute path", "does not exist", ...) so callers can prefix them with the relevant flag/field name. Shared by the CLI (cmd/shed) and the API handler so both validate identically.
func ValidateSessionName ¶
ValidateSessionName validates that a session name is valid. Names must be alphanumeric with underscores and hyphens allowed, must start with an alphanumeric character, and must be at most 63 characters.
func ValidateShedName ¶
ValidateShedName validates that a shed name is valid. Names must be lowercase alphanumeric with hyphens allowed (not at start/end), must start with a letter, and must be at most 63 characters.
func ValidateSnapshotName ¶
ValidateSnapshotName validates that a snapshot name is valid. Snapshot names follow the same rules as shed names.
Types ¶
type APIError ¶
type APIError struct {
Error APIErrorDetail `json:"error"`
}
APIError represents an error response from the API.
func NewAPIError ¶
NewAPIError creates a new APIError with the given code and message.
type APIErrorDetail ¶
APIErrorDetail contains the error code and message.
type AuthConfig ¶ added in v0.7.0
type AuthConfig struct {
// Mode is open | token | mtls (default open; the deprecated "secure"
// spelling is normalized to "token" at config load, with one startup
// deprecation warning). token derives: SSH allowlist enforce, HTTP
// bearer-token enforce, and TLS on (the server serves the TLS listener
// only). mtls derives the same SSH-allowlist-enforce and TLS-only posture,
// but the client credential is a short-lived certificate rather than a
// bearer token. Both require at least one SSH key source.
Mode string `yaml:"mode,omitempty"`
// TokenTTL is the lifetime of a bootstrap-minted HTTP token (default 24h).
TokenTTL Duration `yaml:"token_ttl,omitempty"`
// SSH configures the SSH public-key allowlist (key sources + advanced mode).
SSH *SSHAuthConfig `yaml:"ssh,omitempty"`
}
AuthConfig configures authentication. The headline control is Mode (the open|token|mtls switch). The SSH sub-block carries key sources and the advanced SSH mode override. HTTP bearer-token enforcement is derived purely from token mode — there is no HTTP sub-block.
type ClientConfig ¶
type ClientConfig struct {
Servers map[string]ServerEntry `yaml:"servers"`
DefaultServer string `yaml:"default_server"`
Sheds map[string]ShedCache `yaml:"sheds"`
CreateTimeout time.Duration `yaml:"create_timeout,omitempty"`
// Machines is an OPAQUE passthrough of the `machines:` section owned by the
// Rust porcelain (sx) — remote-machine targets for rc-session kickoff. Go
// neither reads nor validates it (the schema is defined Rust-side); the field
// exists ONLY so SaveToPath's whole-document rewrite round-trips the subtree
// instead of silently deleting user data on the next `shed` command that
// updates this file (cache refresh, `shed server add`, token mint…).
Machines yaml.Node `yaml:"machines,omitempty"`
// contains filtered or unexported fields
}
ClientConfig represents the CLI-side configuration.
func LoadClientConfig ¶
func LoadClientConfig() (*ClientConfig, error)
LoadClientConfig loads the client configuration from the default location.
func LoadClientConfigFromPath ¶
func LoadClientConfigFromPath(path string) (*ClientConfig, error)
LoadClientConfigFromPath loads client configuration from a specific path.
func (*ClientConfig) AddServer ¶
func (c *ClientConfig) AddServer(name string, entry ServerEntry) error
AddServer adds a new server to the configuration.
Like CacheShed, it is NOT safe as an Update mutation: it stamps AddedAt with time.Now() (two applications, two instants) and it can FAIL — and an Update mutation that fails on the second application fails after the file has already been written, turning a successful add into a reported error. Callers under Update validate first and then assign a fully-formed entry; see cmd/shed/server_add.go:saveAddedServer.
func (*ClientConfig) CacheShed ¶
func (c *ClientConfig) CacheShed(name string, server string, status string)
CacheShed caches a shed's location, stamped now.
It is NOT safe inside an Update mutation: Update runs the mutation twice (the fresh on-disk snapshot, then the caller's in-memory one) and the two calls would stamp two different instants, so the file and the running process would hold rows that differ in a field neither ever meant to disagree on. Use CacheShedAt with a timestamp taken once, outside the mutation.
func (*ClientConfig) CacheShedAt ¶ added in v0.8.1
CacheShedAt is CacheShed with the timestamp supplied by the caller, which is what makes a cache write a deterministic function of its inputs and therefore usable as an Update mutation.
func (*ClientConfig) GetCreateTimeout ¶
func (c *ClientConfig) GetCreateTimeout() time.Duration
GetCreateTimeout returns the configured create timeout or the default (10 minutes). One per-manifest mkfs.erofs over a merged tree is single-digit seconds even on cold cache; only the first-pull from the registry adds notable wallclock, and 10 min is enough headroom even for a slow network on a multi-GB image. Cache hits on subsequent creates are sub-second.
func (*ClientConfig) GetDefaultServer ¶
func (c *ClientConfig) GetDefaultServer() (*ServerEntry, string, error)
GetDefaultServer returns the default server entry.
func (*ClientConfig) GetServer ¶
func (c *ClientConfig) GetServer(name string) (*ServerEntry, error)
GetServer returns a server by name.
func (*ClientConfig) GetShedServer ¶
func (c *ClientConfig) GetShedServer(name string) (string, error)
GetShedServer returns the server that hosts a shed.
func (*ClientConfig) RemoveServer ¶
func (c *ClientConfig) RemoveServer(name string) error
RemoveServer removes a server from the configuration.
func (*ClientConfig) RemoveShedCache ¶
func (c *ClientConfig) RemoveShedCache(name string)
RemoveShedCache removes a shed from the cache.
func (*ClientConfig) Save ¶
func (c *ClientConfig) Save() error
Save writes the configuration to disk.
It is the UNSYNCHRONIZED writer: it renames this whole snapshot over the file, discarding anything another process committed since this one loaded. Reach for Update instead for any mutation the CLI performs; Save survives for the first-write paths that own the file outright and for tests.
func (*ClientConfig) SaveToPath ¶
func (c *ClientConfig) SaveToPath(path string) error
SaveToPath writes the configuration to a specific path.
func (*ClientConfig) SetDefaultServer ¶
func (c *ClientConfig) SetDefaultServer(name string) error
SetDefaultServer sets the default server.
func (*ClientConfig) Update ¶ added in v0.8.1
func (c *ClientConfig) Update(mutate func(*ClientConfig)) error
Update applies mutate to the client configuration as a locked read-modify-write against what is CURRENTLY on disk, and is the only supported way to change a config that other processes may also be changing.
The problem it solves is that SaveToPath writes the whole document: a `shed list` that loaded the config a minute ago and then refreshes its shed cache renames its entire stale snapshot over whatever a concurrent `shed server add` — or a credential re-mint — just committed. Last writer wins, and what it wins with is everything, including the fields it never touched.
mutate cannot fail, by type. That is the whole reason this signature is not `func(*ClientConfig) error`: the mutation is applied TWICE (see UpdateChecked), with the file write in between, so a mutation that could fail on its second application would report an error for a change already durably committed. Anything that needs to say no is a precondition, and preconditions belong in UpdateChecked's check — which runs once, on the fresh snapshot, before anything is written.
func (*ClientConfig) UpdateChecked ¶ added in v0.8.1
func (c *ClientConfig) UpdateChecked(check func(*ClientConfig) error, mutate func(*ClientConfig)) error
UpdateChecked is Update with a precondition evaluated under the lock, against the config as it is on disk at that instant.
It exists because "validate, then update" is not a safe shape when another process may be writing the same file: a check performed before the lock is taken reads a snapshot that can be stale by the time the write lands — two concurrent `shed server add`s for one name both see "not taken", and both succeed. The check has to happen inside the locked section, against the fresh snapshot, and it has to happen exactly once.
The sequence is:
- take updateMu (in-process) and then the config file lock (cross-process, blocking; ORDER: the creds lock, when a caller needs both, is taken by that caller BEFORE this — see cmd/shed/client.go — and nothing here ever takes a creds lock);
- load a FRESH snapshot from the receiver's path (a missing file is the empty config, exactly as LoadClientConfigFromPath treats it);
- run check on that snapshot; a non-nil error aborts with NOTHING written and the receiver untouched;
- run mutate on the snapshot and save it;
- run the SAME mutate on the receiver.
Step 5 is a re-application rather than a wholesale replacement on purpose: concurrent readers of the in-memory config hold live map values, and swapping the maps out from under them would make an unrelated entry momentarily vanish. It also means mutate must be a deterministic function of the config it is handed — one that stamps its own time.Now() leaves the file and the running process holding rows that differ (which is why CacheShedAt exists beside CacheShed, and why AddServer must not be called from here).
check is evaluated ONLY on the fresh snapshot. The receiver is this process's view of that same file, so re-checking it would either be redundant or, in the one case where the two disagree, would fail after the write committed.
type CreateShedRequest ¶
type CreateShedRequest struct {
Name string `json:"name"`
Repo string `json:"repo,omitempty"`
Image string `json:"image,omitempty"`
NoProvision bool `json:"no_provision,omitempty"`
// Backend specifies which backend to use ("firecracker" or "vz").
// If empty, uses the server's configured backend.
Backend string `json:"backend,omitempty"`
// CPUs specifies the number of vCPUs (firecracker/vz only)
CPUs int `json:"cpus,omitempty"`
// MemoryMB specifies the memory in MB (firecracker/vz only)
MemoryMB int `json:"memory_mb,omitempty"`
// LocalDir mounts a host directory under the home directory (at
// /home/shed/<basename>) and makes it the landing directory.
// Mutually exclusive with Repo.
LocalDir string `json:"local_dir,omitempty"`
// AddDirs mounts additional host directories under the home directory
// (each at /home/shed/<basename>) as reference siblings of LocalDir.
// Only valid together with LocalDir.
AddDirs []string `json:"add_dirs,omitempty"`
// Egress assigns Level-1 egress-control profiles to this shed (composed,
// first-match). Empty inherits the server `egress.default`; ["off"]
// disables egress for this shed even when a default is set.
Egress []string `json:"egress,omitempty"`
// FromSnapshot spawns the shed from a snapshot's rootfs instead of a base image.
// Mutually exclusive with Image and Repo. Provisioning steps (repo clone, install
// hook, first-time auto-sync) are skipped because the snapshot is already provisioned.
FromSnapshot string `json:"from_snapshot,omitempty"`
// UpperSizeBytes is the logical size of the per-shed writable upper.
// Zero falls back to the backend's upper_size_default config value.
UpperSizeBytes int64 `json:"upper_size_bytes,omitempty"`
}
CreateShedRequest is the request body for POST /api/sheds.
type DiskSize ¶
type DiskSize struct {
LogicalBytes int64 `json:"logical_bytes"`
PhysicalBytes int64 `json:"physical_bytes"`
}
DiskSize captures both apparent (logical) and allocated (physical) bytes. PhysicalBytes comes from stat.Blocks * 512. On APFS and other reflink-capable filesystems, a file's st_blocks counts cloned-but-unmodified extents against every referencing file, so summing PhysicalBytes across files that share extents (via clonefile, FICLONE, or hardlinks) overcounts the actual on-disk usage. This is accepted for v1 and surfaced in DiskUsage.Notes.
type DiskUsage ¶
type DiskUsage struct {
ServerName string `json:"server_name"`
Backend string `json:"backend"` // "vz" | "firecracker" | "none"
GeneratedAt time.Time `json:"generated_at"`
Images []ImageDiskEntry `json:"images"`
Kernel *FileEntry `json:"kernel,omitempty"`
Initrd *FileEntry `json:"initrd,omitempty"` // VZ only
Sheds []ShedDiskEntry `json:"sheds"`
Snapshots []SnapshotDiskEntry `json:"snapshots,omitempty"`
Orphans []FileEntry `json:"orphans"`
Totals DiskUsageTotals `json:"totals"`
// Notes carries advisory caveats (APFS overcount, hardlink double-count, etc.).
Notes []string `json:"notes,omitempty"`
}
DiskUsage is the payload returned by GET /api/system/df.
type DiskUsageOrError ¶
type DiskUsageOrError struct {
ServerName string `json:"server_name"`
Usage *DiskUsage `json:"usage,omitempty"`
Error string `json:"error,omitempty"`
}
DiskUsageOrError is one entry in a multi-server SystemDFResponse. Exactly one of Usage or Error is populated.
type DiskUsageTotals ¶
type DiskUsageTotals struct {
Images DiskSize `json:"images"` // includes kernel + initrd
Sheds DiskSize `json:"sheds"`
Snapshots DiskSize `json:"snapshots"`
Orphans DiskSize `json:"orphans"`
All DiskSize `json:"all"`
}
DiskUsageTotals aggregates bytes across df sections.
type Duration ¶
Duration is a wrapper around time.Duration for YAML marshaling
func (Duration) MarshalYAML ¶
MarshalYAML implements yaml.Marshaler for Duration
func (*Duration) UnmarshalYAML ¶
UnmarshalYAML implements yaml.Unmarshaler for Duration
type EgressConfig ¶ added in v0.7.1
type EgressConfig struct {
// Enabled is the master switch. When false the proxy child is never started
// and no guest injection happens.
Enabled bool `yaml:"enabled"`
// PortRange is the inclusive "lo-hi" range for per-shed listener ports.
PortRange string `yaml:"port_range,omitempty"`
// Default is the profile list applied to sheds created without --egress.
// ABSENT or [] means no egress (NOT [audit]); a list applies those profiles.
Default []string `yaml:"default,omitempty"`
// Profiles are reusable named policy fragments. Names "off", "none", and
// "default" are reserved.
Profiles map[string]EgressProfile `yaml:"profiles,omitempty"`
}
EgressConfig is the server-level Level-1 (audit-first, cooperative) egress policy. Off by default — the common case is unrestricted. See docs/reference/egress.md. The egress proxy is a child process of shed-server that is launched only when Enabled.
func (*EgressConfig) PortRangeBounds ¶ added in v0.7.1
func (c *EgressConfig) PortRangeBounds() (int, int)
PortRangeBounds returns the configured [lo, hi] listener-port range, or a sensible default when unset.
func (*EgressConfig) ResolveProfiles ¶ added in v0.7.1
func (c *EgressConfig) ResolveProfiles(req []string, user map[string]EgressProfile) ([]egress.ProfileSpec, error)
ResolveProfiles returns the composed profile specs for a shed's requested profile list (empty inherits Default), resolving names from the config profiles MERGED with the caller-supplied runtime user profiles. It returns (nil, nil) when egress is disabled or the effective list is empty / "off" / "none" — meaning this shed gets no egress proxy at all.
user is the UserProfileStore snapshot (pass store.List(), nil-safe); a nil/empty map makes resolution byte-identical to the config-only behavior.
func (*EgressConfig) Validate ¶ added in v0.7.1
func (c *EgressConfig) Validate() error
Validate fails fast at config load on a bad port range, a reserved/dangling profile name, or an uncompilable glob/CEL rule.
type EgressProfile ¶ added in v0.7.1
type EgressProfile struct {
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
Allow []string `json:"allow,omitempty" yaml:"allow,omitempty"`
Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
Rule string `json:"rule,omitempty" yaml:"rule,omitempty"`
}
EgressProfile is one named policy fragment. Allow/Deny are domain globs ("*.github.com" suffix, "github.com" exact); Rule is a CEL expression (the power path); Mode "audit" makes the fall-through allow+log instead of deny.
The json tags are the wire contract for the `rules` map in `shed egress show --json` (GET /api/egress/{name}). They are snake_case to match the rest of the API (egress.AuditRecord, EgressStatus); omitempty keeps the output to the fields a profile actually sets.
type EgressProfileInfo ¶ added in v0.7.5
type EgressProfileInfo struct {
Name string `json:"name"`
Source string `json:"source"` // "config" | "user"
Profile EgressProfile `json:"profile"`
}
EgressProfileInfo is one entry in the `GET /api/egress/profiles` response: a named profile plus whether it comes from the server config (read-only baseline) or the runtime user store.
type EgressSetRequest ¶ added in v0.7.1
type EgressSetRequest struct {
Profiles []string `json:"profiles"`
}
EgressSetRequest is the `POST /api/egress/{name}` body (live `shed egress set`).
type EgressStatus ¶ added in v0.7.1
type EgressStatus struct {
Shed string `json:"shed"`
Enabled bool `json:"enabled"` // server-level egress master switch
Profiles []string `json:"profiles,omitempty"` // effective profiles for this shed
Port int `json:"port,omitempty"` // assigned listener port
Rules map[string]EgressProfile `json:"rules,omitempty"` // definitions of the active profiles
Recent []egress.AuditRecord `json:"recent,omitempty"` // recent egress decisions for this shed
}
EgressStatus is the `shed egress show` response: a shed's active egress assignment, the resolved definitions of its profiles, and recent decisions.
type ExtensionHealthInfo ¶
type ExtensionHealthInfo struct {
Guest string `json:"guest"` // "running", "stopped", "failed"
Host string `json:"host"` // "connected", "unreachable", "unknown"
}
ExtensionHealthInfo is the API-facing extension health for a shed.
type ExtensionsConfig ¶
type ExtensionsConfig struct {
// Enabled lists the extension namespaces to activate in VMs
// (e.g., ["ssh-agent", "aws-credentials"]).
Enabled []string `yaml:"enabled"`
}
ExtensionsConfig configures which extensions the agent should enable.
func (*ExtensionsConfig) Validate ¶
func (e *ExtensionsConfig) Validate() error
Validate checks that all extension namespaces are valid and unique.
type FileEntry ¶
type FileEntry struct {
Path string `json:"path"`
Size DiskSize `json:"size"`
// Kind is one of: "rootfs" | "console_log" | "kernel" | "initrd" |
// "lock" | "tmp" | "source" | "metadata" | "snapshot_orphan".
Kind string `json:"kind,omitempty"`
}
FileEntry describes a single file with its size and classification.
type FirecrackerConfig ¶
type FirecrackerConfig struct {
// KernelPath is the path to the Linux kernel image
KernelPath string `yaml:"kernel_path"`
// DefaultImage is the Docker ref (or local rootfs path) used for new
// sheds when no --image is given. Docker refs are resolved by their
// io.shed.source-ref identity and pulled per PullPolicy on first use.
DefaultImage string `yaml:"default_image"`
// ImageAliases maps short alias names to Docker refs (or paths) for
// convenience with: shed create mydev --image <alias>. Aliases resolve
// to the underlying ref; image listings always show the resolved ref.
ImageAliases map[string]string `yaml:"image_aliases,omitempty"`
// PullPolicy controls cache-vs-pull at create: "missing" (default —
// use the cached ref, pull only if absent), "always" (always pull),
// or "never" (error if not cached). Ignored for local-path images.
PullPolicy string `yaml:"pull_policy,omitempty"`
// PullConcurrency caps how many image blobs (layers + kernel/initrd/
// erofs) download in parallel during a registry pull. Defaults to
// DefaultPullConcurrency; must be >= 1 (1 == serial).
PullConcurrency int `yaml:"pull_concurrency,omitempty"`
// ImagesDir is the directory for the content-addressed image store.
ImagesDir string `yaml:"images_dir,omitempty"`
// InstanceDir is the directory for instance data
InstanceDir string `yaml:"instance_dir"`
// SnapshotsDir is the directory where shed snapshots are stored.
SnapshotsDir string `yaml:"snapshots_dir,omitempty"`
// UppersDir is the directory where per-shed writable upper layers
// (sparse ext4 files) are stored.
UppersDir string `yaml:"uppers_dir,omitempty"`
// UpperSizeDefault is the default logical size of the per-shed
// writable upper. Accepted units: G (GB) and M (MB). Range 1G-100G.
UpperSizeDefault string `yaml:"upper_size_default,omitempty"`
// SocketDir is the directory for Firecracker API sockets
SocketDir string `yaml:"socket_dir"`
// DefaultCPUs is the default number of vCPUs for new VMs
DefaultCPUs int `yaml:"default_cpus"`
// DefaultMemoryMB is the default memory in MB for new VMs
DefaultMemoryMB int `yaml:"default_memory_mb"`
// DefaultDiskGB is the default disk size in GB for new VMs
DefaultDiskGB int `yaml:"default_disk_gb"`
// VsockBaseCID is the starting CID for vsock allocation
VsockBaseCID uint32 `yaml:"vsock_base_cid"`
// ConsolePort is the vsock port for console/exec connections
ConsolePort uint32 `yaml:"console_port"`
// NotifyPort is the vsock port for the message channel (health checks, plugins, credentials)
NotifyPort uint32 `yaml:"notify_port"`
// TCPProxyPort is the vsock port for the TCP proxy (used by DialService to
// reach VM services). Must match the guest agent's flagless default (1028);
// the systemd unit starts shed-agent without a --tcp-proxy-port override.
TCPProxyPort uint32 `yaml:"tcp_proxy_port"`
// StartTimeout is the timeout for VM startup
StartTimeout Duration `yaml:"start_timeout"`
// StopTimeout is the timeout for graceful VM shutdown
StopTimeout Duration `yaml:"stop_timeout"`
// GuestMTU, when non-zero, forces the guest's primary interface MTU
// instead of auto-detecting the host's egress path MTU at VM start.
// 0 (the default) means auto-detect: behind a reduced-MTU path (e.g. a
// VPN/overlay) the guest is lowered to match; otherwise it stays at
// 1500. Set this only to pin a value when detection misses. Validated to
// [MinGuestMTU, MaxGuestMTU] when non-zero.
GuestMTU int `yaml:"guest_mtu,omitempty"`
// BridgeName is the name of the Linux bridge for VM networking
BridgeName string `yaml:"bridge_name"`
// BridgeCIDR is the CIDR for the bridge network (e.g., "172.30.0.1/24")
BridgeCIDR string `yaml:"bridge_cidr"`
// TAPPrefix is the prefix for TAP device names
TAPPrefix string `yaml:"tap_prefix"`
}
FirecrackerConfig contains Firecracker-specific configuration.
func DefaultFirecrackerConfig ¶
func DefaultFirecrackerConfig() *FirecrackerConfig
DefaultFirecrackerConfig returns a FirecrackerConfig with default values.
Cross-backend alignment (DO NOT drift between this and DefaultVZConfig without an explicit reason — see DefaultVZConfig's header comment for the rationale on each field):
- DefaultCPUs / DefaultMemoryMB / DefaultDiskGB: same physical resource shape per shed on both backends.
- ConsolePort (1024) / NotifyPort (1026) / TCPProxyPort (1028): the shared agent vsock contract. TCPProxyPort is aligned across backends as of the hub-parity change — FC's DialService routes through the guest agent's TCP proxy on this vsock port exactly like VZ (previously FC dialed the bridge IP directly and could not reach loopback-bound guest services such as the rc hub).
- StopTimeout (10 s): same in-guest stop sequence.
Intentionally divergent from VZ:
- StartTimeout (30 s vs VZ 60 s): FC's create wall time is around 3.7 s baseline (mini3 measurements in §11 of the runtime-opt doc); ~8× headroom. FC's in-guest mkfs.ext4 is ~0.18 s vs VZ's ~4.2 s on vfkit's slower virtio-blk write path — the historical reason VZ's StartTimeout is bigger does not apply here.
- SocketDir under /var/run/shed/firecracker (not a $HOME path) because Firecracker hosts run shed-server as root via systemd (see packaging/shed-server.service); the VZ equivalent runs in $HOME because macOS users typically run via homebrew.
- VsockBaseCID (100): FC needs an explicit CID per VM (vsock CIDs are integers ≥ 3); 100 leaves room for hand-assigned CIDs below it. VZ assigns CIDs through Apple's Virtualization framework, which uses its own scheme, so the field doesn't apply there.
- BridgeName / BridgeCIDR / TAPPrefix: FC uses a Linux bridge + TAP devices for its NAT-style network; VZ uses Apple's built-in vmnet shared/NAT network, which has no analogous tunables.
(KernelPath and BaseRootfs left empty by default — Phase A retired the flat-file layout, Phase B made the in-blob kernel canonical. Operators who want the legacy fallbacks set them explicitly.)
func (*FirecrackerConfig) GetDefaultImage ¶ added in v0.6.0
func (c *FirecrackerConfig) GetDefaultImage() string
GetDefaultImage implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetExtractKernel ¶
func (c *FirecrackerConfig) GetExtractKernel() bool
GetExtractKernel implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetImageAliases ¶ added in v0.6.0
func (c *FirecrackerConfig) GetImageAliases() map[string]string
GetImageAliases implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetImagesDir ¶
func (c *FirecrackerConfig) GetImagesDir() string
GetImagesDir implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetNeedsInitrd ¶
func (c *FirecrackerConfig) GetNeedsInitrd() bool
GetNeedsInitrd implements vmimage.ImageConfig.
Both VZ and Firecracker boot through the shed-overlay initramfs (it owns overlayfs assembly + pivot_root), so an initrd blob must be installed alongside every shed image regardless of backend.
func (*FirecrackerConfig) GetPlatform ¶
func (c *FirecrackerConfig) GetPlatform() string
GetPlatform implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetPullConcurrency ¶ added in v0.6.2
func (c *FirecrackerConfig) GetPullConcurrency() int
GetPullConcurrency implements vmimage.ImageConfig.
func (*FirecrackerConfig) GetPullPolicy ¶ added in v0.6.0
func (c *FirecrackerConfig) GetPullPolicy() string
GetPullPolicy implements vmimage.ImageConfig.
func (*FirecrackerConfig) ResolveBaseRootfs ¶
func (c *FirecrackerConfig) ResolveBaseRootfs() (ResolvedImage, error)
ResolveBaseRootfs resolves the default image (used when no --image is given).
func (*FirecrackerConfig) ResolveImage ¶
func (c *FirecrackerConfig) ResolveImage(image string) (ResolvedImage, error)
ResolveImage resolves an image selector to a local path or Docker ref.
func (*FirecrackerConfig) Validate ¶
func (c *FirecrackerConfig) Validate() error
Validate checks that the Firecracker configuration is valid.
type GitConfig ¶
type GitConfig struct {
// ExtraKnownHosts contains additional lines to append to the in-VM
// ~/.ssh/known_hosts before `git clone` runs over SSH. Each entry must be
// a valid known_hosts line (e.g., "github.com ssh-ed25519 AAAAC3..."),
// typically obtained by running `ssh-keyscan <host>` on a trusted machine.
// Built-in defaults (currently GitHub's published host keys) are always
// included; this list extends them.
ExtraKnownHosts []string `yaml:"extra_known_hosts,omitempty"`
}
GitConfig configures git behaviour for in-VM clones.
type ImageDescriptor ¶
type ImageDescriptor struct {
MediaType string `json:"media_type"`
Digest string `json:"digest"`
Size int64 `json:"size"`
Annotations map[string]string `json:"annotations,omitempty"`
}
ImageDescriptor is the wire-format counterpart of vmimage.Descriptor.
type ImageDiskEntry ¶
type ImageDiskEntry struct {
Name string `json:"name"`
Path string `json:"path"`
DockerRef string `json:"docker_ref,omitempty"`
Size DiskSize `json:"size"`
}
ImageDiskEntry is the df view of a cached image variant, carrying both logical and physical bytes. Kept separate from ImageInfo so /api/images wire format stays stable.
type ImageInfo ¶
type ImageInfo struct {
Name string `json:"name"`
Path string `json:"path,omitempty"`
DockerRef string `json:"docker_ref,omitempty"`
SizeBytes int64 `json:"size_bytes,omitempty"`
Source string `json:"source"` // "config", "user", or "dangling"
Cached bool `json:"cached"`
Digest string `json:"digest,omitempty"` // "sha256:..." digest of the blob
Tag string `json:"tag,omitempty"` // tag name pointing at this blob
InUse bool `json:"in_use,omitempty"` // protected by a shed/snapshot reference
// Alias is the friendly image_aliases key (e.g. "base"), set only for
// config-sourced images; empty for user-pulled or dangling blobs.
Alias string `json:"alias,omitempty"`
// IsDefault is true for the config image whose ref is default_image.
IsDefault bool `json:"is_default,omitempty"`
// BootOnly is true when the image was pulled without layer tarballs
// (boots fine; `shed image push` needs a `--with-layers` re-pull first).
BootOnly bool `json:"boot_only,omitempty"`
}
ImageInfo describes an available image variant.
Storage model is content-addressed: Digest pins the underlying blob, Tag is the optional human-readable name (matches Name for tagged images, empty for dangling blobs).
type ImageInspectResponse ¶
type ImageInspectResponse struct {
Image ImageInfo `json:"image"`
Manifest ImageManifest `json:"manifest"`
}
ImageInspectResponse is returned by GET /api/images/{tag-or-digest}.
type ImageManifest ¶
type ImageManifest struct {
Digest string `json:"digest"`
SchemaVersion int `json:"schema_version"`
MediaType string `json:"media_type,omitempty"`
Config ImageDescriptor `json:"config"`
Layers []ImageDescriptor `json:"layers,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
// Convenience fields lifted from annotations for backwards
// compatibility with consumers that read SourceRef directly.
SourceRef string `json:"source_ref,omitempty"`
Variant string `json:"variant,omitempty"`
KernelDigest string `json:"kernel_digest,omitempty"`
InitrdDigest string `json:"initrd_digest,omitempty"`
RootfsLogicalSize int64 `json:"rootfs_logical_size,omitempty"`
}
ImageManifest mirrors vmimage.OCIManifest for the wire format. Shape parallels the OCI image manifest spec; foreign tools (crane, oras, skopeo) inspecting shed's store see this same JSON on disk at blobs/sha256/<manifest-digest>.
type ImagePullRequest ¶
type ImagePullRequest struct {
DockerRef string `json:"docker_ref"`
Tag string `json:"tag"`
// Platform is an optional override (e.g. "linux/arm64"). Empty
// means the server-side backend's native platform.
Platform string `json:"platform,omitempty"`
// WithLayers pulls the full image (layer tarballs included). Default
// (false) pulls boot-only — config + kernel + initrd + erofs only —
// which the host boots from without the layers. Set true (the CLI's
// --with-layers) when the image will be re-pushed.
WithLayers bool `json:"with_layers,omitempty"`
}
ImagePullRequest is the body of POST /api/images/pull.
type ImagePullResponse ¶
ImagePullResponse is the response of POST /api/images/pull.
type ImagePushRequest ¶
type ImagePushRequest struct {
// Source is the local tag or digest to push.
Source string `json:"source"`
// Destination is the registry reference (e.g. "ghcr.io/org/repo:v1").
Destination string `json:"destination"`
}
ImagePushRequest is the body of POST /api/images/push.
type ImagePushResponse ¶
type ImagePushResponse struct {
Source string `json:"source"`
Destination string `json:"destination"`
}
ImagePushResponse is the response of POST /api/images/push.
type ImageTagRequest ¶
type ImageTagRequest struct {
Source string `json:"source"` // tag name or digest
Target string `json:"target"` // new tag name
}
ImageTagRequest is the body of POST /api/images/tag.
type ImagesResponse ¶
type ImagesResponse struct {
Images []ImageInfo `json:"images"`
}
ImagesResponse is returned by GET /api/images.
type MountConfig ¶
type MountConfig struct {
Source string `yaml:"source" json:"source,omitempty"`
Target string `yaml:"target" json:"target,omitempty"`
ReadOnly bool `yaml:"readonly" json:"readonly,omitempty"`
Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty"`
}
MountConfig represents a bind mount configuration. JSON tags are present so it serializes cleanly when embedded in shed metadata and API responses (config.Shed.ProjectMounts); the yaml tags drive server-config parsing.
func BuildProjectMounts ¶ added in v0.6.4
func BuildProjectMounts(localDir string, addDirs []string) ([]MountConfig, string, error)
BuildProjectMounts performs the structural validation of --local-dir and --add-dir and returns the ordered project mounts (the --local-dir entry first, then each --add-dir) together with the landing directory.
It validates flag combinations and basename uniqueness but performs NO filesystem checks (existence / is-a-directory) — those are done separately on whichever host actually owns the directories. When no local dir is given it returns (nil, HomePath, nil).
func ResolveCreateLayout ¶ added in v0.6.4
func ResolveCreateLayout(req CreateShedRequest) (mounts []MountConfig, landingDir string, err error)
ResolveCreateLayout computes the project mounts and landing directory for a (already-validated) create request. --repo lands in the cloned repository directory (no project mounts); --local-dir/--add-dir mount under the home directory and land in the --local-dir mount; otherwise the home directory.
func (MountConfig) MatchesExclude ¶
func (m MountConfig) MatchesExclude(relPath string) bool
MatchesExclude reports whether the given relative path matches any of the mount's exclude patterns. Patterns use filepath.Match glob syntax.
type PruneImagesResponse ¶
type PruneImagesResponse struct {
Deleted []ImageInfo `json:"deleted"`
}
PruneImagesResponse is returned by POST /api/images/prune.
type PruneReport ¶
type PruneReport struct {
DryRun bool `json:"dry_run"`
ServerName string `json:"server_name"`
Scope []string `json:"scope"`
Until string `json:"until"`
Items []PrunedItem `json:"items"`
Skipped []SkippedItem `json:"skipped,omitempty"`
Notes []string `json:"notes,omitempty"`
Totals PruneReportTotals `json:"totals"`
}
PruneReport is the payload returned by POST /api/system/prune.
type PruneReportOrError ¶
type PruneReportOrError struct {
ServerName string `json:"server_name"`
Report *PruneReport `json:"report,omitempty"`
Error string `json:"error,omitempty"`
}
PruneReportOrError is one entry in a multi-server aggregated response.
type PruneReportTotals ¶
PruneReportTotals summarizes what the prune pass did (or would do).
type PrunedItem ¶
type PrunedItem struct {
// Kind is one of: "image" | "rootfs" | "console_log" | "metadata" |
// "instance" | "lock" | "tmp" | "source" | "snapshot_orphan".
Kind string `json:"kind"`
Path string `json:"path,omitempty"`
// Name is the shed or image name when applicable.
Name string `json:"name,omitempty"`
// Action is "deleted" or "truncated".
Action string `json:"action"`
// Freed is the bytes attributed to this item. For clones or hardlinks
// the physical count reflects attribution, not necessarily reclamation —
// see PruneReport.Notes.
Freed DiskSize `json:"freed"`
// Reason is a human-readable justification (e.g. "stopped 5d ago").
Reason string `json:"reason,omitempty"`
}
PrunedItem describes one file or object removed (or proposed for removal) by `shed system prune`.
type ResolvedImage ¶
type ResolvedImage struct {
// Path is set when the ext4 image already exists on disk.
Path string
// DockerRef is set when the image needs to be pulled and converted.
DockerRef string
// Name is the variant name, used for caching (e.g., "default" → "default-rootfs.ext4").
Name string
// Digest is set when Path came from a tag in the content-addressed
// blob store. Empty for the legacy hardcoded-path escape hatch
// (where the caller can't tell us a digest). Carries the digest
// forward so EnsureImage doesn't have to re-do tag lookup — that
// second lookup was both awkward and racey (tag could advance
// between resolve and ensure).
Digest string
// PullPolicy is the configured pull_policy ("missing"|"always"|"never",
// validated at config load) carried through to EnsureImage. Empty/local
// paths treat it as a no-op.
PullPolicy string
}
ResolvedImage represents the result of resolving an image name. Either Path (ext4 already exists locally) or DockerRef (needs pull + conversion) is set.
type SSHAuthConfig ¶ added in v0.7.0
type SSHAuthConfig struct {
// Mode is off | warn | enforce (default off). off accepts all keys
// (legacy); warn logs would-deny attempts but accepts; enforce rejects
// keys not in the allowlist.
Mode string `yaml:"mode,omitempty"`
// AuthorizedKeys are inline OpenSSH authorized_keys lines.
AuthorizedKeys []string `yaml:"authorized_keys,omitempty"`
// AuthorizedKeysFile is a path to an authorized_keys-format file.
AuthorizedKeysFile string `yaml:"authorized_keys_file,omitempty"`
// GitHubUsers seeds the allowlist from https://github.com/<user>.keys,
// cached to disk and failing closed to the last-known-good cache.
GitHubUsers []string `yaml:"github_users,omitempty"`
// GitHubRefresh is how often to re-fetch GitHub keys (default 1h).
GitHubRefresh Duration `yaml:"github_refresh,omitempty"`
// MaxAuthTries caps public-key attempts per connection (0 = shed default 10).
// Raise it for clients whose agent holds many keys (1Password, Secretive) so
// the allowlisted key is tried before the server gives up.
MaxAuthTries int `yaml:"max_auth_tries,omitempty"`
}
SSHAuthConfig configures the SSH public-key allowlist. Identity comes from the offered key (the username still selects the shed), GitHub-style.
type SSHHostKeyResponse ¶
type SSHHostKeyResponse struct {
HostKey string `json:"host_key"`
}
SSHHostKeyResponse is returned by GET /api/ssh-host-key.
type ServerConfig ¶
type ServerConfig struct {
Name string `yaml:"name"`
HTTPPort int `yaml:"http_port"`
SSHPort int `yaml:"ssh_port"`
// Network surface. All optional. TrustedProxy defaults to false, which
// disables RealIP (see below) — the intended security default, not the
// legacy always-on RealIP.
//
// BindAddress is the interface every listener (HTTP, HTTPS, SSH) binds. It
// defaults to loopback (127.0.0.1) — shed is a local-development tool by
// default. Set a routable address (a LAN/tailnet IP, "0.0.0.0"/"*" for all
// IPv4, or "::" for all interfaces) to reach it off-box. In open mode that
// also requires AllowInsecureExposure, since open mode has no transport
// security; token mode (TLS + tokens) needs no acknowledgment.
BindAddress string `yaml:"bind_address,omitempty"`
// AllowInsecureExposure acknowledges binding an open-mode (plaintext,
// possibly unauthenticated) server to a non-loopback interface. Required to
// start such a server; ignored in token mode. Prefer auth.mode: token.
AllowInsecureExposure bool `yaml:"allow_insecure_exposure,omitempty"`
// TrustedProxy enables chi's RealIP middleware, which trusts the
// client-supplied X-Forwarded-For. Only safe behind a proxy that
// overwrites that header; the default (false) uses the real TCP peer
// so an attacker can't forge a source IP to evade per-IP rate limits
// or poison audit logs.
TrustedProxy bool `yaml:"trusted_proxy,omitempty"`
// HTTPSPort, when >0, starts an HTTPS listener (bound to bind_address)
// serving the same public router as the plain HTTP listener, presenting
// a self-signed cert that clients pin by fingerprint. 0 (default) = no
// HTTPS; plain HTTP only (legacy, unchanged).
HTTPSPort int `yaml:"https_port,omitempty"`
// TLSNames are extra hostnames/IPs added as SANs in the generated cert
// so hostname verification (curl --cacert, browsers) passes for each
// advertised address. localhost + 127.0.0.1 + ::1 are always included.
TLSNames []string `yaml:"tls_names,omitempty"`
// TLSCertFile / TLSKeyFile override where the cert + key are persisted.
// Empty (default) places them next to the SSH host key.
TLSCertFile string `yaml:"tls_cert_file,omitempty"`
TLSKeyFile string `yaml:"tls_key_file,omitempty"`
// Mounts are host directories mounted into every shed (e.g. ~/.ssh,
// ~/.config/gh). This was previously named "credentials".
Mounts map[string]MountConfig `yaml:"mounts"`
// Credentials is the deprecated alias for Mounts. When "mounts" is unset
// it is used as a fallback (see LoadServerConfigFromPath). Remove in a
// future release.
Credentials map[string]MountConfig `yaml:"credentials"`
EnvFile string `yaml:"env_file"`
LogLevel string `yaml:"log_level"`
Terminal *terminal.Config `yaml:"terminal"`
// DefaultBackend specifies the backend type: "vz", "firecracker", or "detect".
// When set to "detect", the backend is chosen based on the platform
// (vz on macOS/arm64, firecracker on linux).
DefaultBackend string `yaml:"default_backend,omitempty"`
// Firecracker contains Firecracker-specific configuration
Firecracker *FirecrackerConfig `yaml:"firecracker,omitempty"`
// VZ contains Apple Virtualization.framework-specific configuration (macOS only)
VZ *VZConfig `yaml:"vz,omitempty"`
// Extensions configures which extensions the agent should enable in VMs.
Extensions *ExtensionsConfig `yaml:"extensions,omitempty"`
// Git configures git-related behaviour for in-VM clones, including
// the SSH known_hosts content seeded before `git clone` runs.
Git *GitConfig `yaml:"git,omitempty"`
// Egress configures optional Level-1 (audit-first) egress control.
// Default (nil / enabled:false) preserves unrestricted networking.
Egress *EgressConfig `yaml:"egress,omitempty"`
// Auth configures optional authentication. Default (nil) preserves the
// legacy accept-all behavior.
Auth *AuthConfig `yaml:"auth,omitempty"`
// Loaded environment variables (not from YAML)
EnvVars map[string]string `yaml:"-"`
}
ServerConfig represents the server-side configuration.
func DefaultServerConfig ¶
func DefaultServerConfig() *ServerConfig
DefaultServerConfig returns a ServerConfig with default values.
func LoadServerConfig ¶
func LoadServerConfig() (*ServerConfig, error)
LoadServerConfig loads server configuration from standard locations. It checks in order: ./server.yaml, ~/.config/shed/server.yaml, /etc/shed/server.yaml
func LoadServerConfigForCLI ¶
func LoadServerConfigForCLI(path string) (*ServerConfig, error)
LoadServerConfigForCLI loads server config the same way as LoadServerConfigFromPath but skips the host/backend OS coupling validation. Used by CLI commands like `shed image push --local` and `shed image build` that read the OCI store via the backend's config block but never actually start a VM. For example, the publish-images.yaml workflow runs on a Linux runner, builds VZ images via `--target shed-vz-*`, and pushes them via `shed image push --local -c <config-with-vz-block>`. The strict validator rejects that combination ("vz backend is only supported on macOS") even though we're never going to boot a VM there.
CALLERS THAT START A VM (or accept arbitrary HTTP traffic that will start a VM) MUST use LoadServerConfigFromPath instead — the OS coupling check matters for them.
func LoadServerConfigFromPath ¶
func LoadServerConfigFromPath(path string) (*ServerConfig, error)
LoadServerConfigFromPath loads server configuration from a specific path. If path is empty, it searches standard locations.
func (*ServerConfig) ActiveDefaultImage ¶ added in v0.6.3
func (c *ServerConfig) ActiveDefaultImage() string
ActiveDefaultImage returns the resolved default_image for the configured default_backend (after ${shed.version} expansion / version synthesis at load), or "" if the active backend block is absent or has no default.
func (*ServerConfig) AuthEnforced ¶ added in v0.8.1
func (c *ServerConfig) AuthEnforced() bool
AuthEnforced reports whether the server enforces a non-open auth posture — token or mtls. SSH allowlist enforce, TLS-only serving (no plain HTTP), the https_port default, and non-loopback bind without an explicit acknowledgment all key off this combined predicate: both modes share the same network/SSH shape and differ only in how the client authenticates.
func (*ServerConfig) AuthModeValue ¶ added in v0.8.1
func (c *ServerConfig) AuthModeValue() string
AuthModeValue returns the effective auth.mode string, defaulting to AuthModeOpen when auth is unset. Config load guarantees this is one of open, token, or mtls by the time the server starts (normalizeAuthMode resolves the deprecated "secure" alias and validateAuth rejects anything else), so callers — e.g. the /api/info handler — can report it directly.
func (*ServerConfig) EffectiveSSHAuth ¶ added in v0.7.1
func (c *ServerConfig) EffectiveSSHAuth() *SSHAuthConfig
EffectiveSSHAuth returns the SSH auth config to build the allowlist from. In an enforced mode (token or mtls) the mode is forced to enforce (key sources still come from the configured auth.ssh block); otherwise the configured block is used verbatim.
func (*ServerConfig) HTTPAuthEnforced ¶ added in v0.7.1
func (c *ServerConfig) HTTPAuthEnforced() bool
HTTPAuthEnforced reports whether the HTTP API requires an authenticated caller at all. It is the gate on the auth middleware, not a statement about WHICH credential: token mode requires a scoped bearer token, mtls mode requires a scoped client certificate, and only open mode passes traffic through unauthenticated. The middleware branches on TokenMode/MTLSMode after this gate says "enforce something".
It is deliberately identical to AuthEnforced today. The two are kept apart because they answer different questions — "does HTTP require a credential" versus "is the network/SSH posture hardened" — and a future mode could answer them differently.
func (*ServerConfig) HTTPListenAddr ¶ added in v0.7.0
func (c *ServerConfig) HTTPListenAddr() string
HTTPListenAddr returns the bind address for the plain-HTTP listener, honoring bind_address (default loopback). The plain-HTTP listener is served only in open mode (see PlainHTTPEnabled); token mode is TLS-only and starts no plain-HTTP listener, so this is not consulted there.
func (*ServerConfig) HTTPSEnabled ¶ added in v0.7.0
func (c *ServerConfig) HTTPSEnabled() bool
HTTPSEnabled reports whether the HTTPS listener is configured.
func (*ServerConfig) HTTPSListenAddr ¶ added in v0.7.0
func (c *ServerConfig) HTTPSListenAddr() string
HTTPSListenAddr returns the bind address for the HTTPS listener (sharing bind_address with the other listeners), or "" when HTTPS is disabled.
func (*ServerConfig) MTLSMode ¶ added in v0.8.1
func (c *ServerConfig) MTLSMode() bool
MTLSMode reports whether the server runs in mtls mode (auth.mode: mtls): the client credential is a short-lived certificate issued over the SSH bootstrap channel rather than a bearer token, verified at the TLS handshake and re-validated on every HTTP request. It shares every other token-mode network/SSH invariant — see AuthEnforced, the combined predicate most call sites should use when they only care that auth is on.
func (*ServerConfig) PlainHTTPEnabled ¶ added in v0.7.2
func (c *ServerConfig) PlainHTTPEnabled() bool
PlainHTTPEnabled reports whether the main plain-HTTP listener should be served. False in an enforced mode (token or mtls; TLS-only — the plain listener is not started).
func (*ServerConfig) PreflightAuth ¶ added in v0.8.1
func (c *ServerConfig) PreflightAuth() error
PreflightAuth gates enforced-mode startup: it refuses to start when auth.mode: token or mtls is set without any SSH key source (github_users / authorized_keys / authorized_keys_file). Both modes enforce the SSH allowlist, so an empty allowlist would lock everyone out; mtls additionally needs a key source to identify which client gets a certificate issued over the bootstrap channel. Inert in open mode.
func (*ServerConfig) SSHAuth ¶ added in v0.7.0
func (c *ServerConfig) SSHAuth() *SSHAuthConfig
SSHAuth returns the SSH auth config, or nil when unset.
func (*ServerConfig) SSHListenAddr ¶ added in v0.7.0
func (c *ServerConfig) SSHListenAddr() string
SSHListenAddr returns the bind address for the SSH listener.
func (*ServerConfig) TokenMode ¶ added in v0.8.1
func (c *ServerConfig) TokenMode() bool
TokenMode reports whether the server runs in token mode (auth.mode: token): SSH allowlist enforce + HTTP bearer-token enforce + TLS-only (no plain HTTP listener faces clients). The default (open) preserves the legacy accept-all tailnet/LAN posture.
func (*ServerConfig) TokenTTL ¶ added in v0.7.1
func (c *ServerConfig) TokenTTL() time.Duration
TokenTTL is the lifetime of a bootstrap-minted HTTP token (auth.token_ttl), defaulting to DefaultTokenTTL when unset.
func (*ServerConfig) Validate ¶
func (c *ServerConfig) Validate() error
Validate checks that the configuration is valid.
func (*ServerConfig) ValidateNoHostCoupling ¶
func (c *ServerConfig) ValidateNoHostCoupling() error
ValidateNoHostCoupling runs the parts of Validate that aren't tied to the host OS/arch. Used by CLI commands that only need to read the OCI store (image build, image push --local), where the strict "vz backend only on macOS" / "firecracker backend only on linux" checks would block cross-platform image operations from CI runners or developers cross-publishing images. Callers that actually start a VM MUST call Validate instead.
type ServerEntry ¶
type ServerEntry struct {
Host string `yaml:"host"`
HTTPPort int `yaml:"http_port,omitempty"`
SSHPort int `yaml:"ssh_port"`
AddedAt time.Time `yaml:"added_at"`
// ControlToken is the bearer token sent on control-plane HTTP requests
// (CLI, desktop). CredentialsToken is sent by the host-agent for the
// credential bus. Both optional; empty when the server isn't token-gated.
ControlToken string `yaml:"control_token,omitempty"`
CredentialsToken string `yaml:"credentials_token,omitempty"`
// ControlTokenExpiresAt is when ControlToken (bootstrap-minted, short-TTL)
// expires, so the CLI can transparently re-mint before/after expiry. Zero
// for a legacy static token or an open server.
ControlTokenExpiresAt time.Time `yaml:"control_token_expires_at,omitempty"`
// APIURL, when set, overrides the scheme+host+port for the control plane
// (e.g. https://host:8443). Empty = plain http://Host:HTTPPort (legacy).
APIURL string `yaml:"api_url,omitempty"`
// TLSCertFingerprint pins the server's self-signed TLS cert as
// "sha256:<hex>", captured at `shed server add`. When set, the client
// verifies the presented cert against it (no CA needed).
TLSCertFingerprint string `yaml:"tls_cert_fingerprint,omitempty"`
// AuthMode records the credential shape the server issued at the last
// bootstrap: AuthModeToken (a bearer token) or AuthModeMTLS (a client
// certificate).
//
// ABSENT MEANS TOKEN. Every entry written before client-certificate support
// existed has no auth_mode key, and those are all token/open servers — so an
// empty value must never be read as "unknown, go find out". It is a CACHE of
// what the server last said, not a setting: a bootstrap that comes back in
// the other mode rewrites it (see the mode-flip path in cmd/shed/client.go),
// which is what lets an operator switch a server's auth.mode without every
// client needing to be re-added.
AuthMode string `yaml:"auth_mode,omitempty"`
// ClientCertFile / ClientKeyFile locate this entry's client certificate and
// its private key under the creds dir (see ServerCredsDir). Paths rather
// than inline PEM: the key must live in a 0600 file of its own, not inside a
// config the user hand-edits, copies between machines, and pastes into
// issues.
ClientCertFile string `yaml:"client_cert_file,omitempty"`
ClientKeyFile string `yaml:"client_key_file,omitempty"`
// ClientCertExpiresAt is when ClientCertFile's certificate expires, so the
// client can re-enroll before a request races expiry — the mtls counterpart
// of ControlTokenExpiresAt. Cached here so the proactive check costs no file
// read; the certificate itself remains the authority.
ClientCertExpiresAt time.Time `yaml:"client_cert_expires_at,omitempty"`
}
ServerEntry represents a configured server.
func (*ServerEntry) BaseURL ¶ added in v0.7.0
func (e *ServerEntry) BaseURL() string
BaseURL returns the control-plane base URL for the entry: APIURL when set (it carries scheme+host+port), else the legacy plain http://Host:HTTPPort.
The host:port is joined with net.JoinHostPort, not printf: Host may be an IPv6 literal ("::1"), and "http://::1:8080" is not a URL any client can parse. The bracketed form is what every http.Client, url.Parse, and SSH-first open-mode add downstream of this needs.
func (*ServerEntry) IsMTLS ¶ added in v0.8.1
func (e *ServerEntry) IsMTLS() bool
IsMTLS reports whether this entry last bootstrapped a client certificate.
func (*ServerEntry) NeedsEnrollment ¶ added in v0.8.1
func (e *ServerEntry) NeedsEnrollment() bool
NeedsEnrollment reports whether this entry holds NO credential it could present to a server that demands one, and has enough information (host + ssh port) to obtain one over the SSH `_bootstrap` channel.
It exists because "no credential recorded" is ambiguous in a stored entry and the two readings need opposite handling:
- an OPEN server legitimately has no credential, and must never pay an SSH round-trip to discover that. It is plain HTTP, so UsesTLS is false.
- a SECURE server's entry that lost its credential fields must enroll. That is a routine condition during an upgrade, not a corner case: a pre-mtls client that loads and re-saves config.yaml silently drops every key its ServerEntry struct does not know (auth_mode, client_cert_file, client_key_file, client_cert_expires_at), leaving exactly this shape — an https entry with nothing to present. Without enrollment such an entry fails forever, because the client has no credential to be rejected and so never reaches its reactive re-mint.
A legacy static token (a control_token with no expiry) is NOT this case: it has something to present and is deliberately never re-minted.
func (*ServerEntry) UsesTLS ¶ added in v0.8.1
func (e *ServerEntry) UsesTLS() bool
UsesTLS reports whether this entry's control plane is HTTPS — which, for a shed-server, means the server enforces auth (token or mtls). Open mode serves plain HTTP and has no HTTPS listener at all, so this is the one durable signal in a stored entry that separates "this server wants a credential" from "this server wants nothing".
Either marker alone is enough: `shed server add` writes both an https api_url and a tls_cert_fingerprint for a secure server, but an entry is user-editable and a half-filled one still points at a listener that will demand a credential.
type ServerInfo ¶
type ServerInfo struct {
Name string `json:"name"`
Version string `json:"version"`
SSHPort int `json:"ssh_port"`
// HTTPPort is the plain-HTTP port (open mode). Omitted in token mode, which
// serves no plain HTTP — clients use the HTTPS endpoint there.
HTTPPort int `json:"http_port,omitempty"`
Backend string `json:"backend"`
// AuthMode is the server's auth.mode, so `shed server add` knows whether
// to bootstrap an HTTP token (or, in mtls mode, a client certificate)
// over SSH. WIRE CONTRACT: on /api/info token mode travels as the legacy
// "secure" spelling — released clients gate their bootstrap on that exact
// string (LegacyWireAuthMode) — and the decode boundary normalizes it back
// to "token" (NormalizeAuthMode, applied in the CLI's GetInfo), so
// in-process consumers only ever see "open", "token", or "mtls".
// Reported on the unauthenticated /api/info in open/token mode — the mode
// is observable from behavior anyway.
AuthMode string `json:"auth_mode,omitempty"`
// DefaultImage is the resolved default_image for the active backend
// (after ${shed.version} expansion / version synthesis at load). Exposed
// so clients can see which image a `shed create` without --image will
// use — useful when the ref is synthesized and never written in config.
DefaultImage string `json:"default_image,omitempty"`
// HTTPSPort is the pinned-TLS listener port in token mode (auth.mode:
// token), so a client adding a token-mode server can learn the TLS
// endpoint. 0/omitted in open mode (no HTTPS listener).
HTTPSPort int `json:"https_port,omitempty"`
// CAFingerprint is the "sha256:<hex>" pin of the internal CA that signs
// client certificates, reported only in mtls mode. It is operator
// visibility, not a trust anchor: a client never needs it (it authenticates
// the SERVER by the tls_cert_fingerprint pin and proves itself with the cert
// the SSH bootstrap handed it), but an operator comparing two servers, or
// confirming a CA rotation landed, does. Omitted in open/token mode.
CAFingerprint string `json:"ca_fingerprint,omitempty"`
// CANotAfter is the client CA's expiry (RFC 3339), reported only in mtls
// mode. Rotating the CA invalidates every issued client certificate
// fleet-wide and must be scheduled, so the deadline is worth surfacing
// somewhere a human or a monitor can read it. Omitted in open/token mode.
//
// A string rather than time.Time so the field can be omitted when unset —
// encoding/json's omitempty does not apply to struct values.
CANotAfter string `json:"ca_not_after,omitempty"`
// Features advertises server capability tokens (e.g. "overview",
// "rc-enrich") for endpoint discovery, so a client learns which endpoints
// and behaviors this server supports without probing each one. The same set
// is mirrored in the GET /api/overview server block. The token list is owned
// by internal/api (serverFeatures); older clients decode it as an empty slice.
Features []string `json:"features,omitempty"`
}
ServerInfo is returned by GET /api/info.
type Session ¶
type Session struct {
Name string `json:"name"`
ShedName string `json:"shed_name"`
ServerName string `json:"server_name,omitempty"`
CreatedAt time.Time `json:"created_at"`
Attached bool `json:"attached"`
WindowCount int `json:"window_count,omitempty"`
// RC carries Remote Control Session Convention metadata for "rc-*" sessions.
// The server populates it by exec'ing the in-shed shed-ext-rc binary over the
// guest agent channel and merging by tmux name (GET /api/sessions and
// GET /api/sheds/{name}/sessions, unless ?rc=0). It is nil for non-RC sessions
// and for rc-* rows on a shed whose enrichment degraded (a warnings entry is
// added in that case).
RC *SessionRC `json:"rc,omitempty"`
}
Session represents a tmux session within a shed container.
type SessionRC ¶ added in v0.7.5
type SessionRC struct {
Kind string `json:"kind,omitempty"`
State string `json:"state,omitempty"`
Managed bool `json:"managed"`
DisplayName string `json:"display_name,omitempty"`
URL string `json:"url,omitempty"`
CreatedBy string `json:"created_by,omitempty"`
// Lane is the session's current lane (contract v2): "tui" or "structured",
// projected straight from rc.Session.Lane. omitempty here (unlike the always-
// present wire field) is deliberate: a shed running a pre-lane guest binary
// yields an empty string, and a client reading an ABSENT lane must treat it as
// "tui" — the same old-payload rule rc.Session documents at its own field.
Lane string `json:"lane,omitempty"`
// Live-activity dimension (Phase C), projected from the rc DTO so listings can
// carry it once a hub is running. Absent when the hub is not running / the kind
// is unsupported. Activity is one of working|needs_input|idle|unknown; ActivityAt
// is RFC3339; LastMessage is a sanitized ≤200-rune preview.
Activity string `json:"activity,omitempty"`
ActivityAt string `json:"activity_at,omitempty"`
LastMessage string `json:"last_message,omitempty"`
}
SessionRC holds the RC Session Convention fields surfaced for an "rc-*" session (a subset of shed-ext-rc's neutral DTO, for display). Managed is false for legacy/unmanaged rc-* sessions.
type SessionsResponse ¶
type SessionsResponse struct {
Sessions []Session `json:"sessions"`
Warnings []string `json:"warnings,omitempty"`
}
SessionsResponse is returned by GET /api/sheds/{name}/sessions and GET /api/sessions.
type Shed ¶
type Shed struct {
Name string `json:"name" yaml:"name"`
Status string `json:"status" yaml:"status"`
CreatedAt time.Time `json:"created_at" yaml:"created_at"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
ContainerID string `json:"container_id" yaml:"container_id"`
Backend string `json:"backend,omitempty" yaml:"backend,omitempty"`
IPAddress string `json:"ip_address,omitempty" yaml:"ip_address,omitempty"`
CPUs int `json:"cpus,omitempty" yaml:"cpus,omitempty"`
MemoryMB int `json:"memory_mb,omitempty" yaml:"memory_mb,omitempty"`
PID int `json:"pid,omitempty" yaml:"pid,omitempty"`
RootfsPath string `json:"rootfs_path,omitempty" yaml:"rootfs_path,omitempty"`
// ProjectMounts are host directories mounted under the home directory
// (--local-dir / --add-dir), each at /home/shed/<basename>.
ProjectMounts []MountConfig `json:"project_mounts,omitempty" yaml:"project_mounts,omitempty"`
// LandingDir is the directory interactive logins land in (a project
// subdirectory of the home directory, or the home directory itself).
LandingDir string `json:"landing_dir,omitempty" yaml:"landing_dir,omitempty"`
Image string `json:"image,omitempty" yaml:"image,omitempty"`
ImageDigest string `json:"image_digest,omitempty" yaml:"image_digest,omitempty"` // pinned manifest digest (sha256:...)
// FromSnapshot records the snapshot name this shed was spawned from (immediate parent only).
FromSnapshot string `json:"from_snapshot,omitempty" yaml:"from_snapshot,omitempty"`
LastHealthy *time.Time `json:"last_healthy,omitempty" yaml:"last_healthy,omitempty"` // last heartbeat from agent (VM backends only)
StartedAt *time.Time `json:"started_at,omitempty" yaml:"started_at,omitempty"` // agent boot time from heartbeat (VM backends only)
Extensions map[string]ExtensionHealthInfo `json:"extensions,omitempty" yaml:"extensions,omitempty"` // per-extension health (VM backends only)
// Egress* track Level-1 egress-control state (set when egress is enabled
// and the shed is assigned a non-empty profile list). EgressPort is the
// per-shed proxy listener port — stable across stop/start because the
// guest's injected HTTP_PROXY is baked into the persistent upper.
EgressProfiles []string `json:"egress_profiles,omitempty" yaml:"egress_profiles,omitempty"`
EgressPort int `json:"egress_port,omitempty" yaml:"egress_port,omitempty"`
// EgressToken is the per-shed proxy-auth token binding the port to this
// shed. It is a secret: never serialized to API/CLI responses (json:"-").
// It lives durably in the per-backend on-disk metadata, not here.
EgressToken string `json:"-" yaml:"-"`
}
Shed represents a development environment container.
type ShedCache ¶
type ShedCache struct {
Server string `yaml:"server"`
Status string `yaml:"status"`
UpdatedAt time.Time `yaml:"updated_at"`
}
ShedCache caches the location of a shed.
type ShedDiskEntry ¶
type ShedDiskEntry struct {
Name string `json:"name"`
Status string `json:"status"`
Image string `json:"image,omitempty"`
Rootfs FileEntry `json:"rootfs"`
ConsoleLog *FileEntry `json:"console_log,omitempty"` // nil for Firecracker
OtherFiles []FileEntry `json:"other_files,omitempty"`
Total DiskSize `json:"total"`
}
ShedDiskEntry describes one shed's per-instance disk footprint.
type ShedsResponse ¶
type ShedsResponse struct {
Sheds []Shed `json:"sheds"`
}
ShedsResponse is returned by GET /api/sheds.
type SkippedItem ¶
type SkippedItem struct {
Kind string `json:"kind"`
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
Reason string `json:"reason"`
}
SkippedItem is an entity the prune pass inspected but left alone, with a short reason. Examples: running shed; stopped but too recent; lock held by an in-flight conversion; malformed metadata.
type Snapshot ¶
type Snapshot struct {
// Version is the snapshot schema version (current: SnapshotSchemaVersion).
Version int `json:"version"`
// Name is the unique snapshot identifier within a server.
Name string `json:"name"`
// Backend is "vz" or "firecracker"; only matching backends can spawn from this snapshot.
Backend string `json:"backend"`
// SourceShed is the shed this snapshot was created from. May reference a deleted shed.
SourceShed string `json:"source_shed,omitempty"`
// SourceImage is the image variant the source shed was created from (provenance hint).
SourceImage string `json:"source_image,omitempty"`
// SourceLocalDirs are the host directories the source shed mounted
// (--local-dir / --add-dir); hint only, not bound at spawn.
SourceLocalDirs []string `json:"source_local_dirs,omitempty"`
// Comment is an optional user-supplied note attached at create time.
Comment string `json:"comment,omitempty"`
// CreatedAt is when the snapshot was captured.
CreatedAt time.Time `json:"created_at"`
// SizeBytes is the apparent (logical) size of the snapshot rootfs.
SizeBytes int64 `json:"size_bytes,omitempty"`
// LowerDigest is the digest of the lower (base) image the source shed
// was created from, in the form "sha256:...". Snapshots count toward
// the lower's refcount: pruning a digest pinned by a snapshot is
// refused. Empty for snapshots created before schema v2.
LowerDigest string `json:"lower_digest,omitempty"`
// LowerCached reports whether the lower digest's blob is currently
// installed in the local image store. Computed at read time, never
// persisted (the on-disk value is recomputed on each load). When
// false, `shed create --from-snapshot` will fail until the lower
// image is pulled or rebuilt; surfaced by `shed snapshot info`.
LowerCached bool `json:"lower_cached,omitempty"`
}
Snapshot represents a captured rootfs that can be used to spawn new sheds.
type SnapshotCreateRequest ¶
type SnapshotCreateRequest struct {
Name string `json:"name"`
SourceShed string `json:"source_shed"`
Comment string `json:"comment,omitempty"`
}
SnapshotCreateRequest is the request body for POST /api/snapshots.
type SnapshotCreateResponse ¶
type SnapshotCreateResponse struct {
Snapshot *Snapshot `json:"snapshot"`
Warnings []string `json:"warnings,omitempty"`
}
SnapshotCreateResponse is returned by POST /api/snapshots. It wraps the created snapshot together with any non-fatal warnings emitted during the operation (e.g., source shed used --local-dir so workspace contents are not captured). Wire format is intentionally distinct from the Snapshot type so warnings can grow without disturbing snapshot.json on disk.
type SnapshotDiskEntry ¶
type SnapshotDiskEntry struct {
Name string `json:"name"`
SourceShed string `json:"source_shed,omitempty"`
Rootfs FileEntry `json:"rootfs"`
OtherFiles []FileEntry `json:"other_files,omitempty"`
Total DiskSize `json:"total"`
}
SnapshotDiskEntry describes one snapshot's disk footprint for `shed system df`. OtherFiles holds metadata sidecars (snapshot.json) so callers can sum the total footprint without hardcoding a per-snapshot file count.
type SnapshotsResponse ¶
type SnapshotsResponse struct {
Snapshots []Snapshot `json:"snapshots"`
}
SnapshotsResponse is returned by GET /api/snapshots.
type StagedClientCredentials ¶ added in v0.8.1
StagedClientCredentials is a client certificate + key written to disk but not yet adopted by the server it belongs to — the transaction handle that lets `shed server add` order the config save before the destructive half of the credential update. See sdk/creds.Staged.
func StageClientCredentials ¶ added in v0.8.1
func StageClientCredentials(name string, certPEM, keyPEM []byte) (*StagedClientCredentials, error)
StageClientCredentials writes an issued certificate + key into the staging area for the named server, WITHOUT touching whatever that server currently has. Call Commit to adopt the pair, or Discard to throw it away.
type SystemDFResponse ¶
type SystemDFResponse struct {
Servers []DiskUsageOrError `json:"servers"`
}
SystemDFResponse is the client-side aggregation of per-server df results produced by `shed system df --all`. Never returned by the API directly.
type SystemPruneResponse ¶
type SystemPruneResponse struct {
Servers []PruneReportOrError `json:"servers"`
}
SystemPruneResponse is the client-side aggregation of per-server prune results from `shed system prune --all`. Never returned by the API directly.
type UserProfileStore ¶ added in v0.7.5
type UserProfileStore struct {
// contains filtered or unexported fields
}
UserProfileStore is the runtime, user-editable egress-profile store — a second source of named profiles alongside the read-only server.yaml ones. Each profile is one whole-document <name>.yaml file, CRUD'd via the API/CLI. It is the FIRST writer into the egress resolution read path (server.yaml is immutable after load), so List/Get return deep copies and all access is RWMutex-guarded.
func OpenUserProfileStore ¶ added in v0.7.5
func OpenUserProfileStore(dir string) (*UserProfileStore, error)
OpenUserProfileStore loads every <name>.yaml under dir into memory. It FAILS HARD on a malformed file or an invalid profile spec — a bad runtime policy file must not be silently skipped (a profile the user thought they had, gone after a restart) nor produce a half-valid effective policy.
func (*UserProfileStore) Delete ¶ added in v0.7.5
func (s *UserProfileStore) Delete(name string) error
Delete removes a user profile (file + map). Returns an error if it doesn't exist.
func (*UserProfileStore) Get ¶ added in v0.7.5
func (s *UserProfileStore) Get(name string) (EgressProfile, bool)
Get returns a deep copy of one profile.
func (*UserProfileStore) List ¶ added in v0.7.5
func (s *UserProfileStore) List() map[string]EgressProfile
List returns a deep copy of every user profile (map + Allow/Deny slices cloned) so a caller can't mutate store state or race a concurrent Put. nil receiver → nil (lets callers pass store.List() unconditionally).
func (*UserProfileStore) Names ¶ added in v0.7.5
func (s *UserProfileStore) Names() []string
Names returns the user-profile names, sorted (deterministic CLI/UX output).
func (*UserProfileStore) Put ¶ added in v0.7.5
func (s *UserProfileStore) Put(name string, p EgressProfile) error
Put validates then atomically writes a profile (<name>.yaml, temp+rename) and updates the in-memory map. On a validation or write error the map is unchanged. Config-name collisions are the API layer's job (the store has no config view).
type VZConfig ¶
type VZConfig struct {
// VfkitPath is the path to the vfkit binary
VfkitPath string `yaml:"vfkit_path"`
// KernelPath is the path to the decompressed Linux kernel
KernelPath string `yaml:"kernel_path"`
// InitrdPath is the path to the initial RAM disk image
InitrdPath string `yaml:"initrd_path"`
// DefaultImage is the Docker ref (or local rootfs path) used for new
// sheds when no --image is given. Docker refs are resolved by their
// io.shed.source-ref identity and pulled per PullPolicy on first use.
DefaultImage string `yaml:"default_image"`
// ImageAliases maps short alias names to Docker refs (or paths) for
// convenience with: shed create mydev --image <alias>. Aliases resolve
// to the underlying ref; image listings always show the resolved ref.
ImageAliases map[string]string `yaml:"image_aliases,omitempty"`
// PullPolicy controls cache-vs-pull at create: "missing" (default —
// use the cached ref, pull only if absent), "always" (always pull),
// or "never" (error if not cached). Ignored for local-path images.
PullPolicy string `yaml:"pull_policy,omitempty"`
// PullConcurrency caps how many image blobs (layers + kernel/initrd/
// erofs) download in parallel during a registry pull. Defaults to
// DefaultPullConcurrency; must be >= 1 (1 == serial).
PullConcurrency int `yaml:"pull_concurrency,omitempty"`
// ImagesDir is the directory for the content-addressed image store.
ImagesDir string `yaml:"images_dir,omitempty"`
// InstanceDir is the directory for instance data
InstanceDir string `yaml:"instance_dir"`
// SnapshotsDir is the directory where shed snapshots are stored.
SnapshotsDir string `yaml:"snapshots_dir,omitempty"`
// UppersDir is the directory where per-shed writable upper layers
// (sparse ext4 files) are stored.
UppersDir string `yaml:"uppers_dir,omitempty"`
// UpperSizeDefault is the default logical size of the per-shed
// writable upper. Accepted units: G (GB) and M (MB). Range 1G-100G.
UpperSizeDefault string `yaml:"upper_size_default,omitempty"`
// SocketDir is the directory for vsock Unix sockets.
// NOTE: This path must not contain spaces. vfkit URL-encodes socket paths,
// turning spaces into %20, which causes connection failures.
SocketDir string `yaml:"socket_dir"`
// DefaultCPUs is the default number of vCPUs for new VMs
DefaultCPUs int `yaml:"default_cpus"`
// DefaultMemoryMB is the default memory in MB for new VMs
DefaultMemoryMB int `yaml:"default_memory_mb"`
// DefaultDiskGB is the default disk size in GB for new VMs
DefaultDiskGB int `yaml:"default_disk_gb"`
// ConsolePort is the vsock port for console/exec connections
ConsolePort uint32 `yaml:"console_port"`
// NotifyPort is the vsock port for the message channel (health checks, plugins, credentials)
NotifyPort uint32 `yaml:"notify_port"`
// TCPProxyPort is the vsock port for the TCP proxy (used by DialService to reach VM services)
TCPProxyPort uint32 `yaml:"tcp_proxy_port"`
// StartTimeout is the timeout for VM startup
StartTimeout Duration `yaml:"start_timeout"`
// StopTimeout is the timeout for graceful VM shutdown
StopTimeout Duration `yaml:"stop_timeout"`
// GuestMTU, when non-zero, forces the guest's primary interface MTU
// instead of auto-detecting the host's egress path MTU at VM start.
// 0 (the default) means auto-detect: behind a reduced-MTU path (e.g. a
// VPN/overlay) the guest is lowered to match; otherwise it stays at
// 1500. Set this only to pin a value when detection misses. Validated to
// [MinGuestMTU, MaxGuestMTU] when non-zero.
GuestMTU int `yaml:"guest_mtu,omitempty"`
}
VZConfig contains Apple Virtualization.framework-specific configuration.
func DefaultVZConfig ¶
func DefaultVZConfig() *VZConfig
DefaultVZConfig returns a VZConfig with default values.
Cross-backend alignment (DO NOT drift between this and DefaultFirecrackerConfig without an explicit reason):
- DefaultCPUs / DefaultMemoryMB / DefaultDiskGB: same physical resource shape per shed on both backends. A user moving a shed between platforms should see identical resource sizing by default.
- ConsolePort (1024) / NotifyPort (1026) / TCPProxyPort (1028): the agent's vsock contract. Same on both so the same shed-agent binary speaks to both backends without per-platform port plumbing. TCPProxyPort is aligned across backends as of the hub-parity change — both backends route DialService through the guest agent's TCP proxy on this port.
- StopTimeout (10 s): the budget for the shutdown-hook + sync + graceful-stop sequence. Same on both because the in-guest work (sync, hook execution) is identical regardless of VMM.
Intentionally divergent from FC:
- StartTimeout (60 s vs FC 30 s): historical VZ create wall time was ~5.9 s (pre-Phase-2 in-guest mkfs.ext4 on the vfkit virtio-blk write path, which is ~20× slower than Firecracker's per §0 of the runtime-opt doc). 60 s gave ~10× headroom for that worst case. As of v0.5.5 warm VZ create is ~1.6 s, so 60 s is generous; the value stands to absorb cold-state and overloaded-host variance without surprising the operator.
- VfkitPath: VZ-only (vfkit is the macOS VMM); Firecracker equivalent is invoked directly by the binary at FirecrackerPath (set elsewhere).
func (*VZConfig) GetDefaultImage ¶ added in v0.6.0
GetDefaultImage implements vmimage.ImageConfig.
func (*VZConfig) GetExtractKernel ¶
GetExtractKernel implements vmimage.ImageConfig.
func (*VZConfig) GetImageAliases ¶ added in v0.6.0
GetImageAliases implements vmimage.ImageConfig.
func (*VZConfig) GetImagesDir ¶
GetImagesDir implements vmimage.ImageConfig.
func (*VZConfig) GetNeedsInitrd ¶
GetNeedsInitrd implements vmimage.ImageConfig.
func (*VZConfig) GetPlatform ¶
GetPlatform implements vmimage.ImageConfig.
func (*VZConfig) GetPullConcurrency ¶ added in v0.6.2
GetPullConcurrency implements vmimage.ImageConfig.
func (*VZConfig) GetPullPolicy ¶ added in v0.6.0
GetPullPolicy implements vmimage.ImageConfig.
func (*VZConfig) ResolveBaseRootfs ¶
func (c *VZConfig) ResolveBaseRootfs() (ResolvedImage, error)
ResolveBaseRootfs resolves the default image (used when no --image is given).
func (*VZConfig) ResolveImage ¶
func (c *VZConfig) ResolveImage(image string) (ResolvedImage, error)
ResolveImage resolves an image selector to a local path or Docker ref.