Skip to main content

Signing qualification evidence

This page is part of the operator guide. The commands are in the CLI reference. Etalon can sign an evidence bundle with Ed25519. The signature is evidence that the hash manifest was produced by the holder of a seed. It does not determine that a regulation, contract, or internal control has been met. The runner version is 0.0.1. Unsigned bundles from earlier runs still verify.

What is signed

etalon sign signs the raw bytes of hashes.json as they are stored on disk. That file lists every evidence file and its SHA-256 digest. The pack summary is one of those files, and it records the pack content hash, so the signature binds the pack identity that the run evaluated. The verifier does not re-serialize hashes.json before checking. signature.json is written next to the bundle. It is not listed in hashes.json. A signature cannot cover a file that contains the signature. Any other extra file in the run directory still fails verification. The signature document records version 1, algorithm ed25519, the payload name hashes.json, the SHA-256 of those bytes, the public key, and the 64-byte signature as hex.

Keys

The algorithm is Ed25519 (RFC 8032). A private key is a 32-byte seed. A public key is 32 bytes. Implementation is the Python standard library in etalon/evidence/ed25519.py. It is not a general-purpose cryptography library. The arithmetic is not written to resist side channels. Use it to sign evidence on the operator machine. Generate a pair:
etalon keygen refuses to overwrite an existing file. The private file is created with mode 0600. Both files are JSON. Private file, purpose etalon-evidence-sign:
Public file, purpose etalon-evidence-verify:
How to hold them:
  • The seed is not encrypted. Protect the private file with filesystem permissions and by keeping it off shared machines.
  • Do not commit the private file. Do not put it in a bundle, a log, or a CI secret that the job prints.
  • Do not pass the private file to etalon verify or to the GitHub Action. Those commands reject a file that contains a seed.
  • The public file is what auditors and the deployment gate receive. It can live in the deploy repository.
  • There is no key server, registry, or rotation service. To change keys, generate a new pair and sign new bundles. Older bundles keep verifying with the public key that signed them.
  • A signature check that uses only the public key embedded in signature.json detects edits that were not re-signed. It does not identify the holder of the seed. Pass the public key from your own files when the signer matters.

Sign and verify with the runner

Signing refuses a bundle that fails hash, fingerprint, or manifest checks.
etalon verify without a public key still checks hashes. If signature.json is present, it must verify under the embedded key. --public-key requires a signature and requires that key. --require-signature fails closed when the signature file is absent. Exit 0 means the check passed. Exit 1 means it did not. These edits fail a signed bundle:
  • a hashed file changes and hashes.json is left as it was
  • a hashed file changes and hashes.json is rewritten to the new digest without a new signature
  • signature.json is replaced by a signature from a different seed while verification uses the original public key

Verify without installing Etalon

examples/verify_bundle.py checks the file set, the SHA-256 digests, manifest decision agreement, and the Ed25519 signature. It loads etalon/evidence/ed25519.py by path. It does not import the Etalon package, so it does not need the runner’s dependencies and it does not use the network. Keep this layout, or an equivalent checkout:
The public key is required. Exit 0 is a match. Exit 1 is a failed check. Exit 2 means the public key file could not be read. etalon verify also checks the fingerprint against the fingerprint schema. etalon sign refuses to sign a bundle that fails that check. The air-gapped script then checks that those signed bytes are unchanged. It does not re-implement the fingerprint schema.

Deployment gate

.github/actions/etalon-verify runs the air-gapped script. The step fails when the bundle path is missing, the public key path is missing, or verification exits non-zero. Later deploy steps do not run after that failure.
.github/workflows/etalon-evidence-gate.yml is the same check, triggered only by workflow_dispatch or workflow_call. It is not part of this repository’s pull-request test workflow. The gate checks a qualification evidence bundle. It does not decide that a deployment meets a regulation or a contract.

Auditor export

The command writes a directory. It does not modify the bundle.
  • auditor-report.html — decision, fingerprint, coverage statement, judge and calibration summary when a judge ran, hashes, and signature status
  • manifest.json — the same record, plus the SHA-256 of the HTML file
Coverage text states that realised coverage is value-presence and is not a cross-product of coverage cells. The export does not copy raw case inputs or outputs. When the bundle fails verification, the package still records the failure and the command exits 1. The statement on the package is: this is qualification evidence for human review. It does not determine that a regulation, contract, or internal control has been met.

Out of scope

Passphrase encryption of the seed, a key registry, signing a pack directory as its own artifact, and a hosted verification service are not part of this version. Pack identity is already inside the signed hash manifest.