Bastion (J-5) — bakeoff: result signing and verification scheme — 221424ZMAY26
Spun off from #13 (221322ZMAY26 reply). Deferred pending scheme definition.
Background
Standalone runner writes results to results/<model>/<timestamp>.json. #13 established results should be signed and verified — scheme TBD. This thread is the home for that design.
Q1 — Signing scheme
- Ed25519 — fast, small signatures (64 bytes), widely supported. Asymmetric: runner holds private key, verifier holds public key only.
- HMAC-SHA256 — symmetric; simpler to implement, no asymmetric key infrastructure. Requires distributing a shared secret to every authorized runner.
- GPG / minisign — well-known tooling; heavier dependency.
Recommendation: Ed25519. Asymmetric signing means the verifier (submission pipeline or bakeoff-results query layer) never holds the signing key — compromising the verifier does not compromise result integrity. Minisign is a clean minimal implementation if a standalone tool is preferred over bare crypto primitives.
Q2 — Key distribution
- Per-runner keys: each runner generates its own keypair; public key registered in
runners.public_key. Supports attribution per runner; revocation = mark runner DEAD, remove public key from trusted set.
- Shared org key: one keypair for all authorized runners. Simpler; no per-runner attribution.
Recommendation: per-runner keys. Aligns with the runners table already tracking runner identity. runners.public_key TEXT stores the Ed25519 public key. Revocation is clean and already in the dead-runner detection path.
Q3 — What is signed?
- Option A: raw result file bytes —
sign(file_bytes).
- Option B: canonical JSON (sorted keys, no whitespace) —
sign(canonical(result)).
- Option C: content hash sidecar —
sign(SHA256(file_bytes)); embed signature + hash in results/<model>/<timestamp>.json.sig.
Recommendation: Option C. Sidecar keeps result file unchanged and human-readable. Verification is a two-step hash check then signature verify. Sidecar format:
{
"sha256": "<hex>",
"signature": "<base64>",
"runner_id": "<runner_id>",
"signed_at": "<timestamptz>"
}
Q4 — Verification trigger
When is the signature checked?
- At submission time (before DB insert into
run_model_metrics)
- At display time (bakeoff-results query layer)
- Both
Recommendation: submission time as the gate. Reject unsigned or invalid-signature results before they enter the DB. Display-time re-verification is optional hardening — add only if the submission pipeline is not trusted as a sufficient choke point.
@gissf1 — opinions on Q1–Q4 above?
— Bastion
Bastion (J-5) — bakeoff: result signing and verification scheme — 221424ZMAY26
Spun off from #13 (221322ZMAY26 reply). Deferred pending scheme definition.
Background
Standalone runner writes results to
results/<model>/<timestamp>.json. #13 established results should be signed and verified — scheme TBD. This thread is the home for that design.Q1 — Signing scheme
Recommendation: Ed25519. Asymmetric signing means the verifier (submission pipeline or bakeoff-results query layer) never holds the signing key — compromising the verifier does not compromise result integrity. Minisign is a clean minimal implementation if a standalone tool is preferred over bare crypto primitives.
Q2 — Key distribution
runners.public_key. Supports attribution per runner; revocation = mark runner DEAD, remove public key from trusted set.Recommendation: per-runner keys. Aligns with the
runnerstable already tracking runner identity.runners.public_key TEXTstores the Ed25519 public key. Revocation is clean and already in the dead-runner detection path.Q3 — What is signed?
sign(file_bytes).sign(canonical(result)).sign(SHA256(file_bytes)); embed signature + hash inresults/<model>/<timestamp>.json.sig.Recommendation: Option C. Sidecar keeps result file unchanged and human-readable. Verification is a two-step hash check then signature verify. Sidecar format:
{ "sha256": "<hex>", "signature": "<base64>", "runner_id": "<runner_id>", "signed_at": "<timestamptz>" }Q4 — Verification trigger
When is the signature checked?
run_model_metrics)Recommendation: submission time as the gate. Reject unsigned or invalid-signature results before they enter the DB. Display-time re-verification is optional hardening — add only if the submission pipeline is not trusted as a sufficient choke point.
@gissf1 — opinions on Q1–Q4 above?
— Bastion