Documentation

Releases

A release is the unit of trust. When a user is running Eidola release N, what they are trusting is the entire bundle that comprises release N — client, server, build inputs, measurements, and the signatures attesting to them. This page explains how a new release becomes trustable. For the technical specification, see trust-root.md.

What a release contains

A single release ships:

The client carries enough information to verify all of this locally before installing an update.

Two cryptographic systems, one transparency log

Eidola uses two signing systems in coordination. Both ride the same Sigstore Rekor transparency log via the same entry shape (hashedrekord v0.0.1).

SurfaceSignatureIdentity binding
CI signs the manifestSigstore bundleFulcio keyless cert tied to the GitHub OIDC workflow identity
Engineer signs a release attestationcosign sign-blob against a hardware-held key (YubiKey-PIV, KMS, etc.)sha256(PKIX SubjectPublicKeyInfo) matches a fingerprint pinned in the client

The CI side gives us “this artifact came from the release workflow on this tag.” The engineer side gives us “a named human, signing under their legal identity, attests to the properties this release claims.” Neither alone is sufficient; both are required.

What the engineer attests to

Every release attestation is a structured JSON document where the engineer makes specific claims under their legal identity. The full template is at releases/schema/attestation-templates.json. The copy that binds is the one embedded in the previous release’s clients — they re-render every claim and reject any mismatch — so release-tool attest renders from the templates as committed at the previous release tag, and a template change takes effect for the release after the one that ships it. The claims include:

These claims are recorded verbatim in the attestation document, hashed into the Sigstore Rekor transparency log, and verified by the client during self-update. The verifier re-renders each claim from a pinned template and rejects any attestation whose claim text does not match character-for-character.

How the client verifies a release

When the user runs eidola update, the client:

  1. Downloads the release index, manifest, and attestation bundles from the published source.
  2. Verifies CI’s manifest signature against the pinned Fulcio identity pattern and the embedded Sigstore trusted root.
  3. Verifies each human attestation: cosign signature against the pinned attestant fingerprint, Sigstore bundle integrity, Rekor inclusion proof.
  4. Counts independent attestations and fails if fewer than MIN_HUMAN_ATTESTATIONS (pinned in the current client) have verified.
  5. Re-renders each claim from the pinned template and checks character equality with the attestation’s recorded claim text.
  6. Checks continuity: the new release’s previous_release.git_commit must equal the currently-installed git_commit.
  7. Surfaces the verified prose to the user before approving the install.

If any step fails, the update is rejected. There is no override prompt.

Why each piece is necessary

A reader might ask: why is CI’s signature not enough? Why is a single engineer’s attestation not enough?

Schema versions: every change is breaking

Every release document carries an integer schema_version. The client refuses to parse any document outside its supported set. There is no “compatible minor” tolerance. This is deliberate: silent acceptance of new fields would let a future release add a claim that older clients ignored, weakening the contract for those clients without their knowledge.

Schema rotations themselves go through a release: a new client release adds the new schema version to its supported set, while still signing releases under the old schema. Only after rolled-out clients accept both versions does the engineer start signing under the new schema.

Release continuity

The client’s update path requires that each release’s claimed previous_release.git_commit equals the currently-installed commit. This rules out two adversary moves:

  1. Stale-release substitution. An attacker serving a real but older release cannot route an updating client onto it; the continuity check fails.
  2. Rollback to a known-bad past release. Even if a past release was later discovered to have a vulnerability and was superseded, the continuity check prevents an attacker from walking an updating client backwards into it. The only release the verifier will accept is the one whose previous_release.git_commit matches the installed commit.

The first-install case (no prior installed commit) is the residual gap; see gaps.md.

For the technical specification

For exact pinning, signing-system formats, schema document shapes, and the build-input vs. build-output story (why artifact-manifest.json and server-enclave.json are separate files), see trust-root.md.

For the operational side — how to actually cut a release, rotate an attestant key, or update the Sigstore trusted root — see releases/README.md.

Edit this page on GitHub