Documentation
¶
Overview ¶
Package daemonservice owns the platform-neutral contracts for Graith's app-associated per-user daemon. Darwin-specific registration is isolated in build-tagged files; other platforms retain direct spawning.
Index ¶
- Constants
- Variables
- func BeginStart(receipt *Receipt, intent StartIntent) error
- func BundleCandidates(executable string) []string
- func CacheRoot(uid int) (string, error)
- func CancelStart(receipt *Receipt, label, nonce string) error
- func CanonicalPathContains(parent, child string) (bool, error)
- func CanonicalPathsOverlap(first, second string) (bool, error)
- func CommitProfile(receipt *Receipt, lease Lease) error
- func CompleteStart(receipt *Receipt, label, nonce string) error
- func GenerateAssets(launchAgentsDir, swiftOutput string) error
- func GenerationID(version, commit string) (string, error)
- func InstallRequestEnvironment(request StartupRequest) error
- func IsManagedBuild() bool
- func MarkCurrentProcessStopped(profile string) error
- func OSUserHome(uid int) (string, error)
- func ProfileForDefinition(definition Definition, requestedProfile string) error
- func ProjectEnvironment(environ []string, inherit []string, uid int) (map[string]string, error)
- func QuarantineSlot(receipt *Receipt, slot, reason string) error
- func ReceiptRoot(uid int) (string, error)
- func ReleaseProfile(receipt *Receipt, profile string, confirmedDownAndUnregistered bool) error
- func RemoveStartupRequest(root string, uid int, definition Definition) error
- func ResolveUpgradeCandidateContext(ctx context.Context, currentPath, version, commit string, uid int) (string, bool, error)
- func ServiceControlRoot(int) (string, error)
- func ServiceControlRootContext(context.Context, int) (string, error)
- func ValidateManagedInstallationContext(ctx context.Context, executable, version, commit string) error
- func VerifyDarwinDistributionContext(context.Context, string) error
- func WithStartLock(root string, uid int, definition Definition, fn func() error) error
- func WriteStartupRequest(root string, request StartupRequest) error
- type Bootstrap
- type BundleExpectations
- type DarwinController
- func (DarwinController) JobState(context.Context, int, Definition) (JobState, error)
- func (DarwinController) Kickstart(context.Context, int, Definition) error
- func (DarwinController) Register(context.Context, string, Definition) (ServiceStatus, error)
- func (DarwinController) RegisterFresh(context.Context, string, Definition) (ServiceStatus, error)
- func (DarwinController) Status(context.Context, string, Definition) (ServiceStatus, error)
- func (DarwinController) Unregister(context.Context, string, Definition) (ServiceStatus, error)
- type Definition
- func DefinitionForSlot(slot string) (Definition, error)
- func Definitions() []Definition
- func PrepareManagedUpgrade(profile, candidatePath string) (Definition, func() error, bool, error)
- func PrepareRetainedManagedUpgrade(label, slot, profile, currentCandidatePath, nextCandidatePath string) (Definition, func() error, error)
- func ValidateMarker(label, slot string) (Definition, error)
- func ValidateRetainedAdoptedService(label, slot, profile, candidatePath string) (Definition, error)
- type ExpectedStartup
- type Generation
- type JobState
- type Lease
- type ManagedProcess
- type Manager
- func (manager *Manager) CurrentDefinition(profile string) (Definition, bool, error)
- func (manager *Manager) GarbageCollectCache() ([]string, error)
- func (manager *Manager) Launch(ctx context.Context, cfg *config.Config, paths config.Paths, configFile string, ...) error
- func (manager *Manager) MarkStopped(profile string, pid int) error
- func (manager *Manager) Receipt() (Receipt, error)
- func (manager *Manager) Remove(ctx context.Context, profile string, all bool, stop StopDaemonFunc) error
- func (manager *Manager) Repair(ctx context.Context) ([]string, error)
- func (manager *Manager) Reports(ctx context.Context, profile string, all bool) ([]ServiceReport, error)
- func (manager *Manager) ReserveForCleanRestart(ctx context.Context, profile string) error
- type Manifest
- type Mode
- type PendingOperation
- type Receipt
- type ReceiptStore
- type Registration
- type Resolution
- func Resolve(executable, version, commit string, uid int) (Resolution, error)
- func ResolveContext(ctx context.Context, executable, version, commit string, uid int) (Resolution, error)
- func ResolveForRepair(executable, version, commit string, uid int) (Resolution, error)
- func ResolveWith(options ResolveOptions) (Resolution, error)
- type ResolveOptions
- type ServiceController
- type ServiceReport
- type ServiceStatus
- type SignatureInfo
- type StartIntent
- type StartupRequest
- type StopDaemonFunc
- type ValidatedBundle
- func CacheBundle(source ValidatedBundle, expectations BundleExpectations, uid int) (ValidatedBundle, error)
- func CacheBundleContext(ctx context.Context, source ValidatedBundle, expectations BundleExpectations, ...) (ValidatedBundle, error)
- func DiscoverBundle(executable string, expectations BundleExpectations) (ValidatedBundle, bool, error)
- func ValidateBundle(appPath string, expectations BundleExpectations) (ValidatedBundle, error)
Constants ¶
const ( DefaultSlot = "default" ControllerExecutable = "graith-service-controller" DaemonExecutable = "gr" AppBundleName = "Graith.app" )
Variables ¶
var ( // ManagedBuild is set to true only for a release/development channel that // also packages a matching signed Graith.app. Source and go-install builds // deliberately retain direct spawning. ManagedBuild = "false" // ExpectedTeamID and ExpectedRequirementBase64 are stamped into stable builds. // Development-signed builds may leave them empty only when DevelopmentBuild // is explicitly true. The requirement is base64-encoded so codesign's // whitespace and quoting cannot be reinterpreted by go build's -ldflags. ExpectedTeamID = "" ExpectedRequirementBase64 = "" DevelopmentBuild = "false" )
var ( ErrReceiptMissing = errors.New("daemon service receipt and backup are missing") ErrSlotCapacity = errors.New("all daemon service profile slots are leased") )
var BuiltInEnvironmentNames = []string{
"PATH", "SHELL", "TMPDIR", "LANG", "LC_*",
"XDG_CONFIG_HOME", "XDG_CACHE_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_RUNTIME_DIR",
}
var ErrPendingInProgress = errors.New("daemon service transaction is still in progress")
var ErrStartupRequestExists = errors.New("daemon service startup request already exists")
Functions ¶
func BeginStart ¶
func BeginStart(receipt *Receipt, intent StartIntent) error
func BundleCandidates ¶
BundleCandidates returns only package-relative locations. It never searches /Applications or trusts an environment override because a matching app must come from the same installed release as the CLI.
func CancelStart ¶
func CanonicalPathContains ¶
CanonicalPathContains reports whether parent contains child after applying the same fail-closed canonicalization as CanonicalPathsOverlap.
func CanonicalPathsOverlap ¶
CanonicalPathsOverlap reports whether either absolute path contains the other after resolving every existing symlink component. Missing leaf components are retained beneath their nearest verified ancestor; broken symlinks and paths that cannot be inspected fail closed.
func CommitProfile ¶
func CompleteStart ¶
func GenerateAssets ¶
GenerateAssets writes every static LaunchAgent plist plus the Swift controller's generated lookup table from the embedded source manifest.
func GenerationID ¶
func InstallRequestEnvironment ¶
func InstallRequestEnvironment(request StartupRequest) error
func IsManagedBuild ¶
func IsManagedBuild() bool
func MarkCurrentProcessStopped ¶
MarkCurrentProcessStopped is a no-op outside a managed packaged build. A managed foreground daemon invokes it on every ordinary exit; same-PID exec never runs defers, so preserved upgrades keep their running marker intact.
func OSUserHome ¶
func ProfileForDefinition ¶
func ProfileForDefinition(definition Definition, requestedProfile string) error
func ProjectEnvironment ¶
func QuarantineSlot ¶
func ReceiptRoot ¶
func ReleaseProfile ¶
func RemoveStartupRequest ¶
func RemoveStartupRequest(root string, uid int, definition Definition) error
func ResolveUpgradeCandidateContext ¶
func ResolveUpgradeCandidateContext(ctx context.Context, currentPath, version, commit string, uid int) (string, bool, error)
ResolveUpgradeCandidateContext returns the immutable cached service payload that a managed daemon may exec. Fallback installations keep using the caller's current executable.
func ServiceControlRoot ¶
func ValidateManagedInstallationContext ¶
func ValidateManagedInstallationContext(ctx context.Context, executable, version, commit string) error
ValidateManagedInstallationContext verifies the exact package-associated app without caching it or creating service state. Diagnostic commands use this read-only gate before reporting a managed installation as healthy.
func WithStartLock ¶
func WithStartLock(root string, uid int, definition Definition, fn func() error) error
func WriteStartupRequest ¶
func WriteStartupRequest(root string, request StartupRequest) error
Types ¶
type Bootstrap ¶
type Bootstrap struct {
Definition Definition
Request StartupRequest
Generation Generation
// contains filtered or unexported fields
}
func BootstrapFreshService ¶
BootstrapFreshService is the only fresh managed-service entry point. It derives both roots from the OS account, validates the immutable cached app and receipt/lease agreement, consumes the request once, installs its minimal environment, and clears the durable nonce.
type BundleExpectations ¶
type DarwinController ¶
type DarwinController struct{}
func (DarwinController) JobState ¶
func (DarwinController) JobState(context.Context, int, Definition) (JobState, error)
func (DarwinController) Kickstart ¶
func (DarwinController) Kickstart(context.Context, int, Definition) error
func (DarwinController) Register ¶
func (DarwinController) Register(context.Context, string, Definition) (ServiceStatus, error)
func (DarwinController) RegisterFresh ¶
func (DarwinController) RegisterFresh(context.Context, string, Definition) (ServiceStatus, error)
func (DarwinController) Status ¶
func (DarwinController) Status(context.Context, string, Definition) (ServiceStatus, error)
func (DarwinController) Unregister ¶
func (DarwinController) Unregister(context.Context, string, Definition) (ServiceStatus, error)
type Definition ¶
func DefinitionForSlot ¶
func DefinitionForSlot(slot string) (Definition, error)
func Definitions ¶
func Definitions() []Definition
func PrepareManagedUpgrade ¶
func PrepareManagedUpgrade(profile, candidatePath string) (Definition, func() error, bool, error)
PrepareManagedUpgrade validates candidatePath as an exact embedded payload already recorded by a trusted new CLI. It updates only running-generation state; launchd registration remains on the old app until the daemon is down.
func PrepareRetainedManagedUpgrade ¶
func PrepareRetainedManagedUpgrade( label, slot, profile, currentCandidatePath, nextCandidatePath string, ) (Definition, func() error, error)
PrepareRetainedManagedUpgrade validates a previously adopted managed daemon whose running image is an immutable retained copy, then stages the exact next cached generation. currentCandidatePath is the manifest-bound original payload for the running generation; the daemon layer remains responsible for binding the retained running image to that manifest before calling here.
Unlike PrepareManagedUpgrade, this function is always fail-closed: it is used only after the caller has explicitly selected managed adoption, so missing or mismatched receipt state is an error rather than an unmanaged fallback.
func ValidateMarker ¶
func ValidateMarker(label, slot string) (Definition, error)
func ValidateRetainedAdoptedService ¶
func ValidateRetainedAdoptedService(label, slot, profile, candidatePath string) (Definition, error)
ValidateRetainedAdoptedService validates a managed daemon after an exec upgrade whose running image may be an immutable retained copy. Unlike RunningManagedProcess, this does not compare os.Executable with the cached app payload: the daemon layer has already bound and verified the running image against its upgrade manifest before calling this function. Instead it anchors the manifest's original candidate path to the exact validated generation recorded as running for this service and PID.
This is deliberately adoption-only and fail-closed. A missing receipt or service registration is an error, never an unmanaged result.
type ExpectedStartup ¶
type Generation ¶
type Generation struct {
ID string `json:"id"`
AppPath string `json:"app_path"`
Version string `json:"version"`
BundleBuild string `json:"bundle_build"`
Commit string `json:"commit"`
PayloadHash string `json:"payload_hash"`
TeamID string `json:"team_id,omitempty"`
Requirement string `json:"requirement,omitempty"`
}
type Lease ¶
type Lease struct {
Profile string `json:"profile"`
Slot string `json:"slot"`
UID int `json:"uid"`
RegisteredGeneration string `json:"registered_generation,omitempty"`
RunningGeneration string `json:"running_generation,omitempty"`
RunningPID int `json:"running_pid,omitempty"`
Paths config.Paths `json:"paths,omitempty"`
}
type ManagedProcess ¶
type ManagedProcess struct {
Definition Definition
Profile string
Generation Generation
PID int
Store ReceiptStore
}
func RunningManagedProcess ¶
func RunningManagedProcess(profile string) (ManagedProcess, bool, error)
type Manager ¶
type Manager struct {
UID int
Bundle ValidatedBundle
Store ReceiptStore
ControlRoot string
Controller ServiceController
Now func() time.Time
TeamID string
Requirement string
Development bool
Verifier func(string) (SignatureInfo, error)
SkipCacheGC bool // test-only seam for synthetic managers without real bundles
// contains filtered or unexported fields
}
func (*Manager) CurrentDefinition ¶
func (manager *Manager) CurrentDefinition(profile string) (Definition, bool, error)
func (*Manager) GarbageCollectCache ¶
GarbageCollectCache removes only validated immutable generations that are unreferenced by the current receipt, its backup, or this installed CLI. The receipt lock stays held across the decision and removal so another profile cannot acquire a generation between the reference check and deletion.
func (*Manager) ReserveForCleanRestart ¶
ReserveForCleanRestart makes a supported profile service-addressable before a caller stops a working daemon. Existing leases are intentionally left on their registered generation while the process is live; Launch rotates the dormant registration after the exact old process has exited. A new profile is allocated and registered here so capacity or approval failure cannot turn an intentional restart into avoidable downtime.
type Manifest ¶
type Manifest struct {
Schema int `json:"schema"`
BundleIdentifier string `json:"bundle_identifier"`
DefaultLabel string `json:"default_label"`
NamedLabelPrefix string `json:"named_label_prefix"`
ProfileSlots int `json:"profile_slots"`
}
func ServiceManifest ¶
func ServiceManifest() Manifest
type PendingOperation ¶
type PendingOperation struct {
Kind string `json:"kind"`
Profile string `json:"profile,omitempty"`
Slot string `json:"slot"`
UID int `json:"uid"`
Generation string `json:"generation"`
Token string `json:"token,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
OwnerPID int `json:"owner_pid,omitempty"`
OwnerPIDStartTime int64 `json:"owner_pid_start_time,omitempty"`
}
type Receipt ¶
type Receipt struct {
Schema int `json:"schema"`
Transaction uint64 `json:"transaction"`
Default *Registration `json:"default,omitempty"`
Leases map[string]Lease `json:"leases"`
Quarantined map[string]string `json:"quarantined,omitempty"`
Generations map[string]Generation `json:"generations,omitempty"`
Pending *PendingOperation `json:"pending,omitempty"`
Starts map[string]StartIntent `json:"starts,omitempty"`
Checksum string `json:"checksum"`
}
func NewReceipt ¶
func NewReceipt() Receipt
func ReadReceipt ¶
type ReceiptStore ¶
ReceiptStore serializes global profile-slot mutations across every profile. Its root is outside profile data_dir so named daemons cannot allocate the same static service slot independently.
func (ReceiptStore) InitializeAfterTotalLoss ¶
func (s ReceiptStore) InitializeAfterTotalLoss(receipt Receipt) error
InitializeAfterTotalLoss replaces only receipts already proven unusable by the caller. Original bytes are retained under explicit .corrupt names for diagnosis; they are never silently overwritten with an empty mapping.
func (ReceiptStore) Load ¶
func (s ReceiptStore) Load() (Receipt, error)
type Registration ¶
type Registration struct {
Slot string `json:"slot"`
Label string `json:"label"`
RegisteredGeneration string `json:"registered_generation,omitempty"`
RunningGeneration string `json:"running_generation,omitempty"`
RunningPID int `json:"running_pid,omitempty"`
Paths config.Paths `json:"paths,omitempty"`
}
type Resolution ¶
func ResolveContext ¶
func ResolveForRepair ¶
func ResolveForRepair(executable, version, commit string, uid int) (Resolution, error)
func ResolveWith ¶
func ResolveWith(options ResolveOptions) (Resolution, error)
type ResolveOptions ¶
type ServiceController ¶
type ServiceController interface {
Status(ctx context.Context, controllerPath string, definition Definition) (ServiceStatus, error)
Register(ctx context.Context, controllerPath string, definition Definition) (ServiceStatus, error)
// RegisterFresh registers only when the controller can prove the job was
// absent immediately before this call. It must return an error rather than
// normalize an already-registered race to StatusEnabled.
RegisterFresh(ctx context.Context, controllerPath string, definition Definition) (ServiceStatus, error)
Unregister(ctx context.Context, controllerPath string, definition Definition) (ServiceStatus, error)
Kickstart(ctx context.Context, uid int, definition Definition) error
JobState(ctx context.Context, uid int, definition Definition) (JobState, error)
}
type ServiceReport ¶
type ServiceReport struct {
Profile string `json:"profile,omitempty"`
Slot string `json:"slot"`
Label string `json:"label"`
LeaseState string `json:"lease_state"`
RegisteredGeneration string `json:"registered_generation,omitempty"`
RunningGeneration string `json:"running_generation,omitempty"`
Status ServiceStatus `json:"status"`
JobPresent bool `json:"job_present"`
JobRunning bool `json:"job_running"`
PID int `json:"pid,omitempty"`
RecordedPID int `json:"recorded_pid,omitempty"`
QuarantineReason string `json:"quarantine_reason,omitempty"`
Paths config.Paths `json:"-"`
Job JobState `json:"-"`
}
type ServiceStatus ¶
type ServiceStatus string
const ( StatusNotRegistered ServiceStatus = "not-registered" StatusEnabled ServiceStatus = "enabled" StatusRequiresApproval ServiceStatus = "requires-approval" StatusNotFound ServiceStatus = "not-found" )
type SignatureInfo ¶
func VerifyDarwinSignature ¶
func VerifyDarwinSignature(string) (SignatureInfo, error)
func VerifyDarwinSignatureContext ¶
func VerifyDarwinSignatureContext(context.Context, string) (SignatureInfo, error)
type StartIntent ¶
type StartupRequest ¶
type StartupRequest struct {
Schema int `json:"schema"`
Profile string `json:"profile,omitempty"`
Slot string `json:"slot"`
Label string `json:"label"`
ConfigFile string `json:"config_file,omitempty"`
Paths config.Paths `json:"paths"`
Generation string `json:"generation"`
Environment map[string]string `json:"environment"`
Nonce string `json:"nonce"`
UID int `json:"uid"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
Checksum string `json:"checksum"`
}
func ConsumeStartupRequest ¶
func ConsumeStartupRequest(root string, uid int, definition Definition, expected ExpectedStartup, now time.Time) (StartupRequest, error)
type StopDaemonFunc ¶
type StopDaemonFunc func(ServiceReport) error
type ValidatedBundle ¶
type ValidatedBundle struct {
AppPath string
Generation Generation
}
func CacheBundle ¶
func CacheBundle(source ValidatedBundle, expectations BundleExpectations, uid int) (ValidatedBundle, error)
func CacheBundleContext ¶
func CacheBundleContext(ctx context.Context, source ValidatedBundle, expectations BundleExpectations, uid int) (ValidatedBundle, error)
func DiscoverBundle ¶
func DiscoverBundle(executable string, expectations BundleExpectations) (ValidatedBundle, bool, error)
DiscoverBundle distinguishes an absent package payload (explicit unbundled fallback) from a present-but-invalid app, which callers must fail closed.
func ValidateBundle ¶
func ValidateBundle(appPath string, expectations BundleExpectations) (ValidatedBundle, error)