launcher

package
v0.1.112 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	Schema          = 1
	ProtocolVersion = "v1.0.0"
)
View Source
const (
	ReasonPointerUnreadable  = "pointer-unreadable"
	ReasonDescriptorInvalid  = "descriptor-invalid"
	ReasonPayloadMissing     = "payload-missing"
	ReasonPayloadNotRegular  = "payload-not-regular"
	ReasonPayloadUnreadable  = "payload-unreadable"
	ReasonPayloadCorrupt     = "payload-corrupt"
	ReasonPayloadStartFailed = "payload-start-failed"
)

Reason codes name why an install could not be used, in words that stay the same across operating systems and filesystems. The Go error text behind one does not: "GetFileAttributesEx ...: The system cannot find the file specified" and "no such file or directory" are the same fact. A caller that has to decide "is this install broken, or did my command fail?" -- an MCP host reading stderr, an operator reading a recovery diagnostic -- matches on these.

View Source
const (
	RehydrateOutcomeRestored = "restored"
	RehydrateOutcomeFailed   = "failed"
)

RehydrateOutcomeRestored and RehydrateOutcomeFailed are the only two values Outcome takes.

View Source
const RecoveryRecordName = "launcher-recovery.json"

RecoveryRecordName is the file inside the install root that keeps what the launcher's stderr line cannot: that a recovery happened at all.

Recovery is designed to be survivable, and that is exactly the problem this file exists for. The launcher notices the current payload is unusable, runs the last-known-good one, repairs the pointer, and the command the user asked for then succeeds and exits 0 — correctly, because it did run. The single stderr line announcing it goes to an MCP host's log that nobody reads, and once the pointer is repaired no later run says anything at all. From then on the install looks healthy and the operator has no way to find out that a released payload was destroyed on this machine.

On Windows the cause so far has always been Microsoft Defender quarantining `csx-payload.exe` as a false positive minutes to hours after a verified update committed it. Whether the launcher survived it is a different question from whether it happened, and only the second one belongs in a release decision.

View Source
const RecoveryRecordSchema = 1

RecoveryRecordSchema is the on-disk version of the record below.

View Source
const RehydrateRecordName = "launcher-rehydrate.json"

RehydrateRecordName is the durable evidence that this install had to refetch a payload from the release it was originally installed from.

It is a separate fact from RecoveryRecordName and deserves its own file. A recovery record says the launcher fell back to a payload that was still on this machine; this one says there was nothing left to fall back to. The second is a far stronger signal about a release — the local recovery set was exhausted — and it must not be overwritten by the ordinary recoveries that follow a successful repair.

View Source
const RehydrateRecordSchema = 1

RehydrateRecordSchema is the on-disk version of the record below.

Variables

This section is empty.

Functions

func AcquireUpdateLock added in v0.1.45

func AcquireUpdateLock(path string, wait time.Duration) (func(), error)

AcquireUpdateLock acquires the token/pid lock shared by launcher recovery and updater writes. It is exported only within this repository's internal packages so every Windows participant uses the same identity-safe stale-file takeover instead of reintroducing a read-path/remove-path race.

func Path

func Path(root string) string

func PayloadPath

func PayloadPath(root, version string) (string, error)

func Reason added in v0.1.45

func Reason(err error) string

Reason classifies an error from this package into a stable reason code. Anything it does not recognize is reported as an unusable pointer, which is the truthful default: the launcher could not establish what to run.

func RecordRecovery added in v0.1.46

func RecordRecovery(root string, res Resolution, now time.Time) error

RecordRecovery folds one recovery into the install's record.

It is deliberately total: every failure mode returns an error the caller is expected to ignore. Losing the evidence is bad; refusing to run csx because the evidence could not be written would be worse, and this is called on the path where the install is already damaged.

func RecordRehydrate added in v0.1.63

func RecordRehydrate(root string, r RehydrateRecord, now time.Time) error

