agentd

package
v0.27.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	SecretsEnvPath  = "/sandbox-runtime/secrets-env"
	AgentConfigPath = "/sandbox-runtime/agent-config.json"
	AdminPromptPath = "/sandbox-runtime/admin-prompt.md"
	PasswordPath    = "/sandbox-cfg/password"
	SecretsBasePath = "/sandbox-runtime/rt/secrets"
	WorkspacePath   = "/workspace"
	// StagedFilesMaxBytes is the W8 file-class delivery budget: the
	// staged manifest + bytes MUST fit under it because it is also the
	// /v1/spawn-files response cap the supervisor enforces. Binding-time
	// per-secret validation (pkg/secrets) is the user-facing gate; the
	// materializer's per-entry and whole-batch checks are the
	// defense-in-depth ceiling (size_exceeded).
	StagedFilesMaxBytes = 8 << 20
	// AllowedDirsPath is where the bootstrap subcommand writes the instance's
	// allowedExternalDirectories setting (a JSON array of glob patterns). The
	// AgentConfigWriter reads it once at init and merges each pattern into
	// agent-config.json's mode.permissions.external_directory as an "allow"
	// rule, so agents stop prompting for /tmp/* on every session. Lives on
	// /sandbox-runtime tmpfs: survives container restart, wiped on pod
	// death, no plaintext-on-PVC concern (it's a list of public path
	// globs, not secrets).
	AllowedDirsPath = "/sandbox-runtime/allowed-dirs.json"
	// ModelResolutionWarningPath is where the materialize subcommand records
	// that the workspace's persisted default model could not be resolved to
	// any provider in agent-config.json (e.g. a relay-only model selected
	// while the relay injector failed at boot — incident 2026-08-16). The
	// model key is deliberately omitted from agent-config.json in that case
	// (a bare ID poisons opencode's model resolution: every prompt in every
	// session fails with ProviderModelNotFoundError until the pod rebuilds).
	// agentd's healthz/statusz read this marker so the controller can relay
	// the condition into the AgentHealthy condition message the user sees.
	// Same tmpfs lifecycle as agent-config.json; removed by the next
	// successful resolution.
	ModelResolutionWarningPath = "/sandbox-runtime/model-resolution-warning.json"
	// SidecarRestartMarkerPath is where the restart-reason marker lives
	// in sidecar mode (design 0051 US-2): the shared /sandbox-runtime
	// tmpfs. Writers straddle uids (sidecar 2000, supervisor 1000) with
	// the pod's shared group 1000 in common, so markers there are 0640.
	// The controller stamps it into LLMSAFESPACES_RESTART_MARKER_PATH on
	// BOTH containers; env unset keeps the single-container default.
	SidecarRestartMarkerPath = "/sandbox-runtime/last-restart-reason.json"
	// UploadsPath is the Epic 68 file-ingest root on the workspace PVC:
	// every PUT /v1/files upload lands here as <uuid>-<sanitized-name>
	// (atomic tmp+rename, design epic-68 D2/D3). Override via
	// LLMSAFESPACES_UPLOADS_PATH (tests, dev relocatability).
	UploadsPath = "/workspace/uploads"
)

Well-known paths shared between entrypoint scripts and agentd. Shell scripts reference these by convention — keep in sync.

