// Package bundle is the declaration the host carries. // // novox/hq ADR 0038: the host has one behaviour and two sources of declaration — the control // plane when a mesh is reachable, and this when none is. The first node is not a different // kind of node; it is a node whose mesh is not up yet, and this is what it applies until it is. // // Carried inside the binary rather than beside it, because "copy it onto a machine and run it // is the whole installation" (ADR 0041) stops being true the moment a second file has to // arrive with it. package bundle import ( _ "embed" "errors" "strings" "github.com/novox/mesh-host/internal/declaration" ) // substrate is the pinned tier-1 descriptor, appliable with no mesh present. // // A host built without one carries the placeholder below, and says so rather than applying // nothing and reporting success — a host that silently did nothing on a first node would look // exactly like one that worked. // //go:embed substrate.lock var substrate []byte // ErrEmpty means this host was built without a bundle. var ErrEmpty = errors.New( "this host carries no bundle. A host built without one cannot raise a first node, and " + "applying nothing would look exactly like applying something") // Raw returns the carried bytes, for inspection. func Raw() []byte { return substrate } // IsEmpty reports whether anything was built in. A bundle of only comments and whitespace is // empty for this purpose: the placeholder is a comment, and treating it as content would mean // a default build claims to carry a substrate. func IsEmpty() bool { for _, line := range strings.Split(string(substrate), "\n") { line = strings.TrimSpace(line) if line != "" && !strings.HasPrefix(line, "//") { return false } } return true } // Load parses the carried bundle. // // The same parser the link will use. A bundle that reaches a machine and is then refused by the // host that carries it would be a build-time mistake discovered at the worst possible moment, // which is why `mesh-host bundle` exists to ask before it matters. func Load() (*declaration.Declaration, error) { if IsEmpty() { return nil, ErrEmpty } return declaration.Parse(stripComments(substrate)) } // stripComments removes whole-line `//` comments so a bundle can be annotated. // // It is JSON on the wire and a pinned, hand-authored artefact here, and a pinned thing nobody // can annotate is a pinned thing nobody can review. Only whole lines: anything cleverer would // need to know where strings begin and end, and a parser that half-understands its input is // worse than one that does not try. func stripComments(raw []byte) []byte { var kept []string for _, line := range strings.Split(string(raw), "\n") { if strings.HasPrefix(strings.TrimSpace(line), "//") { continue } kept = append(kept, line) } return []byte(strings.Join(kept, "\n")) }