RecordRehydrate folds one repair attempt into the install's record. Like RecordRecovery it is total: the caller is on the damaged path already and must not be stopped by an evidence file it could not write.

func RecoveryRecordPath added in v0.1.46

func RecoveryRecordPath(root string) string

RecoveryRecordPath is where the record lives for a given install root.

func RehydrateRecordPath added in v0.1.63

func RehydrateRecordPath(root string) string

RehydrateRecordPath is where the record lives for a given install root.

func RestorePayload added in v0.1.63

func RestorePayload(root string, d Descriptor, staged string) error

RestorePayload puts bytes back at a descriptor's immutable payload path.

This exists for one situation: the payload file a still-correct pointer names is gone, and so is every fallback the pointer records, so nothing can run and nothing on this machine can repair it. The bytes then have to come from outside — see internal/update.RehydrateInstall, which re-fetches them from the official release path — and this is where they re-enter the install.

The descriptor's own SHA-256 is the whole trust boundary. It was recorded by this install from a signed manifest at commit time, and staged bytes that do not hash to it never reach the payload path. A caller cannot use this to adopt a file it merely found: it must already know the exact digest, and this refuses everything else. After the move the payload is verified again from disk, so a file that is quarantined or truncated between the hash check and the rename is reported as a failed restore rather than a repair.

func Validate

func Validate(root string, a Active) error

func VerifyPayload added in v0.1.63

func VerifyPayload(root string, d Descriptor) error

VerifyPayload reports whether the payload a descriptor names is present on disk and hashes to what the descriptor recorded. It is the same check the launcher makes before executing anything, exported so a repair can ask "is this one already fine?" without duplicating the rule.

func Write

func Write(root string, a Active) error

Types

type Active

type Active struct {
	Schema       int         `json:"schema"`
	Current      Descriptor  `json:"current"`
	Previous     *Descriptor `json:"previous,omitempty"`
	RollbackHold *Descriptor `json:"rollbackHold,omitempty"`
}

func CommitPayload

func CommitPayload(root, staged string, d Descriptor) (Active, error)

func ImportPrevious

func ImportPrevious(root, source string, d Descriptor) (Active, error)

func Load

func Load(root string) (Active, error)

func Read

func Read(root string) (Active, error)

Read parses and structurally validates the pointer without hashing payloads. The launcher itself uses Load before execution; stale-process polling uses Read so every MCP tool call does not reread the complete executable.

func Rollback

func Rollback(root string) (Active, error)

Rollback returns the install to its previous payload.

It reads the pointer instead of loading it: a current that will not verify is the state people reach for rollback in, and verifying it first made the documented recovery unusable in exactly that case.

type Descriptor

type Descriptor struct {
	Version  string `json:"version"`
	SHA256   string `json:"sha256"`
	Sequence uint64 `json:"sequence"`
}

type RecoveryRecord added in v0.1.46

type RecoveryRecord struct {
	Schema int `json:"schema"`

	// FailedVersion and FailedReason describe the payload that could not be
	// used. FailedReason is one of this package's stable reason codes.
	FailedVersion string `json:"failedVersion"`
	FailedReason  string `json:"failedReason"`

	// RanVersion is the last-known-good payload that ran instead.
	RanVersion string `json:"ranVersion"`

	// PointerRepaired reports whether active.json was healed. PointerError
	// says why not when it was not; an install that keeps recovering without
	// ever repairing the pointer is a different fault from one that recovered
	// once and moved on.
	PointerRepaired bool   `json:"pointerRepaired"`
	PointerError    string `json:"pointerError,omitempty"`

	// FirstObservedAt and Observations cover the run of consecutive
	// recoveries from this same failed version and reason; a different
	// failure starts a new incident. Observations is a floor, not a count:
	// several csx processes can start at once and the last write wins.
	FirstObservedAt time.Time `json:"firstObservedAt"`
	LastObservedAt  time.Time `json:"lastObservedAt"`
	Observations    int       `json:"observations"`
}

RecoveryRecord is the durable evidence of payload recoveries in one install.

It holds the most recent incident rather than a log. A launcher that fails to start its payload can be invoked hundreds of times a day by an editor, and an unbounded file in the install root would be its own operational problem.

func ReadRecoveryRecord added in v0.1.46

func ReadRecoveryRecord(root string) (RecoveryRecord, bool, error)

ReadRecoveryRecord returns the install's record. The second result is false when no recovery has ever been recorded, which is a different fact from a record that could not be read.

func (RecoveryRecord) Summary added in v0.1.46

func (r RecoveryRecord) Summary() string

Summary renders the record as one operator-facing line.

type RehydrateRecord added in v0.1.63

type RehydrateRecord struct {
	Schema int `json:"schema"`

	// AttemptedAt is when the attempt finished. Outcome is "restored" or
	// "failed"; Error carries why a failed one failed.
	AttemptedAt time.Time `json:"attemptedAt"`
	Outcome     string    `json:"outcome"`
	Error       string    `json:"error,omitempty"`

	// ExhaustedVersion is the current payload that had no fallback left, and
	// RestoredVersions are the versions whose bytes came back.
	ExhaustedVersion string   `json:"exhaustedVersion,omitempty"`
	RestoredVersions []string `json:"restoredVersions,omitempty"`

	// Attempts counts consecutive attempts since the last successful one.
	Attempts int `json:"attempts"`
}

RehydrateRecord is the most recent repair attempt in one install.

func ReadRehydrateRecord added in v0.1.63

func ReadRehydrateRecord(root string) (RehydrateRecord, bool, error)

ReadRehydrateRecord returns the install's repair record. The second result is false when no repair has ever been attempted, which is a different fact from a record that could not be read.

func (RehydrateRecord) Summary added in v0.1.63

func (r RehydrateRecord) Summary() string

Summary renders the record as one operator-facing line.

type Resolution added in v0.1.45

type Resolution struct {
	Descriptor  Descriptor
	PayloadPath string

	// Recovered is set when current failed verification and a descriptor this
	// same pointer already recorded as previous passed it instead.
	// RollbackHold is rejection metadata, never an execution candidate.
	// FailedVersion and FailedReason describe the rejected current.
	Recovered     bool
	FailedVersion string
	FailedReason  string

	// Healed reports whether the recovered pointer was written back to disk. A
	// read-only or contended install still runs the fallback payload for this
	// invocation; HealError then says why the pointer itself stayed broken.
	Healed    bool
	HealError error
}

Resolution is the payload the launcher will execute and, when the recorded current turned out to be unusable, how it got back to a verified one.

func RecoverAfterStartFailure added in v0.1.45

func RecoverAfterStartFailure(root string, failed Descriptor, startErr error) (Resolution, error)

RecoverAfterStartFailure closes the verification-to-CreateProcess window. A payload can hash correctly and then be quarantined, have its permission changed, or otherwise become unstartable before the operating system opens it. That is the same unusable-current condition as a failed hash check, so a descriptor this pointer already recorded as last-known-good gets one bounded retry. The launcher never retries an unrecorded directory or loops twice.

func Resolve added in v0.1.45

func Resolve(root string) (Resolution, error)

Resolve picks the payload to execute, falling back to the last known good one when current cannot be verified.

A verified payload does not stay verified. On Windows the install lost its current payload twice to Defender quarantining the executable minutes after a correctly staged, hashed and self-tested update had committed it -- the pointer was right, the file was simply gone. Failing hard there takes csx and its MCP server down over a payload the same pointer still records a working alternative for, and leaves no way back: Rollback and every ownership check in internal/update load the pointer, which verifies current first.

So an unusable current is recoverable, not fatal. Only descriptors this pointer recorded with their own SHA-256 are candidates: a payload directory left on disk by an older release was never verified by this install and is not adopted. Nothing is ever executed unverified, and when no candidate verifies, Resolve fails with a reason instead of returning something to run.

Jump to

Keyboard shortcuts

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