Documentation
¶
Overview ¶
Package daemon owns the durable local lifecycle record for the WB daemon.
The record deliberately contains scheduler ownership, rather than command output, so a later ConnectRPC/gRPC transport and the MCP adapter can use the same queue handoff contract without reading a dashboard-only file.
Package daemon owns WB's durable local operation queue.
Index ¶
- Constants
- func LegacyRuntimeDir(projectsRoot string) string
- func LegacyStatePath(projectsRoot string) string
- func LoadRawExecutionPolicy(path, projectsRoot string) (bool, error)
- func OperationsDir(projectsRoot string) (string, error)
- func ParseProcStatBootTime(contents string) (time.Time, bool)
- func ParseProcStatStartTicks(contents string) (uint64, bool)
- func ProcessStartFromProcStat(ticks uint64, bootTime time.Time) time.Time
- func ProcessStartTime(pid int) (time.Time, bool)
- func RawExecutionPolicyPath() (string, error)
- func RequireRawExecutionPolicy(path, projectsRoot string) error
- func RuntimeDir(projectsRoot string) (string, error)
- func SocketPath(projectsRoot string) (string, error)
- func StatePath(projectsRoot string) (string, error)
- func SupportedStateSchemaVersions() []int
- type Provenance
- type Queue
- type Service
- func (service *Service) CancelOperation(_ context.Context, request *connect.Request[daemonv1.CancelOperationRequest]) (*connect.Response[daemonv1.Operation], error)
- func (service *Service) CompleteOperation(_ context.Context, request *connect.Request[daemonv1.CompleteOperationRequest]) (*connect.Response[daemonv1.CompleteOperationResponse], error)
- func (service *Service) DisconnectWorker(_ context.Context, request *connect.Request[daemonv1.DisconnectWorkerRequest]) (*connect.Response[daemonv1.DisconnectWorkerResponse], error)
- func (service *Service) GetDaemonInfo(_ context.Context, _ *connect.Request[daemonv1.GetDaemonInfoRequest]) (*connect.Response[daemonv1.GetDaemonInfoResponse], error)
- func (service *Service) GetOperation(_ context.Context, request *connect.Request[daemonv1.GetOperationRequest]) (*connect.Response[daemonv1.Operation], error)
- func (service *Service) HeartbeatOperation(_ context.Context, ...) (*connect.Response[daemonv1.HeartbeatOperationResponse], error)
- func (service *Service) LeaseOperation(ctx context.Context, request *connect.Request[daemonv1.LeaseOperationRequest]) (*connect.Response[daemonv1.LeaseOperationResponse], error)
- func (service *Service) RegisterWorker(_ context.Context, request *connect.Request[daemonv1.RegisterWorkerRequest]) (*connect.Response[daemonv1.RegisterWorkerResponse], error)
- func (service *Service) StartLeaseRecovery(ctx context.Context)
- func (service *Service) SubmitOperation(_ context.Context, request *connect.Request[daemonv1.SubmitOperationRequest]) (*connect.Response[daemonv1.Operation], error)
- func (service *Service) WaitOperation(ctx context.Context, request *connect.Request[daemonv1.WaitOperationRequest]) (*connect.Response[daemonv1.Operation], error)
- type State
- func (s State) IsForeignHome(currentHome string) (foreign bool, known bool)
- func (s *State) MarkDraining(now time.Time)
- func (s *State) MarkReady(pid int, now time.Time)
- func (s *State) MarkReadyWithProcess(pid int, processStartedAt time.Time, now time.Time)
- func (s *State) MarkStartingPID(pid int, now time.Time)
- func (s *State) MarkStopped(now time.Time)
- func (s *State) MarkStoppedWithReason(reason string, now time.Time)
- func (s State) ProcessGenerationMatches(observedStart time.Time, observed bool) (match bool, known bool)
- func (s State) Valid() error
- type Status
- type Store
Constants ¶
const ( // StateSchemaVersion is the version this build writes. Load accepts every // version in SupportedStateSchemaVersions: a running daemon's record must // stay readable across an upgrade, and refusing an older record would make // the daemon that wrote it look absent. StateSchemaVersion = 2 MinSupportedStateSchema = 1 QueueSchemaVersion = 1 )
const ( ProtocolVersion = 1 QueueSchema = 1 )
const RawExecutionPolicyVersion = 1
const RuntimeDirName = "runtime"
RuntimeDirName is the directory inside WB's home that holds daemon runtime state: the local socket, the lifecycle record, and the daemon's log.
const SocketFileName = "daemon.sock"
SocketFileName is the local endpoint's name inside the runtime directory.
const StateFileName = "daemon-state.json"
StateFileName is the durable lifecycle record inside the runtime directory.
Variables ¶
This section is empty.
Functions ¶
func LegacyRuntimeDir ¶ added in v0.134.0
LegacyRuntimeDirName reproduces the runtime directory WB used before the daemon consulted the home resolver: a literal ".wb" beneath the projects root. It exists so a daemon left there can be *detected* and reported rather than silently doubled. Nothing in this package writes to it.
It is spelled out here, rather than reusing wbhome, precisely because it is the shape that must not be produced again: wbhome resolves a home, and this is a fixed historical path.
func LegacyStatePath ¶ added in v0.134.0
LegacyStatePath is the lifecycle record a daemon wrote before the runtime directory followed WB's home. It exists for detection only; nothing here reads it as this build's own state.
func LoadRawExecutionPolicy ¶ added in v0.105.0
LoadRawExecutionPolicy enables raw subprocesses only through a protected, explicit administrator opt-in outside the agent-writable projects tree.
func OperationsDir ¶ added in v0.134.0
OperationsDir is the daemon's durable operation store.
It is derived from the same home as every other runtime artefact, and it is passed to NewService explicitly rather than resolved inside it: a constructor that reads the environment makes every caller share one store, which is both untestable in isolation and wrong for a library.
func ParseProcStatBootTime ¶ added in v0.134.0
ParseProcStatBootTime extracts the btime (boot time, in Unix seconds) from the contents of /proc/stat.
func ParseProcStatStartTicks ¶ added in v0.134.0
ParseProcStatStartTicks extracts the process start time from the contents of a Linux /proc/<pid>/stat file, in clock ticks since boot.
The comm field is wrapped in parentheses and may itself contain spaces and parentheses, so the fields are counted from the last ')' rather than by splitting the whole line: a process named "my (odd) name" must not shift every field after it. Start time is field 22 of the record, which is the twentieth field after the state field that follows comm.
func ProcessStartFromProcStat ¶ added in v0.134.0
ProcessStartFromProcStat converts a /proc start-tick count and the system boot time into an absolute start time.
func ProcessStartTime ¶ added in v0.134.0
ProcessStartTime observes when pid started, so a recorded PID can be checked against the process now holding it. It reports false when the platform cannot answer, which callers must surface as unknown rather than as a match.
func RawExecutionPolicyPath ¶ added in v0.105.0
func RequireRawExecutionPolicy ¶ added in v0.105.0
RequireRawExecutionPolicy re-reads and validates the protected host policy. Callers must invoke it at every execution boundary so revocation is immediate.
func RuntimeDir ¶ added in v0.134.0
RuntimeDir resolves the daemon's runtime directory through WB's one home resolver, so a WB_HOME move moves the daemon with every other subsystem.
This is the whole point of the function: the daemon previously built its runtime path by joining the projects root with a literal ".wb", which meant that when WB_HOME moved — including the symlink-then-revert migration of 2026-09-15 — every subsystem followed and the daemon stayed behind, still serving a socket inside a directory the rest of WB had abandoned.
func SocketPath ¶ added in v0.134.0
SocketPath is the home-derived local endpoint of the daemon.
The path is also an identity: unlike the shared loopback port it names the one home whose daemon may own it, which is why status reports it rather than inferring ownership from whoever answers on the port.
func SupportedStateSchemaVersions ¶ added in v0.134.0
func SupportedStateSchemaVersions() []int
SupportedStateSchemaVersions lists the record versions this build can read.
Types ¶
type Provenance ¶
type Provenance struct {
Executable string `json:"executable"`
SHA256 string `json:"sha256"`
Version string `json:"version"`
Revision string `json:"revision,omitempty"`
Built string `json:"built,omitempty"`
}
Provenance identifies the exact executable trusted to own a daemon generation. SHA256 is intentionally included even when the released version is known: a development binary can otherwise look identical to a release.
func ProvenanceForExecutable ¶
func ProvenanceForExecutable(executable, version, revision, built string) (Provenance, error)
ProvenanceForExecutable produces exact local evidence for a running binary.
func (Provenance) SameBinary ¶
func (p Provenance) SameBinary(other Provenance) bool
type Queue ¶
type Queue struct {
SchemaVersion int `json:"schema_version"`
Generation uint64 `json:"generation"`
Owner Provenance `json:"owner"`
OwnerToken string `json:"owner_token"`
HandoffFrom *Provenance `json:"handoff_from,omitempty"`
HandoffAt *time.Time `json:"handoff_at,omitempty"`
}
Queue describes durable queue ownership. Operations are added by the async scheduler lane; lifecycle code preserves this object byte-for-byte apart from a fenced owner/generation transition.
type Service ¶ added in v0.105.0
type Service struct {
// contains filtered or unexported fields
}
Service implements the generated ConnectRPC service and persists every state transition before it is returned to the caller.
func NewService ¶ added in v0.105.0
func NewService(projectsRoot, operationsDirectory, build, generation string, authorizeRaw func() error) (*Service, error)
NewService opens the durable operation store at operationsDirectory for the projects root the daemon serves.
The store's location is an argument rather than something NewService resolves from the environment: the daemon's runtime directory follows WB's home (see RuntimeDir and OperationsDir), and a constructor that looked that up itself would silently put every caller — including every test in this package — on one shared store.
func (*Service) CancelOperation ¶ added in v0.105.0
func (*Service) CompleteOperation ¶ added in v0.106.0
func (*Service) DisconnectWorker ¶ added in v0.106.0
func (*Service) GetDaemonInfo ¶ added in v0.105.0
func (*Service) GetOperation ¶ added in v0.105.0
func (*Service) HeartbeatOperation ¶ added in v0.106.0
func (*Service) LeaseOperation ¶ added in v0.106.0
func (*Service) RegisterWorker ¶ added in v0.106.0
func (*Service) StartLeaseRecovery ¶ added in v0.106.0
StartLeaseRecovery watches running worker leases even when no client is polling an operation. The daemon owns only this recovery clock; execution remains in the connected worker process.
func (*Service) SubmitOperation ¶ added in v0.105.0
type State ¶
type State struct {
SchemaVersion int `json:"schema_version"`
Status Status `json:"status"`
PID int `json:"pid,omitempty"`
OwnerToken string `json:"owner_token,omitempty"`
Listen string `json:"listen"`
Provenance Provenance `json:"provenance"`
Queue Queue `json:"queue"`
StartedAt time.Time `json:"started_at,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
// WBHome and StatePath record *where* this daemon lives, so a reader can
// tell "the daemon belonging to my home" from "a daemon answering on my
// endpoint". They are empty in records written before schema 2, which is
// reported as unknown home identity rather than assumed to match.
WBHome string `json:"wb_home,omitempty"`
StatePath string `json:"state_path,omitempty"`
// ProcessStartedAt is the recorded process's start time. PID alone is a
// liveness coordinate that a recycled number can satisfy; the start time is
// what makes "still running" a claim about *this* process. Zero means the
// platform could not observe it, which is reported as unknown rather than
// treated as a match.
ProcessStartedAt time.Time `json:"process_started_at,omitempty"`
// StoppedReason explains a stop the daemon did not choose — a runtime
// directory removed underneath it, or an endpoint it could not bind. It is
// recorded so the condition survives the process that hit it: a supervisor
// restarts the daemon, and without this the reason would exist only in a
// log the same removal may have unlinked.
StoppedReason string `json:"stopped_reason,omitempty"`
}
State is private local state. It is never served by the dashboard API.
func NewStarting ¶
func NewStarting(previous *State, listen string, provenance Provenance, ownerToken string, now time.Time) State
NewStarting creates the next fenced queue generation. Existing queue jobs stay in the durable queue file owned by the scheduler; this record tells the replacement scheduler exactly which binary owned the preceding generation.
func NewStartingAt ¶ added in v0.134.0
func NewStartingAt(previous *State, listen string, provenance Provenance, ownerToken, wbHome, statePath string, now time.Time) State
NewStartingAt is NewStarting with the daemon's own location recorded, so the generation it opens names the home it belongs to.
func (State) IsForeignHome ¶ added in v0.134.0
IsForeignHome reports whether a record was written for a different WB home than the one this invocation resolves.
An empty recorded home means the record predates home identity. That is reported as unknown rather than as a match: claiming a record is ours because it failed to say otherwise is exactly how a daemon from an abandoned home gets presented as this machine's daemon.
func (*State) MarkDraining ¶
func (*State) MarkReadyWithProcess ¶ added in v0.134.0
MarkReadyWithProcess records the process generation that is now serving. A zero processStartedAt records that the platform could not observe one.
func (*State) MarkStartingPID ¶ added in v0.127.1
func (*State) MarkStopped ¶
func (*State) MarkStoppedWithReason ¶ added in v0.134.0
MarkStoppedWithReason records a stop the daemon did not choose, keeping the reason readable after the process is gone.
func (State) ProcessGenerationMatches ¶ added in v0.134.0
func (s State) ProcessGenerationMatches(observedStart time.Time, observed bool) (match bool, known bool)
ProcessGenerationMatches reports whether the process now holding the recorded PID is the generation this record was written for.
The second result is whether the comparison was possible at all. A record with no recorded start time, or a platform that cannot observe one, yields known=false and leaves the caller to fall back to PID liveness and to say that it did. A recycled PID that *is* observable and differs is reported as a mismatch, because a confident "still running" about someone else's process is worse than an admitted unknown.