update

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package update implements the v0.1.0 signed update system (owner directive §5).

Trust chain (in enforcement order — verification is FIRST, download is LAST):

  1. SIGNATURE: the update manifest carries an Ed25519 signature over its canonical core bytes; the public key is EMBEDDED in the binary. A manifest from any URL is worthless without a valid signature — "never trust an unsigned update simply because it comes from a familiar download URL".
  2. VERSION: the manifest version must be strictly newer than the running binary (semver, pre-release aware). Downgrades and same-version re-writes are refused without --force.
  3. PLATFORM: the manifest must name an artifact for the running GOOS/GOARCH pair (no best-effort guesses).
  4. CHECKSUM: the downloaded artifact must match the manifest's SHA-256 exactly, and its size must match.
  5. REPLACE: the payload is written to a temp file NEXT TO the target (same filesystem → rename is atomic), fsynced, mode- preserved, then renamed over the running binary; the previous binary is kept as <target>.bak for recovery. A failure at any step restores the .bak and removes temps.

The HTTP fetcher caps the body size, enforces timeouts, redacts any credential-looking URL in errors (the httpx pattern), and refuses non-HTTP(S) schemes.

Index

Constants

View Source
const EmbeddedUpdatePublicKeyB64 = "hB+FoV/HjZ4AQlsaCosmdMkzX3LsyWkDwZeGNFAHlsc="

keys.go — the update trust anchor.

EmbeddedUpdatePublicKey is the base64 Ed25519 PUBLIC key that signed release manifests must verify against. It is baked into the binary on purpose: an update manifest is only as trustworthy as this key, never as the URL it was fetched from.

Key ceremony (docs/UPDATE.md §2): the project owner generates the keypair with `gseer-release keygen`, keeps the PRIVATE key offline with the release process, and replaces the constant below with the matching public half before cutting a release. The private key must NEVER live in this repository. Rotation = new keypair + new embedded anchor + a signed revocation note in the release channel.

View Source
const MaxArtifactBytes = 512 << 20

MaxArtifactBytes caps an update payload (a GSEER binary is tens of MB; 512 MiB is generous and refuses memory-bomb URLs).

Variables

This section is empty.

Functions

func CompareVersions

func CompareVersions(a, b string) (int, error)

CompareVersions returns -1/0/+1: a<b → -1. A pre-release (-rc1) sorts BEFORE its release. Unparseable versions are NEVER treated as newer (fail-closed comparison).

func IsDirSyncWarning

func IsDirSyncWarning(err error) bool

IsDirSyncWarning reports whether err is the "replaced, but the directory entry could not be flushed to disk" durability warning. When it is true the binary WAS replaced — callers must not treat the update as failed or rolled back; they should surface the warning.

func IsUpToDate

func IsUpToDate(err error) bool

IsUpToDate reports whether err is the "not older" condition (which is a successful check, not a failure).

func LoadManifest

func LoadManifest(ctx context.Context, location string, fetcher *Fetcher) ([]byte, error)

LoadManifest reads manifest bytes from a path or fetches a URL.

func ParseVersion

func ParseVersion(v string) (major, minor, patch int, pre string, err error)

ParseVersion parses "vMAJOR.MINOR.PATCH[-PRERELEASE]" into comparable parts. Errors name the offending version.

func RuntimeGOARCH

func RuntimeGOARCH() string

func RuntimeGOOS

func RuntimeGOOS() string

RuntimeGOOS/RuntimeGOARCH expose the platform pair for display.

func SetTestPublicKey

func SetTestPublicKey(pub ed25519.PublicKey)

SetTestPublicKey swaps the trust anchor (TESTS ONLY — the CLI wires it through an internal seam so the embedded key stays the only production trust root).

func Sign

func Sign(m Manifest, priv ed25519.PrivateKey) ([]byte, error)

