launcher

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package launcher manages the optional OS-backed worker launcher.

Index

Constants

View Source
const LabelWorker = "sh.semantica.worker"

LabelWorker is the worker service label, used as the launchd plist label, the systemd unit base name, and the Task Scheduler task name. Same identifier across all backends so diagnostics stay consistent.

Variables

View Source
var ErrUnsupportedOS = errors.New("launcher: unsupported OS")

ErrUnsupportedOS reports that the launcher has no backend on the current OS.

Functions

func Delete

func Delete(path string) error

Delete removes a marker file. Missing files are ignored.

func EnsureFreshBinary added in v0.6.0

func EnsureFreshBinary(ctx context.Context) (bool, error)

EnsureFreshBinary checks the registered binary's identity and refreshes the launcher when it is stale. Returns whether a refresh ran. The dispatch path calls this so a binary replacement self-heals on the next commit.

func IsEnabled

func IsEnabled() bool

IsEnabled reports whether the launcher is enabled. Read errors fall back to false so hook-side callers do not break commits.

func Kickstart

func Kickstart(ctx context.Context, target string) error

Kickstart triggers an on-demand activation of the worker unit. The target argument is the systemd unit name; honors the caller-supplied target rather than deriving its own so callers retain control over which unit is started.

func List

func List(repoRoot string) ([]string, error)

List returns committed marker paths in lexical order. A missing directory is treated as empty.

func MarkerPath

func MarkerPath(repoRoot, checkpointID string) string

MarkerPath returns the canonical marker path.

func PendingDir

func PendingDir(repoRoot string) string

PendingDir returns <repoRoot>/.semantica/pending.

func SettingsPath

func SettingsPath() (string, error)

SettingsPath returns the launcher settings path. It honors SEMANTICA_HOME via broker.GlobalBase.

func TimerPath added in v0.6.0

func TimerPath() (string, error)

TimerPath returns the systemd user timer path for the periodic worker drain.

func TimerTarget added in v0.6.0

func TimerTarget() string

TimerTarget returns the systemd timer unit name.

func UnitPath added in v0.3.7

func UnitPath() (string, error)

UnitPath returns the systemd user unit path for the worker. Honors XDG_CONFIG_HOME via os.UserConfigDir; falls back to $HOME/.config when XDG_CONFIG_HOME is unset.

func UnitTarget added in v0.3.7

func UnitTarget() string

UnitTarget returns the systemd unit name. systemctl --user accepts this string as the unit argument.

func UserDomain

func UserDomain() string

UserDomain returns the empty string on Linux. The systemd user instance has no analog to launchctl's gui/<uid> tuple; the systemctl --user invocation is the access point.

func WorkerLogPath

func WorkerLogPath() (string, error)

WorkerLogPath returns the launcher worker log path. Cross-platform.

func Write

func Write(m Marker) error

Write atomically persists a marker to the pending directory.

func WriteSettings

func WriteSettings(s UserSettings) error

WriteSettings atomically writes the user-level settings file.

Types

type BinaryIdentity added in v0.6.0

type BinaryIdentity struct {
	Path  string
	Size  int64
	ModMS int64 // modification time, Unix milliseconds
}

BinaryIdentity is a cheap fingerprint of the binary registered with the launcher. Size plus modification time changes on normal upgrades and local installs, which is enough to detect when the service should be re-registered before the next kick.

func StatBinaryIdentity added in v0.6.0

func StatBinaryIdentity(path string) (BinaryIdentity, error)

StatBinaryIdentity reads the current identity of the binary at path.

type DisableResult

type DisableResult struct {
	// WasEnabled reflects the settings flag prior to disable.
	WasEnabled bool

	// RemovedUnitPath is the unit/plist/task path that was
	// removed, if any. Empty when no file was on disk.
	RemovedUnitPath string

	// Warnings lists cleanup steps that did not complete (for
	// example a periodic timer that could not be disabled). The
	// disable still succeeds; callers should surface these.
	Warnings []string
}

DisableResult describes the outcome of a Disable call.

func Disable

func Disable(ctx context.Context) (*DisableResult, error)

Disable unregisters the worker, removes the unit/plist/task file, and clears the launcher settings. Idempotent. Returns ErrUnsupportedOS on platforms without a launcher backend.

