Commitments
A commitment is the keccak256 hash of exact content — how Elgora addresses a document, and how anyone checks they were served the right one.
A commitment is keccak256(bytes) — the hash of exact content, and nothing
more. Same bytes, same 32-byte value; one byte different, an unrelated one.
It does two jobs. It addresses content: one value names one document, which is how a bounty refers to its challenge and how the API is keyed. And it makes content checkable: anyone holding the bytes recomputes the value and knows they have the same document as everyone else.
So storage here is an availability layer, not a source of truth. Wrong bytes are caught by re-hashing them, and if storage disappears the record of what was committed survives on-chain.
keccak256 because it is the EVM's native hash. The digests inside a
Submission envelope are SHA-256 — those are checked off-chain.
What a commitment is not
| Not | Because |
|---|---|
| Secret | A hash of public bytes hides nothing. Submissions are private because they are encrypted, not because they are committed |
| Access | Holding a submission_commitment gets you nothing. The API decides access from live contract state |
| Authorship | It says a document exists, never who wrote it. Signatures do that |
| Availability | It proves what the bytes were, not that anyone still has them |
The three on-chain commitments
| Commitment | Commits to | Written by | Recorded on-chain at |
|---|---|---|---|
spec_commitment | The exact approved bounty_challenge.md bytes | The Poster | Bounty creation |
submission_commitment | One Solver's sealed Submission envelope | The Solver | Each submit call |
report_commitment | One Guardian's written Verdict document | A Guardian | Each recorded Verdict |
There is no canonicalization step for the challenge: a single changed byte is a different commitment, and therefore a different bounty.
spec_commitment
keccak256(bounty_challenge.md UTF-8 bytes). This is what makes a published
challenge immutable: the Poster cannot soften a criterion after Solvers start
work, and Elgora cannot serve one page to a Solver and a different one to a
Guardian.
Reproduce it with no network, no wallet, and no configuration:
elgora-cli spec-commitment ./bounty_challenge.mdWhat it does not cover: discovery metadata, where the page is served from, drafting history, written Verdicts, or Submission artifacts. And if the challenge lists outside input files, their names, locations, and stated SHA-256 hashes are committed — the file bytes at those locations are not. The listed hash is what identifies them.
submission_commitment
keccak256 of the canonical JSON of your sealed envelope. The envelope
contains the deployment context, digests of the encrypted bundle, the
recipients commitment for the pinned Guardian roster, the reveal policy, the
time-locked key bag, and the storage locator of the ciphertext — but no
plaintext and no usable key.
So the commitment proves which sealed submission you recorded, at the block you recorded it, without revealing anything about its content. A Guardian later fetches the envelope, re-derives the commitment, and refuses to judge anything that does not match what the contract has.
One per Solver per bounty: re-submitting overwrites the value on-chain.
report_commitment
keccak256 of a Guardian's written Verdict document. The on-chain Verdict
carries the commitment; the document itself is published through the API and is
public from the moment it exists.
Note what the contract counts as agreement: outcome, named winner, awarded
submission commitment, and payout data. The report_commitment is not part
of that tuple — Guardians must reach the same decision, not write the same
words.
Resolving a commitment back to its bytes
| Have | Want | Do this |
|---|---|---|
spec_commitment | The challenge | GET /api/bounties/{bounty_id} returns the committed Markdown; re-hash it and compare |
submission_commitment | The envelope | GET /api/bounties/{bounty_id}/submissions/{submission_commitment}/content — authenticated, and only for the funding Poster or a pinned Guardian |
report_commitment | The written Verdict | GET /api/written-verdicts/{report_commitment} — public |
In every case the same rule applies: re-derive and compare before you rely on it. A commitment you did not recompute is a claim, not a proof.
Where to keep them
They are all public, so there is nothing to protect — only something to not lose.
- Posters: keep
bounty_id,spec_commitment, and the creation transaction hash. Keep your own copy of the approvedbounty_challenge.mdtoo; it is the preimage, and holding it means you can prove what you published without depending on anyone's storage. - Solvers: keep your final
submission_commitmentand its transaction hash. Keep your artifact directory: it is the only copy of your plaintext that exists anywhere. - Integrators: commitments are the natural join key. Both the API and the subgraph key on them, so recording the commitment is enough to look everything else up later.
What auditability actually gives you
Chain state is permanent and independently verifiable: which bounty existed, what it committed to, who submitted what and when, which Guardians voted for which outcome, and how the money moved.
The readable artifacts — the challenge text, the written Verdicts, the Submission envelopes — are off-chain. As long as those bytes are still available, from Elgora or from a copy you kept, anyone can prove they are the committed ones. If a preimage is lost, the commitment remains as a record that something specific was committed, but the document itself is not recoverable from the chain. That is the honest boundary: the chain proves integrity, not availability.