Skip to content

Bundle spec — what bundle.json is and how install.sh consumes it

A bundle is the per-customer, per-release configuration document Seglamater ships to you alongside the installer. It pins exactly which image digest your install pulls, on which channel you'll receive future updates, and which cosign public key your updater trusts. The bundle is small — a single JSON object — but it is the source of truth for your install's identity from the moment you run install.sh.

The release-side authoring code lives in crates/hivemind-release/src/bundle.rs. The consumer side lives in scripts/install.sh (the validation block). When the two diverge, new installs break; v0.1.0 shipped a manifest-shaped document where the bundle should have been and AIWF's first install rejected it. The v0.1.1+ shape documented here is the one install.sh validates.

Where the bundle comes from

You receive the bundle via your invite URL, the one-shot bootstrap endpoint your Seglamater contact emails you:

curl -fsSL https://s.seglamater.app/i/<invite-id> | bash

The bootstrap script:

  1. Fetches bundle.json (this document) + a sibling bundle.json.sha256 integrity hash from s.seglamater.app.
  2. Writes both into your install dir under ./scripts/bundle.json.
  3. Invokes ./scripts/install.sh --bundle ./scripts/bundle.json to render .env + bring up the Compose stack with the pinned image.

You never edit bundle.json by hand. If anything inside it needs to change (you're switching channels, a new release is out, your support tier changed), you ask your Seglamater contact to issue a fresh invite and re-run the bootstrap.

Lifecycle: when is a bundle minted?

  • First install — Seglamater issues an invite scoped to your organization. The bundle on the other side of that invite is pinned to a specific image digest on your initial channel (usually canary for new customers, stable for established ones).
  • Major upgrade — when you opt in to a major version Seglamater may cut you a fresh bundle so the pinned image digest in your install dir matches the new line.
  • Channel change — switching canarybetastable (or back) is bundle-mediated. There is no in-app channel switcher today.

Day-to-day point releases do not require a new bundle. The managed-update sidecar polls updates.seglamater.app on your bundle's pinned channel + applies the new signed image digest in-place. See upgrade.md.

Fields

The shape install.sh validates is schema_version: 1. The required fields are the four at the top of install.sh's python validator (line 240-257 in scripts/install.sh, kept in sync with the schema):

Required

Field Type Validation Why
schema_version integer MUST equal 1 Lets the installer fail fast on a v2 bundle it can't read.
app string MUST equal "hivemind" Prevents accidentally piping an atlas or vantage bundle here.
version string Free-form, conventionally semver Stamped into .env as HIVEMIND_VERSION; surfaced at /api/v1/health.
channel string "canary", "beta", or "stable" What hivemind-updater polls. Bundle wins over the operator's --channel flag.

Optional but always present in real bundles

Field Type What
created_at string RFC-3339 timestamp the bundle was cut. Diagnostic.
manifest_url string Where the public release manifest lives — https://updates.seglamater.app/hivemind/releases/<version>/manifest.json. Used by cosign verify + by the updater poll.
registry.image string Floating-tag image ref, e.g. registry.seglamater.app/seglamater/hivemind-server:0.1.8.
registry.digest string sha256:<hex> — the pinned image digest. This is the tamper-evident pin.
registry.ref_with_digest string Convenience: <image>@<digest>. What install.sh actually pulls.
cosign.pubkey_url string Where to fetch the cosign public key (default https://updates.seglamater.app/.well-known/keys/hivemind.pub).
cosign.pubkey_sha256 string SHA-256 of the pubkey. install.sh fetches the key + asserts the hash matches before writing secrets/cosign_pub. A mismatch aborts install.
support.email string The mailbox for your install's tier — usually support@seglamater.com.
support.docs_url string Where the published customer docs live.
files array of {path, role} What the bootstrap fetched alongside the bundle. Informational; install.sh doesn't re-fetch from this list. Common roles: compose, env-template, license.

What's NOT in the bundle (intentional)

  • No secrets. Database passwords, admin API keys, JWT keys, the integration encryption key, cookie secrets — all of those are generated locally by install.sh into ./secrets/ on your host and never travel over the network. Seglamater never sees them. This is the security model: even a compromised bundle delivery channel cannot leak your install's keys.
  • No LLM API key. Hivemind is BYOK; you set HIVEMIND_LLM_API_KEY in .env post-install. See byok-llm.md.
  • No URL / hostname your install runs at. HIVEMIND_PUBLIC_URL is set by you in .env; the bundle has nothing to say about your reverse-proxy hostname.

Integrity + signing

Three layers of integrity gate the install:

  1. Bundle SHAbundle.json is delivered alongside a sibling bundle.json.sha256. install.sh reads both, recomputes the SHA-256 of bundle.json, and aborts if it doesn't match. A missing .sha256 is a warning, not a hard fail (some out-of-band delivery paths skip it), but the warning is loud.
  2. Cosign pubkey pin — the cosign public key fetched from pubkey_url is hash-checked against cosign.pubkey_sha256 before being written to secrets/cosign_pub. A mismatch aborts install immediately (no .tmp left around to confuse retries).
  3. Image digest pininstall.sh pulls registry.ref_with_digest (the image@sha256:… form). Docker verifies the manifest digest at pull time; substituting a different image on the registry can't slip through.

The bundle itself is also cosign-signed on the publisher side (see dist/bundle.json.sig in the cut). v1 install.sh does not yet verify that signature — it relies on the SHA-256 + the bootstrap delivery channel. The bundle-cosign verification step is on the roadmap; the artifacts are already published so the verification can be turned on without a re-cut.

What install.sh actually does with each field

Tracing the consumer code: scripts/install.sh starts in argument parsing, hits step 2 ("Validating bundle"), runs python against bundle.json to validate + extract a small set of fields into shell variables, then proceeds through env rendering + image pull.

Bundle field Effect on install
app Asserted equal to "hivemind".
schema_version Asserted equal to 1.
version Written to .env as HIVEMIND_VERSION=<version>. Surfaced at /api/v1/health and used in the boot log version line.
channel Written to .env as HIVEMIND_UPDATER_CHANNEL=<channel>. The updater sidecar polls that channel's manifest.
registry.ref_with_digest docker pull <ref_with_digest> — the actual server image fetch.
cosign.pubkey_url + cosign.pubkey_sha256 Fetched + hash-checked into secrets/cosign_pub.
Everything else Available for diagnostic logging or future use; not load-bearing for the initial install.

If a required field is missing, install.sh prints bundle missing required fields: … and exits non-zero before touching docker. If schema_version isn't 1 or app isn't hivemind, it prints the same sort of error. The fail-closed posture is intentional: a bad bundle should never produce a half-installed stack.

Error modes + what they mean

Symptom What's wrong Fix
install.sh: bundle.json parse failed: … The file isn't valid JSON. Bootstrap pulled a corrupted file; re-run the invite. Don't hand-edit.
install.sh: bundle missing required fields: … One of schema_version / app / version / channel is absent. Bundle is malformed — contact your Seglamater contact for a fresh invite.
install.sh: bundle schema_version != 1 You're on an installer that pre-dates a v2 bundle (forward), or the bundle was issued for a different installer line. Upgrade install.sh (re-run the invite) or ask for a v1 bundle.
install.sh: bundle app != 'hivemind' You piped an atlas / vantage / other bundle through hivemind's installer. Re-fetch the right invite.
cosign public key SHA-256 mismatch The fetched pubkey doesn't match the pin in the bundle. Investigate before re-running. This is either an in-flight MITM or a stale bundle against a key rotation. Don't override; contact security@seglamater.com.
Bundle SHA-256 mismatch warning bundle.json was modified after bundle.json.sha256 was generated. Re-bootstrap from the invite. Don't edit the bundle.

When to ask Seglamater for a fresh bundle

  • You're switching channel and want it to stick.
  • You believe the bundle was corrupted in delivery.
  • Cosign pubkey rotation happened on Seglamater's side (we'll tell you, but if you see the SHA mismatch first the fix is a new bundle).
  • A major version cut you opted in to that the v1 updater couldn't apply in-place (rare — most upgrades are point-release in-channel).

For routine point releases you do not need a fresh bundle — the sidecar pulls the new signed image on the channel your existing bundle already pins.

See also

  • bundle-schema.json — the formal JSON Schema (draft 2020-12) for programmatic validation in your tooling.
  • bundle-example.json — a complete, realistic bundle you can use as a reference when reading your own.
  • install.md — the install walkthrough; what happens before, during, and after install.sh consumes the bundle.
  • upgrade.md — what the channel + image digest in your bundle drive through the managed-update path.
  • license.md — the license terms governing your use of the product the bundle gives you access to.