release

package
v0.6.1 Latest Latest
Warning

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

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

Documentation

Overview

Package release builds immutable, content-addressed release bundles: a deterministic tar holding manifest.json plus every rendered file (as blobs/<sha256>). The tar's sha256 is the release digest.

Index

Constants

View Source
const (
	BOMMediaType        = "application/vnd.cyclonedx+json"
	ProvenanceMediaType = "application/vnd.in-toto+json"
	StatementType       = "https://in-toto.io/Statement/v1"
	SLSAProvenanceType  = "https://slsa.dev/provenance/v1"
	BuildType           = "https://github.com/dshakes/halos/release-build/v1"
)

Media and predicate types.

View Source
const AnyPlatform = "any"

AnyPlatform keys an artifact that is the same on every OS/arch (npm).

Variables

This section is empty.

Functions

func BuildRing

func BuildRing(org *policy.Org, ring string, opts Options) (rel *Release, variants []*Release, err error)

BuildRing builds ring's release plus one release per variant of every running client-axis experiment enrolling ring. The ring release manifest lists those experiments (salt, weights, channel per variant) so halod can assign a device to a variant with the gateway's hash and pull the channel. Each variant release is built from the variant's profile (extends resolved) with Experiment/Variant set, so its harness config carries the attribution.

func CheckRendered

func CheckRendered(harnessName string, os harness.OS, files []harness.File, p *policy.Profile, g *policy.Gateway) error

CheckRendered asserts security invariants on the files an adapter rendered, by parsing the output rather than trusting how it was produced. It is the backstop for adapter regressions and for harnesses.<h>.overrides, which are deep-merged after rendering. Any violation fails the build.

func Diff

func Diff(a, b *Release) string

Diff returns a human-readable summary of what changes going from a to b: harness version/install changes, and per-file added/removed/changed with a line diff (changed lines only, no context; ponytail: add hunks if reviewers ask). Empty string means no differences.

func VariantVersion

func VariantVersion(ver, ring, exp, variant string) string

VariantVersion is the version label of the variant release published with ring release ver: "<ver>-x-<experiment>.<variant>" (or the hashed channel suffix, see policy.ChannelName). Deterministic, so a rollback of the ring to ver can re-point every channel at the variant releases published with it.

func VerifyAttestations added in v0.4.0

func VerifyAttestations(rel *Release, bom, prov []byte) error

VerifyAttestations checks that bom and prov describe rel: both name rel's digest, every rendered file, artifact and install artifact in the manifest is in the BOM with the same hash and nothing else is, the provenance's artifact and install dependencies match the manifest, and its BOM byproduct digest is bom's. Signatures are checked by internal/bundle.

Types

type Artifact

type Artifact struct {
	URL       string       `json:"url"`
	SHA256    string       `json:"sha256,omitempty"`    // lowercase hex
	Integrity string       `json:"integrity,omitempty"` // SRI "sha512-<base64>" (npm dist.integrity)
	Size      int64        `json:"size,omitempty"`      // exact byte size when known
	Kind      ArtifactKind `json:"kind"`
	BinPath   string       `json:"binPath"`           // binary: installed file name; npm: bin command name
	Package   string       `json:"package,omitempty"` // npm package name
}

Artifact is one downloadable, hash-pinned installer input.

func (Artifact) Validate

func (a Artifact) Validate() error

Validate is what halod checks before downloading anything.

type ArtifactKind

type ArtifactKind string

ArtifactKind says how halod installs an artifact.

const (
	// KindBinary is a single self-contained executable, placed atomically in
	// halod's root-owned bin dir.
	KindBinary ArtifactKind = "binary"
	// KindNPMTgz is an npm package tarball installed with lifecycle scripts
	// disabled and no user/global npmrc.
	KindNPMTgz ArtifactKind = "npm-tgz"
)

type ArtifactResolver

type ArtifactResolver struct {
	HTTP        *http.Client // default: 60s timeout
	ClaudeBase  string       // default https://downloads.claude.ai/claude-code-releases
	NPMRegistry string       // default https://registry.npmjs.org
}