type InstallResult

type InstallResult struct {
	// UnitPath is the installed unit/plist/task path.
	UnitPath string

	// UnitTarget is the OS-specific service identifier.
	UnitTarget string

	// Reinstalled reports whether a previous service was already
	// loaded at the time of install.
	Reinstalled bool
}

InstallResult describes a successful Enable or reinstall.

func Enable

func Enable(ctx context.Context, binaryPath string) (*InstallResult, error)

Enable installs the worker, registers it with the OS daemon manager, and records the enabled state in user settings. The OS-specific work runs through the active manager backend; on platforms without one, this returns ErrUnsupportedOS.

The backend check runs before binary-path validation so the "unsupported OS" contract holds for callers that use errors.Is(err, ErrUnsupportedOS): a non-darwin host returns ErrUnsupportedOS regardless of whether the supplied path is absolute or exists.

type LauncherSettings

type LauncherSettings struct {
	// Enabled reports whether the launcher is enabled.
	Enabled bool `json:"enabled"`

	// InstalledUnitPath is the launcher install path written by
	// Enable. The JSON tag is "installed_unit_path"; the legacy
	// "installed_plist_path" key is handled by the dual-key
	// MarshalJSON / UnmarshalJSON below.
	InstalledUnitPath string `json:"installed_unit_path,omitempty"`

	// InstalledAt is the enable-time Unix millisecond timestamp.
	InstalledAt int64 `json:"installed_at,omitempty"`

	// InstalledBinaryPath is the binary path the OS daemon manager was
	// bound to at enable time.
	InstalledBinaryPath string `json:"installed_binary_path,omitempty"`

	// InstalledBinarySize and InstalledBinaryModMS fingerprint that
	// binary so dispatch can detect in-place replacement (which leaves
	// launchd holding stale code-signing state) and self-heal via
	// Refresh.
	InstalledBinarySize  int64 `json:"installed_binary_size,omitempty"`
	InstalledBinaryModMS int64 `json:"installed_binary_mod_ms,omitempty"`
}

LauncherSettings records the launcher's installed state.

The canonical on-disk install-path key is "installed_unit_path". During the transition, the legacy "installed_plist_path" key is also written with the same value, and the read path accepts either key.

func (LauncherSettings) MarshalJSON added in v0.3.7

func (s LauncherSettings) MarshalJSON() ([]byte, error)

MarshalJSON writes both install-path keys with the same value while the legacy key is still supported.

func (LauncherSettings) RecordedIdentity added in v0.6.0

func (s LauncherSettings) RecordedIdentity() (BinaryIdentity, bool)

RecordedIdentity returns the identity captured at enable time, and whether one was recorded. Settings written by older versions carry no identity; callers treat that as stale so one refresh migrates them.

func (*LauncherSettings) UnmarshalJSON added in v0.3.7

func (s *LauncherSettings) UnmarshalJSON(data []byte) error

UnmarshalJSON reads both install-path keys and prefers installed_unit_path when it is present, even if it is the empty string. A nil pointer means the key was absent or null, so the legacy installed_plist_path fallback applies.

Conflicting non-empty values resolve to the canonical key. The next WriteSettings overwrites both keys with the canonical value, so the conflict cannot persist past one read/write cycle.

type Marker

type Marker struct {
	// CheckpointID also becomes the filename stem.
	CheckpointID string `json:"checkpoint_id"`

	// CommitHash is the linked commit.
	CommitHash string `json:"commit_hash"`

	// RepoRoot is the absolute repository path.
	RepoRoot string `json:"repo_root"`

	// WrittenAt is the hook-side Unix millisecond timestamp.
	WrittenAt int64 `json:"written_at"`
}

Marker is the handoff written by the hook and read by the drain command.

func Read

func Read(path string) (Marker, error)

Read loads and validates one marker. Use ReadInQueue when the caller discovered the file from a specific queue directory.

func ReadInQueue

func ReadInQueue(repoRoot, path string) (Marker, error)

ReadInQueue loads a marker and checks that it matches the queue it came from.

func (Marker) Validate

func (m Marker) Validate() error

