Help us shape the future of Go! Take the 2026 Go Developer Survey

selfupdate

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package selfupdate turns a `go install` build of celeste into the official release binary of the same version, and backs `celeste update` (W5 rulings 25–30). It checks everything it downloads against the release signing key embedded in the binary and never installs a file that failed a check.

Index

Constants

View Source
const ModulePath = "github.com/whykusanagi/celeste-cli"

ModulePath is the module whose go install builds upgrade to the official release; a fork's build (another path) is left alone.

View Source
const Repo = "https://github.com/whykusanagi/celeste-cli"

Repo is where official releases are published.

Variables

View Source
var (
	ErrNoAsset    = errors.New("there is no official build for this platform")
	ErrBadArchive = errors.New("the release archive does not hold the expected binary")
	ErrTooLarge   = errors.New("the download is larger than celeste allows")
)
View Source
var (
	ErrBadSignature = errors.New("the release signature does not verify with celeste's release key")
	ErrChecksum     = errors.New("the download does not match the signed checksums")
	ErrManifest     = errors.New("the signed release manifest does not describe this download")
)
View Source
var ErrBadTag = errors.New("not a release tag")

ErrBadTag is a version string that is not a release tag.

View Source
var ErrHost = errors.New("the updater only talks HTTPS to GitHub")

ErrHost is a request to anything but HTTPS on an allowed host.

View Source
var Hosts = []string{"github.com", "objects.githubusercontent.com", "release-assets.githubusercontent.com"}

Hosts are the only hosts the updater talks to, over HTTPS only (ruling 26). GitHub redirects release downloads to release-assets.githubusercontent.com (objects.githubusercontent.com before 2025).

View Source
var Platforms = []Platform{
	{"linux", "amd64", "celeste-linux-amd64.tar.gz", "celeste-linux-amd64"},
	{"linux", "arm64", "celeste-linux-arm64.tar.gz", "celeste-linux-arm64"},
	{"darwin", "amd64", "celeste-darwin-amd64.tar.gz", "celeste-darwin-amd64"},
	{"darwin", "arm64", "celeste-darwin-arm64.tar.gz", "celeste-darwin-arm64"},
	{"windows", "amd64", "celeste-windows-amd64.zip", "celeste-windows-amd64.exe"},
}

Platforms is every platform release.yml publishes. Renaming any of these breaks every installed updater; TestReleaseWorkflowKeepsTheUpdaterContract checks the workflow still produces them.

View Source
var ReleaseKey []byte

ReleaseKey is celeste's release signing key: a byte-identical copy of the repository's whykusanagi.asc (TestEmbeddedReleaseKeyMatchesTheRepositoryKey). Primary 940490EF09DA31322BF7FD83875849AB1D541C55; releases are signed by its subkey F4C254F6EE5D7F086C921DEBA6BB54DDC70EE8FB.

Functions

func CheckManifest

func CheckManifest(manifest []byte, tag, archive string) ([32]byte, error)

CheckManifest checks that a signed manifest.json describes tag and returns the SHA-256 it lists for archive. The caller requires that sum to equal checksums.txt's, which binds the download to its version (ruling 26): checksums.txt names no version, so an older release's signed checksums and archive copied into this release would otherwise pass. A missing, malformed or repeated entry is ErrManifest. Call it only on a manifest whose signature verified.

func CleanupOld

func CleanupOld(exe string)

CleanupOld deletes the <exe>.old* files a Windows upgrade leaves behind. Best effort: one still running stays until the next start.

func Extract

func Extract(archiveName string, data []byte, binary string) ([]byte, error)