US-35.7: credential output paths point to /sandbox-runtime (tmpfs, RAM-backed) so no plaintext persists on the PVC at rest. $HOME-relative paths (SSH, git, secrets) are symlinks created by the init container pointing into /sandbox-runtime/rt/*.

AdminPromptPath also lives on /sandbox-runtime — both because the merged platform/org/role/user system prompt is sensitive control-surface content (canary tokens, safety guardrails) that should die with the pod, and because the credential-setup init container that writes it has ReadOnlyRootFilesystem without a writable emptyDir at /tmp. Previously (#483) the constant pointed at /tmp/admin-prompt.md and the bootstrap subcommand silently failed every write — the admin prompt never reached opencode on any workspace, breaking the three-tier prompt chain end-to-end.

View Source
const (
	AgentPort       = 4096 // opencode serve listens here
	AgentdPort      = 4097 // agentd user-facing HTTP API (resync-secrets, future proxy)
	AgentdAddr      = "0.0.0.0:4097"
	AgentdAdminPort = 4098 // agentd admin HTTP API (healthz, readyz, statusz) — US-22.8
	AgentdAdminAddr = "0.0.0.0:4098"
	AuthUsername    = "opencode" // Basic Auth username for opencode
)

Ports and network constants shared between agentd and the controller.

View Source
const DeliveryCapability = "v2"

DeliveryCapability is this build's secret-delivery generation (US-70.5 fleet-version evidence): "v2" marks the conditional-pull stack — the v2 bootstrap envelope contract, revision anchoring, the resync endpoint, and spawn-rev terminal verification. Pre-US-70.2 runtimes omit the healthz delivery field entirely, so a fleet scrape can count pods still on the legacy stack (the W15 mixed-fleet gauge). It is a capability statement only — convergence is still judged from the revision signals (spawned_rev), never from this field.

View Source
const MaxFilenameBytes = 200

MaxFilenameBytes is the sanitized-filename byte cap applied by both upload layers (agentd PUT /v1/files authoritative, API defense-in-depth — design epic-68 D9). Truncation lands on a rune boundary.

Variables

This section is empty.

Functions

func IsForbiddenFilenameRune added in v0.24.0

func IsForbiddenFilenameRune(r rune) bool

IsForbiddenFilenameRune reports whether r is stripped during filename sanitization: quote characters, path separators (backslash arrives here only when residual after the separator split), bidi/RTL overrides, and control characters.

func SanitizeFilename added in v0.24.0

func SanitizeFilename(raw string) (string, bool)

SanitizeFilename flattens an untrusted filename to a safe base name. It is the single source shared by agentd's PUT /v1/files handler (authoritative) and the API's upload proxy (defense-in-depth), so the two layers cannot drift:

  1. basename — both path separators (/ and \) are split so Windows-shaped traversal cannot survive
  2. strip control characters (C0/C1 incl. \n \r \x1b), bidi/RTL overrides (U+202A-U+202E, U+2066-U+2069), quotes, backslashes, and any residual slash
  3. trim trailing dots and spaces (Windows-hostile suffixes)
  4. truncate to MaxFilenameBytes on a rune boundary, re-trim
  5. reject empty or whitespace-only results (ok=false)

Types

type CPUUsage

type CPUUsage struct {
	// UsageMicros is cumulative CPU microseconds consumed (cpu.stat usage_usec).
	// Monotonically increasing — callers compute delta between polls for rate.
	UsageMicros int64 `json:"usage_micros"`
	// LimitMicrosPerSec is the CPU quota in µs/s from cpu.max (quota/period×1e6).
	// 0 means no quota (unlimited).
	LimitMicrosPerSec int64 `json:"limit_micros_per_sec,omitempty"`
}

CPUUsage reports cumulative CPU consumption from cgroup v2 cpu.stat.

type ContextUsage

type ContextUsage struct {
	UsedTokens  int64 `json:"used_tokens"`
	TotalTokens int64 `json:"total_tokens"`
}

ContextUsage reports LLM context window usage across active sessions.

type DiskUsage

type DiskUsage struct {
	UsedBytes  int64 `json:"used_bytes"`
	TotalBytes int64 `json:"total_bytes"`
}

DiskUsage reports workspace filesystem usage.

type FileUploadResponse added in v0.23.0

type FileUploadResponse struct {
	Path string `json:"path"`
	Name string `json:"name"`
	Size int64  `json:"size"`
}

FileUploadResponse is the response for PUT /v1/files (Epic 68 US-68.1). Path is the absolute final location (/workspace/uploads/<uuid>-<name> in production); Name is the sanitized filename; Size the byte count on disk. Error responses never echo .tmp or internal paths (design U1.1.20).

type HealthzResponse

type HealthzResponse struct {
	Healthy       bool   `json:"healthy"`
	Version       string `json:"version"`
	UptimeSeconds int    `json:"uptime_seconds"`
	// Delivery is the build's delivery capability marker
	// (DeliveryCapability; absent on pre-US-70.2 runtimes).
	Delivery string `json:"delivery,omitempty"`
	// CommitSHA is the git commit this agentd binary was built from
	// (pkg/version, -ldflags injected; buildinfo vcs fallback). Surfaced
	// so a deployed agentd can be traced to source without binary
	// forensics — a devel hash build reporting a clean release Version
	// (incident 2026-08-15) must be distinguishable from the real tag.
	// "unknown" means neither ldflags nor vcs buildinfo could supply it.
	CommitSHA string `json:"commit_sha,omitempty"`
	// BuildTime is the UTC build timestamp (pkg/version). Same purpose
	// as CommitSHA; "unknown" when not stamped via ldflags (unstamped
	// builds never emit an empty string, so the omitempty tags on both
	// fields are inert by design — kept for forward-compatibility if the
	// defaults ever change).
	BuildTime string `json:"build_time,omitempty"`
	// Warnings are boot-time degradation notices (e.g. the persisted
	// default model could not be resolved — ModelResolutionWarningPath).
	// Observability only: never affects Healthy, never gates liveness.
	// The controller relays them into the AgentHealthy condition message
	// so users see why their selected model is not in use.
	Warnings []string `json:"warnings,omitempty"`
	// SpawnEnv (US-70.1, design 0057) is the supervisor's
	// terminal-verified spawn-env state: the revision of the secrets
	// delta the agent process actually spawned with, plus a
	// machine-readable degrade reason when delivery is incomplete.
	// Nil when the reporter has no supervisor evidence yet
	// (single-container mode, or before the first status poll) — no
	// evidence is not a degrade. Observability only, like Warnings.
	SpawnEnv *SpawnEnvHealth `json:"spawnEnv,omitempty"`
}

HealthzResponse is the response for GET /v1/healthz.

type MemoryUsage

type MemoryUsage struct {
	UsedBytes  int64 `json:"used_bytes"`
	TotalBytes int64 `json:"total_bytes"`
}

MemoryUsage reports workspace memory usage.

type ReadyzResponse

type ReadyzResponse struct {
	Ready               bool     `json:"ready"`
	ProvidersConnected  []string `json:"providers_connected"`
	ProvidersConfigured int      `json:"providers_configured"`
	AgentVersion        string   `json:"agent_version"`
	AgentType           string   `json:"agent_type"`
	// RelayInjected is true when the relay injector successfully completed
	// (wrote relay config and restarted opencode). False before the injector
	// has run, if it was skipped (personal opencode key), or if it failed.
	// Included here (readyz) rather than statusz because: relay injection is
	// a one-time boot event with the same semantics as pod readiness, readyz
	// is cache-based and lightweight (no synchronous opencode calls), and the
	// API server needs this flag on every ListModels cache miss — using statusz
	// (which has no latency upper bound) would be unsafe.
	RelayInjected bool `json:"relay_injected"`
}

ReadyzResponse is the response for GET /v1/readyz.

type SessionInfo

type SessionInfo struct {
	ID          string         `json:"id"`
	Title       string         `json:"title,omitempty"`
	Status      string         `json:"status"` // "idle" | "busy"
	Tokens      *SessionTokens `json:"tokens,omitempty"`
	Model       string         `json:"model,omitempty"` // model ID, e.g. "claude-sonnet-4-20250514"
	ContextUsed int64          `json:"contextUsed"`
}

SessionInfo describes a single opencode session.

type SessionTokens

type SessionTokens struct {
	Input      int64 `json:"input"`
	Output     int64 `json:"output"`
	Reasoning  int64 `json:"reasoning"`
	CacheRead  int64 `json:"cache_read"`
	CacheWrite int64 `json:"cache_write"`
}

SessionTokens describes token usage for a session.

type SpawnEnvHealth added in v0.26.0

type SpawnEnvHealth struct {
	SpawnedRev string `json:"spawnedRev,omitempty"`
	Degraded   bool   `json:"degraded"`
	Reason     string `json:"reason,omitempty"`
	// FilesRev / FilesDegraded / FilesReason (R2b, #1165): the file-class
	// delivery slice — the terminal revision over the files the uid-1000
	// supervisor actually wrote, and its machine-readable degrade reason
	// ("" healthy).
	FilesRev      string `json:"filesRev,omitempty"`
	FilesDegraded bool   `json:"filesDegraded,omitempty"`
	FilesReason   string `json:"filesReason,omitempty"`
}

SpawnEnvHealth is the spawn-env delivery slice of HealthzResponse. Degraded=true carries a non-empty Reason from the closed reason set (spawn_env_unavailable, spawn_env_unauthorized, spawn_env_no_credential, spawn_env_bad_response); the controller mirrors it into the Workspace CRD's secretsDelivery status.

type StatuszResponse

type StatuszResponse struct {
	Healthy             bool          `json:"healthy"`
	Ready               bool          `json:"ready"`
	Connected           []string      `json:"connected"`
	ProvidersConfigured int           `json:"providers_configured"`
	Sessions            []SessionInfo `json:"sessions"`
	SessionsActive      int           `json:"sessions_active"`
	SessionsError       int           `json:"sessions_error"`
	LastError           string        `json:"last_error"`
	AgentType           string        `json:"agent_type"`
	AgentVersion        string        `json:"agent_version"`
	UptimeSeconds       int           `json:"uptime_seconds"`
	Disk                *DiskUsage    `json:"disk,omitempty"`
	Memory              *MemoryUsage  `json:"memory,omitempty"`
	CPU                 *CPUUsage     `json:"cpu,omitempty"`
	// RelayFreeModels: 0 unknown, 1 ok, 2 degraded (injector deadline
	// exhausted — free-tier routing unavailable until the next agent
	// restart; #901 G8).
	RelayFreeModels int32 `json:"relay_free_models"`
	// InFlightDeliveries: the ledger's unresolved delivery count pod-wide
	// (ledgered + admitted + stalled — the flip gate's drain signal,
	// design 0055 M4; 0 when the authority is not mounted).
	InFlightDeliveries int64 `json:"ledger_in_flight"`
	// OldestBusySeconds: age of the longest-busy session, 0 when idle
	// (D6/#998: unattended-escalation detection input — a session busy
	// this long with sustained watchdog flat-suppressions is likely
	// hung-and-alive; the API escalates to the owner, never executes).
	OldestBusySeconds int            `json:"oldest_busy_seconds"`
	BusyAges          map[string]int `json:"busy_ages,omitempty"`
	Context           *ContextUsage  `json:"context,omitempty"`
	// MemoryPressure is true when memory usage exceeds the warning
	// threshold (85% of cgroup limit). Set by agentd's periodic memory
	// check (US-44.5). The controller reads this to set the
	// WorkspaceConditionMemoryPressure condition.
	MemoryPressure bool `json:"memory_pressure,omitempty"`
	// Warnings mirrors HealthzResponse.Warnings: boot-time degradation
	// notices (ModelResolutionWarningPath). The deep-status scrape feeds
	// the controller's AgentHealthy condition message, so the warning
	// must appear here too or it would be erased between scrapes.
	Warnings []string `json:"warnings,omitempty"`
	// SpawnedRev (US-70.1 / design 0057 I4): the env revision the agent
	// child ACTUALLY spawned with — terminal verification at the point
	// of consumption, relayed from the supervisor's control socket.
	SpawnedRev string `json:"spawned_rev,omitempty"`
	// Degraded (US-70.1 / design 0057 I10): active machine-readable
	// degrade reason ("" healthy). First value of the family:
	// spawn_env_unavailable — the child spawned platform-env-only from
	// the last-good cache because the pull missed its bound.
	Degraded string `json:"degraded,omitempty"`
}

StatuszResponse is the response for GET /v1/statusz.

Directories

Path Synopsis
Package secrets materializes user-supplied secrets onto the sandbox pod filesystem with strict validation and TOCTOU-safe permissions.
Package secrets materializes user-supplied secrets onto the sandbox pod filesystem with strict validation and TOCTOU-safe permissions.

Jump to

Keyboard shortcuts

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