Validate checks the on-disk marker contract. RepoRoot uses the host OS's absolute-path rules. Plist paths stay POSIX-only because launchd consumes them directly.

type RefreshResult added in v0.6.0

type RefreshResult struct {
	// Enabled is false when the launcher is not enabled; Refresh is a
	// no-op in that case.
	Enabled bool

	// BinaryPath is the binary the launcher was re-bound to.
	BinaryPath string

	// Install carries the re-install result when Enabled.
	Install *InstallResult
}

RefreshResult reports what Refresh did.

func Refresh added in v0.6.0

func Refresh(ctx context.Context) (*RefreshResult, error)

Refresh re-binds the OS daemon manager to the currently executing binary. It is a no-op when the launcher is disabled, and a full re-enable when it is enabled.

The target is always os.Executable(), never the path recorded in settings: installer hooks invoke the freshly installed binary, so preferring the recorded path would re-bind launchd to a stale install location after a user migrates (Homebrew to curl, custom INSTALL_DIR), and then stamp a fresh identity on the old binary, making status look healthy while the launcher runs the wrong build.

Refresh deliberately does not kickstart; callers decide whether to kick (the CLI command does, to drain queued markers).

type ServiceHealth added in v0.6.0

type ServiceHealth struct {
	// LastExitReason is the daemon's recorded last exit reason, when
	// exposed (e.g. launchd's OS_REASON_CODESIGNING).
	LastExitReason string

	// NeedsLWCRUpdate reports launchd's "needs LWCR update" property:
	// the pinned code requirement no longer matches the binary.
	NeedsLWCRUpdate bool

	// SpawnRefused reports that the service is registered but the OS
	// daemon manager is refusing to run it.
	SpawnRefused bool
}

ServiceHealth is the OS daemon manager's spawn-health view of the worker service.

type StatusResult

type StatusResult struct {
	// OS is the runtime.GOOS value at the time of the call.
	OS string

	// SettingsEnabled is the launcher.enabled flag from settings.json.
	SettingsEnabled bool

	// InstalledUnitPath is the unit/plist/task path recorded in
	// settings.
	InstalledUnitPath string

	// InstalledAt is the enable-time Unix millisecond timestamp.
	InstalledAt int64

	// SettingsError is set when the settings file exists but could
	// not be read cleanly.
	SettingsError string

	// ExpectedUnitPath is the canonical unit/plist/task path for
	// the current user.
	ExpectedUnitPath string

	// UnitOnDisk reports whether the unit/plist/task file exists
	// at the recorded or expected path.
	UnitOnDisk bool

	// UnitTarget is the OS-specific service identifier.
	UnitTarget string

	// LoadedInDaemon reports whether the OS daemon manager has the
	// service registered.
	LoadedInDaemon bool

	// ServiceState is a short summary of what the OS daemon manager
	// reported:
	//   - "loaded"       : service is registered
	//   - "not loaded"   : service is not registered
	//   - "unsupported"  : no launcher backend on this OS
	//   - "error: <msg>" : daemon-manager call failed
	ServiceState string

	// LogPath is the launcher worker log path.
	LogPath string

	// Health carries OS-specific daemon spawn-health signals.
	Health ServiceHealth

	// BinaryStale reports that the registered binary no longer matches
	// the identity recorded at enable time; BinaryStaleReason explains.
	// Remediation for both this and Health.SpawnRefused is
	// `semantica launcher refresh`.
	BinaryStale       bool
	BinaryStaleReason string
}

StatusResult describes launcher state as reported by settings, the unit/plist/task file on disk, and the OS daemon manager.

func Status

func Status(ctx context.Context) (StatusResult, error)

Status gathers launcher state from settings, the filesystem, and the OS daemon manager. Non-fatal problems are encoded into StatusResult so the caller can render a coherent view even when one source disagrees with another.

type UserSettings

type UserSettings struct {
	Launcher LauncherSettings `json:"launcher,omitempty"`
}

UserSettings is the user-level settings file at $HOME/.semantica/settings.json.

func ReadSettings

func ReadSettings() (UserSettings, error)

ReadSettings loads the user-level settings file. A missing file returns the zero value. A malformed file returns an error.

Jump to

Keyboard shortcuts

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