Extract returns the single regular-file entry named binary from a .tar.gz or .zip archive (by archiveName's extension), in memory. Every other entry is skipped and nothing is written to disk, so entry names can't escape anywhere.

func FindChecksum

func FindChecksum(checksums []byte, name string) ([32]byte, error)

FindChecksum returns the SHA-256 that a sha256sum-format file lists for name (text "<hex> <name>" or binary "<hex> *<name>" lines). Call it only on a file whose signature verified. A name listed twice is an error.

func Newer

func Newer(a, b string) bool

Newer reports whether release tag a comes after b in semver order. A string that is not a ValidTag is never newer, and nothing is newer than it.

func Reexec

func Reexec(exe string, argv, env []string) error

Reexec replaces this process with exe, keeping argv and env (ruling 26). It returns only on failure.

func ValidTag

func ValidTag(s string) bool

ValidTag reports whether s is a release tag celeste will put in a URL: vMAJOR.MINOR.PATCH with an optional pre-release, not a Go pseudo-version.

func VerifyDist

func VerifyDist(dir, tag string, pubKey []byte) error

VerifyDist checks a local release directory exactly as the updater checks a download (ruling 30): both signatures with pubKey, the manifest's tag, and for every platform of the contract the archive's signed checksum and the binary inside it. release.yml runs it through the release binary, with its embedded key, before anything is published.

func VerifySignature

func VerifySignature(pubKey, signed, armoredSig []byte) error

VerifySignature checks an ASCII-armoured detached signature over signed with the armoured public key pubKey (ruling 26). It verifies as of the time the signature was made, so a release stays verifiable after the signing subkey expires; only the key's holder can date a signature. Every failure is ErrBadSignature: there is no outcome but nil or an error.

Types

type Kind

type Kind int

Kind is what sort of build a binary is (ruling 25).

const (
	// Source is a checkout build, a pseudo-version or anything unrecognised:
	// the public persona, and the updater never touches the network.
	Source Kind = iota
	// Module is `go install …/cmd/celeste@vX.Y.Z` or @latest: no persona
	// key, a release tag, no VCS stamp. It upgrades itself.
	Module
	// Keyed is a local build that carries the persona key (make build with
	// ~/.celeste/persona.key): the full persona; it never downloads.
	Keyed
	// Official is a release binary: main.Channel == "release".
	Official
)

func Classify

func Classify(info *debug.BuildInfo, ok, official, hasKey bool) (Kind, string)

Classify sorts a binary into a Kind (ruling 25). official is main.Channel == "release" and hasKey is prompts.HasPersonaKey(). The tag is returned only for Module: the version go install recorded.

type Platform

type Platform struct{ GOOS, GOARCH, Archive, Binary string }

Platform is one entry of the release asset contract (ruling 30).

func Asset

func Asset(goos, goarch string) (Platform, bool)

Asset returns the contract entry for goos/goarch; false for a platform celeste does not release.

type Throttle

type Throttle struct {
	Path string // ~/.celeste/cache/selfupdate.json
	Now  func() time.Time
}

Throttle remembers the last failed attempt, so an offline module build doesn't try on every run.

func DefaultThrottle

func DefaultThrottle() (*Throttle, error)

DefaultThrottle keeps its record in ~/.celeste/cache/selfupdate.json.

func (*Throttle) Clear

func (t *Throttle) Clear()

Clear forgets the record after a success.

func (*Throttle) Due

func (t *Throttle) Due(tag string) bool

Due reports whether an upgrade to tag may be tried now. A missing or unreadable record, another tag, or a record from the future all mean yes.

func (*Throttle) Record

func (t *Throttle) Record(tag string) error

Record notes a failed attempt at tag now.

type Updater

type Updater struct {
	Base      string            // the release repository, Repo
	Hosts     []string          // allowed hosts, Hosts
	Transport http.RoundTripper // nil: http.DefaultTransport's clone with ruling 26's timeouts
	PublicKey []byte            // the release key, ReleaseKey
	GOOS      string
	GOARCH    string
	Exe       string // the file to replace; "" = os.Executable() with symlinks resolved
	Rename    func(oldpath, newpath string) error
	Now       func() time.Time
}

Updater downloads, verifies and installs official release binaries. New returns the production one; tests replace fields.

func New

func New() *Updater

New returns an Updater for this binary and the official releases.

func (*Updater) LatestTag

func (u *Updater) LatestTag(ctx context.Context) (string, error)

LatestTag reads the newest release's tag from the redirect of <Base>/releases/latest (ruling 29): no API call, no rate limit.

func (*Updater) Upgrade

func (u *Updater) Upgrade(ctx context.Context, tag string) (string, error)

Upgrade installs the official build of tag over the executable and returns the path it replaced. It replaces nothing unless every check of ruling 26 passed.

Directories

Path Synopsis
Package selfupdatetest builds signed fake releases for the updater's tests (W5 rulings 26, 30): a generated OpenPGP key with a signing subkey, like the real release key, and the release archives.
Package selfupdatetest builds signed fake releases for the updater's tests (W5 rulings 26, 30): a generated OpenPGP key with a signing subkey, like the real release key, and the release archives.

Jump to

Keyboard shortcuts

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