ArtifactResolver fills HarnessEntry.Artifacts from the vendors' release metadata.

func (ArtifactResolver) Resolve

func (r ArtifactResolver) Resolve(ctx context.Context, rel *Release) (*Release, error)

Resolve returns a copy of rel (re-packed, so the digest changes) with artifacts for every harness pinned to an exact version. Harnesses without a known source or with a non-exact version get a manifest warning instead.

type AttestOptions added in v0.4.0

type AttestOptions struct {
	HalosVersion string // version of the halo that built it
	BuilderID    string // default https://github.com/dshakes/halos/cmd/halo@<HalosVersion>
	PolicyRepo   string // policy repo URL, if known
	PolicyCommit string // policy repo commit (git rev-parse HEAD), if known
	PolicyDirty  bool   // the working tree had uncommitted changes
}

AttestOptions are the build facts the release itself does not carry.

type Attestations added in v0.4.0

type Attestations struct {
	BOM        []byte
	Provenance []byte
}

Attestations are a release's CycloneDX 1.6 AI-BOM and its in-toto Statement v1 with a SLSA provenance v1 predicate, as JSON.

func Attest added in v0.4.0

func Attest(rel *Release, org *policy.Org, o AttestOptions) (*Attestations, error)

Attest builds rel's AI-BOM and provenance. org supplies the models and MCP servers rel's profile references (nil: only what the manifest holds). The output is deterministic for the same inputs.

type CloudAgentFile added in v0.4.0

type CloudAgentFile struct {
	Vendor string   `json:"vendor"`
	Path   string   `json:"path"` // relative to the repository root
	SHA256 string   `json:"sha256"`
	Repos  []string `json:"repos,omitempty"` // policy repo patterns it is for
}

CloudAgentFile is one rendered cloud agent repository file.

type Experiment

type Experiment struct {
	Name     string              `json:"name"`
	Salt     string              `json:"salt"`
	Variants []ExperimentVariant `json:"variants"`
}

Experiment is one client-axis experiment in a ring release manifest: just what halod needs to compute the same assignment as the gateway.

func (Experiment) Policy

func (e Experiment) Policy(ring string) *policy.Experiment

Policy returns the policy experiment halod evaluates for a manifest entry: running, enrolling ring, with the manifest's salt and weights, so policy.Experiment.ResolveVariant (the gateway's function) does the hashing.

type ExperimentVariant

type ExperimentVariant struct {
	Name    string  `json:"name"`
	Weight  float64 `json:"weight"`
	Channel string  `json:"channel"`
}

ExperimentVariant is a variant and the channel serving its release.

type FileEntry

type FileEntry struct {
	Path   string `json:"path"`
	Mode   uint32 `json:"mode"`
	SHA256 string `json:"sha256"`
	// Artifact/ArtifactPath tie the file to a Manifest.SupplyChain entry and
	// its path inside it; halod checks both before writing (ADR-0028).
	Artifact     string `json:"artifact,omitempty"`
	ArtifactPath string `json:"artifactPath,omitempty"`
}

FileEntry describes one rendered file in the manifest.

type HarnessEntry

type HarnessEntry struct {
	Version        string                 `json:"version"`
	InstallCommand map[string]string      `json:"installCommand,omitempty"`
	Files          map[string][]FileEntry `json:"files"`
	// Artifacts are verified install artifacts keyed by "<goos>-<goarch>"
	// (or "any"), filled by ResolveArtifacts; see artifacts.go.
	Artifacts map[string]Artifact `json:"artifacts,omitempty"`
}

HarnessEntry is one harness's pinned version, installer and files, per OS.

func (HarnessEntry) ArtifactFor

func (h HarnessEntry) ArtifactFor(goos, goarch string) (Artifact, bool)

ArtifactFor picks the artifact for goos/goarch, falling back to AnyPlatform.

type Manifest

type Manifest struct {
	SchemaVersion int                     `json:"schemaVersion"`
	Org           string                  `json:"org"`
	Version       string                  `json:"version"`
	Profile       string                  `json:"profile"`
	Ring          string                  `json:"ring"`
	CreatedAt     time.Time               `json:"createdAt"`
	Harnesses     map[string]HarnessEntry `json:"harnesses"`
	Warnings      []string                `json:"warnings,omitempty"`
	// Experiment/Variant are set on a client-axis variant release only: the
	// release served on channel policy.ChannelName(Ring, Experiment, Variant).
	Experiment string `json:"experiment,omitempty"`
	Variant    string `json:"variant,omitempty"`
	// Experiments (ring releases only) lists the running client-axis
	// experiments enrolling Ring. Signed with the release, it is what halod
	// uses to pick a device's variant channel.
	Experiments []Experiment `json:"experiments,omitempty"`
	// Toggles are the client-axis feature toggles: targeting rules plus the
	// per-harness fragments halod merges for the toggles that are on for it.
	Toggles []ToggleEntry `json:"toggles,omitempty"`
	// Units are the org units (ADR-0018): member selectors plus each unit's
	// rendered files, which halod applies in place of the release's own.
	Units []UnitEntry `json:"units,omitempty"`
	// RemoteAgents pins the A2A agents this ring's members (or some of its
	// units' members) may call, with each AgentCard's digest (ADR-0024):
	// signed with the release, so a changed card is a policy change.
	RemoteAgents []RemoteAgentEntry `json:"remoteAgents,omitempty"`
	// SupplyChain pins the extension artifacts this release delivers
	// (ADR-0028): each one's digest and file listing, signed with the release.
	SupplyChain []SupplyChainEntry `json:"supplyChain,omitempty"`
	// MCPMode and MCPServers carry the halos.yaml mcp registry (ADR-0029):
	// its mode and the servers ring's members (or some of its units' members)
	// get, pinned, so halod reports unapproved and version-drifted servers in
	// user configs. Empty when the policy has no registry.
	MCPMode    string          `json:"mcpMode,omitempty"`
	MCPServers []policy.MCPPin `json:"mcpServers,omitempty"`
	// CloudAgents are the repository files of the workflow-configured cloud
	// agents (ADR-0034), signed with the release for review and the PR that
	// adds them. halod never writes them: they belong in repositories.
	CloudAgents []CloudAgentFile `json:"cloudAgents,omitempty"`
}

Manifest is manifest.json inside the bundle.

func (Manifest) Artifact added in v0.4.0

func (m Manifest) Artifact(name string) (SupplyChainEntry, bool)

Artifact returns the supply-chain entry named name.

func (Manifest) Channels

func (m Manifest) Channels(ring string) []string

Channels returns the variant channels m routes ring's devices to. A release built for another ring (promoted across rings) routes nowhere: its experiments enrolled the ring it was built for.

func (Manifest) CheckArtifactFile added in v0.4.0

func (m Manifest) CheckArtifactFile(f FileEntry, data []byte) error

CheckArtifactFile is what halod runs before writing an artifact file: the artifact's listing must hash to its pinned digest and list f's path with f's hash, and data (when non-nil) must hash to that too.

func (Manifest) CheckArtifactFiles added in v0.4.0

func (m Manifest) CheckArtifactFiles() error

CheckArtifactFiles runs CheckArtifactFile (without content) over every artifact file the manifest delivers: harness, unit and toggle files.

func (Manifest) MatchUnit added in v0.3.0

func (m Manifest) MatchUnit(s policy.Subject) *UnitEntry

MatchUnit returns the manifest unit s belongs to (policy.MatchUnit), or nil.

type Options

type Options struct {
	Org       string          // defaults to the resolver's Org.Name when it is a *policy.Org
	Version   string          // release version label, e.g. "1.4.0"
	CreatedAt time.Time       // zero => Unix epoch, keeping builds reproducible
	OSes      []harness.OS    // defaults to darwin, linux, windows
	Gateway   *policy.Gateway // defaults to Org.Gateway when resolver is a *policy.Org
	// Experiment/Variant mark a client-axis variant release: stamped into the
	// manifest and into the harness config (telemetry resource attributes).
	Experiment, Variant string
	// Experiments is the ring release's client-axis experiment table (see BuildRing).
	Experiments []Experiment
}

Options tunes Build. Zero value is valid.

type ProfileResolver

type ProfileResolver interface {
	ResolveProfile(name string) (*policy.Profile, error)
}

ProfileResolver is satisfied by *policy.Org once it grows ResolveProfile.

type Release

type Release struct {
	Manifest Manifest
	Blobs    map[string][]byte // sha256 hex -> content
	Tar      []byte
	Digest   string // "sha256:<hex>" of Tar
	// Attestations (AI-BOM and provenance, see Attest) describe the release
	// by digest; they are not part of Tar. bundle.Publish stores them as a
	// signed OCI referrer when set.
	Attestations *Attestations
}

Release is a built (or opened) bundle.

func Build

func Build(org ProfileResolver, profileName, ringName string, opts Options) (*Release, error)

Build renders profileName for every enabled harness and OS.

func Open

func Open(tarData []byte) (*Release, error)

Open parses a bundle tar and verifies every blob against its name and every manifest entry against a blob. It does NOT verify signatures; see internal/bundle. Only regular files are accepted, and since the tar is uncompressed no entry may yield more bytes than the tar itself holds: archive/tar expands PAX/GNU sparse entries, so a few KiB could otherwise decode to gigabytes.

func (*Release) Content

func (r *Release) Content(f FileEntry) ([]byte, bool)

Content returns the bytes for a manifest file entry.

type RemoteAgentEntry added in v0.3.0

type RemoteAgentEntry struct {
	Name    string   `json:"name"`
	CardURL string   `json:"cardURL"`
	Digest  string   `json:"digest"`
	Skills  []string `json:"skills,omitempty"`
	// Units, when set, are the only units (and units below them) whose
	// members may call it; empty = every member of the ring.
	Units []string `json:"units,omitempty"`
}

RemoteAgentEntry is one allowlisted remote agent in a release manifest.

type SupplyChainEntry added in v0.4.0

type SupplyChainEntry struct {
	Name   string `json:"name"`
	Kind   string `json:"kind"`
	Source string `json:"source"`
	// Digest is supplychain.DigestOf(Files): the content pin.
	Digest string `json:"digest"`
	// Signer is the trusted identity whose signature over Digest verified at
	// build time, if the artifact is signed.
	Signer string `json:"signer,omitempty"`
	// Files is the listing the digest covers: path in the artifact -> sha256
	// hex. Empty when the content was not resolved (nothing is delivered).
	Files map[string]string `json:"files,omitempty"`
}

SupplyChainEntry is one extension artifact in a release manifest (ADR-0028).

type ToggleEntry

type ToggleEntry struct {
	Name    string              `json:"name"`
	Default bool                `json:"default"`
	Expires string              `json:"expires,omitempty"`
	Rules   []policy.ToggleRule `json:"rules,omitempty"`
	// Fragments is harness -> OS -> deltas, one per file the toggle changes
	// (FileEntry.Path is the managed file the delta applies to; the blob is
	// JSON, see toggle.Delta). The release's own files are the toggle-off state.
	Fragments map[string]map[string][]FileEntry `json:"fragments,omitempty"`
}

ToggleEntry is one client-axis toggle in a release manifest. Being inside the signed manifest, neither the rules nor the fragments can be altered in transit.

type UnitEntry added in v0.3.0

type UnitEntry struct {
	Name   string   `json:"name"`
	Parent string   `json:"parent,omitempty"`
	Groups []string `json:"groups,omitempty"`
	Users  []string `json:"users,omitempty"`
	// Files is harness -> OS -> the files the unit's effective profile renders
	// differently from the release's own: whole files (not deltas), so an
	// overlay can narrow a list. The release's own files are the no-unit state.
	Files map[string]map[string][]FileEntry `json:"files,omitempty"`
}

UnitEntry is one org unit in a release manifest (ADR-0018): its member selector, so halod picks the device's unit with policy.MatchUnit, and the unit's rendered files. Being inside the signed manifest, neither can be altered in transit.

func (UnitEntry) Policy added in v0.3.0

func (e UnitEntry) Policy() *policy.Unit

Policy returns the unit as halod evaluates it.

Jump to

Keyboard shortcuts

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