selfupdate

package
v0.1.3 Latest Latest
Warning

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

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

Documentation

Overview

Package selfupdate lets an installed flynn replace itself with a newer one, without a shell script, a package manager, or any tool the user does not already have.

The security of an update path is not in how it downloads: it is in what it refuses to install. Everything this package fetches is treated as hostile until the signed provenance in internal/release says otherwise, and the provenance is what the download is then pinned to. A compromised mirror, a hostile proxy, a certificate authority in the wrong hands, or a GitHub outage substituting the wrong bytes all end the same way: a digest that does not match, and nothing written.

The release listing is the one input that is not signed, and it is used only to enumerate candidates, never as evidence. An attacker who controls it can hide new versions or offer old ones, so this package remembers the highest version it has ever verified and the newest it has ever been offered, and refuses to move backwards past either without being asked in as many words.

The install is the other half, and it is where update mechanisms usually break: the new binary is staged in the same directory as the one it replaces so the swap is a rename within one filesystem, the running binary's path is resolved through its symlinks so the write lands where it was aimed, an install owned by a package manager is refused rather than trampled, and the new binary is made to prove it runs before it is kept. Nothing that has not verified is ever executed.

Index

Constants

View Source
const (
	CodeListing    = "selfupdate.listing"
	CodeNoRelease  = "selfupdate.no_release"
	CodeDowngrade  = "selfupdate.downgrade"
	CodeStale      = "selfupdate.stale_listing"
	CodeArchive    = "selfupdate.archive"
	CodeInstall    = "selfupdate.install"
	CodeManaged    = "selfupdate.managed_install"
	CodePermission = "selfupdate.permission"
	CodeState      = "selfupdate.state"
	CodeSmokeTest  = "selfupdate.smoke_test"
	CodeDevBuild   = "selfupdate.dev_build"
)

Failure codes.

Variables

This section is empty.

Functions

func SweepSuperseded

func SweepSuperseded(exe string) int

SweepSuperseded removes the outgoing binaries an earlier upgrade left behind on the platforms that cannot delete a running executable. It is called at startup, is best effort, and reports how many it collected. A leftover file is untidy, not unsafe: nothing executes it, and the next upgrade does not read it.

Types

type Option

type Option func(*Updater)

Option configures an Updater.

type Plan

type Plan struct {
	Current    Version
	Target     Version
	Provenance release.Provenance
	Asset      string
	Digest     string
	URL        string
	// Path is the binary that will be replaced: the running executable with its
	// symlinks resolved, which is not always the path the user typed.
	Path string

	// Downgrade is set when the target is older than the floor, which only a Request
	// that said so out loud can produce.
	Downgrade bool
	// Warning carries something the operator needs to read even though the plan is
	// valid, such as a listing that went backwards.
	Warning string
	// contains filtered or unexported fields
}

Plan is a verified, ready-to-apply upgrade. Holding one means the provenance already checked out: the signature, the identity, and the transparency-log entry. What is left is the download, which is pinned to Digest, and the install.

func (Plan) UpToDate

func (p Plan) UpToDate() bool

UpToDate reports whether the plan would install what is already running.

type Release

type Release struct {
	Version     Version
	Prerelease  bool
	PublishedAt time.Time
	// Current marks the release this binary is running.
	Current bool
}

Release is one release the listing offered. It is unverified: the tag is a candidate to go and check, not a fact.

type Request

type Request struct {
	// To pins an exact version. Empty means the newest release.
	To string
	// AllowPrerelease lets a prerelease be chosen as the newest. An explicit To always
	// wins over this: asking for a version by name is asking for it.
	AllowPrerelease bool
	// AllowDowngrade permits installing a version older than the running one (or older
	// than the highest ever verified here). It is off by default because a downgrade is
	// an attack far more often than it is an intention.
	AllowDowngrade bool
}

Request is what the operator asked for.

type Updater

type Updater struct {
	// contains filtered or unexported fields
}

Updater upgrades the running binary.

func New

func New(dataDir string, opts ...Option) *Updater

New builds an Updater that keeps its state under dataDir.

func (*Updater) Apply

func (u *Updater) Apply(ctx context.Context, p Plan) error

Apply installs a plan. It is separate from Check so that the command can show the operator exactly what verified, and what it is about to do, before it does it.

func (*Updater) Check

func (u *Updater) Check(ctx context.Context, req Request) (Plan, error)

Check verifies what the newest (or requested) release is and what installing it would mean, without installing anything and without writing to the binary's directory. It is what `flynn version check` and `flynn upgrade --check` run.

func (*Updater) List

func (u *Updater) List(ctx context.Context) ([]Release, error)

List reports the releases that exist, newest first. It verifies nothing, because there is nothing here to verify: the listing exists to answer "what should I go and check", and every answer it gives is checked before a byte of it is installed.

func (*Updater) RecordSeen

func (u *Updater) RecordSeen(releases []Release)

RecordSeen remembers the newest release the listing has offered, so a listing that later goes backwards can be noticed. It is called after a successful listing, and a failure to write it is not a failure of the command.

type Version

type Version struct {
	// contains filtered or unexported fields
}

Version is a parsed release version. Only the shape flynn actually tags is accepted: "v" followed by major.minor.patch, optionally a prerelease. A version this package cannot parse is never compared, because a comparison that quietly treats an unparseable version as zero is a downgrade attack with extra steps.

func ParseVersion

func ParseVersion(s string) (Version, bool)

ParseVersion reads a version tag. It is strict on purpose.

func (Version) Compare

func (v Version) Compare(w Version) int

Compare orders two versions by semantic-versioning precedence: -1 if v sorts before w, 0 if they are the same version, +1 if v sorts after w. A prerelease sorts before the release it leads to, so v0.2.0-rc.1 is older than v0.2.0, which is what makes "do not install something older than what I am running" mean the right thing.

func (Version) IsPrerelease

func (v Version) IsPrerelease() bool

IsPrerelease reports whether this version carries a prerelease suffix.

func (Version) String

func (v Version) String() string

String returns the version as it was written.

Jump to

Keyboard shortcuts

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