update: docs + publish script for the self-update feature
README gets a top-level Updating section; docs/privileges.md gains a step-by-step trust-model writeup of `banger update`. The new scripts/publish-banger-release.sh drives the manual release cut: build, tar, sha256sum, cosign sign-blob, verify against the embedded public key, jq-merge into manifest.json, rclone upload to the R2 bucket. Refuses outright if the embedded key is still the placeholder so we can't accidentally publish an unverifiable release. Also folds in gofmt drift accumulated across the updater package and a few sibling files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8ed351ea47
commit
fae28e3d8b
10 changed files with 310 additions and 33 deletions
|
|
@ -125,4 +125,3 @@ func lastID(xs []int) int {
|
|||
}
|
||||
return max
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ func noParamHandler[R any](call func(ctx context.Context, d *Daemon) (R, error))
|
|||
// live below the map; they need pre-service validation or raw result
|
||||
// encoding that the generic wrapper can't express.
|
||||
var rpcHandlers = map[string]handler{
|
||||
"ping": pingHandler,
|
||||
"shutdown": shutdownHandler,
|
||||
"ping": pingHandler,
|
||||
"shutdown": shutdownHandler,
|
||||
"daemon.operations.list": noParamHandler(daemonOperationsListDispatch),
|
||||
|
||||
"vm.create": paramHandler(vmCreateDispatch),
|
||||
|
|
|
|||
|
|
@ -468,9 +468,9 @@ func TestFirecrackerInstallHintDispatchesByDistro(t *testing.T) {
|
|||
// dispatcher lets us run a real script for one command without
|
||||
// rewiring the rest.
|
||||
type firecrackerVersionRunner struct {
|
||||
real system.Runner
|
||||
canned []byte
|
||||
bin string
|
||||
real system.Runner
|
||||
canned []byte
|
||||
bin string
|
||||
}
|
||||
|
||||
func (r *firecrackerVersionRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -63,11 +63,11 @@ type Manifest struct {
|
|||
// not from the manifest, so manifest tampering can't substitute a
|
||||
// hash for a known-good tarball.
|
||||
type Release struct {
|
||||
Version string `json:"version"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
SHA256SumsURL string `json:"sha256sums_url"`
|
||||
SHA256SumsSigURL string `json:"sha256sums_sig_url,omitempty"`
|
||||
ReleasedAt time.Time `json:"released_at"`
|
||||
Version string `json:"version"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
SHA256SumsURL string `json:"sha256sums_url"`
|
||||
SHA256SumsSigURL string `json:"sha256sums_sig_url,omitempty"`
|
||||
ReleasedAt time.Time `json:"released_at"`
|
||||
}
|
||||
|
||||
// ManifestSchemaVersion is the SchemaVersion this CLI knows how to
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ var expectedReleaseEntries = []string{
|
|||
// StagedRelease describes the result of unpacking a release tarball
|
||||
// into a staging directory.
|
||||
type StagedRelease struct {
|
||||
BangerPath string
|
||||
BangerdPath string
|
||||
VsockAgentPath string
|
||||
BangerPath string
|
||||
BangerdPath string
|
||||
VsockAgentPath string
|
||||
}
|
||||
|
||||
// StageTarball reads the gzipped tar at tarballPath and extracts the
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ const previousSuffix = ".previous"
|
|||
// banger update is a system-mode operation; the developer non-
|
||||
// system-mode flow doesn't go through this code path.
|
||||
type InstallTargets struct {
|
||||
Banger string // /usr/local/bin/banger
|
||||
Bangerd string // /usr/local/bin/bangerd
|
||||
VsockAgent string // /usr/local/lib/banger/banger-vsock-agent
|
||||
Banger string // /usr/local/bin/banger
|
||||
Bangerd string // /usr/local/bin/bangerd
|
||||
VsockAgent string // /usr/local/lib/banger/banger-vsock-agent
|
||||
}
|
||||
|
||||
// DefaultInstallTargets returns the canonical paths a system install
|
||||
|
|
|
|||
|
|
@ -25,25 +25,26 @@ const MaxSignatureBytes int64 = 1024
|
|||
//
|
||||
// Production-cut workflow (for the maintainer cutting v0.1.0):
|
||||
//
|
||||
// 1. Generate the keypair (one-time, store the private key offline):
|
||||
// cosign generate-key-pair
|
||||
// Produces cosign.key (private) and cosign.pub (public). The
|
||||
// private key is password-protected; remember the password.
|
||||
// 1. Generate the keypair (one-time, store the private key offline):
|
||||
// cosign generate-key-pair
|
||||
// Produces cosign.key (private) and cosign.pub (public). The
|
||||
// private key is password-protected; remember the password.
|
||||
//
|
||||
// 2. Replace the PEM block below with the contents of cosign.pub.
|
||||
// Commit. From this point on, every banger CLI baked from this
|
||||
// repo will only trust signatures made with cosign.key.
|
||||
// 2. Replace the PEM block below with the contents of cosign.pub.
|
||||
// Commit. From this point on, every banger CLI baked from this
|
||||
// repo will only trust signatures made with cosign.key.
|
||||
//
|
||||
// 3. At release time, sign SHA256SUMS:
|
||||
// cosign sign-blob --key cosign.key --output-signature \
|
||||
// SHA256SUMS.sig SHA256SUMS
|
||||
// Publish SHA256SUMS.sig alongside SHA256SUMS in the bucket;
|
||||
// the manifest's `sha256sums_sig_url` field references it.
|
||||
// 3. At release time, sign SHA256SUMS:
|
||||
// cosign sign-blob --key cosign.key --output-signature \
|
||||
// SHA256SUMS.sig SHA256SUMS
|
||||
// Publish SHA256SUMS.sig alongside SHA256SUMS in the bucket;
|
||||
// the manifest's `sha256sums_sig_url` field references it.
|
||||
//
|
||||
// 4. Rotating the key after publication means publishing a new
|
||||
// banger release that embeds the new key, then re-signing
|
||||
// every release artifact with the new key. v0.1.x is too
|
||||
// early to design a clean rotation story; defer.
|
||||
//
|
||||
// 4. Rotating the key after publication means publishing a new
|
||||
// banger release that embeds the new key, then re-signing
|
||||
// every release artifact with the new key. v0.1.x is too
|
||||
// early to design a clean rotation story; defer.
|
||||
// var (rather than const) only because tests need to swap it for an
|
||||
// in-test-generated key; production sets it at compile time and
|
||||
// never mutates it.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue