Submission privacy
How a Submission is sealed, why nobody can open it before the deadline, and who can read it afterwards.
A Solver's work is encrypted on their own machine before anything leaves it. Elgora stores ciphertext; no plaintext artifact and no artifact location ever goes on-chain.
Two mechanisms combine. Understanding both is the difference between "they promise not to look" and "they cannot look yet".
Sealing, in order
A fresh content key encrypts the artifact bundle
Your files are packaged with their names, media types, sizes, and digests, and encrypted under a randomly generated AES-256-GCM key that exists only for this Submission. The encryption is bound to the deployment context — chain, contract, bounty, your address — the bundle digests, the roster commitment, and the reveal policy, so ciphertext cannot be lifted into a different bounty and still open.
The content key is wrapped once per Guardian
Each Guardian on the bounty's pinned roster gets their own copy of that key, encrypted to their X25519 public key. Only that Guardian's private key can unwrap their copy.
The whole bag of wrapped keys is time-locked
The set of per-Guardian wrapped keys is then encrypted again, to a future round of a public randomness beacon — the first round at or after the bounty's submission deadline. The decryption key for that round does not exist yet; the beacon publishes it, on schedule, when the round arrives.
Only ciphertext is uploaded
The encrypted bundle goes to private object storage at a deterministic,
content-addressed locator. The envelope — context, digests, recipients
commitment, reveal policy, the time-locked key bag, and the locator — is stored
as canonical JSON. The keccak256 of that JSON is your
submission_commitment, and it is the only
part that goes on-chain.
That second layer is what makes the guarantee structural. Before the deadline, a Guardian holding their own private key still cannot reach their wrapped copy, because the bag itself is locked. Neither can Elgora, the Poster, another Solver, or anyone who steals the ciphertext.
Opening, after the deadline
The beacon publishes the round. A Guardian on the pinned roster then:
- verifies the bounty and the exact Solver and Submission against the contract;
- signs a request and fetches the stored envelope and ciphertext;
- re-derives the commitment and confirms it matches the on-chain one, and checks the ciphertext against the digest inside the envelope;
- opens the time-locked bag, unwraps their own copy of the content key with their retained private key, and decrypts the bundle locally.
Each of those steps can fail — an unavailable beacon, a missing artifact, a mismatched digest. When one does, the correct outcome is a reported operational blocker, never a Verdict against the Submission.
Guardian private keys stay on independently operated Guardian machines. Elgora does not hold them and cannot decrypt a Submission by holding any administrative role.
Who can read your work
Before the deadline, nobody — the time-lock sees to that. After it, the bounty's pinned Guardians, and the protocol layer that brokers delivery between them and the winning Poster.
That is the whole list. Not other Solvers, not the Poster before finality, and not anyone holding the ciphertext without a wrapped key. Losing work is never published, and Guardians must write their Verdicts without revealing any Submission's method, code, data, or results.
Two things the encryption does not do, worth knowing before you submit:
- It is not anonymity. Your Solver address, your
submission_commitment, and the time you submitted are public on-chain from the moment you submit. - Winning means delivery. If you win, the funding Poster receives your artifacts. That is the point of the bounty — see Delivering the winning work.
Envelope internals, for implementers
The stored envelope has a fixed, strictly validated shape. Every field is checked server-side and re-checked by clients:
| Field | Purpose |
|---|---|
context | chain id, Hub address, bounty id, Solver address |
public_manifest + its digest | the non-secret context, hashed |
encrypted_bundle_sha256 | digest of the canonical bundle that was encrypted |
ciphertext_sha256 | digest of the uploaded ciphertext |
guardian_recipients + guardian_recipients_commitment | the exact roster keys sealed for, and their hash |
reveal_policy | beacon network, chain hash, round, and not_before |
envelope_nonce, iv | per-envelope randomness |
wrapped_key_bag | the time-locked bag of per-Guardian wrapped keys |
ciphertext_locator | deterministic storage key derived from context and ciphertext digest |
The API independently verifies that the recipients commitment matches the
bounty's pinned roster, that the reveal policy matches the bounty's deadline,
that the locator is the one those inputs derive, and that the uploaded bytes
hash to ciphertext_sha256 — before it stores anything. A Submission that
would be unopenable by the roster is rejected at the boundary rather than
discovered later by a Guardian.
The ceiling on the encrypted bundle is 50 MiB across at most 500 files. Sealing inflates the Solver's files by about a third, so the directory itself stays under 37 MiB. What the Submission extracts to is capped separately at 250 MB, which only a Guardian can check.
The Guardian roster
What a roster entry contains, where to get the roster for a specific bounty, why the hash matters, and how a client proves it got the right one.
How judging works
What a Guardian does after the deadline, what a written Verdict may and may not say, and the exact moment a Submission becomes the winner.