workspace

package
v0.27.4 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: AGPL-3.0 Imports: 42 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LabelApp       = "app"
	LabelComponent = "component"
	LabelWorkspace = "llmsafespaces.dev/workspace"
	LabelRuntime   = "runtime"
	LabelTenant    = "llmsafespaces.dev/tenant"

	AppName            = "llmsafespaces"
	ComponentWorkspace = "workspace"
)

Labels applied to workspace pods.

View Source
const (

	// AgentdPinsConfigMapName caches the last successfully resolved pins
	// per digest so a controller restart during a registry outage does
	// not brick startup.
	AgentdPinsConfigMapName = "llmsafespaces-agentd-pins"
)
View Source
const (

	// OpencodePinsConfigMapName caches the last successfully resolved
	// opencode pins per digest so a controller restart during a registry
	// outage does not brick startup.
	OpencodePinsConfigMapName = "llmsafespaces-opencode-pins"
)
View Source
const (

	// SidecarRestartMarkerEnv wires LLMSAFESPACES_RESTART_MARKER_PATH on
	// both containers so the sidecar's and the supervisor's marker writes
	// land on one cross-uid-readable file (0640 + shared gid 1000).
	SidecarRestartMarkerEnv = agentd.SidecarRestartMarkerPath
)
View Source
const WorkspaceFinalizer = "workspace.llmsafespaces.dev/finalizer"

Variables

View Source
var ErrAgentdPinsUnavailable = errors.New("agentd pins unavailable (registry unreachable and no usable cache)")

ErrAgentdPinsUnavailable wraps every agentd resolution failure where the registry was unreachable AND no usable cache existed. Callers can errors.Is this to offer the manual-pin hint.

View Source
var ErrOpencodePinsUnavailable = errors.New("opencode pins unavailable (registry unreachable and no usable cache)")

ErrOpencodePinsUnavailable wraps every opencode resolution failure where the registry was unreachable AND no usable cache existed. Callers can errors.Is this to offer the manual-pin hint.

Functions

func ValidateAgentdDelivery added in v0.15.8

func ValidateAgentdDelivery(image, amd64, arm64 string) error

ValidateAgentdDelivery is the exported startup guard used by controller main. Wraps validateAgentdDeliveryConfig.

func ValidateAgentdSidecar added in v0.18.0

func ValidateAgentdSidecar(sidecarEnabled bool, agentdImage string) error

ValidateAgentdSidecar is the exported startup guard used by controller main. Wraps validateAgentdSidecarConfig.

func ValidateOpencodeDelivery added in v0.25.5

func ValidateOpencodeDelivery(image, amd64, arm64 string) error

ValidateOpencodeDelivery is the exported startup guard used by controller main. Wraps validateOpencodeDeliveryConfig.

Types

type AgentdDeliveryConfig added in v0.15.8

type AgentdDeliveryConfig struct {
	Image             string
	BinarySHA256AMD64 string
	BinarySHA256ARM64 string
}

AgentdDeliveryConfig is the #863 overlay-delivery configuration passed from controller main (flags) to the reconciler. Zero value = legacy baked-in mode.

type BinaryPins added in v0.25.5

type BinaryPins struct {
	SHA256AMD64 string
	SHA256ARM64 string
}

BinaryPins are the resolved per-arch binary sha256s of one overlay artifact.

func ResolveOpencodePinsWithCache added in v0.25.5

func ResolveOpencodePinsWithCache(ctx context.Context, image, flagAMD64, flagARM64 string) (BinaryPins, error)

ResolveOpencodePinsWithCache is the opencode startup entrypoint used by controller main. validateOpencodeDeliveryConfig has already run.

func ResolvePinsWithCache added in v0.15.10

func ResolvePinsWithCache(ctx context.Context, image, flagAMD64, flagARM64 string) (BinaryPins, error)

ResolvePinsWithCache is the agentd startup entrypoint used by controller main. validateAgentdDeliveryConfig has already run.

type CachedOrgStatusClient

type CachedOrgStatusClient struct {
	// contains filtered or unexported fields
}

CachedOrgStatusClient is an OrgStatusClient backed by the API service's internal org-status endpoint with a per-org TTL cache.

Cache semantics (D20):

  • On a fresh cache hit (< TTL): return the cached status, no fetch.
  • On a stale entry (>= TTL) or cache miss: attempt a fetch.
  • Fetch success: update the cache and return the new status.
  • Fetch failure + prior entry: serve the STALE cached status so a transient API outage does not flip the controller into fail-open and leave suspended orgs running. This is the "30s cache absorbs transient failures" guarantee.
  • Fetch failure + no entry: return ok=false (fail open — D20: an unwarranted suspension is more disruptive than leaving a pod running).

