Documentation
¶
Overview ¶
Package admincore holds the protocol-agnostic configuration operations outpost exposes — pairing, app CRUD, outbound mounts, built-in toggles, cluster kubeconfig, restart. Both the human-facing admin UI (HTTP + session cookie) and the agent-facing MCP server (HTTP + bearer token) dispatch into the same Server methods here, so validation rules and persistence semantics ship once.
What lives here vs. in the HTTP layer:
- admincore: validate input, mutate FileConfig under a shared mutex, update the live AppRegistry / OutboundManager, debounce restart.
- HTTP layer (adminui, mcpapi): authenticate the caller, parse the wire format, translate admincore errors into the protocol's status codes, render the response.
Errors returned by admincore are *APIError when callers need to map them to a transport-level status, plain errors when the operation was unable to even start. HTTP wrappers use RespondError to translate.
Reachability-ledger plumbing for the daemon-side dial path. Wave 3B.1 records-only — every successful sshclient.Dial in dialSSHChain appends one ReachabilityEdge to the JSONL ledger. Wave 3B.2 wires this into the Memberlist gossip layer so peers learn about each other's recent contacts.
Self-PeerID is derived lazily from the SSH host key at the first call and cached for the daemon's lifetime; subsequent appends are just a file write.
SSH-target CRUD + one-shot Exec. These methods back both the `outpost ssh ...` CLI subtree and the `outpost_*_ssh_target` / `outpost_ssh_exec` MCP tools, keeping validation + filesystem access in one place.
Targets are persisted as per-alias JSON files under $XDG_CONFIG_HOME/outpost/ssh/<name>.json (see conf/sshtargets.go). Mutation does NOT trigger admincore's restart-debounce — friendly aliases are pure-cache state.
ExecSSH opens a fresh WS+SSH connection to cloudbox per call. Wave 1 trades the per-call setup latency for simplicity; Wave 2 will add pooling if measurements show it matters.
Index ¶
- Constants
- Variables
- func CloudboxHTTPBase(fc *conf.FileConfig) string
- func ValidateApp(ac *conf.AppConfig) error
- func ValidateOutbound(p *OutboundParams) error
- type APIError
- type AppHealthView
- type AppUpsertParams
- type AppstoreCatalogView
- type AppstoreInstallParams
- type AppstoreInstallResult
- type AppstoreShowResult
- type AppstoreStatusParams
- type AppstoreStatusResult
- type AppstoreUninstallParams
- type AppstoreUninstallResult
- type BackupApplier
- type BackupParams
- type BuiltinInstallParams
- type BuiltinInstallResult
- type BuiltinStatusParams
- type BuiltinStatusResult
- type BuiltinUninstallParams
- type BuiltinUninstallResult
- type BuiltinView
- type BuiltinsParams
- type BuiltinsResult
- type BundleApplyParams
- type BundleApplyResult
- type BundleCatalogView
- type BundleKubeconfigView
- type BundleObjectStatus
- type BundleStatusParams
- type BundleStatusResult
- type BundleUninstallParams
- type BundleUninstallResult
- type ClusterLLMView
- type ClusterView
- type ControlPlaneParams
- type ControlPlaneResult
- type ControlPlaneStatus
- type ControlPlaneStatusProber
- type Deps
- type ExecSSHParams
- type ExecSSHResult
- type KubeconfigResult
- type LLMPoolStatusView
- type MeshConsumeView
- type MeshForwardOps
- type MeshForwardView
- type MeshHostLinkView
- type MeshLinkInfo
- type MeshListenerView
- type MeshPeerConnView
- type MeshResolvedPeer
- type MeshServiceView
- type MeshStatusView
- type MirrorJobView
- type MirrorView
- type NetworkingParams
- type NetworkingResult
- type Node
- type NodeTokenResult
- type OutboundParams
- type OutboundSuggestion
- type PairParams
- type PairResult
- type PeerImageOps
- type PeerImageVerb
- type PeerPlaneParams
- type PeerPlaneResult
- type PeerTierView
- type SSHTargetView
- type SafeView
- type Server
- func (s *Server) AppHealth() []AppHealthView
- func (s *Server) AppSuggestions() ([]Suggestion, error)
- func (s *Server) ApplyPendingUpgrade(ctx context.Context) (upgrade.Result, error)
- func (s *Server) AppstoreCatalog(explicit string) (AppstoreCatalogView, error)
- func (s *Server) AppstoreInstall(ctx context.Context, p AppstoreInstallParams) (AppstoreInstallResult, error)
- func (s *Server) AppstoreShow(explicitCatalog, id string) (AppstoreShowResult, error)
- func (s *Server) AppstoreStatus(ctx context.Context, p AppstoreStatusParams) (AppstoreStatusResult, error)
- func (s *Server) AppstoreUninstall(ctx context.Context, p AppstoreUninstallParams) (AppstoreUninstallResult, error)
- func (s *Server) AttachBackup(applier BackupApplier)
- func (s *Server) AttachUpgrade(worker *upgrade.Worker, ledger *upgrade.Ledger)
- func (s *Server) BackupHistory(n int) ([]backup.Candidate, error)
- func (s *Server) BuiltinInstall(ctx context.Context, p BuiltinInstallParams) (BuiltinInstallResult, error)
- func (s *Server) BuiltinStatus(ctx context.Context, p BuiltinStatusParams) (BuiltinStatusResult, error)
- func (s *Server) BuiltinUninstall(ctx context.Context, p BuiltinUninstallParams) (BuiltinUninstallResult, error)
- func (s *Server) BundleApply(ctx context.Context, p BundleApplyParams) (BundleApplyResult, error)
- func (s *Server) BundleCatalog(explicit string) (BundleCatalogView, error)
- func (s *Server) BundleKubeconfig() (BundleKubeconfigView, error)
- func (s *Server) BundleStatus(ctx context.Context, p BundleStatusParams) (BundleStatusResult, error)
- func (s *Server) BundleUninstall(ctx context.Context, p BundleUninstallParams) (BundleUninstallResult, error)
- func (s *Server) ClearKubeconfig() (KubeconfigResult, error)
- func (s *Server) ConnectOutbound(path, password string) error
- func (s *Server) ControlPlaneNodeToken(ctx context.Context) (NodeTokenResult, error)
- func (s *Server) ControlPlaneStatusView(ctx context.Context) (ControlPlaneStatus, error)
- func (s *Server) ControlPlaneVKCredential(ctx context.Context, namespaces []string) (VKCredentialResult, error)
- func (s *Server) ControlPlaneView(reveal bool) (ControlPlaneResult, error)
- func (s *Server) DeleteApp(name string) error
- func (s *Server) DeleteOutbound(path string) error
- func (s *Server) DeleteSSHTarget(name string) error
- func (s *Server) Deps() Deps
- func (s *Server) DisconnectOutbound(path string) error
- func (s *Server) ExecSSH(ctx context.Context, p ExecSSHParams) (*ExecSSHResult, error)
- func (s *Server) GetBackup() (conf.BackupConfig, error)
- func (s *Server) GetSSHTarget(name string) (SSHTargetView, error)
- func (s *Server) GetSSOSecret(name string) (string, error)
- func (s *Server) JoinCluster() (KubeconfigResult, error)
- func (s *Server) JoinPeerPlane(p PeerPlaneParams) (PeerPlaneResult, error)
- func (s *Server) LeaveCluster(ctx context.Context) (KubeconfigResult, error)
- func (s *Server) LeavePeerPlane() (PeerPlaneResult, error)
- func (s *Server) ListApps() ([]conf.AppConfig, error)
- func (s *Server) ListOutbound() []agent.OutboundView
- func (s *Server) ListSSHTargets() ([]SSHTargetView, error)
- func (s *Server) LoadConfig() (*conf.FileConfig, error)
- func (s *Server) MeshCloseListen(addr string) error
- func (s *Server) MeshConsumeDelete(service, localAddr string) error
- func (s *Server) MeshConsumeUpsert(service, peerID, localAddr string) (string, error)
- func (s *Server) MeshConsumes() ([]MeshConsumeView, error)
- func (s *Server) MeshDial(service, localAddr string) (addr, host string, err error)
- func (s *Server) MeshExpose(service, addr string) error
- func (s *Server) MeshForwards() (MeshForwardView, error)
- func (s *Server) MeshHostLink(host string) MeshHostLinkView
- func (s *Server) MeshListen(peerID, service, localAddr string) (string, error)
- func (s *Server) MeshResolve(service string) ([]MeshResolvedPeer, error)
- func (s *Server) MeshServiceDelete(name string) error
- func (s *Server) MeshServiceUpsert(name, addr string) error
- func (s *Server) MeshServices() ([]MeshServiceView, error)
- func (s *Server) MeshStatus() *MeshStatusView
- func (s *Server) MeshUnexpose(service string) error
- func (s *Server) Mirror() (MirrorView, error)
- func (s *Server) MirrorDelete(source string) error
- func (s *Server) MirrorUpsert(source, service string, lanOnly bool) error
- func (s *Server) OutboundSuggestions(ctx context.Context) ([]OutboundSuggestion, error)
- func (s *Server) Pair(ctx context.Context, p PairParams) (PairResult, error)
- func (s *Server) PeerImageEnsure(ctx context.Context, name string) (peerimage.EnsureResult, error)
- func (s *Server) PeerImageMeshResolve(ctx context.Context, service string, minimum int) (peerimage.ResolveResult, error)
- func (s *Server) PeerImagePublications() ([]peerimage.Publication, error)
- func (s *Server) PeerImagePublish(ctx context.Context, name, body string) (peerimage.Publication, error)
- func (s *Server) PeerImageReport(ctx context.Context, ch peerimage.Challenge) (peerimage.Report, error)
- func (s *Server) PeerPlaneView() (PeerPlaneResult, error)
- func (s *Server) PeerStatus(ctx context.Context) ([]peerstatus.Peer, error)
- func (s *Server) PeerTiers() []PeerTierView
- func (s *Server) RefreshUserKubeconfig(ctx context.Context) (userkube.Status, error)
- func (s *Server) RollbackUpgrade(ctx context.Context) (upgrade.RollbackResult, error)
- func (s *Server) RotateControlPlaneToken() (ControlPlaneResult, error)
- func (s *Server) RotateProvisioningToken(name string) (string, error)
- func (s *Server) RotateSSOSecret(name string) (string, error)
- func (s *Server) RunBackupNow(ctx context.Context) ([]backup.Candidate, error)
- func (s *Server) SafeView() (SafeView, error)
- func (s *Server) ScheduleRestart()
- func (s *Server) SetAppEnabled(name string, enabled bool) (conf.AppConfig, error)
- func (s *Server) SetBackup(p BackupParams) (conf.BackupConfig, error)
- func (s *Server) SetBuiltins(p BuiltinsParams) (BuiltinsResult, error)
- func (s *Server) SetCloudbox(base, accessToken, agentName string)
- func (s *Server) SetControlPlane(p ControlPlaneParams) (ControlPlaneResult, error)
- func (s *Server) SetNetworking(p NetworkingParams) (NetworkingResult, error)
- func (s *Server) SetWarmDesired(models []string) error
- func (s *Server) ShardLog(ctx context.Context, host string) (string, error)
- func (s *Server) ShardStatus(ctx context.Context, host string) (any, error)
- func (s *Server) ShardTrigger(ctx context.Context, host, model string) error
- func (s *Server) Status() (StatusView, error)
- func (s *Server) Unpair() (PairResult, error)
- func (s *Server) UpgradeOverview() (UpgradeOverview, error)
- func (s *Server) UpsertApp(p AppUpsertParams) (conf.AppConfig, error)
- func (s *Server) UpsertOutbound(p OutboundParams) error
- func (s *Server) UpsertSSHTarget(t SSHTargetView) (SSHTargetView, error)
- func (s *Server) UserKubeconfigStatus() userkube.Status
- type StatusView
- type Suggestion
- type UpgradeOverview
- type UpgradeSource
- type VKCredentialResult
- type YcodeShareSurfaceView
- type YcodeView
Constants ¶
const ControlPlaneTokenRotationHint = "worker recovery — on this control-plane host, run once for each joined worker: " +
"`outpost cluster control-plane token --quiet | ssh <worker> outpost cluster join --token-stdin` " +
"(only the tunnel token changed; endpoint, stcp secret, and node token stay as already configured)"
ControlPlaneTokenRotationHint is the recovery instruction handed back with a rotated tunnel token. It deliberately never embeds the token itself — piping it straight from the reveal command to the join command on the worker keeps the value out of shell history and process args on both ends, same discipline `outpost cluster token | ssh worker … --token-stdin` already uses for the k3s node token (cmd/outpost/cluster_node_token.go).
Only the token needs to move: JoinPeerPlane treats an omitted field as "leave alone" (cluster_join.go), so a worker that already joined keeps its endpoint, STCP secret, and node token untouched by this one-field update.
const DefaultVKNamespace = "default"
DefaultVKNamespace is the allow-list a mint falls back to when the operator names none. `default` always exists, so the zero-flag path yields a policy that both admits something and stays explicit about WHAT (fail-closed everywhere else).
Variables ¶
var PeerImageToolNames = map[PeerImageVerb]string{ PeerImageVerbPublish: "outpost_publish_image_recipe", PeerImageVerbMeshResolve: "outpost_mesh_resolve_image_recipes", PeerImageVerbEnsure: "outpost_ensure_image", PeerImageVerbReport: "outpost_report_image", }
PeerImageToolNames maps each verb to its MCP tool name. MCP tool names are verb-noun; the CLI subcommand is the kebab-case verb under `outpost peer-image`. Both surfaces read this table rather than hard-coding strings, so a rename cannot desynchronize them.
var PeerImageVerbs = []PeerImageVerb{ PeerImageVerbPublish, PeerImageVerbMeshResolve, PeerImageVerbEnsure, PeerImageVerbReport, }
PeerImageVerbs is the canonical ordering used by the parity tests and the CLI help.
Functions ¶
func CloudboxHTTPBase ¶
func CloudboxHTTPBase(fc *conf.FileConfig) string
CloudboxHTTPBase derives the HTTP(S) base URL of cloudbox from the matrix-tunnel pairing fields. Protocols are paired (wss↔https, websocket/ws/tcp↔http). Returns empty when the FileConfig isn't paired yet.
func ValidateApp ¶
ValidateApp normalizes ac in place (lowercasing scheme, defaulting host, trimming whitespace) and rejects invalid combinations. Returns *APIError so callers can map straight to a transport status code.
Same rules the admin SPA enforces client-side, replicated here as the authoritative gate.
func ValidateOutbound ¶
func ValidateOutbound(p *OutboundParams) error
ValidateOutbound trims, normalizes, and rejects bad combinations on p. After a successful call p.Scheme is one of "" (treated as "http"), "tcp", or "ssh"; required fields per-scheme are non-empty.
Types ¶
type APIError ¶
APIError carries an HTTP-style status alongside the human message so adminui can map it back to a gin status code and mcpapi can render an MCP-conformant error response.
func AsAPIError ¶
AsAPIError unwraps err into an *APIError if it is one (directly or via errors.As). Returns nil otherwise. HTTP layers call this to pick the right status code; plain (non-APIError) errors should be treated as 500.
func (*APIError) HTTPStatus ¶
HTTPStatus returns the suggested HTTP status code for this error.
type AppHealthView ¶ added in v0.10.0
type AppHealthView struct {
Name string `json:"name"`
Scheme string `json:"scheme"`
Target string `json:"target"`
Reachable bool `json:"reachable"`
RTTms float64 `json:"rtt_ms"`
Tier string `json:"tier"`
StatusCode int `json:"status_code,omitempty"`
Error string `json:"error,omitempty"`
At time.Time `json:"at,omitzero"`
}
AppHealthView is one app's reachability measurement (rendered into SafeView).
type AppUpsertParams ¶
AppUpsertParams is the wire shape for adding or updating an app. The URL field is an alternative to the {Scheme, Host, Port, Socket} quartet — when non-empty, it is parsed via conf.AppTargetFromURL and wins over the split fields.
type AppstoreCatalogView ¶
type AppstoreCatalogView struct {
OK bool `json:"ok"`
Catalog string `json:"catalog"`
Apps []string `json:"apps"`
}
AppstoreCatalogView reports the effective catalog and its installable app ids.
type AppstoreInstallParams ¶
type AppstoreInstallParams struct {
ID string `json:"id"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
Namespace string `json:"namespace"`
Release string `json:"release,omitempty"`
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
PollSeconds int `json:"poll_seconds,omitempty"`
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
NoRollback bool `json:"no_rollback,omitempty"`
SaveKubeconfig bool `json:"save_kubeconfig,omitempty"`
SaveCatalog bool `json:"save_catalog,omitempty"`
}
AppstoreInstallParams installs one operator-named OSS appstore app (a Helm chart, rendered to a HelmChart CR) into the caller's own namespace/release on a peer-hosted DKS plane.
type AppstoreInstallResult ¶
type AppstoreInstallResult struct {
OK bool `json:"ok"`
ID string `json:"id"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
Namespace string `json:"namespace"`
Release string `json:"release"`
ObjectName string `json:"object_name"`
Kubeconfig string `json:"kubeconfig"`
Applied int `json:"applied"`
Ready int `json:"ready"`
Created []string `json:"created,omitempty"`
RolledBack []string `json:"rolled_back,omitempty"`
CleanupFailed []string `json:"cleanup_failed,omitempty"`
KubeconfigSaved bool `json:"kubeconfig_saved,omitempty"`
CatalogSaved bool `json:"catalog_saved,omitempty"`
}
AppstoreInstallResult reports one appstore app install run.
type AppstoreShowResult ¶
type AppstoreShowResult struct {
OK bool `json:"ok"`
ID string `json:"id"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
Description string `json:"description,omitempty"`
Homepage string `json:"homepage,omitempty"`
Categories []string `json:"categories,omitempty"`
Tags []string `json:"tags,omitempty"`
Featured bool `json:"featured,omitempty"`
ChartRepo string `json:"chart_repo"`
ChartName string `json:"chart_name"`
ChartVersion string `json:"chart_version"`
ClusterScoped bool `json:"cluster_scoped,omitempty"`
HasValues bool `json:"has_values"`
}
AppstoreShowResult previews a resolved, VALIDATED app manifest without touching any cluster — the apiVersion/kind envelope, id match, and chart shape are checked here exactly as install would check them, so an operator can see WHY an app is unsupported before ever naming a kubeconfig. The fields mirror the real dhnt/appstore AppEntry metadata.
type AppstoreStatusParams ¶
type AppstoreStatusParams struct {
ID string `json:"id"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
Namespace string `json:"namespace"`
Release string `json:"release,omitempty"`
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
}
AppstoreStatusParams resolves one operator-named OSS appstore app and reports its live state on a peer-hosted plane — read-only.
type AppstoreStatusResult ¶
type AppstoreStatusResult struct {
OK bool `json:"ok"`
ID string `json:"id"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
Namespace string `json:"namespace"`
Release string `json:"release"`
ObjectName string `json:"object_name"`
Kubeconfig string `json:"kubeconfig"`
Installed bool `json:"installed"`
AllReady bool `json:"all_ready"`
Reason string `json:"reason,omitempty"`
}
AppstoreStatusResult reports the resolved app plus its HelmChart CR's live state.
type AppstoreUninstallParams ¶
type AppstoreUninstallParams struct {
ID string `json:"id"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
Namespace string `json:"namespace"`
Release string `json:"release,omitempty"`
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
PollSeconds int `json:"poll_seconds,omitempty"`
}
AppstoreUninstallParams resolves one operator-named OSS appstore app and removes its HelmChart CR from a peer-hosted plane.
type AppstoreUninstallResult ¶
type AppstoreUninstallResult struct {
OK bool `json:"ok"`
ID string `json:"id"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
Namespace string `json:"namespace"`
Release string `json:"release"`
ObjectName string `json:"object_name"`
Kubeconfig string `json:"kubeconfig"`
Deleted []string `json:"deleted,omitempty"`
Failed []string `json:"failed,omitempty"`
Gone int `json:"gone,omitempty"`
}
AppstoreUninstallResult reports one uninstall run.
type BackupApplier ¶ added in v0.4.2
type BackupApplier interface {
Apply(cfg *conf.BackupConfig) error
RunNow(ctx context.Context) ([]backup.Candidate, error)
History(n int) ([]backup.Candidate, error)
}
BackupApplier is what admincore needs from main.go's backup.Manager without taking on the package import in Deps (admincore stays protocol-agnostic; the backup package is implementation-specific).
type BackupParams ¶ added in v0.4.2
type BackupParams struct {
Enabled bool `json:"enabled"`
Schedule string `json:"schedule"`
Folders []string `json:"folders"`
LedgerPath string `json:"ledger_path,omitempty"`
}
BackupParams is the wire shape the admin UI POSTs. Folder paths are trimmed and absolute-path-normalised before persisting; empty lines are dropped (the UI accepts a textarea so blank lines are common).
type BuiltinInstallParams ¶
type BuiltinInstallParams struct {
Name string `json:"name"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
PollSeconds int `json:"poll_seconds,omitempty"`
CRDTimeoutSeconds int `json:"crd_timeout_seconds,omitempty"`
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
NoRollback bool `json:"no_rollback,omitempty"`
SaveKubeconfig bool `json:"save_kubeconfig,omitempty"`
SaveCatalog bool `json:"save_catalog,omitempty"`
}
BuiltinInstallParams installs one operator-named OSS appstore built-in by feeding its resolved manifest into the peer-only BundleApply transaction.
type BuiltinInstallResult ¶
type BuiltinInstallResult struct {
BundleApplyResult
Name string `json:"name"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
CatalogSaved bool `json:"catalog_saved,omitempty"`
}
type BuiltinStatusParams ¶
type BuiltinStatusParams struct {
Name string `json:"name"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
}
BuiltinStatusParams resolves one operator-named OSS appstore built-in and reports its live state on a peer-hosted plane — read-only, applies nothing.
type BuiltinStatusResult ¶
type BuiltinStatusResult struct {
BundleStatusResult
Name string `json:"name"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
}
BuiltinStatusResult reports the resolved built-in plus its bundle-wide status snapshot.
type BuiltinUninstallParams ¶
type BuiltinUninstallParams struct {
Name string `json:"name"`
Catalog string `json:"catalog,omitempty"`
Kubeconfig string `json:"kubeconfig,omitempty"`
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
PollSeconds int `json:"poll_seconds,omitempty"`
}
BuiltinUninstallParams resolves one operator-named OSS appstore built-in and removes it from a peer-hosted plane.
type BuiltinUninstallResult ¶
type BuiltinUninstallResult struct {
BundleUninstallResult
Name string `json:"name"`
Catalog string `json:"catalog"`
Manifest string `json:"manifest"`
}
BuiltinUninstallResult reports the resolved built-in plus its uninstall accounting.
type BuiltinView ¶
type BuiltinView struct {
Enabled bool `json:"enabled"`
Available bool `json:"available"`
Target string `json:"target,omitempty"`
}
BuiltinView is the wire shape for one optional local-daemon proxy (podman/ollama). Enabled reflects the saved config; Available is the live detection result so the SPA can grey out the toggle when the daemon isn't running.
type BuiltinsParams ¶
type BuiltinsParams struct {
Shell *bool `json:"shell,omitempty"`
Desktop *bool `json:"desktop,omitempty"`
Clipboard *bool `json:"clipboard,omitempty"`
SSH *bool `json:"ssh,omitempty"`
SSHAllowLocalForward *bool `json:"ssh_allow_local_forward,omitempty"`
SSHAllowRemoteForward *bool `json:"ssh_allow_remote_forward,omitempty"`
SSHAllowAgentForward *bool `json:"ssh_allow_agent_forward,omitempty"`
SSHForwardSockets []string `json:"ssh_forward_sockets,omitempty"`
SFTP *bool `json:"sftp,omitempty"`
// Files builtin (embedded File Browser). Files toggles the mount;
// FilesAllowWrite flips read-only⇄read-write (all write ops together);
// FilesScope sets the confined root (nil = leave unchanged, empty
// string = the OS user's home). FilesAllowWrite is intentionally only
// settable here on the loopback admin plane — the cloud-facing surface
// has no path to it, which is what keeps "read-only by default" a real
// guarantee rather than a default.
Files *bool `json:"files,omitempty"`
FilesAllowWrite *bool `json:"files_allow_write,omitempty"`
FilesScope *string `json:"files_scope,omitempty"`
Podman *bool `json:"podman,omitempty"`
Sandbox *bool `json:"sandbox,omitempty"`
Ollama *bool `json:"ollama,omitempty"`
OllamaPool *bool `json:"ollama_pool,omitempty"`
// WarmServing toggles the adaptive, considerate warm-serving plane:
// keep a small conservative set of models resident (zero cold-start),
// yielding (unloading) whenever the host is busy with the user's own
// work and restoring when idle. Default ON for a paired Ollama node.
// WarmBudgetFrac sets the fraction of usable memory dedicated to warm
// preload (clamped to (0,1]; default 0.33). nil = leave unchanged.
WarmServing *bool `json:"warm_serving,omitempty"`
WarmBudgetFrac *float64 `json:"warm_budget_frac,omitempty"`
Otel *bool `json:"otel,omitempty"`
OtelPool *bool `json:"otel_pool,omitempty"`
Ycode *bool `json:"ycode,omitempty"`
Cluster *bool `json:"cluster,omitempty"`
ClusterAgent *bool `json:"cluster_agent,omitempty"`
ClusterVirtual []string `json:"cluster_virtual,omitempty"`
// UpdateMode is one of "auto" / "manual" / "never" (see
// conf.UpdateMode* constants). Pointer-string so nil = "leave
// unchanged"; non-nil with an invalid value is rejected by
// SetBuiltins with a 400-class APIError.
UpdateMode *string `json:"update_mode,omitempty"`
// AutoRollback arms the auto-rollback watchdog's DESTRUCTIVE revert
// (default off / observe-only). nil = leave unchanged.
AutoRollback *bool `json:"auto_rollback,omitempty"`
// Mesh toggles the libp2p mesh data plane (the peer node carrying
// authenticated, NAT-traversing peer↔peer streams). MeshPort sets its
// TCP+QUIC listen port (0 = ephemeral). nil = leave unchanged.
Mesh *bool `json:"mesh,omitempty"`
MeshPort *int `json:"mesh_port,omitempty"`
// LANInference toggles the same-LAN direct-inference listener: a
// LAN-reachable reverse proxy to the local inference server, advertised
// to cloudbox so same-LAN callers reach this host's LLM directly (lower
// latency, bypassing the relay). LANInferencePort sets its listen port
// (0 = default 11435). This is a LAN-TRUST endpoint (no per-request
// auth) — an explicit opt-in. nil = leave unchanged.
LANInference *bool `json:"lan_inference,omitempty"`
LANInferencePort *int `json:"lan_inference_port,omitempty"`
// Shard is the Ollama sharding sub-feature: serve a model bigger than one
// node by splitting it across mesh peers. nil = leave unchanged; *bool=false
// opts OUT of the zero-config default (on for an owner-registered Ollama
// node). ShardPeers selects worker hostnames (nil = leave; empty/["auto"] =
// every same-LAN peer); ShardRole is "auto"/"leader"/"worker".
Shard *bool `json:"shard,omitempty"`
ShardPeers []string `json:"shard_peers,omitempty"`
ShardRole *string `json:"shard_role,omitempty"`
// Back-compat convenience for the generic bashy service "loom".
Loom *bool `json:"loom,omitempty"`
LoomPort *int `json:"loom_port,omitempty"`
// Back-compat convenience for the generic bashy service "meet" (the
// web chat room). Like Loom/LoomPort, but with NO mesh service — a
// personal chat room has no peer consumer. The Command base
// (["meet","service"]) is pinned in DefaultBashyServices so the
// supervisor drives `bashy meet service {start,status,stop}`.
Meet *bool `json:"meet,omitempty"`
MeetPort *int `json:"meet_port,omitempty"`
// BashyServices replaces the whole generic service set when non-nil.
BashyServices []conf.BashyService `json:"bashy_services,omitempty"`
// BashyVersion pins the bashy release the self-heal auto-install fetches
// when bashy is missing ("" / "latest" = newest; a tag pins it). Takes
// effect on the next restart. nil = leave unchanged.
BashyVersion *string `json:"bashy_version,omitempty"`
// Zot toggles running the Zot OCI registry as a managed external binary on a
// loopback port, auto-exposed over the mesh as `registry`. ZotPort sets its
// HTTP port (0 = default 5000). nil = leave unchanged.
Zot *bool `json:"zot,omitempty"`
ZotPort *int `json:"zot_port,omitempty"` // Seaweedfs toggles running SeaweedFS (object/blob store, S3 gateway) as a
// managed external binary on a loopback port, auto-exposed over the mesh as
// `s3`. SeaweedfsPort sets its S3 port (0 = default 8333). nil = unchanged.
Seaweedfs *bool `json:"seaweedfs,omitempty"`
SeaweedfsPort *int `json:"seaweedfs_port,omitempty"`
// Kopia toggles running the Kopia snapshot-backup repository server as a
// managed external binary on a loopback port, auto-exposed over the mesh as
// `backup`. KopiaPort sets its port (0 = default 51515). nil = unchanged.
Kopia *bool `json:"kopia,omitempty"`
KopiaPort *int `json:"kopia_port,omitempty"`
// Actrunner toggles running Gitea act_runner (the CI executor) as a managed
// external binary. Unlike loom/zot it's a CONSUMER: it registers against a
// Gitea instance and dials OUT. ActrunnerInstance is the Gitea base URL
// (empty = local loom forge); ActrunnerToken is the registration token;
// ActrunnerLabels are the executor labels (default "host:host"). nil = unchanged.
Actrunner *bool `json:"actrunner,omitempty"`
ActrunnerInstance *string `json:"actrunner_instance,omitempty"`
ActrunnerToken *string `json:"actrunner_token,omitempty"`
ActrunnerLabels *string `json:"actrunner_labels,omitempty"`
// CloudDOEnabled toggles Digital Ocean provider support; CloudDOToken is the
// DO API token (exported as DIGITALOCEAN_ACCESS_TOKEN). nil = unchanged.
CloudDOEnabled *bool `json:"cloud_do_enabled,omitempty"`
CloudDOToken *string `json:"cloud_do_token,omitempty"`
// ActrunnerSandbox opts the runner into the tier-3 sandbox (container)
// executor (runs-on: sandbox → OCI container via bashy podman), additive to
// the host build lane. ActrunnerSandboxImage / ActrunnerDockerHost override
// the image / DOCKER_HOST (empty docker-host = auto-resolve bashy podman).
ActrunnerSandbox *bool `json:"actrunner_sandbox,omitempty"`
ActrunnerSandboxImage *string `json:"actrunner_sandbox_image,omitempty"`
ActrunnerDockerHost *string `json:"actrunner_docker_host,omitempty"`
// headlamp toggles deploying Headlamp (the operating UI) on the peer DKS
// plane. HeadlampPort sets its supervised loopback forward port (0 = default
// 18466). nil = leave unchanged. See docs/peer-dks-headlamp.md.
Headlamp *bool `json:"headlamp,omitempty"`
HeadlampPort *int `json:"headlamp_port,omitempty"`
}
BuiltinsParams is the partial-update shape for SetBuiltins. Pointer- bool fields mean "leave unchanged when nil"; non-nil fields are written through to the FileConfig.
The set of fields here is broader than the admin SPA currently surfaces — SSHAllowRemoteForward, SSHAllowAgentForward, and SSHForwardSockets exist in FileConfig but the SPA has no toggle for them. MCP / CLI callers can drive them directly.
type BuiltinsResult ¶
BuiltinsResult reports what happened. RestartPending is true when the change is one the tunnel / built-in routes need to reload to observe — callers should poll Status until the daemon is back.
type BundleApplyParams ¶
type BundleApplyParams struct {
// Kubeconfig is the venue. Empty falls back to the persisted
// cluster.bundle_kubeconfig, then to the conventional peer
// control-plane path (~/.kube/outpost-control-plane/k3s.yaml). It is
// canonicalized (symlinks resolved) and refused when it lands on the
// cloudbox kubeconfig — whichever source supplied it.
Kubeconfig string `json:"kubeconfig,omitempty"`
// Bundle is the manifest file or directory to apply (required).
Bundle string `json:"bundle"`
// TimeoutSeconds bounds the readiness wait for the whole bundle.
// Default 300.
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
// PollSeconds is the readiness re-check interval. Default 2.
PollSeconds int `json:"poll_seconds,omitempty"`
// CRDTimeoutSeconds bounds, per CRD, the Established+discovery wait
// before dependent custom resources apply. Default 60.
CRDTimeoutSeconds int `json:"crd_timeout_seconds,omitempty"`
// AllowScaleToZero is the explicit opt-in for spec.replicas: 0
// workloads counting as rolled out once drained.
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
// NoRollback leaves objects this run created in place on failure
// (still reported precisely).
NoRollback bool `json:"no_rollback,omitempty"`
// SaveKubeconfig persists a non-empty Kubeconfig as
// cluster.bundle_kubeconfig after the venue guard accepts it, so
// later applies can omit the path. Live — no restart.
SaveKubeconfig bool `json:"save_kubeconfig,omitempty"`
}
BundleApplyParams is one apply request.
type BundleApplyResult ¶
type BundleApplyResult struct {
OK bool `json:"ok"`
// Kubeconfig is the CANONICAL venue the apply ran against (symlinks
// resolved) — proof of which plane was touched.
Kubeconfig string `json:"kubeconfig"`
Applied int `json:"applied"`
Ready int `json:"ready"`
// Created / RolledBack / CleanupFailed are the transactional
// accounting: what this run brought into existence, and (on the
// failure path) what the cleanup removed or could not remove.
Created []string `json:"created,omitempty"`
RolledBack []string `json:"rolled_back,omitempty"`
CleanupFailed []string `json:"cleanup_failed,omitempty"`
KubeconfigSaved bool `json:"kubeconfig_saved,omitempty"`
}
BundleApplyResult reports one apply run. On failure the same accounting travels inside the returned error text (the HTTP/MCP layers only carry the error), so nothing about a partial apply is ever silent.
type BundleCatalogView ¶
type BundleKubeconfigView ¶
type BundleKubeconfigView struct {
OK bool `json:"ok"`
// Kubeconfig is the persisted cluster.bundle_kubeconfig ("" when
// unset — applies then default to the conventional peer path).
Kubeconfig string `json:"kubeconfig"`
// Default is the conventional peer path used when nothing is
// persisted or passed.
Default string `json:"default"`
}
BundleKubeconfigView reports the persisted default venue (no secrets involved — a path, not a credential).
type BundleObjectStatus ¶
type BundleObjectStatus struct {
Kind string `json:"kind"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name"`
Exists bool `json:"exists"`
Ready bool `json:"ready"`
Reason string `json:"reason,omitempty"`
// Installer marks an outpost.dhnt.io/lifecycle=installer object — judged
// by its declared durable outputs, not by its own presence (a
// ttlSecondsAfterFinished Job is reaped by design after success).
Installer bool `json:"installer,omitempty"`
// DeclaredOutput marks a row asserted from an installer's
// outpost.dhnt.io/installs declaration rather than decoded from the
// manifest.
DeclaredOutput bool `json:"declared_output,omitempty"`
}
BundleObjectStatus is one bundle object's live state.
type BundleStatusParams ¶
type BundleStatusParams struct {
// Kubeconfig follows the same resolution and venue guard as
// BundleApply.
Kubeconfig string `json:"kubeconfig,omitempty"`
// Bundle is the manifest file or directory to check (required).
Bundle string `json:"bundle"`
// AllowScaleToZero mirrors BundleApply's opt-in: without it a
// spec.replicas: 0 workload reports not-ready with the same terminal
// reason an apply would have failed on, instead of "scaled to zero".
AllowScaleToZero bool `json:"allow_scale_to_zero,omitempty"`
}
BundleStatusParams is one status request — read-only, applies nothing.
type BundleStatusResult ¶
type BundleStatusResult struct {
OK bool `json:"ok"`
// Kubeconfig is the CANONICAL venue the check ran against.
Kubeconfig string `json:"kubeconfig"`
// Installed is true only when every non-installer object exists AND
// every installer's declared durable outputs exist. An installer's own
// absence never decides it (see the bundleapply lifecycle contract).
Installed bool `json:"installed"`
// AllReady is true only when every asserted object (non-installer
// bundle objects plus declared installer outputs) exists AND reports
// Ready — the exact bar BundleApply's readiness wait confirms. A
// garbage-collected installer does not count against it.
AllReady bool `json:"all_ready"`
Objects []BundleObjectStatus `json:"objects"`
}
BundleStatusResult is the bundle-wide status snapshot.
type BundleUninstallParams ¶
type BundleUninstallParams struct {
// Kubeconfig follows the same resolution and venue guard as
// BundleApply.
Kubeconfig string `json:"kubeconfig,omitempty"`
// Bundle is the manifest file or directory to remove (required).
Bundle string `json:"bundle"`
// TimeoutSeconds bounds an optional wait for the deleted objects to
// actually vanish. 0 skips the wait (delete-and-return).
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
// PollSeconds is the gone-check re-poll interval. Default 2.
PollSeconds int `json:"poll_seconds,omitempty"`
}
BundleUninstallParams is one uninstall request.
type BundleUninstallResult ¶
type BundleUninstallResult struct {
OK bool `json:"ok"`
// Kubeconfig is the CANONICAL venue the uninstall ran against.
Kubeconfig string `json:"kubeconfig"`
// Deleted lists the objects (kind ns/name) this run removed, in
// reverse apply order.
Deleted []string `json:"deleted,omitempty"`
// Failed lists objects that could NOT be deleted — left behind for
// the operator to remove by hand.
Failed []string `json:"failed,omitempty"`
// Gone is the count confirmed absent after the wait (only meaningful
// when TimeoutSeconds > 0).
Gone int `json:"gone,omitempty"`
}
BundleUninstallResult reports one uninstall run.
type ClusterLLMView ¶ added in v0.7.3
type ClusterLLMView struct {
Configured bool `json:"configured"`
Backend string `json:"backend,omitempty"`
State string `json:"state"`
Endpoint string `json:"endpoint,omitempty"`
Version string `json:"version,omitempty"`
HasAPIKey bool `json:"has_api_key"`
MemberCount int `json:"member_count,omitempty"`
AggregateVRAMBytes uint64 `json:"aggregate_vram_bytes,omitempty"`
}
ClusterLLMView is the operator-facing snapshot of the intra-home distributed-inference backend (GPUStack first). State is one of clusterllm's StateUnconfigured / Running / NotReachable. HasAPIKey reflects whether a management key is set (the secret itself is never surfaced); without it AggregateVRAMBytes stays 0 and the cloudbox size filter is inert. MemberCount / AggregateVRAMBytes are the live cluster shape the registry push advertises.
type ClusterView ¶
type ClusterView struct {
Enabled bool `json:"enabled"`
Agent bool `json:"agent"`
Virtual []string `json:"virtual,omitempty"`
APIURL string `json:"api_url,omitempty"`
NodeName string `json:"node_name,omitempty"`
HasToken bool `json:"has_token"`
HasCA bool `json:"has_ca"`
HasNodeToken bool `json:"has_node_token,omitempty"`
HasSTCPSecret bool `json:"has_stcp_secret,omitempty"`
K8sAPIPort int `json:"k8s_api_port,omitempty"`
// ControlPlane reports whether this host HOSTS the apiserver rather than
// merely joining a cluster. Surfaced because it changes what the host is:
// a control-plane host runs a tunnel server other machines depend on, so
// restarting it is a cluster-wide event rather than a local one.
//
// NOT omitempty. This flag has a history of being dropped by config-write
// paths (see mergePairing's inverted merge), and an omitempty bool renders
// the dropped case and the never-hosted case identically as a missing key —
// so `outpost status` answered "is this host the control plane?" with null
// either way. An explicit false is the whole point of reporting it.
ControlPlane bool `json:"control_plane"`
// ControlPlaneKubeconfig is the path to the admin kubeconfig for the plane
// this host HOSTS. Reported because it is the input the control-plane
// reconcilers resolve at boot: an empty value means they declined to start,
// which is directly observable here instead of only in the daemon log. It
// is a path, not a credential — the file's contents never leave the host.
ControlPlaneKubeconfig string `json:"control_plane_kubeconfig,omitempty"`
TunnelBindAddr string `json:"tunnel_bind_addr,omitempty"`
TunnelBindPort int `json:"tunnel_bind_port,omitempty"`
HasTunnelToken bool `json:"has_tunnel_token,omitempty"`
TunnelLANExposed bool `json:"tunnel_lan_exposed,omitempty"`
// JoinEndpoint names the PEER-hosted control plane this host joins, empty
// when it joins the cloudbox-hosted one. The address is reportable; the
// credential that goes with it is not, so only its presence appears here —
// same treatment cluster.token and the tunnel token get.
JoinEndpoint string `json:"join_endpoint,omitempty"`
HasJoinToken bool `json:"has_join_token,omitempty"`
// HasCloudSTCPSecret reports whether cloudbox's own cluster STCP secret
// is retained (cluster.cloud_stcp_secret) — the credential a
// peer-joined worker's overlay-control relay needs to register on the
// tailnet. Its absence is why a peer-flannel runtime refuses to start,
// so the presence flag is what makes that refusal diagnosable from the
// UI/MCP without reading daemon logs.
HasCloudSTCPSecret bool `json:"has_cloud_stcp_secret,omitempty"`
// PodNetworkMode is "overlay" (cloudbox allocated a per-node pod
// CIDR), "peer-flannel" (a peer-hosted plane; stock flannel VXLAN
// over the tailnet allocates from Node.spec.podCIDR — also
// multi-node-correct, and PodCIDR is empty because this side does
// not know it), or "single-node-fallback" (no CIDR: a fixed range
// identical on every node, so pod IPs collide the moment a second
// node joins). Read-only derived state,
// not a config key — see runtime.ClassifyPodNetwork. PodCIDR is the
// range that mode actually allocates from.
PodNetworkMode string `json:"pod_network_mode,omitempty"`
PodCIDR string `json:"pod_cidr,omitempty"`
// Observability fleet-aggregation URLs cloudbox provisioned for
// this outpost. Empty when the AppStore observability bundle
// isn't installed; non-empty means ycode is expected to
// remote_write metrics / push logs / OTLP-export traces here
// through the tailscale overlay.
MetricsRemoteURL string `json:"metrics_remote_url,omitempty"`
LogsRemoteURL string `json:"logs_remote_url,omitempty"`
TracesRemoteURL string `json:"traces_remote_url,omitempty"`
}
ClusterView is the redacted cluster status sent to UI / MCP callers. Token + CA bytes never leave the agent; presence is reported via has_token / has_ca.
type ControlPlaneParams ¶
type ControlPlaneParams struct {
Enabled *bool `json:"enabled,omitempty"`
BindAddr *string `json:"bind_addr,omitempty"`
BindPort *int `json:"bind_port,omitempty"`
}
ControlPlaneParams is a partial update. A nil field means "leave alone", so flipping the switch does not silently reset a customized bind.
type ControlPlaneResult ¶
type ControlPlaneResult struct {
OK bool `json:"ok"`
ControlPlane bool `json:"control_plane"`
BindAddr string `json:"bind_addr"`
BindPort int `json:"bind_port"`
HasToken bool `json:"has_token"`
// TunnelToken is empty unless the caller asked to reveal or rotate it.
TunnelToken string `json:"tunnel_token,omitempty"`
// STCPSecret is the SECOND credential a worker needs — it authorizes
// reaching the published apiserver, where the token authorizes the tunnel
// session itself. Revealed on the same terms as the token: a worker given
// only one of the two fails, so handing over one without the other would
// be a half-answer.
STCPSecret string `json:"stcp_secret,omitempty"`
// APIAddr is where the apiserver listens on THIS host — what the
// publisher bridges workers to.
APIAddr string `json:"api_addr,omitempty"`
// LANExposed is true when the bind address is not loopback. Surfaced as
// its own flag rather than left for the reader to infer from an address,
// because "this host is accepting cluster joins from the network" is the
// one property of this config worth noticing at a glance.
LANExposed bool `json:"lan_exposed"`
RestartPending bool `json:"restart_pending"`
// WorkerRejoinHint is the recovery instruction for a token rotation — set
// only by RotateControlPlaneToken. A rotate that just prints "reconfigure
// your workers" and stops is not a recovery path, it is a warning; this is
// the actual command, safe to display because it never embeds a literal
// secret (see ControlPlaneTokenRotationHint).
WorkerRejoinHint string `json:"worker_rejoin_hint,omitempty"`
}
ControlPlaneResult is the redacted status. TunnelToken is populated only by the explicit reveal/rotate paths.
type ControlPlaneStatus ¶
type ControlPlaneStatus struct {
// Hosted is true when this host is configured to host a control plane.
Hosted bool `json:"hosted"`
// ContainerExists reports whether the control-plane container exists.
ContainerExists bool `json:"container_exists"`
// ContainerRunning reports whether the control-plane container is running.
// Only meaningful when ContainerExists is true.
ContainerRunning bool `json:"container_running"`
// APIServerServing reports whether the apiserver is accepting HTTP connections.
// Requires the container to be running; a dead container always has
// APIServerServing false.
APIServerServing bool `json:"apiserver_serving"`
// APIServerStatusCode is the HTTP status when APIServerServing is true.
APIServerStatusCode int `json:"apiserver_status_code,omitempty"`
// Nodes is the list of cluster nodes joining this plane, with readiness status.
// Empty when the plane is not hosted or cannot be queried.
Nodes []Node `json:"nodes,omitempty"`
// NodeCount is the number of nodes in the cluster.
NodeCount int `json:"node_count"`
// JoinEndpoint is the endpoint URL workers can use to join this control plane.
// Empty when this host has not configured a join endpoint.
JoinEndpoint string `json:"join_endpoint,omitempty"`
// HasJoinToken reports whether a join token credential exists (presence only).
HasJoinToken bool `json:"has_join_token"`
// HasNodeToken reports whether a node token credential exists (presence only).
HasNodeToken bool `json:"has_node_token"`
// HasSTCPSecret reports whether an STCP secret credential exists (presence only).
HasSTCPSecret bool `json:"has_stcp_secret"`
// NodeAddrReconcilerRunning reports whether the apiserver→kubelet address
// reconciler (internal/agent/nodeaddr) has actually started in this daemon.
//
// NOT omitempty, on purpose. False is the interesting value: it is the
// exact state in which nodes go Ready and schedule pods while every
// `kubectl logs` / `exec` / `top nodes` fails, because no Node ever
// received the unique loopback ExternalIP the apiserver dials kubelets
// through. Hiding a false here would hide the diagnosis.
NodeAddrReconcilerRunning bool `json:"nodeaddr_reconciler_running"`
// NodeAddrLastRunAt is when its most recent reconcile pass completed.
NodeAddrLastRunAt time.Time `json:"nodeaddr_last_run_at,omitzero"`
// NodeAddrLastError is its most recent pass error, empty when healthy.
// Running-and-failing and never-started are different problems with
// different fixes; this is what tells them apart.
NodeAddrLastError string `json:"nodeaddr_last_error,omitempty"`
// CheckedAt is when this status was last measured.
CheckedAt time.Time `json:"checked_at,omitzero"`
}
ControlPlaneStatus is a read-only snapshot of the hosted control plane's health and readiness. It never carries credential values — presence is reported as has_* booleans only.
type ControlPlaneStatusProber ¶
type ControlPlaneStatusProber interface {
// ProbeControlPlaneStatus returns the current health and readiness.
ProbeControlPlaneStatus(ctx context.Context) (ControlPlaneStatus, error)
}
ControlPlaneStatusProber is the interface seam for testing. Faked implementations replace real probes (container inspection, apiserver health checks, cluster queries) with test data.
func NewDefaultControlPlaneStatusProber ¶
func NewDefaultControlPlaneStatusProber( agentName string, controlPlaneEnabled func() bool, joinEndpoint func() string, nodes func(ctx context.Context) ([]Node, error), hasJoinToken func() bool, hasNodeToken func() bool, hasSTCPSecret func() bool, ) ControlPlaneStatusProber
NewDefaultControlPlaneStatusProber wires the real implementation. Called from cmd/outpost/main.go when the cluster runtime is configured.
The closures capture the dependencies so the prober doesn't need to import the cluster packages directly — keeping admincore focused on the interface.
type Deps ¶
type Deps struct {
// ConfigPath is where the persistent FileConfig lives. The Server
// serializes all read-modify-write sequences against ConfigPath
// under its own mutex.
ConfigPath string
// Apps is the live registry — admincore mutates it directly when
// the operator adds/removes/toggles custom apps. Concurrent-safe.
Apps *agent.AppRegistry
// Outbound manages local mount paths that proxy through cloudbox
// to remote outposts' apps. Optional — when nil the outbound
// operations report "not configured" rather than panic.
Outbound *agent.OutboundManager
// Restart, when set, is invoked (debounced) after a save that
// requires the tunnel or built-in routes to reload. Nil during
// tests; admincore short-circuits ScheduleRestart in that case.
Restart func()
// CloudboxBase + CloudboxAccessToken + AgentName feed the outbound-
// suggestions endpoint and the provisioning relay. CloudboxBase is
// empty until pairing completes; admincore returns a clear error
// instead of dialing nothing when the bearer is absent.
CloudboxBase string
CloudboxAccessToken string
AgentName string
// LLMPoolStatus, when set, returns the live pool diagnostic block
// rendered into SafeView. Nil when the pool service wasn't wired
// (Ollama off or daemon undetected). Closure rather than a concrete
// type so admincore doesn't import the ollama package.
LLMPoolStatus func() LLMPoolStatusView
// PeerTiers, when set, returns the latest measured peer-locality tiers
// (the p2p peer-plane probe's ground truth — TP/LAN/WAN per peer).
// Closure so admincore doesn't import the peerplane package. Nil when
// the service isn't wired.
PeerTiers func() []PeerTierView
// MeshStatus, when set, returns the libp2p mesh host's live status
// (peer ID, listen addrs, connected-peer count). Closure so admincore
// doesn't import the mesh package. Nil when the host isn't wired.
MeshStatus func() *MeshStatusView
// MeshForward, when set, is the mesh forwarder's operation surface
// (expose/listen/forwards). Nil when the mesh data plane is off.
MeshForward MeshForwardOps
// MeshResolver, when set, queries the cloudbox service registry for the
// peers exposing a named mesh service (the "who runs <service>" lookup).
// Closure so admincore doesn't import the peerplane client. Nil when the
// host isn't paired / mesh is off.
MeshResolver func(service string) ([]MeshResolvedPeer, error)
// MeshLinkInfoByHost, when set, returns the live mesh link class
// ("tp"/"lan"/"wan"/"") AND the LAN label of the DIRECT connection to a
// paired host — the accurate same-LAN signal that overrides cloudbox's
// egress-IP location heuristic in PeerStatus, enriched with WHICH LAN the
// link rides over. Closure so admincore doesn't import the mesh package; it
// captures the rendezvous's host→peer-id map. Nil when the mesh data plane
// is off.
MeshLinkInfoByHost func(host string) MeshLinkInfo
// MeshPeerIDByHost, when set, returns the libp2p peer id the mesh actually
// connected to <host> with, or "" if none is known. Paired with MeshStatus
// it answers the only question a transport chooser needs — "is there a live
// DIRECT connection to this host right now?" — WITHOUT mDNS, multicast, a
// per-host discovery flag, or a fresh dial. An existing direct connection is
// itself the proof of reachability. Closure so admincore doesn't import the
// mesh package. Nil when the mesh data plane is off.
MeshPeerIDByHost func(host string) string
// ShardTrigger, when set, tells <host> to LEAD a shard for <model> over
// the mesh (no ssh). Closure so admincore doesn't import the shard /
// peerplane packages; it captures the shard.Manager + host→peer-id
// resolution. Nil when sharding / mesh isn't wired.
ShardTrigger func(ctx context.Context, host, model string) error
// ShardStatus, when set, returns a node's shard readiness over the mesh:
// the local node when host == "", otherwise a resolved peer. Returns an
// opaque value (a shard.StatusReport) the HTTP layers JSON-encode. Nil
// when sharding / mesh isn't wired.
ShardStatus func(ctx context.Context, host string) (any, error)
// ShardLog, when set, returns a node's recent prima-rank shard logs over
// the mesh: the local node when host == "", otherwise a resolved peer.
// Closure (captures the shard.Manager + host→peer-id resolution). Nil when
// sharding / mesh isn't wired.
ShardLog func(ctx context.Context, host string) (string, error)
// ClusterRuntimeDown, when set, SYNCHRONOUSLY stops this node's cluster
// runtime container and — when purge is true — removes its persistent-
// identity volumes (k3s node-id / tailscale machine key / CNI). LeaveCluster
// calls it only when the node was actually active, and with a plane-
// dependent purge: purge=true for a cloud-managed node, because cloudbox
// has already deleted its Headscale registration and a stale machine key
// would leave the overlay unable to converge on rejoin; purge=false for a
// peer-joined worker, because leave never deregisters anything on the peer
// plane and purging the local identity would desync it from a registration
// that still exists there — the overlay identity is preserved instead.
// Closure so admincore doesn't import the runtime package. Nil in tests /
// when the cluster runtime isn't wired.
ClusterRuntimeDown func(ctx context.Context, purge bool) error
// AppHealth, when set, returns the latest per-app reachability
// measurements (TCP/HTTP probes, no ICMP). Nil when the service
// isn't wired.
AppHealth func() []AppHealthView
// Upgrader + UpgradeLedger feed the Update tab on the admin UI
// and the corresponding MCP tools. Nil on unpaired hosts (the
// route falls back to a graceful 404 — see handlers/server.go
// for the gate). Threaded through admincore so the surface
// stays uniform across MCP / REST / future CLI.
Upgrader *upgrade.Worker
UpgradeLedger *upgrade.Ledger
// Backup, when set, is the live scheduler+worker for the folder-
// watcher backup feature (admincore/backup.go). Optional — when
// nil, SetBackup still persists the config to FileConfig (so a
// future restart with the manager wired picks it up) but cannot
// re-register the scheduler entry live.
Backup BackupApplier
// ControlPlaneStatusProber, when set, probes the hosted control plane's
// container health, apiserver readiness, and cluster node count. Nil when
// the cluster runtime isn't wired (no hosted plane configured). The probe
// reuses existing readiness surfaces (runtime.CheckServer, runtime.ProbeAPIServer,
// cached LastServerHealth) and threads them through an interface seam for
// testing. Closure-captured dependencies keep admincore focused.
ControlPlaneStatusProber ControlPlaneStatusProber
// BundleApplyClient, when set, builds the Kubernetes client
// BundleApply drives — tests inject a deterministic fake here. Nil
// means the production bundleapply.NewDynamicClient. The kubeconfig
// argument is ALREADY canonicalized and venue-checked: admincore runs
// bundleapply.ResolveVenue before calling the factory, so an injected
// client can never bypass the cloudbox-venue guard.
BundleApplyClient func(kubeconfig string) (bundleapply.ResourceClient, error)
// PeerImage, when set, is the peer image distribution engine (the four
// verbs publish / mesh-resolve / ensure / report) backing
// admincore/peerimage.go. Nil when the feature is off or its prerequisites
// (cluster runtime, recipe store) aren't met — the verbs then report
// "not enabled" rather than panicking. Interface so admincore never
// imports the peerimage wiring details, and so the parity tests can
// substitute a recording fake.
PeerImage PeerImageOps
}
Deps is what main.go threads into admincore.New. Everything here is concurrent-safe (or stateless): the Server doesn't own these values, it borrows them. AppRegistry and OutboundManager are live mutated across goroutines as the SPA / agent flips switches.
type ExecSSHParams ¶ added in v0.1.4
type ExecSSHParams struct {
// Name is the configured target alias (`outpost ssh add <name>`).
Name string
// Command is the literal command line to run on the remote host.
// Quoting / escaping is the caller's responsibility — this is
// fed verbatim to `ssh.Session.Run`.
Command string
// JumpOverride, when non-empty, overrides the target's persisted
// Via field for this one call (analogous to ssh's `-J <alias>`).
// Use the empty string to honor the on-disk Via.
JumpOverride string
// Timeout caps the remote process's wall-clock runtime. Default
// 60s; capped at 600s server-side to keep MCP callers from
// holding the connection forever.
Timeout time.Duration
// MaxStdout / MaxStderr cap captured output. Default 1 MiB / 256 KiB.
MaxStdout int64
MaxStderr int64
// Stdin, when non-nil, is fed to the remote process. The MCP
// surface accepts base64-encoded bytes and constructs an
// io.Reader here; CLI callers (outpost repair remote-binary,
// etc.) can pass any io.Reader directly. Closed when copy
// completes (sshclient does this).
Stdin io.Reader
}
ExecSSHParams is the input shape for ExecSSH. Defaults match the constraints the MCP tool surfaces.
type ExecSSHResult ¶ added in v0.1.4
type ExecSSHResult struct {
Stdout []byte `json:"stdout"`
Stderr []byte `json:"stderr"`
ExitCode int `json:"exit_code"`
StdoutTruncated bool `json:"stdout_truncated,omitempty"`
StderrTruncated bool `json:"stderr_truncated,omitempty"`
}
ExecSSHResult is the output shape.
type KubeconfigResult ¶
type KubeconfigResult struct {
OK bool `json:"ok"`
Cluster ClusterView `json:"cluster"`
RestartPending bool `json:"restart_pending"`
// Peer is true when LeaveCluster acted on a node that had joined a
// PEER-hosted control plane rather than the cloudbox-hosted one. Surfaced
// so a caller (the CLI) knows to SKIP the cloudbox reclaim: cloudbox never
// issued this node, the peer plane did, and the worker holds no admin
// credential to delete the Node object from that plane's apiserver — that
// deletion is the control-plane host's garbage-collection story, not the
// worker's. omitempty so the cloud path's result is unchanged.
Peer bool `json:"peer,omitempty"`
}
KubeconfigResult reports the cluster view after a mutation plus whether the daemon will restart to apply it. Returned from ClearKubeconfig today; previously also from SetKubeconfig (the bring-your-own paste path, removed — outposts only join their owning cloudbox's cluster now; for a different cluster, pair a second outpost against that cloudbox).
type LLMPoolStatusView ¶
type LLMPoolStatusView struct {
Enabled bool `json:"enabled"`
Running bool `json:"running"`
LastPushAt time.Time `json:"last_push_at,omitzero"`
LastModels int `json:"last_models"`
PushCount int64 `json:"push_count"`
LastError string `json:"last_error,omitempty"`
MaxParallel int `json:"max_parallel"`
InFlight int `json:"in_flight"`
CloudboxURL string `json:"cloudbox_url,omitempty"`
OllamaURL string `json:"ollama_url,omitempty"`
}
LLMPoolStatusView is the wire shape rendered into SafeView. Kept here (rather than in the ollama package) so the HTTP layers can read it without taking on an ollama dependency.
type MeshConsumeView ¶ added in v0.13.3
type MeshConsumeView struct {
Service string `json:"service"`
PeerID string `json:"peer_id"`
LocalAddr string `json:"local_addr"`
}
MeshConsumeView is one persistent mesh consume (the dial side).
type MeshForwardOps ¶ added in v0.10.0
type MeshForwardOps interface {
Expose(service, addr string) error
Unexpose(service string) error
Listen(peerID, service, localAddr string) (boundAddr string, err error)
CloseListen(addr string) error
Forwards() MeshForwardView
}
MeshForwardOps is the mesh forwarder's operation surface. The daemon wires in an adapter over mesh.Forwarder (nil when the mesh data plane is off); admincore stays independent of the mesh package. These drive the loopback-TCP-over-mesh transport: Expose a local service on the worker side, Listen for a (peer, service) on the client/leader side.
type MeshForwardView ¶ added in v0.10.0
type MeshForwardView struct {
Exposed map[string]string `json:"exposed"`
Listeners []MeshListenerView `json:"listeners"`
}
MeshForwardView is the live forwarder state (exposed services + listeners).
type MeshHostLinkView ¶
type MeshHostLinkView struct {
Found bool `json:"found"`
PeerID string `json:"peer_id,omitempty"`
Direct bool `json:"direct"`
LinkClass string `json:"link_class,omitempty"`
Remote []string `json:"remote,omitempty"`
}
MeshHostLinkView answers "can I reach <host> directly over the mesh right now?" for a single paired host. It exists because the reachability ladder used to have no way to ask: its LAN rung is an mDNS browse followed by a dial of a self-advertised endpoint, which fails whenever multicast is not delivered, the peer has discovery disabled, or a router sits between the peers — even when a direct QUIC link to that very host is already up.
Found is false when the mesh is off or no peer id is known for the host. Direct distinguishes a real hole-punched/LAN connection from a relayed circuit: a relayed mesh path is NOT a local route and must not outrank cloudbox.
type MeshLinkInfo ¶ added in v0.12.25
MeshLinkInfo is the mesh direct-link class plus the LAN label of the path to a paired host, fed by Deps.MeshLinkInfoByHost into PeerStatus's location override. Class is "tp"/"lan"/"wan"/"" (same vocabulary as the old link-class signal); LAN names which local LAN the link uses (e.g. "wired", "10.0.0") and is "" when there's no LAN label.
type MeshListenerView ¶ added in v0.10.0
type MeshListenerView struct {
Addr string `json:"addr"`
PeerID string `json:"peer_id"`
Service string `json:"service"`
}
MeshListenerView describes one active forward listener.
type MeshPeerConnView ¶ added in v0.12.24
type MeshPeerConnView struct {
ID string `json:"id"`
Direct bool `json:"direct"`
LinkClass string `json:"link_class"`
Remote []string `json:"remote,omitempty"`
}
MeshPeerConnView is the per-connected-peer link detail (which remote address + link class each peer is reached over) for the LOCAL mesh-status debug surface. Raw remote addrs are intentionally surfaced here (owner inspecting their own daemon over loopback) — this is NOT the cross-account peer-status API, which never returns raw IPs.
type MeshResolvedPeer ¶ added in v0.10.0
type MeshResolvedPeer struct {
Host string `json:"host"`
PeerID string `json:"peer_id"`
Services []string `json:"services"`
}
MeshResolvedPeer is one peer from the cloudbox service registry.
type MeshServiceView ¶ added in v0.10.0
MeshServiceView is one persistently-exposed mesh service (the wrap harness).
type MeshStatusView ¶ added in v0.10.0
type MeshStatusView struct {
PeerID string `json:"peer_id"`
ListenAddrs []string `json:"listen_addrs,omitempty"`
ConnectedPeers int `json:"connected_peers"`
Peers []MeshPeerConnView `json:"peers,omitempty"`
}
MeshStatusView is the libp2p mesh host's live status (rendered into SafeView + the status surfaces). Nil/absent when the mesh data plane is off.
type MirrorJobView ¶ added in v0.10.0
type MirrorJobView struct {
Source string `json:"source"`
Service string `json:"service"`
LANOnly bool `json:"lan_only"`
}
MirrorJobView is one continuous, mobility-aware directory-mirror job.
type MirrorView ¶ added in v0.10.0
type MirrorView struct {
Enabled bool `json:"enabled"`
Jobs []MirrorJobView `json:"jobs"`
}
MirrorView is the mirror feature's read shape.
type NetworkingParams ¶
type NetworkingParams struct {
// LocalAddr — bind for the matrix-tunnel ingress. Empty to clear.
// Use *string so callers can distinguish "leave alone" (nil) from
// "clear to default" (pointer to "").
LocalAddr *string `json:"local_addr,omitempty"`
// VNCAddr — upstream for the /desktop bridge.
VNCAddr *string `json:"vnc_addr,omitempty"`
// AdminAddr — bind for the admin UI + MCP listener.
AdminAddr *string `json:"admin_addr,omitempty"`
// AdminUsers — when non-nil, replaces the entire allowlist. Pass
// an empty slice to revert to the legacy "anyone with the OS
// password is admin" mode.
AdminUsers *[]string `json:"admin_users,omitempty"`
// DiscoveryEnabled flips the mDNS + HTTP discovery master switch.
DiscoveryEnabled *bool `json:"discovery_enabled,omitempty"`
// SSHListenAddr binds the LAN-direct SSH listener. Empty disables.
SSHListenAddr *string `json:"ssh_listen_addr,omitempty"`
// DiscoveryHTTPListenAddr binds the /api/v1/discover/* listener.
DiscoveryHTTPListenAddr *string `json:"discovery_http_listen_addr,omitempty"`
// PeerTrustPolicy is one of "same-owner" / "same-cloudbox" /
// "tofu-allow". Validated server-side.
PeerTrustPolicy *string `json:"peer_trust_policy,omitempty"`
// ClusterLLMEndpoint is the base URL of an intra-home
// distributed-inference backend (GPUStack). Empty disables detection.
// Validated as an http(s) URL. Read once at boot (the detector is
// built in main.go), so a change restarts like the bind fields.
ClusterLLMEndpoint *string `json:"cluster_llm_endpoint,omitempty"`
// ClusterLLMAPIKey is the optional Bearer key for that backend's
// management API. Empty to clear.
ClusterLLMAPIKey *string `json:"cluster_llm_api_key,omitempty"`
}
NetworkingParams is the partial-update shape for SetNetworking. All fields are pointers / nil-able so the caller can change one knob without resetting the others. Pass an explicit empty string to clear a field (revert to env / hardcoded default).
type NetworkingResult ¶
NetworkingResult reports what changed. RestartPending is true whenever any field was modified — the listener bind addresses and the admin-users allowlist all take effect at boot only.
type Node ¶
type Node struct {
// Name is the node's registered name.
Name string `json:"name"`
// Ready reports whether the node is ready to accept workloads.
Ready bool `json:"ready"`
}
Node represents a cluster node joining the control plane.
func ReadControlPlaneNodes ¶
ReadControlPlaneNodes queries the cluster nodes from a kubeconfig file. Returns empty list if the kubeconfig is unavailable or the query fails; an error is only for unexpected system failures. The context timeout is honored; a timeout returns an empty list, not an error.
type NodeTokenResult ¶
type NodeTokenResult struct {
OK bool `json:"ok"`
NodeToken string `json:"node_token"`
// Endpoint is the tunnel address a worker pairs the token with, so the
// caller can render the whole join line without a second round-trip.
Endpoint string `json:"endpoint,omitempty"`
}
NodeTokenResult carries the k3s node token. The value is a CREDENTIAL: it is returned only from this explicitly-named operation, never from a status read.
type OutboundParams ¶
type OutboundParams struct {
Path string `json:"path"`
Name string `json:"name"`
Host string `json:"host"`
User string `json:"user"`
Scheme string `json:"scheme,omitempty"`
LocalPort int `json:"local_port,omitempty"`
TTLSeconds int64 `json:"ttl_seconds,omitempty"`
}
OutboundParams mirrors the wire payload of POST /api/outbound. Lifted out of adminui so MCP tools can populate the same struct without reaching across packages.
type OutboundSuggestion ¶
type OutboundSuggestion struct {
Host string `json:"host"`
OsUser string `json:"os_user,omitempty"`
Name string `json:"name"`
Scheme string `json:"scheme,omitempty"`
RequireLogin bool `json:"require_login"`
IndexPath string `json:"index_path,omitempty"`
Title string `json:"title,omitempty"`
Online bool `json:"online"`
}
OutboundSuggestion is one row in the "Remote" dropdown — a host + an app on that host (or a synthetic SSH row pointing at the host's built-in /ssh endpoint).
type PairParams ¶
type PairParams struct {
Server string `json:"server,omitempty"`
Code string `json:"code"`
Name string `json:"name"`
Title string `json:"title,omitempty"`
AuthURL string `json:"auth_url,omitempty"`
ClientOnly bool `json:"client_only,omitempty"`
}
PairParams is the wire shape for the portal exchange.
- Server: portal URL (defaults to https://ai.dhnt.io when empty).
- Code: one-time pairing code from the portal (required).
- Name: host name to register (required).
- Title: optional human-readable subtitle shown in the portal.
- AuthURL: optional external app-level auth endpoint.
- ClientOnly: register as a credential-only outpost (no inbound listeners, no matrix tunnel) — see register --client-only.
type PairResult ¶
type PairResult struct {
OK bool `json:"ok"`
AgentName string `json:"agent_name"`
RestartPending bool `json:"restart_pending"`
}
PairResult reports the new AgentName cloudbox assigned (typically echoing the requested Name) plus the restart signal callers should poll on.
type PeerImageOps ¶
type PeerImageOps interface {
Publish(ctx context.Context, name, body string) (peerimage.Publication, error)
Publications() ([]peerimage.Publication, error)
MeshResolve(ctx context.Context, service string, minimum int) (peerimage.ResolveResult, error)
Ensure(ctx context.Context, name string) (peerimage.EnsureResult, error)
Report(ctx context.Context, ch peerimage.Challenge) (peerimage.Report, error)
}
PeerImageOps is the daemon-side engine admincore drives. main.go wires in a *peerimage.Service; admincore keeps the nil-check, the validation and the error mapping so every surface gets identical behaviour.
type PeerImageVerb ¶
type PeerImageVerb string
PeerImageVerb names one of the four operations.
const ( PeerImageVerbPublish PeerImageVerb = "publish" PeerImageVerbMeshResolve PeerImageVerb = "mesh-resolve" PeerImageVerbEnsure PeerImageVerb = "ensure" PeerImageVerbReport PeerImageVerb = "report" )
type PeerPlaneParams ¶
type PeerPlaneParams struct {
// Endpoint is the peer's tunnel server as host or host:port. A bare host
// takes conf.DefaultTunnelBindPort.
Endpoint *string `json:"endpoint,omitempty"`
// Token is the peer's cluster.tunnel_token.
Token *string `json:"token,omitempty"`
// STCPSecret authorizes this worker's visitor of the published apiserver.
STCPSecret *string `json:"stcp_secret,omitempty"`
// NodeToken is the k3s join token the hosting side prints with
// `outpost cluster token`.
NodeToken *string `json:"node_token,omitempty"`
// APIPort is the LOCAL port this worker's visitor binds the joined
// apiserver on (cluster.k8s_api_port). Optional; 0 means the 6443 default.
APIPort *int `json:"api_port,omitempty"`
// VKBundle is the peer-issued least-privilege virtual-kubelet credential —
// the FIFTH join value, needed only when a virtual runtime is selected.
// Minted on the hosting machine with `outpost cluster control-plane
// vk-credential`; the node token above cannot stand in for it (a k3s
// node-join token is not an apiserver bearer credential, so presenting it
// there gets a 401 that reads like a network fault). Applying it writes
// cluster.ca + cluster.token + cluster.allowed_namespaces and clears any
// stale client-cert pair — the bundle is authoritative for all three.
VKBundle *string `json:"vk_bundle,omitempty"`
// Agent / Virtual select which Nodes this worker registers on the joined
// plane — cluster.runtimes.agent and cluster.runtimes.virtual, the same
// two fields SetBuiltins writes and with the same partial-update rules
// (nil leaves the persisted value alone; a non-nil Virtual REPLACES the
// complete set, so a non-nil empty slice deselects every virtual backend).
//
// A peer plane can host virtual-kubelet nodes as-is: vknode's kubeconfig
// loader already accepts the client-certificate credentials k3s issues, as
// distinct from a cloudbox-minted bearer token (see vknode/kubeconfig.go).
// So this is selection, not new runtime support — before it existed a
// worker could only reach a vk node by hand-editing agent.json.
//
// Leaving BOTH nil preserves the historical behaviour exactly: the join
// falls through to selecting the agent runtime, and only when nothing is
// selected already.
Agent *bool `json:"cluster_agent,omitempty"`
Virtual []string `json:"cluster_virtual,omitempty"`
}
PeerPlaneParams is a partial update. A nil field means "leave alone", so re-running the join with a rotated token does not clear the other three.
type PeerPlaneResult ¶
type PeerPlaneResult struct {
OK bool `json:"ok"`
// Joined is true when a peer endpoint is configured, i.e. this host joins
// a plane OTHER than the cloudbox-hosted one.
Joined bool `json:"joined"`
Endpoint string `json:"endpoint,omitempty"`
APIPort int `json:"api_port,omitempty"`
HasToken bool `json:"has_token"`
HasSTCPSecret bool `json:"has_stcp_secret"`
HasNodeToken bool `json:"has_node_token"`
// HasVKCredential / VKCredentialKind report whether a virtual-kubelet node
// on this worker can authenticate to the joined apiserver, and with which
// credential form ("token" from a vk bundle, or "client-cert" when the
// operator provisioned a k3s client-certificate pair by hand). Presence
// only — the credential itself is never returned.
HasVKCredential bool `json:"has_vk_credential"`
VKCredentialKind string `json:"vk_credential_kind,omitempty"`
// AllowedNamespaces is the fail-closed namespace policy the vk nodes
// enforce. Namespace NAMES are policy, not secrets, so they are reported
// verbatim — an operator debugging "every pod is denied" needs to see the
// list, not a boolean.
AllowedNamespaces []string `json:"allowed_namespaces,omitempty"`
// ClusterEnabled reflects cluster.enabled AFTER the operation. Surfaced
// because a join that persisted its credentials but left cluster mode off
// looks successful and does nothing.
ClusterEnabled bool `json:"cluster_enabled"`
RestartPending bool `json:"restart_pending"`
// RuntimeAgent / RuntimeVirtual report the runtime selection AFTER the
// operation — which Nodes this worker will register. Reported because the
// join's default is implicit (agent when nothing was selected), so without
// this an operator cannot tell a defaulted selection from one they made.
RuntimeAgent bool `json:"runtime_agent"`
RuntimeVirtual []string `json:"runtime_virtual,omitempty"`
}
PeerPlaneResult is the redacted join status. It never carries a credential — the three secrets are reported as presence flags only.
type PeerTierView ¶ added in v0.10.0
type PeerTierView struct {
Host string `json:"host"`
Tier string `json:"tier"`
RTTms float64 `json:"rtt_ms"`
Addr string `json:"addr,omitempty"`
EgressSameLANHint bool `json:"egress_same_lan_hint"`
At time.Time `json:"at,omitzero"`
}
PeerTierView is one peer's measured locality (rendered into SafeView + the outpost_peer_tiers MCP tool). Tier is GROUND TRUTH (measured RTT: "tp" <=2ms wired/dedicated, "lan" pipeline, "wan"/"unreached"); EgressSameLANHint is cloudbox's egress-IP guess, surfaced so operators see where the heuristic disagrees with the measurement.
type SSHTargetView ¶ added in v0.1.4
SSHTargetView is the wire shape returned by list / upsert / show. Exactly the on-disk struct; defined as a separate name so we can add presentation-only fields later without breaking the file format.
type SafeView ¶
type SafeView struct {
AgentName string `json:"agent_name"`
ServerAddr string `json:"server_addr"`
ServerPort int `json:"server_port"`
CloudboxURL string `json:"cloudbox_url,omitempty"`
Protocol string `json:"protocol,omitempty"`
RemotePort int `json:"remote_port"`
AuthURL string `json:"auth_url,omitempty"`
HasToken bool `json:"has_token"`
LocalAddr string `json:"local_addr,omitempty"`
VNCAddr string `json:"vnc_addr,omitempty"`
AdminAddr string `json:"admin_addr,omitempty"`
// Wave 3A discovery + LAN-direct knobs (all default off).
DiscoveryEnabled bool `json:"discovery_enabled"`
SSHListenAddr string `json:"ssh_listen_addr,omitempty"`
DiscoveryHTTPListenAddr string `json:"discovery_http_listen_addr,omitempty"`
PeerTrustPolicy string `json:"peer_trust_policy,omitempty"`
AssignedHostname string `json:"assigned_hostname,omitempty"`
OAuth2Email string `json:"oauth2_email,omitempty"`
AdminUsers []string `json:"admin_users"`
Apps []conf.AppConfig `json:"apps"`
ShellEnabled bool `json:"shell_enabled"`
DesktopEnabled bool `json:"desktop_enabled"`
ClipboardEnabled bool `json:"clipboard_enabled"`
SSHEnabled bool `json:"ssh_enabled"`
SSHAllowLocalForward bool `json:"ssh_allow_local_forward"`
SSHAllowRemoteForward bool `json:"ssh_allow_remote_forward"`
SSHAllowAgentForward bool `json:"ssh_allow_agent_forward"`
SSHForwardSockets []string `json:"ssh_forward_sockets"`
SFTPEnabled bool `json:"sftp_enabled"`
FilesEnabled bool `json:"files_enabled"`
FilesAllowWrite bool `json:"files_allow_write"`
FilesScope string `json:"files_scope"`
ClientOnly bool `json:"client_only"`
Podman BuiltinView `json:"podman"`
Sandbox BuiltinView `json:"sandbox"`
Ollama BuiltinView `json:"ollama"`
OllamaPoolEnabled bool `json:"ollama_pool_enabled"`
WarmServingEnabled bool `json:"warm_serving_enabled"`
WarmBudgetFrac float64 `json:"warm_budget_frac,omitempty"`
WarmDesired []string `json:"warm_desired,omitempty"`
LANInferenceEnabled bool `json:"lan_inference_enabled"`
LANInferencePort int `json:"lan_inference_port,omitempty"`
MeshEnabled bool `json:"mesh_enabled"`
MeshPort int `json:"mesh_port,omitempty"`
BashyServices []conf.BashyService `json:"bashy_services,omitempty"`
BashyVersion string `json:"bashy_version,omitempty"`
ShardEnabled bool `json:"shard_enabled"`
LoomEnabled bool `json:"loom_enabled"`
LoomPort int `json:"loom_port,omitempty"`
MeetEnabled bool `json:"meet_enabled"`
MeetPort int `json:"meet_port,omitempty"`
ZotEnabled bool `json:"zot_enabled"`
ZotPort int `json:"zot_port,omitempty"`
SeaweedfsEnabled bool `json:"seaweedfs_enabled"`
SeaweedfsPort int `json:"seaweedfs_port,omitempty"`
KopiaEnabled bool `json:"kopia_enabled"`
KopiaPort int `json:"kopia_port,omitempty"`
ActrunnerEnabled bool `json:"actrunner_enabled"`
ActrunnerInstance string `json:"actrunner_instance,omitempty"`
ActrunnerLabels string `json:"actrunner_labels,omitempty"`
ActrunnerSandbox bool `json:"actrunner_sandbox"`
ActrunnerSandboxImage string `json:"actrunner_sandbox_image,omitempty"`
ActrunnerDockerHost string `json:"actrunner_docker_host,omitempty"`
HeadlampEnabled bool `json:"headlamp_enabled"`
HeadlampPort int `json:"headlamp_port,omitempty"`
CloudDOEnabled bool `json:"cloud_do_enabled"`
HasCloudDOToken bool `json:"has_cloud_do_token"`
OtelEnabled bool `json:"otel_enabled"`
OtelPoolEnabled bool `json:"otel_pool_enabled"`
Ycode YcodeView `json:"ycode"`
// YcodeShareSurfaces is the catalog rendered as effective state:
// every entry the SPA might offer, with the boolean folding the
// per-surface overlay against the catalog's DefaultOn. The SPA
// renders one toggle row per entry; the value drives the switch.
UpdateMode string `json:"update_mode"`
LLMPool LLMPoolStatusView `json:"llm_pool"`
PeerTiers []PeerTierView `json:"peer_tiers,omitempty"`
Mesh *MeshStatusView `json:"mesh,omitempty"`
AppHealth []AppHealthView `json:"app_health,omitempty"`
ClusterLLM ClusterLLMView `json:"cluster_llm"`
Cluster ClusterView `json:"cluster"`
ControlPlane *ControlPlaneStatus `json:"control_plane,omitempty"`
Outbound []agent.OutboundView `json:"outbound"`
Defaults map[string]string `json:"defaults"`
}
SafeView is the redacted FileConfig sent over the API. Token never leaves the agent; presence is reported as has_token instead.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the stateful object that the HTTP layers share. Holds the FileConfig serialization mutex and the restart-debounce timer so that adminui and mcpapi calling the same operations in quick succession (e.g. the SPA toggling several builtins) collapse into a single save dance and a single restart.
func New ¶
New constructs an admincore.Server. Deps.ConfigPath is required; other fields are optional (nil-checked at the call sites that need them).
func (*Server) AppHealth ¶ added in v0.10.0
func (s *Server) AppHealth() []AppHealthView
AppHealth returns the latest per-app reachability measurements, or nil when the app-health service isn't wired.
func (*Server) AppSuggestions ¶
func (s *Server) AppSuggestions() ([]Suggestion, error)
AppSuggestions probes well-known socket paths and the local ycode manifest, returning the apps the user could enable with one click. Never mutates configuration.
func (*Server) ApplyPendingUpgrade ¶
ApplyPendingUpgrade — admincore-side wrapper around the Worker's LoadPending + Apply with Force=true. Same flow as the MCP tool outpost_apply_pending; exposed here so the adminui /api/upgrade/ apply route doesn't need its own copy of the worker handle.
func (*Server) AppstoreCatalog ¶
func (s *Server) AppstoreCatalog(explicit string) (AppstoreCatalogView, error)
AppstoreCatalog lists the app ids installable from the effective catalog (explicit param, else persisted cluster.bundle_catalog).
func (*Server) AppstoreInstall ¶
func (s *Server) AppstoreInstall(ctx context.Context, p AppstoreInstallParams) (AppstoreInstallResult, error)
AppstoreInstall resolves apps/<id>/app.yaml (+ optional values.yaml), fails closed on an unsupported apiVersion/kind envelope, a mismatched metadata.id, or an unsafe chart reference (repo scheme / pinned version), renders the single HelmChart object for the caller's namespace/release, and applies it through the same peer venue guard, readiness wait, and rollback accounting BundleApply gives the builtins path.
func (*Server) AppstoreShow ¶
func (s *Server) AppstoreShow(explicitCatalog, id string) (AppstoreShowResult, error)
AppstoreShow resolves and validates apps/<id>/app.yaml the same way AppstoreInstall would, and reports its metadata. Read-only — nothing is applied, and no kubeconfig is required.
func (*Server) AppstoreStatus ¶
func (s *Server) AppstoreStatus(ctx context.Context, p AppstoreStatusParams) (AppstoreStatusResult, error)
AppstoreStatus resolves apps/<id>/app.yaml exactly the way AppstoreInstall does (same fail-closed apiVersion/kind/id/chart check) and recomputes the identical deterministic HelmChart object name for the given namespace/release, then reports its live state — reusing the same readiness evidence (bundleapply.StatusBundle -> evalReadiness) ApplyBundle's wait uses, so installed=true/all_ready=true means exactly what a successful AppstoreInstall would have confirmed.
func (*Server) AppstoreUninstall ¶
func (s *Server) AppstoreUninstall(ctx context.Context, p AppstoreUninstallParams) (AppstoreUninstallResult, error)
AppstoreUninstall resolves apps/<id>/app.yaml exactly the way AppstoreInstall does, recomputes the identical HelmChart object for the given namespace/release, and removes it — deleting the HelmChart CR is what tells the k3s helm-controller to `helm uninstall` the release. The caller's target namespace is left alone: it may be hosting other apps for the same user (per-user namespace isolation is about SHARING a namespace across a user's installs, not owning it exclusively), so uninstall never deletes it.
func (*Server) AttachBackup ¶ added in v0.4.2
func (s *Server) AttachBackup(applier BackupApplier)
AttachBackup injects the live backup.Manager after admincore construction. Same setter pattern as AttachUpgrade — the manager needs the scheduler which is built alongside the errgroup, so it can't be passed through the initial Deps. Safe to call once at startup, no concurrent readers yet.
func (*Server) AttachUpgrade ¶
AttachUpgrade injects the upgrade Worker + Ledger after admincore construction. The Worker's Restart closure normally points at the admincore Server's ScheduleRestart, which means worker construction needs the Server to already exist — so we can't pass them through the initial Deps. Setter pattern instead; safe to call once at startup, no concurrent readers yet.
func (*Server) BackupHistory ¶ added in v0.4.2
BackupHistory returns the last `n` ledger entries (newest last). n<=0 returns all. Used by the admin UI's "Recent backups" panel.
func (*Server) BuiltinInstall ¶
func (s *Server) BuiltinInstall(ctx context.Context, p BuiltinInstallParams) (BuiltinInstallResult, error)
BuiltinInstall resolves only <appstore>/builtin/<name>/install.yaml. It does not consult cloudbox or accept an arbitrary manifest path; BundleApply then enforces the cloudbox venue guard, readiness gates, and rollback semantics.
func (*Server) BuiltinStatus ¶
func (s *Server) BuiltinStatus(ctx context.Context, p BuiltinStatusParams) (BuiltinStatusResult, error)
BuiltinStatus resolves <catalog>/builtin/<name>/install.yaml exactly the way BuiltinInstall does, then delegates to BundleStatus — so "is this built-in installed and ready" always answers against the same manifest BuiltinInstall would apply, through the same venue guard.
func (*Server) BuiltinUninstall ¶
func (s *Server) BuiltinUninstall(ctx context.Context, p BuiltinUninstallParams) (BuiltinUninstallResult, error)
BuiltinUninstall resolves only <appstore>/builtin/<name>/install.yaml — the identical resolution BuiltinInstall uses — and delegates to BundleUninstall, so removing a built-in reuses the exact venue guard and deletion mechanics BuiltinInstall's rollback path already relies on. It does not consult cloudbox or accept an arbitrary manifest path.
func (*Server) BundleApply ¶
func (s *Server) BundleApply(ctx context.Context, p BundleApplyParams) (BundleApplyResult, error)
BundleApply loads a bundle, enforces the kubeconfig venue guard, applies the bundle in prerequisite order with the CRD Established+discovery gate, and waits — bounded — for every workload to be ROLLED OUT (updated replicas, StatefulSet revision parity; zero-desired refused without the explicit opt-in). On failure it rolls back what the run created (unless NoRollback) and reports the accounting in the error.
Side-effect class: Live. No restart is ever scheduled — the operation touches the peer cluster, not this daemon's own wiring.
func (*Server) BundleCatalog ¶
func (s *Server) BundleCatalog(explicit string) (BundleCatalogView, error)
BundleCatalog reports the effective catalog and its installable names.
func (*Server) BundleKubeconfig ¶
func (s *Server) BundleKubeconfig() (BundleKubeconfigView, error)
BundleKubeconfig returns the persisted default venue.
func (*Server) BundleStatus ¶
func (s *Server) BundleStatus(ctx context.Context, p BundleStatusParams) (BundleStatusResult, error)
BundleStatus reports the live state of every object in a bundle without applying or deleting anything. It reuses the same venue guard and safe path resolution as BundleApply, and the same readiness evidence (bundleapply.StatusBundle calls the identical evalReadiness logic ApplyBundle's wait uses) — "installed and ready" here means exactly what a successful BundleApply would have confirmed.
Side-effect class: Live, read-only. Nothing is applied, deleted, or persisted.
func (*Server) BundleUninstall ¶
func (s *Server) BundleUninstall(ctx context.Context, p BundleUninstallParams) (BundleUninstallResult, error)
BundleUninstall removes every object in a bundle from a peer-hosted control plane. It reuses BundleApply's venue guard and safe path resolution (resolveBundleVenue) and bundleapply's deletion mechanics — the SAME reverse-order, best-effort delete loop ApplyBundle's own failure-rollback path uses — so an operator-initiated uninstall can never behave differently from an apply-triggered rollback of the same objects.
Side-effect class: Live. No restart is ever scheduled.
func (*Server) ClearKubeconfig ¶
func (s *Server) ClearKubeconfig() (KubeconfigResult, error)
ClearKubeconfig disables DKS and removes cloud-issued membership while preserving the configured runtime set. Callers apply teardown through the pending restart.
func (*Server) ConnectOutbound ¶
ConnectOutbound runs the cloudbox elevate flow for the named mount using the supplied OS password and starts the matrix_elev pinger. Returns 404 when the path is unknown.
func (*Server) ControlPlaneNodeToken ¶
func (s *Server) ControlPlaneNodeToken(ctx context.Context) (NodeTokenResult, error)
ControlPlaneNodeToken reads the k3s node token from the control plane this host hosts.
It refuses on a host that is not hosting a plane rather than returning an empty value: "this machine has no control plane" and "the control plane has no token" need different fixes, and an empty string reads as the second.
func (*Server) ControlPlaneStatusView ¶
func (s *Server) ControlPlaneStatusView(ctx context.Context) (ControlPlaneStatus, error)
ControlPlaneStatusView returns the current hosted control plane's status. Requires a configured control plane; returns hosted=false when none exists.
This method does not return an error for a missing control plane — an unpaired host or one that has not enabled hosting returns a zero status with hosted=false. It only errors on unexpected probe failures (e.g. system lookup failures that should never happen).
func (*Server) ControlPlaneVKCredential ¶
func (s *Server) ControlPlaneVKCredential(ctx context.Context, namespaces []string) (VKCredentialResult, error)
ControlPlaneVKCredential mints (or re-reads — the operation is idempotent) the least-privilege virtual-kubelet credential of the control plane this host hosts, provisioning the named workload namespaces as it goes.
It refuses on a host that is not hosting a plane: the credential describes THIS machine's plane, and minting one anywhere else would be a category error dressed as success.
func (*Server) ControlPlaneView ¶
func (s *Server) ControlPlaneView(reveal bool) (ControlPlaneResult, error)
ControlPlaneView returns the current placement config. reveal=true includes the tunnel token — the value an operator hands to workers.
func (*Server) DeleteApp ¶
DeleteApp removes an app by name from FileConfig and from the live AppRegistry. No-op when the name isn't registered (idempotent — the SPA's "remove" button doesn't care about prior state).
func (*Server) DeleteOutbound ¶
DeleteOutbound removes an outbound mount by path. Idempotent — no error when the path doesn't exist.
func (*Server) DeleteSSHTarget ¶ added in v0.1.4
DeleteSSHTarget is idempotent — no error when the alias doesn't exist (so a retry after a partial failure still succeeds).
func (*Server) Deps ¶
Deps returns the underlying dependency struct (read-only access for HTTP layers that need e.g. AgentName or CloudboxBase).
func (*Server) DisconnectOutbound ¶
DisconnectOutbound drops the matrix_elev cookie for the named mount. Idempotent.
func (*Server) ExecSSH ¶ added in v0.1.4
func (s *Server) ExecSSH(ctx context.Context, p ExecSSHParams) (*ExecSSHResult, error)
ExecSSH resolves the target chain (including any Via hops), dials each leg, opens an in-process SSH client on the innermost connection, and runs Command.
Errors map as follows:
- target missing → 404 NotFound
- any chain target.User empty → 400 BadRequest with guidance
- elev cookie missing/stale → 401 with EAUTHREQUIRED hint
- cloudbox unreachable / SSH handshake → 502 BadGateway
- timeout → wrapped as upstream() (502)
- remote exit-code != 0 → NOT an error — result is returned with .ExitCode set; lets agents distinguish "command ran and failed" from "couldn't get to the host."
func (*Server) GetBackup ¶ added in v0.4.2
func (s *Server) GetBackup() (conf.BackupConfig, error)
GetBackup returns the persisted backup config — never nil. Empty fields mean "feature not configured yet" which the UI renders as a blank form.
func (*Server) GetSSHTarget ¶ added in v0.1.4
func (s *Server) GetSSHTarget(name string) (SSHTargetView, error)
GetSSHTarget returns one target by alias, or a 404 APIError.
func (*Server) GetSSOSecret ¶ added in v0.4.0
GetSSOSecret returns the current SSO HMAC secret for the named app. Errors with 404 when the app doesn't exist and 400 when TrustCloudIdentity is off. This is what `outpost apps secret <name>` surfaces so the operator can paste it into the cooperating app's config.
func (*Server) JoinCluster ¶ added in v0.14.11
func (s *Server) JoinCluster() (KubeconfigResult, error)
JoinCluster is the symmetric partner to LeaveCluster: it re-ENABLES cluster mode, retaining the runtimes + NodeName LeaveCluster preserved. The cloud-issued credentials LeaveCluster cleared are re-fetched by the boot-time reattach — this method only flips the desired state on; the reconcile happens on the ensuing restart. Idempotent.
func (*Server) JoinPeerPlane ¶
func (s *Server) JoinPeerPlane(p PeerPlaneParams) (PeerPlaneResult, error)
JoinPeerPlane points this host at a peer-hosted control plane.
It ENABLES cluster mode (selecting the agent runtime when no runtime is configured) as part of the same save. A "join" that persisted an endpoint and left the node not joining anything would be a config editor wearing a verb's name.
Cluster runtime config is read once at boot, so any change is restart- pending and ScheduleRestart is called — same contract as SetControlPlane.
func (*Server) LeaveCluster ¶ added in v0.14.11
func (s *Server) LeaveCluster(ctx context.Context) (KubeconfigResult, error)
LeaveCluster is the per-node "leave DKS" state change — distinct from ClearKubeconfig's full wipe. It DISABLES cluster mode but PRESERVES the node identities + runtime set, clearing only the MEMBERSHIP fields so a rejoin's boot reattach (cloud plane) or a fresh `outpost cluster join` (peer plane) re-supplies them.
It is membership-only by construction: it never touches cloudbox pairing (access_token) or any app / shell / LLM / outbound / mesh setting, so leaving the cluster does not log the host out of the portal or drop unrelated services. It works for BOTH a cloud-managed node and a peer-joined worker — disableClusterMembership clears the cloud-issued creds AND the peer join_endpoint/join_token, while preserving the hosting block when this host is itself a control plane (see disableClusterMembership).
Disabling (not deleting) the Cluster block means the next boot takes the cluster-OFF path, which tears the runtime container down (main.go), instead of a stale k3s kubelet retry-looping forever on a Node the plane already deleted. RestartPending=true when the node was joined so the caller applies it. Idempotent: a second call on an already-left node is a no-op save with RestartPending=false.
Result.Peer reports whether this was a peer-joined worker. The CLI uses it to skip the cloudbox reclaim — cloudbox never issued this node. Deleting the Node object from the PEER apiserver is deliberately NOT done here: the worker holds only a k3s join token, not an admin credential for that plane, and leave does not add one. That deletion is the control-plane host's garbage-collection story.
The runtime teardown itself is also plane-aware (see the purge comment inline below): a cloud-managed leave purges the local overlay identity because cloudbox has already deregistered it from Headscale, while a peer-joined leave PRESERVES the overlay identity — no deregistration ever happened (leave makes no call to the peer plane), so purging here would desync the local machine key from a registration that still exists wherever the peer plane's overlay is registered.
func (*Server) LeavePeerPlane ¶
func (s *Server) LeavePeerPlane() (PeerPlaneResult, error)
LeavePeerPlane reverts this host to the cloudbox-hosted plane.
It clears the endpoint, the join token, AND the two peer-issued credentials (node token, STCP secret) — those describe the PEER's cluster, and leaving them behind is what makes the next boot's reattach look broken: cloudbox refuses to refresh cluster membership while a peer endpoint is configured (see applyCloudboxClusterMembership), so a half-cleared config would keep presenting a foreign node token to cloudbox's apiserver and fail the join with a CA-hash mismatch.
Cluster mode itself is LEFT ALONE. "Stop joining that plane" is not "stop being a cluster node" — the cloudbox-hosted plane is the default, and the boot reattach re-fetches its credentials. Use `outpost cluster leave` to leave the cluster entirely.
func (*Server) ListApps ¶
ListApps returns the apps slice from the on-disk FileConfig. Returns an empty slice (never nil) when no apps are registered, so JSON serialization stays a list.
func (*Server) ListOutbound ¶
func (s *Server) ListOutbound() []agent.OutboundView
ListOutbound returns the live state of every registered outbound mount. When no manager is wired (unpaired host), returns an empty slice instead of nil so JSON renders as a list.
func (*Server) ListSSHTargets ¶ added in v0.1.4
func (s *Server) ListSSHTargets() ([]SSHTargetView, error)
ListSSHTargets enumerates configured aliases, sorted by name. Returns an empty slice (never nil) when nothing is configured.
func (*Server) LoadConfig ¶
func (s *Server) LoadConfig() (*conf.FileConfig, error)
LoadConfig is the exported read-only variant. HTTP layers use it for pure renders (GET /api/config, MCP resource reads) that don't need to hold the save mutex. Returns a copy view; mutators must go through the typed operations.
func (*Server) MeshCloseListen ¶ added in v0.10.0
MeshCloseListen closes the forward listener bound at addr.
func (*Server) MeshConsumeDelete ¶ added in v0.13.3
MeshConsumeDelete removes a persisted mesh consume and closes its live listener.
func (*Server) MeshConsumeUpsert ¶ added in v0.13.3
MeshConsumeUpsert persists a mesh consume (service ← peer id → local addr) so the daemon re-establishes the forward on every boot, and establishes it live now if the forwarder is up. Keyed by (service, local_addr) so two consumes of the same service on different local ports coexist.
func (*Server) MeshConsumes ¶ added in v0.13.3
func (s *Server) MeshConsumes() ([]MeshConsumeView, error)
MeshConsumes lists the persisted (auto-established) mesh consumes.
func (*Server) MeshDial ¶ added in v0.10.0
MeshDial resolves a peer exposing the named service and opens a local forward listener to it, returning the bound local address + the chosen peer host — the zero-config consume side ("dial git" without knowing the peer id).
func (*Server) MeshExpose ¶ added in v0.10.0
MeshExpose registers a local loopback service reachable over the mesh.
func (*Server) MeshForwards ¶ added in v0.10.0
func (s *Server) MeshForwards() (MeshForwardView, error)
MeshForwards returns the forwarder's exposed services + active listeners.
func (*Server) MeshHostLink ¶
func (s *Server) MeshHostLink(host string) MeshHostLinkView
MeshHostLink reports whether this daemon currently holds a direct mesh connection to host. Pure read of live state — no dial, no discovery.
func (*Server) MeshListen ¶ added in v0.10.0
MeshListen opens a local TCP listener forwarding to (peerID, service) over the mesh and returns the bound local address. localAddr "" → 127.0.0.1:0.
func (*Server) MeshResolve ¶ added in v0.10.0
func (s *Server) MeshResolve(service string) ([]MeshResolvedPeer, error)
MeshResolve returns the peers exposing the named mesh service (the registry).
func (*Server) MeshServiceDelete ¶ added in v0.10.0
MeshServiceDelete removes a persisted mesh service and unexposes it live.
func (*Server) MeshServiceUpsert ¶ added in v0.10.0
MeshServiceUpsert persists a mesh service (name → loopback addr) so it is auto-exposed on every boot, and exposes it live now if the forwarder is up.
func (*Server) MeshServices ¶ added in v0.10.0
func (s *Server) MeshServices() ([]MeshServiceView, error)
MeshServices lists the persisted (auto-exposed) mesh services.
func (*Server) MeshStatus ¶ added in v0.10.0
func (s *Server) MeshStatus() *MeshStatusView
MeshStatus returns the libp2p mesh host's live status, or nil when the mesh data plane isn't wired.
func (*Server) MeshUnexpose ¶ added in v0.10.0
MeshUnexpose removes a service from the allowlist.
func (*Server) Mirror ¶ added in v0.10.0
func (s *Server) Mirror() (MirrorView, error)
Mirror returns the persisted live-mirror config.
func (*Server) MirrorDelete ¶ added in v0.10.0
MirrorDelete removes a mirror job by source dir and schedules a restart. Disables the feature when the last job is removed.
func (*Server) MirrorUpsert ¶ added in v0.10.0
MirrorUpsert adds (or updates) a mobility-aware mirror job keyed by source dir: mirror Source to the peer exposing mesh Service, only while reachable (and same-LAN when lanOnly). Enables the feature, persists, schedules a restart.
func (*Server) OutboundSuggestions ¶
func (s *Server) OutboundSuggestions(ctx context.Context) ([]OutboundSuggestion, error)
OutboundSuggestions calls cloudbox's /api/v1/hosts and flattens it into one row per (host, app), plus a synthetic SSH row per host whose built-in /ssh is mounted. Returns ServiceUnavailable when the outpost isn't paired yet (no AccessToken to authenticate with).
func (*Server) Pair ¶
func (s *Server) Pair(ctx context.Context, p PairParams) (PairResult, error)
Pair runs the portal exchange and merges the result into the persisted FileConfig (preserving locally-managed fields: Apps, Outbound, built-in toggles, Cluster). Schedules a restart so the new tunnel/identity takes effect.
func (*Server) PeerImageEnsure ¶
PeerImageEnsure makes a published recipe's image resident on THIS node and confirms it by containerd content digest. A digest that disagrees with the recorded provenance fails loudly and is never repaired by pulling anything.
Side-effect class: Live.
func (*Server) PeerImageMeshResolve ¶
func (s *Server) PeerImageMeshResolve(ctx context.Context, service string, minimum int) (peerimage.ResolveResult, error)
PeerImageMeshResolve finds the DISTINCT peers exposing the recipe service.
minimum is the number of distinct peers the caller intends to claim it reached; falling short is an error, and an empty registry answer is an error too. Neither is reported as an empty success.
Side-effect class: Live (read-only).
func (*Server) PeerImagePublications ¶
func (s *Server) PeerImagePublications() ([]peerimage.Publication, error)
PeerImagePublications lists what this node publishes.
func (*Server) PeerImagePublish ¶
func (s *Server) PeerImagePublish(ctx context.Context, name, body string) (peerimage.Publication, error)
PeerImagePublish publishes a build recipe so peers can fetch and build it themselves. No image blob is transferred — that is the model.
Side-effect class: Live.
func (*Server) PeerImageReport ¶
func (s *Server) PeerImageReport(ctx context.Context, ch peerimage.Challenge) (peerimage.Report, error)
PeerImageReport answers an inspector's identity-bound challenge with this node's evidence. The reply always names THIS node; a challenge addressed to another node is refused rather than answered on its behalf.
Side-effect class: Live (read-only).
func (*Server) PeerPlaneView ¶
func (s *Server) PeerPlaneView() (PeerPlaneResult, error)
PeerPlaneView reports which control plane this host joins. Redacted — there is no reveal counterpart, because unlike the hosting side's token these are values the operator brought WITH them; the source of truth is the host that issued them.
func (*Server) PeerStatus ¶ added in v0.7.3
PeerStatus queries cloudbox for the peer status board — online state, a same-LAN/remote location hint, and the build/OS/arch details each host last reported — for the paired hosts this account can see (its owned hosts plus hosts shared with it). Requires a paired host (CloudboxBase + access token are set). Backs the outpost_peers_status MCP tool; the `outpost peers status` CLI calls peerstatus.Fetch directly so it works without the daemon running.
Cloudbox computes each peer's location by comparing the host's last-recorded egress IP to the caller's source IP — a heuristic that false-negatives (reports "remote" for hosts that ARE on the same LAN when the recorded egress IPs differ). When this daemon's mesh data plane holds a DIRECT (non-relayed) link to a peer, its link class is the ground truth, so we override the cloudbox hint with it.
func (*Server) PeerTiers ¶ added in v0.10.0
func (s *Server) PeerTiers() []PeerTierView
PeerTiers returns the latest measured peer-locality tiers, or nil when the peer-plane service isn't wired.
func (*Server) RefreshUserKubeconfig ¶ added in v0.1.0
RefreshUserKubeconfig re-mints the kubectl-ready kubeconfig from cloudbox and rewrites the on-disk file. The admin UI's "Refresh" button under the Cluster section drives this; cloudbox-side token rotation is the canonical reason to call it. Returns the status after the attempt (so the UI can render the new state without a second round-trip).
func (*Server) RollbackUpgrade ¶
RollbackUpgrade — admincore-side wrapper around Worker.Rollback. Same return shape as the MCP tool outpost_rollback.
func (*Server) RotateControlPlaneToken ¶
func (s *Server) RotateControlPlaneToken() (ControlPlaneResult, error)
RotateControlPlaneToken mints a new tunnel token and returns it.
EVERY WORKER MUST BE RECONFIGURED after this — the token authenticates the frpc session, so existing workers fail to re-login on their next reconnect. That is the point of a rotate verb (revoking a leaked credential), but it is destructive enough that callers should say so in their own words; this method will not do it implicitly as part of some other operation.
Rotation touches ONLY the tunnel token. The STCP secret and the k3s node token are separate credentials minted by separate owners (this method and k3s respectively, see cluster_node_token.go) and are left alone — a worker only has to re-supply the one value that actually changed. That is what makes WorkerRejoinHint a single-field `outpost cluster join --token-stdin` rather than the full three-credential join: this method never strands a worker without a cheap, explicit way back in.
func (*Server) RotateProvisioningToken ¶
RotateProvisioningToken mints a new 32-byte hex bearer for the named app and updates both the persisted FileConfig and the live registry. Errors with 404 when the app doesn't exist and 400 when TrustCloudIdentity is off (rotation is only meaningful when the relay is in use).
func (*Server) RotateSSOSecret ¶ added in v0.4.0
RotateSSOSecret mints a new 32-byte hex HMAC key for the named app and updates both the persisted FileConfig and the live registry. Errors with 404 when the app doesn't exist and 400 when TrustCloudIdentity is off (rotation is only meaningful when the SSO handshake is in use). Rotating breaks the cooperating app until the operator pastes the new value — same trade-off as RotateProvisioningToken.
func (*Server) RunBackupNow ¶ added in v0.4.2
RunBackupNow triggers an immediate fire against the currently- applied folders, regardless of Enabled. Returns the candidates so the admin UI can render the result inline ("3 folders scanned; 1 new file picked, 2 skipped").
func (*Server) SafeView ¶
SafeView returns the redacted view of the on-disk FileConfig + live state (built-in availability probes, outbound mount status, pool diagnostic). The Token / AccessToken / ProvisioningToken values are NEVER included — presence is reported via has_token only.
func (*Server) ScheduleRestart ¶
func (s *Server) ScheduleRestart()
ScheduleRestart asynchronously triggers Deps.Restart after a short debounce so the in-flight HTTP response has time to flush AND so multiple back-to-back operations (the SPA auto-saves on every toggle) collapse into a single re-exec. Each call resets the timer.
func (*Server) SetAppEnabled ¶ added in v0.1.0
SetAppEnabled flips an app's Enabled flag without re-supplying the rest of its config — what `outpost apps stop`/`start` and the outpost_set_app_enabled MCP tool delegate to. Persists the change and updates the live AppRegistry: enabling re-mounts the proxy, disabling unregisters it. Idempotent — setting to the current value is a no-op (still returns the row so callers can confirm the state).
This only flips the proxy gate. The upstream container/process is untouched — operators stop those out-of-band (e.g. `podman stop`). 404s when the app name isn't registered.
func (*Server) SetBackup ¶ added in v0.4.2
func (s *Server) SetBackup(p BackupParams) (conf.BackupConfig, error)
SetBackup validates the params, persists them into FileConfig, and re-applies the live scheduler entry via the Applier. LIVE mutation — no restart needed (the scheduler's Register replaces any prior entry for the same name).
Validation:
- Schedule, when non-empty, must parse under cron/v3's standard 5-field parser plus descriptors.
- Folders are required when Enabled (no point in scheduling against nothing). Each path is checked for absoluteness only; existence is NOT enforced because the cooperating app may not have written its first artifact yet.
func (*Server) SetBuiltins ¶
func (s *Server) SetBuiltins(p BuiltinsParams) (BuiltinsResult, error)
SetBuiltins applies the partial update p to the persisted FileConfig and (when the host is paired) schedules a restart so the new toggles take effect. On a first-time setup (AgentName empty) nothing is mounted yet, so the save is harmless and no restart is triggered.
func (*Server) SetCloudbox ¶
SetCloudbox updates the cloudbox base URL + access token + agent name after a re-pair (Pair mutates the FileConfig but the in-memory deps snapshot is stale until callers refresh it). HTTP layers call this after Pair returns successfully.
func (*Server) SetControlPlane ¶
func (s *Server) SetControlPlane(p ControlPlaneParams) (ControlPlaneResult, error)
SetControlPlane enables or disables hosting the apiserver on this host and updates the tunnel server's bind.
Enabling MINTS THE TOKEN as a side effect, so the operator never has a control plane that is on but unjoinable. Disabling does NOT delete it: a host toggled off and on again would otherwise invalidate every worker's configuration, and the token is worthless while no server is listening.
Any change is restart-pending — the tunnel server is built once at boot, like every other listener outpost owns.
func (*Server) SetNetworking ¶
func (s *Server) SetNetworking(p NetworkingParams) (NetworkingResult, error)
SetNetworking applies the partial update to the persisted FileConfig and (if anything changed and the host is paired) schedules a restart so the new listener bind / allowlist takes effect. First-time-setup hosts (AgentName empty) skip the restart — nothing is mounted yet, so a save is harmless.
func (*Server) SetWarmDesired ¶ added in v0.12.32
SetWarmDesired persists the DESIRED warm set (the models cloudbox last asked this host to keep warm). Called by the warm executor whenever a /admin/warm load/shard/unload changes the set, so the intent survives a daemon restart. Writes through the shared config mutex so it can't race a concurrent builtins toggle; no restart is scheduled (the live executor already holds the in-memory set — this is durability only).
func (*Server) ShardLog ¶ added in v0.12.16
ShardLog returns the local node's (host == "") or a peer's recent prima-rank shard logs over the mesh — the captured exit reason a crashed shard left behind, no ssh.
func (*Server) ShardStatus ¶ added in v0.12.2
ShardStatus returns the local node's (host == "") or a peer's shard readiness over the mesh.
func (*Server) ShardTrigger ¶ added in v0.12.2
ShardTrigger tells <host> to LEAD a shard for <model> over the mesh.
func (*Server) Status ¶
func (s *Server) Status() (StatusView, error)
Status returns the lightweight paired-yet payload.
func (*Server) Unpair ¶
func (s *Server) Unpair() (PairResult, error)
Unpair clears the portal-controlled fields (AgentName, Token, etc.) while preserving locally-managed config (Apps, Outbound, builtins). Schedules a restart so the daemon drops its tunnel and reverts to the unpaired admin-UI-only mode.
New capability — the admin UI doesn't expose this today, but agents occasionally need to nuke a stale pairing without editing agent.json by hand.
func (*Server) UpgradeOverview ¶
func (s *Server) UpgradeOverview() (UpgradeOverview, error)
UpgradeOverview returns the consolidated payload for the Update tab. History is bounded to the most recent 20 entries — operators don't typically need more than that, and the JSONL ledger is unbounded in principle but rare in practice.
func (*Server) UpsertApp ¶
func (s *Server) UpsertApp(p AppUpsertParams) (conf.AppConfig, error)
UpsertApp validates the params, persists the merged FileConfig, and mutates the live AppRegistry. No restart required — AppRegistry is concurrent-safe.
func (*Server) UpsertOutbound ¶
func (s *Server) UpsertOutbound(p OutboundParams) error
UpsertOutbound validates the params, refuses collisions with local app names and other listener-binding mounts, persists to FileConfig, and re-registers the live OutboundManager so the change takes effect without a restart.
func (*Server) UpsertSSHTarget ¶ added in v0.1.4
func (s *Server) UpsertSSHTarget(t SSHTargetView) (SSHTargetView, error)
UpsertSSHTarget validates + persists. Idempotent.
User is optional at upsert time — the caller can leave it blank and ExecSSH will return a clear "user not set" error at run time. The CLI typically resolves the OS username from cloudbox before calling here so the on-disk record carries everything needed.
func (*Server) UserKubeconfigStatus ¶ added in v0.1.0
UserKubeconfigStatus returns the last-known state of the kubectl- ready kubeconfig file on disk — path, existence, refresh timestamp, last error. Rendered into the admin UI's Cluster section so the operator sees at-a-glance whether kubectl is ready + what to fix when it isn't.
type StatusView ¶
type StatusView struct {
Configured bool `json:"configured"`
AgentName string `json:"agent_name,omitempty"`
ServerAddr string `json:"server_addr,omitempty"`
CloudboxURL string `json:"cloudbox_url,omitempty"`
CurrentOSUser string `json:"current_os_user,omitempty"`
Build agent.BuildInfo `json:"build"`
BinaryPath string `json:"binary_path,omitempty"`
ControlPlaneSummary string `json:"control_plane_summary,omitempty"`
}
StatusView is the small "is outpost paired yet?" shape the SPA polls to decide what to render. Mirrors the legacy /api/status payload.
Build + BinaryPath are added so a remote operator (e.g. `outpost upgrade` running on another box that drives this daemon over MCP, or cloudbox's fleet view) can see the running daemon's provenance and the path of the binary to swap on disk.
type Suggestion ¶
type Suggestion struct {
Name string `json:"name"`
Scheme string `json:"scheme"`
Socket string `json:"socket,omitempty"`
Host string `json:"host,omitempty"`
Port int `json:"port,omitempty"`
Role string `json:"role"`
Source string `json:"source"` // "wellKnown" | "ycodeManifest"
Note string `json:"note,omitempty"` // human-readable hint
Existing bool `json:"existing,omitempty"` // already registered with this name
Managed bool `json:"managed,omitempty"` // bashy start/status/stop service
}
Suggestion is one auto-detected local app the operator can register with a single click (in the admin UI) or a single MCP call.
type UpgradeOverview ¶
type UpgradeOverview struct {
Build agent.BuildInfo `json:"build"`
BinaryPath string `json:"binary_path,omitempty"`
UpdateMode string `json:"update_mode"`
RollbackAvailable bool `json:"rollback_available"`
CurrentSource *UpgradeSource `json:"current_source,omitempty"`
Pending *upgrade.Envelope `json:"pending,omitempty"`
History []upgrade.LedgerEntry `json:"history"`
}
UpgradeOverview is the wire shape rendered into the admin UI's Update tab. One round-trip surfaces everything the operator usually wants to see when thinking about versions: what build is running, where it came from, when it landed, whether a pending envelope is queued, and the recent ledger.
All fields are zero-valued / nil / empty on unpaired hosts (no Upgrader was threaded into Deps). The handler 404s before this runs on those hosts — but the struct stays well-formed either way.
type UpgradeSource ¶
type UpgradeSource struct {
Kind string `json:"kind"` // "cloudbox" / "cli-url" / "cli-local" / "unknown"
URL string `json:"url,omitempty"` // GitHub release URL for cloudbox / cli-url paths
ReleaseID string `json:"release_id,omitempty"`
At time.Time `json:"at,omitzero"`
}
UpgradeSource describes where the currently-running binary came from. Derived by walking the ledger backwards from the most recent swap_done entry; nil when no swap has ever run on this host (the binary is whatever the operator manually installed).
type VKCredentialResult ¶
type VKCredentialResult struct {
OK bool `json:"ok"`
// Bundle is the opaque one-line value a worker passes to
// `outpost cluster join --vk-bundle`.
Bundle string `json:"bundle"`
// Namespaces is the allow-list embedded in the bundle — the namespace
// policy the worker will enforce fail-closed.
Namespaces []string `json:"namespaces"`
// Endpoint is the tunnel address a worker pairs the bundle with, so the
// caller can render the whole join line without a second round-trip.
Endpoint string `json:"endpoint,omitempty"`
}
VKCredentialResult carries the encoded bundle. It is a CREDENTIAL: returned only from this explicitly-named operation, never from a status read.
type YcodeShareSurfaceView ¶ added in v0.1.1
type YcodeShareSurfaceView struct {
}
YcodeShareSurfaceView is one row in the SPA's ycode-share toggle list — the catalog entry's metadata plus the effective on/off state (after applying per-surface overlay against catalog default).
type YcodeView ¶ added in v0.1.0
type YcodeView struct {
Enabled bool `json:"enabled"`
Running bool `json:"running"`
Installed bool `json:"installed"`
StaleManifest bool `json:"stale_manifest"`
PlatformSupported bool `json:"platform_supported"`
BinaryPath string `json:"binary_path,omitempty"`
APIEndpoint string `json:"api_endpoint,omitempty"`
Version string `json:"version,omitempty"`
DownloadURL string `json:"download_url"`
}
YcodeView is the redacted-and-flattened ycode status the admin UI / MCP API consume. Mirrors ycode.Info but flattens the State enum into named bools so the JS doesn't have to know the State vocabulary. Detection-only — outpost never spawns or restarts ycode itself.
Source Files
¶
- apps.go
- appstore.go
- backup.go
- builtin.go
- builtins.go
- bundle.go
- cluster.go
- cluster_join.go
- cluster_node_token.go
- cluster_vk_credential.go
- control_plane.go
- control_plane_status.go
- core.go
- ledger.go
- mesh.go
- mirror.go
- networking.go
- outbound.go
- pair.go
- peerimage.go
- peers.go
- shard.go
- ssh_targets.go
- suggestions.go
- upgrade.go
- validate.go
- view.go
- warm.go