Sign produces the signed manifest bytes from the core and a private key (the release tool's operation; exported for tests and cmd/gseer-release).

func TrustAnchor

func TrustAnchor() ed25519.PublicKey

TrustAnchor returns the active public key (test override wins).

Types

type ApplyResult

type ApplyResult struct {
	PreviousVersion string
	NewVersion      string
	Replaced        string
	Backup          string // "" when the replace had no prior file
	Bytes           int64
}

ApplyResult reports a completed replacement.

func Apply

func Apply(ctx context.Context, manifestBytes []byte, pub []byte, currentVersion, targetPath string, force bool, fetcher *Fetcher) (*ApplyResult, error)

Apply runs the full chain: verify → version check → platform select → fetch → checksum → atomic replace (+ backup, + rollback on failure). currentVersion is the running binary's version string.

If the replace itself succeeded but the directory durability sync failed, Apply returns the result AND a warning error (see IsDirSyncWarning): the binary was swapped and nothing was rolled back, but the change may not survive a crash or power loss.

type Artifact

type Artifact struct {
	OS     string `json:"os"`
	Arch   string `json:"arch"`
	URL    string `json:"url"`
	SHA256 string `json:"sha256"` // hex
	Size   int64  `json:"size"`
}

Artifact is one platform payload inside a manifest.

func (Artifact) VerifyChecksum

func (a Artifact) VerifyChecksum(payload []byte) error

VerifyChecksum reports whether the artifact's recorded SHA-256 and size match the downloaded payload.

type Error

type Error struct {
	Code string
	What string
	Why  string
	Next string
	Err  error
}

Error is the update system's structured error (what → why → next). Err carries the wrapped underlying cause: Why renders it for humans while Err keeps it machine-inspectable — errors.Is / errors.As walk the live chain (e.g. MoveFileExW error → *os.LinkError → *Error) and the cause is never flattened to a string alone.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the wrapped cause so errors.Is / errors.As can reach it through every layer of the replacement chain.

type Fetcher

type Fetcher struct {
	Client *http.Client
}

Fetcher downloads a URL with a hard cap and timeout. It refuses non-HTTP(S) schemes and redacts userinfo-bearing URLs in errors.

func (*Fetcher) Fetch

func (f *Fetcher) Fetch(ctx context.Context, rawURL string) ([]byte, error)

Fetch reads the whole body (≤ MaxArtifactBytes).

type Manifest

type Manifest struct {
	Version    string     `json:"version"`
	ReleasedAt time.Time  `json:"released_at"`
	Artifacts  []Artifact `json:"artifacts"`
	NotesURL   string     `json:"notes_url,omitempty"`
	Signature  string     `json:"signature,omitempty"` // base64 Ed25519 over the canonical core
}

Manifest is the signed update manifest. The SIGNATURE covers the canonical JSON of ManifestCore (the struct below minus Signature), marshaled by encoding/json (field order is declaration order → deterministic). The signed file is the Manifest with signature set; verification re-marshals the core and checks the Ed25519 signature over those exact bytes.

func Check

func Check(manifestBytes []byte, pub []byte, currentVersion string) (*Manifest, error)

Check runs the verification chain WITHOUT downloading: signature, version relation, and platform availability.

func ParseManifest

func ParseManifest(data []byte) (*Manifest, error)

ParseManifest decodes and structurally validates manifest bytes.

func (Manifest) CanonicalCore

func (m Manifest) CanonicalCore() ([]byte, error)

CanonicalCore renders the exact bytes the signature covers.

func (Manifest) SelectArtifact

func (m Manifest) SelectArtifact(goos, goarch string) (*Artifact, error)

SelectArtifact returns the artifact for the running platform. No guessing: a missing os/arch pair is a hard error.

func (Manifest) VerifySignature

func (m Manifest) VerifySignature(pub ed25519.PublicKey) error

VerifySignature checks the manifest's Ed25519 signature over its canonical core.

Jump to

Keyboard shortcuts

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