Concurrency: each orgID has its own entry guarded by a mutex, so concurrent refreshes for the SAME org are deduplicated (only one in-flight fetch per org) while different orgs refresh in parallel. The mutex is held for the duration of the fetch, bounding concurrent same-org fetches to one.

func NewCachedOrgStatusClient

func NewCachedOrgStatusClient(baseURL, token string, ttl time.Duration, logger OrgStatusLogger) *CachedOrgStatusClient

NewCachedOrgStatusClient constructs the client. baseURL is the API service root (e.g. http://llmsafespaces-api.llmsafespaces.svc:8080); leave empty to disable org-suspension (the reconciler then never org-suspends). token, when non-empty, is sent as X-Internal-Token. ttl is the cache freshness window (production: 30s). logger may be nil.

func (*CachedOrgStatusClient) GetOrgStatus

func (c *CachedOrgStatusClient) GetOrgStatus(ctx context.Context, orgID string) (string, bool)

GetOrgStatus implements OrgStatusClient. See CachedOrgStatusClient docs for the cache + fail-open contract.

type FailureClass

type FailureClass string
const (
	FailureClassNone           FailureClass = ""
	FailureClassInfrastructure FailureClass = "Infrastructure"
	FailureClassResource       FailureClass = "Resource"
	FailureClassProcess        FailureClass = "Process"
	FailureClassConfiguration  FailureClass = "Configuration"
)

type HostResolver

type HostResolver interface {
	LookupHost(ctx context.Context, host string) ([]string, error)
}

HostResolver resolves a hostname to a list of IP strings. Tests inject a stub via WorkspaceReconciler.HostResolver to avoid hitting real DNS; production uses net.DefaultResolver via defaultHostResolver.

type OpencodeDeliveryConfig added in v0.25.5

type OpencodeDeliveryConfig struct {
	Image             string
	BinarySHA256AMD64 string
	BinarySHA256ARM64 string
}

OpencodeDeliveryConfig is the design 0053 §4.2 overlay-delivery configuration passed from controller main (flags) to the reconciler. Zero value = opencode stays baked into runtimes/base (S1 default).

type OrgStatusClient

type OrgStatusClient interface {
	GetOrgStatus(ctx context.Context, orgID string) (status string, ok bool)
}

OrgStatusClient reports an org's operational status. ok is false when the status could not be determined (API unreachable and no cached value); callers MUST fail open in that case (D20). ok is true whenever a value — fresh or stale — is available.

The interface lets tests inject a fake without an HTTP server.

type OrgStatusLogger

type OrgStatusLogger interface {
	Info(msg string, keysAndValues ...any)
	Error(err error, msg string, keysAndValues ...any)
}

OrgStatusLogger is the minimal logging surface CachedOrgStatusClient uses. controller-runtime's logr.Logger satisfies it; nil disables logging.

type PodObservation

type PodObservation struct {
	Exists             bool
	Phase              corev1.PodPhase
	Reason             string
	Message            string
	ContainerReason    string
	ContainerExitCode  int32
	ContainerOOMKilled bool
	CrashLoop          bool
	Unschedulable      bool
	Scheduled          bool
}

type RecoveryPolicy

type RecoveryPolicy struct {
	MaxAttempts    int
	BackoffBase    time.Duration
	BackoffMax     time.Duration
	BackoffFactor  int
	StabilityReset time.Duration
	SafeModeAfter  int32
}

type WorkspaceReconciler

type WorkspaceReconciler struct {
	client.Client
	Scheme *runtime.Scheme
	// HostResolver is used by the per-workspace NetworkPolicy generator
	// (network_policy.go) to resolve declared FQDNs to /32 ipBlocks at
	// reconcile time. Tests inject a stub; production uses
	// defaultHostResolver (net.DefaultResolver) when nil.
	HostResolver HostResolver

	// InferenceRelayURL is the self-hosted relay URL for free-tier inference
	// (Epic 42 InferenceRelay fleet). When set, workspace pods route opencode
	// free-tier provider requests through this URL for IP distribution. When
	// empty (the chart default), opencode uses its default gateway
	// (opencode.ai/zen/v1) directly with the built-in `public` key.
	InferenceRelayURL string

	// OrgStatusClient, when non-nil AND the workspace belongs to an org
	// (Spec.Owner.OrgID != ""), is consulted on every Active reconcile to drive
	// D20 org-level suspension: if the org is suspended, the workspace
	// transitions Active → Suspending (pod killed, PVC retained). The client is
	// nil when --api-service-url is unset, disabling the feature. Lookups are
	// cached (30s TTL); a lookup failure fails open (workspace keeps running).
	OrgStatusClient OrgStatusClient

	// DefaultRuntimeClass is the container runtime class applied to all
	// workspace pods (Epic 51 S51.1). Typically "gvisor" for production
	// multi-tenant deployments to provide kernel-level isolation against
	// container escape. Empty means use the default (runc). Set via
	// --default-runtime-class controller flag, sourced from Helm
	// .Values.gvisor.defaultRuntimeClass (only set when .Values.gvisor.enabled
	// is true). Individual workspaces can override via spec.runtimeClass for
	// compatibility opt-out (admin-gated).
	DefaultRuntimeClass string

	// APIServiceURL is the in-cluster URL of the API service, used by the
	// workspace init container's bootstrap subcommand (Epic 35 US-35.4) to
	// fetch decrypted credentials via POST /internal/v1/pod-bootstrap. Same
	// value as --api-service-url (also used for OrgStatusClient). When empty,
	// the bootstrap subcommand degrades gracefully (empty secrets, pod boots
	// without credentials; the notify-pull path handles delivery).
	APIServiceURL string

	// PreviewOriginBaseDomain (Epic 66 Phase 1): when non-empty, workspace
	// pods receive PREVIEW_ORIGIN_BASE_DOMAIN so agentd's dev_preview_url
	// MCP tool emits the per-workspace-origin bootstrap URL instead of the
	// path-based URL. Sourced from the same previewOrigin.baseDomain chart
	// value the API consumes — one flag enables the deployment-wide mode.
	// Empty = path-based dev preview (unchanged behavior).
	PreviewOriginBaseDomain string

	// #863 agentd overlay delivery. When AgentdImage is set, buildPod pins a
	// digest-addressed image volume into every workspace pod and the
	// entrypoint verifies the binary's sha256 against the per-arch pins
	// before exec. All three fields must be set together (validated at
	// startup by validateAgentdDeliveryConfig). Empty AgentdImage = legacy
	// mode (binary baked into runtimes/base; no volume, mount, or env).
	AgentdImage             string
	AgentdBinarySHA256AMD64 string
	AgentdBinarySHA256ARM64 string

	// Design 0053 §4.2 opencode overlay delivery. When OpencodeImage is
	// set, buildPod pins a digest-addressed image volume into every
	// workspace pod (RO mount at /opencode on the workspace container
	// only) and the supervisor verifies the binary's sha256 against the
	// per-arch pins before spawn. All three fields must be set together
	// (validated at startup by validateOpencodeDeliveryConfig). Empty
	// OpencodeImage = baked-in mode (opencode stays in runtimes/base; S1
	// default — the feature is opt-in and inert until the base strip).
	OpencodeImage             string
	OpencodeBinarySHA256AMD64 string
	OpencodeBinarySHA256ARM64 string

	// AgentdSidecarEnabled (design 0051 US-2, chart-gated, default
	// false): when true, buildPod appends the native agentd sidecar and
	// switches the main container to supervise-opencode mode. Requires
	// AgentdImage (validated at startup) — the sidecar runs the same
	// digest-pinned delivery artifact.
	AgentdSidecarEnabled bool

	// Recorder emits Kubernetes events on the Workspace (agentd verify
	// failures). Injected from mgr.GetEventRecorderFor() in production;
	// a FakeRecorder in tests.
	Recorder record.EventRecorder

	// MaxConcurrentReconciles: how many DIFFERENT workspaces reconcile in
	// parallel (controller-runtime never runs the same object concurrently,
	// so per-workspace state stays single-writer). 0/negative = framework
	// default (1 = fully serial). The default serial behavior made fleet
	// resume linear (~8s per workspace — 40 workspaces ≈ 5+ minutes, run
	// 33811397260); main.go clamps this to 1..64 to bound apiserver load.
	MaxConcurrentReconciles int
	// contains filtered or unexported fields
}

func (*WorkspaceReconciler) Reconcile

func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

func (*WorkspaceReconciler) SetupWithManager

func (r *WorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error

Jump to

Keyboard shortcuts

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