दस्तावेज़
Proof
इस पन्ने पर
This page is for a judge with ten minutes. Every row is one claim, the screen that shows it, the file that does it, the test that holds it, and the transaction on Monad where there is one.
Nothing here is planned work. Everything in these tables ships today, on Monad mainnet, chain 143.
Checked on 2026-09-22: every link on this page answered, every file is in the repo, every test name
is in the suite, and every transaction was read back from Monad by its hash and is recorded in
proofs/ or in the attack transcript that names it. Where a claim is smaller than it looks, the row says so,
and what is deliberately not claimed is its own section at the
bottom.
The contracts, the receivers the policy lists today, and their Sourcify matches are on the Introduction. The long version of any row is in Architecture, the Threat model or the API pages.
The track: trust, identity and AI infrastructure
Track 04 is "Trust, Identity & AI Infrastructure". The program says: "AI has made identity, provenance, data ownership, and agent trust urgent infrastructure problems." One of its examples is "Provenance for generated media that survives re-encoding". Moolam is that example.
The grand champion is "The best build of Metropolis." The program's own FAQ says: "Judges need to be able to verify what you built during the six weeks." This section answers that sentence.
| Claim | See it | File | Test or run | On Monad |
|---|---|---|---|---|
| A primitive, not a platform feature. Four contracts, no upgrade proxy, ownership renounceable, and the agent identity belongs to a registry Moolam did not deploy | Contracts | packages/ | test_ | Registry 0xa191…80C0, Sourcify match |
| A passport survives re-encoding. Six mangled copies of one picture and a copy stripped of all metadata resolved to the same passport. The mirrored copy came back 0 of 64 bits apart | Verify an image | packages/, dihedral.ts, match.ts | npm run prove sections 2 and 3, saved in prove-it.txt; 360 copies and 0 false positives in robustness.txt | Registration 0x599b…5834 |
| The holder's statement on how AI may use a picture is a dated public record, appended and never edited. Thirteen pictures carry one | Monsoon passport, "How AI may use this picture" | packages/, packages/ | test_, invariant_ | Statement 0x79d4…4b61, block 105833742 |
The day scrubber asks the chain what was in force on a past day. One consent call per day landed on, not today's answer with an old date printed over it | The same passport page | packages/ | consent-scrubber.test.ts, "hands back the statement the chain says was in force, with the moment asked" | Consent register 0x1151…1277 |
| A picture can be registered sealed: the chain commits to the file's SHA-256, Moolam pins one small document and keeps no copy, and only the holder can publish it later, with exactly the registered bytes. The first one was unsealed by its holder four minutes after it was registered | Old but strong, sealed then published by its holder | packages/, packages/ | attacks-sealed.test.ts, 14 of 14, and the live half, both in pass-3.txt; the unseal in sealed-passports.txt | Registry 0xa191…80C0, registered in block 107,034,322. The unseal changes nothing on chain |
| A challenge costs a bond, only the named resolver settles it, and the money leaves by a pull payment | Disputes | packages/ | test_, invariant_ | Three refusals sent on mainnet: InvalidBond, NotResolver, NothingToWithdraw |
| An edit is a child passport with a parent link, not an overwrite. The original stays the root of its tree | Edits and lineage | packages/, packages/ | test_; the indexer test "keeps the original as the root all the way down a two level edit chain" | Edit 0xbb12…dbf4 |
An agent's identity is an ERC-8004 token on Monad's canonical registry. Moolam reads owner and can never mint, revoke or edit one | Agent 10249 | packages/ | test_, a fork test against the live registry | ERC-8004 0x8004…a432 |
| The attacks were executed, not described. Of the twelve, eight run twice each, as a read and then as a real transaction, and four are refused before any transaction exists, as their files say. Eight more against the consent register, eight more on a mainnet fork, and twenty-three on sealed pictures: fourteen on the verify service's routes and nine on the live service | Threat model | docs/, consent-attacks.txt, pass-2.txt, pass-3.txt | npm run prove section 5, 12 of 12 refused; npx vitest run test/, 14 of 14; npm run attack:pass3 | Passkey replay refused, attestation from a stranger refused |
| The private re-check was attacked too. Twenty-eight attacks on the verify service's link, status, delete, prepare and sweep paths, and twelve checks against the live service and the sealed receiver on mainnet. None got through where it should have been refused; one, the sweep deleting a 49 hour old copy with no passport, landed as designed | Threat model, "The private re-check" | packages/, packages/, pass-4.txt | npx vitest run test/, 28 of 28; npm run attack:pass4, exit code 0 | Reads only, at block 107,820,614: a stranger's report to the sealed receiver (off the policy since 2026-09-26) reverted Invalid, and a stranger's attest reverted Not |
| Every promise carries an invariant, and the invariant names the file that holds it | Threat model, entry points | docs/ | Moolam, Moolam | Ran against the deployed bytecode in the fork suite, fork-tests.txt |
Chainlink CRE: an orchestration layer
The bounty asks for "meaningful use as an orchestration layer", "blockchain plus external API, data source, LLM or agent", and "a successful CLI simulation".
| Claim | See it | File | Test or run | On Monad |
|---|---|---|---|---|
| The workflow orchestrates, it does not read a feed. A log trigger fires on a registration, the workflow reads the passport from Monad, fetches the picture over HTTP, decodes the JPEG and recomputes the fingerprint in the sandbox, then writes the verdict back on chain | Chainlink CRE workflow | packages/, decode.ts, phash.ts | Moolam, 16 tests | Public receiver 0x4a, where the simulator's runs write since 2026-09-27; 0x0d69…ff04, where Chainlink's network writes, listed again on 2026-10-02 in 0x446c…ff9f; the re-checks before 2026-09-26 sit under it too |
The workflow ran on Chainlink's network, and ten nodes agreed. The first network verdict: passport 0x793a…5782 was registered at 07:23:26 UTC on 2026-10-02, ten Chainlink nodes recomputed its fingerprint, distance 0, matched, and the verdict was written at 07:23:42 UTC, 16 seconds later, to the Keystone Forwarder. The deployment is moolam-verifier, workflow ID 00c4dd797775bb465fff638ab5171b19a35ed11d4513eb4890661935d4801890, owner 0xf072a8c620bfc818875736e3f2d3a2a339c06584, verdict step only | Chainlink CRE workflow, "On Chainlink's network" | packages/ | Read the verdict transaction on MonadVision: its to is the Keystone forwarder, and the passport page says so under the verdict | Registered in 0x162c…feaa, verdict in 0x760d…2882, block 109,833,597, to the Keystone forwarder 0x76c9…5E62 |
| A successful CLI simulation, seventeen times. Every one of the seventeen real pictures on the registry carries a re-check delivered on chain, with its recomputed fingerprint and its bit distance | Monsoon passport, "Second opinion" | rechecks.txt | verify-recheck.test.ts, "reads the live shape: one re-check that matched, with its distance, day and receiver" | Re-check 0x59e6…80fa, distance 0 |
| The receiver accepts this workflow and nothing else. It is pinned to the workflow author and the workflow name, and a report from any other sender is refused before it is read | Policy and receiver | packages/ for the simulator's runs, and packages/ for Chainlink's network | Moolam: test_, test_, test_. For the receivers written to since 2026-09-27, Simulation: test_, test_, test_; and Simulation: test | Report from a fake forwarder refused |
| The delivery label is honest. Each verdict says which forwarder delivered it, and a verdict says network forwarder only when Chainlink's Keystone forwarder delivered it, as the first one did on 2026-10-02. Every earlier one says simulation forwarder rather than claiming a network reached consensus | The same passport page, under each verdict | packages/ | attestation-delivery.test.ts, "says a simulation run when the report came through the simulation forwarder" and "names the workflow rather than claiming an independent network" | Simulation forwarder 0x9e beside the production Keystone forwarder |
| Confidential Workflows, Chainlink's CRE release of 7 August 2026 (CLI v1.29.0, the release that also added Monad mainnet support), run as a spike. A private file nobody can fetch from a public gateway was pulled with a credential inside the handler, fingerprinted there, and matched the fingerprint on chain at 0 bits. Only the verdict crossed out | No screen. This one is a spike, and the release is on Chainlink's CRE release notes | packages/ and its README.md | cre workflow simulate moolam-sealed-spike, and a wrong credential ends the run at the link call with a 401 and no fingerprint | Nothing. No transaction, no --broadcast, no receiver. The simulator says of itself: "The simulator is not a real TEE, and is meant to debug." One machine, no hardware isolation, no node consensus |
A Chainlink confidential workflow re-checked a picture that has never been published. moolam-sealed-verifier, registered with handler, opened Moolam's 512 pixel private copy through a link the verify service mints only under a queue claim, recomputed the fingerprint and wrote only the verdict: distance 0, matched. It ran in Chainlink's simulator with --broadcast, which is one machine and not a real enclave | Kanchipuram Silk Loom at First Light, "Second opinion", marked "Private re-check" | packages/, private-copy.ts | cre workflow simulate moolam-sealed-verifier --broadcast, recorded in sealed-passports.txt; sealed-private-copy.test.ts | Report 0xa38f…c954, block 107,834,613, on this passport, and the first run's report 0xdc18…0bef, block 107,817,793, both through the simulation forwarder |
The chain says which workflow wrote a verdict. Private re-checks go through a second receiver pinned to moolam-sealed-verifier, and the registry stores the receiver with every attestation, so a private verdict can be told from a public one without asking Moolam | The same passport page, "Written by" | packages/, packages/ | npm run attack:pass4, 8b reads the pin back and 8d the row's receiver, in pass-4.txt, run against the older sealed receiver | Sealed receiver 0x52b8…57f6, listed by the policy after its 24 hour wait in 0x0e1e…457d; the first two private verdicts carry the older 0x7b9e…E45A (off the policy since 2026-09-26) |
| A copy of the public workflow cannot write a verdict. Since 2026-09-26 simulated reports have two receivers of their own that accept a report only in a transaction signed by Moolam's CRE wallet, fixed at deployment with no setter. On a mainnet fork, the Monsoon report sent through Chainlink's real mock forwarder wrote an attestation from the CRE wallet and nothing from a stranger, and a replay wrote nothing | Chainlink CRE workflow, "The receiver, and what a simulation needs" | packages/, simulation-receivers.txt | test_ on the fork; test, 512 runs | Public receiver 0x4a and sealed receiver 0x52b8…57f6, Sourcify match, listed in the policy since 2026-09-27 14:18 UTC in 0xac64…0bfb and 0x0e1e…457d; the two Moolam contracts came off the policy on 2026-09-26, history kept, and the public one has been listed again since 2026-10-02 |
The re-checks run themselves. Since 2026-09-27 15:12 UTC the re-check runner takes new registrations and requested re-checks every two minutes, at most 5 runs a round and 40 a day, and runs the workflow with --broadcast. Its first two verdicts were written with nobody pressing anything, 451 and 832 blocks after each picture was registered, and both matched | Chainlink CRE workflow, "How a run is driven today" | packages/, rechecks.txt | The live runner on its server; the caps are "The guards" in packages/ | Report 0x3771…e24b, block 108,499,297, 1 of 64 bits, and report 0x35c1…4c1a, block 108,505,240, 0 of 64 bits, both from the CRE wallet through the simulation forwarder to the public receiver 0x4a |
Envio: depth of use
The Envio bounty judges six things: multichain use, a non-trivial schema, derived or aggregated entities, live and correct data, originality, and craft. The program's public page gives only the bounty's title, so these six are in our words, not quoted. Moolam indexes one chain, Monad mainnet. The rows below answer the other five.
| Claim | See it | File | Test or run | On Monad |
|---|---|---|---|---|
Derived entities, not a log mirror. Creator, Generator with its trust score, Creator, Daily, Global, Consent, and the flattened statement on every Passport | Envio indexer | packages/, packages/, packages/, packages/ | 47 indexer tests, saved in indexer-tests.txt; "splits the daily counters by UTC day while the global totals keep adding up" | Watches all five contracts it reads from their deploy blocks |
inherited: an edit made after its parent lost a challenge is marked, so a reader does not have to walk the tree to find out | Explore, the Disputed chip | packages/ | "gives an edit made after its parent lost a challenge the inherited mark", and "marks every edit already made from a passport when its challenge is upheld" | No dispute has ever been opened on mainnet, so this is held by the tests and by attack 6 on a mainnet fork in pass-2.txt |
| Working, live, correct. This query was run against the hosted endpoint on 2026-09-22 and the answer is below, untouched | GraphQL | packages/ | The 47 tests above | On that day the fourteen re-checks then in rechecks.txt were fourteen of the fifteen attestation rows the index returned, the fifteenth being an earlier delivery; two more were added on 2026-09-23 |
| The app reads the index, never a fixture. Explore, its AI-use filter, Agents, and the film's own counts all read live and say so when the index does not answer | Explore and Agents | packages/, packages/ | explore-consent.test.ts, "says how many pictures carry a statement, counted by the index itself"; index-retry.test.ts | The registry and the consent register, read through the index |
query ProofToday {
GlobalStats {
passports
creators
generators
attestations
disputes
consentStatements
passportsWithConsent
}
}{ "passports": 37, "creators": 4, "generators": 2, "attestations": 15,
"disputes": 0, "consentStatements": 13, "passportsWithConsent": 13 }Privy: beyond login
The bounty asks for use "beyond login", a demo that must "clearly show the Privy-powered function", and gives a bonus for several Privy features. Moolam uses five: embedded wallets, passkeys, gas sponsorship, session signers under a policy, and server wallets with a key quorum.
| Claim | See it | File | Test or run | On Monad |
|---|---|---|---|---|
| Embedded wallets. An email code creates an account on Monad with no seed phrase and no extension | Register an image | packages/, packages/ | wallet-gate.test.ts, wallet-returning.test.ts | Creator 0x85a8…39a6 |
Passkeys, bound on chain and checked by Monad's P-256 precompile at 0x0100. Both user presence and user verification are required, so only someone who touched the sensor can make a passport | The same screen, "Use this device's unlock" | packages/, verify.ts, packages/ | parity.test.ts, "agrees field for field on one signature"; test_ | Bind 0x34cf…fffa, sponsored |
| Gas sponsorship on every write. The recorded run printed "sponsorship: on, the app paid the gas, not the creator's wallet" | Every receipt, where "Network fee" reads "Paid by Moolam through Privy" | packages/ | privy.txt | The two passkey sends below went through the ERC-4337 EntryPoint from a bundler, so the creator paid nothing |
Session signers under a policy. A creator grants the editing app one signer, held to append on the Moolam registry and nothing else. Revoke it and Privy refuses the same call before it reaches Monad | "Allow edits from this app", on any passport you hold. The walkthrough is in Edits and lineage | packages/, packages/ | packages/; the revoked signer is refused in privy.txt step 7 | Edit signed by the session signer 0xbb12…dbf4 |
Server wallets with a key quorum: an agent transacting from a wallet built on Privy. The demo agent's key lives in Privy's enclave under a policy that allows register on one contract on chain 143 carrying no MON, and EIP-712 signing over that same contract. It signs the generator half of every picture it draws | Agent 10249, and the studio's "Make one with the agent" | packages/, generator, policies.ts | privy.txt steps 3 to 5: the agent registered under policy kvq9627q5dfpv90xegi5h1cj, and both a MON transfer and an append came back policy_ with nothing signed | Register by the agent 0xbf6a…9079 |
| Two transactions a person really made, with a real passkey, on 20 September 2026. One from a passport page on an existing picture, one from the studio at the moment of making | The passport that was stated on | consent-statements.txt | Read back from the chain with cast call consent the next day | Holder's statement 0x4601…5361 in block 106391876, and the studio's statement 0xfd6d…6d74 seven seconds after its registration |
Monad itself
| Claim | See it | File | Test or run | On Monad |
|---|---|---|---|---|
| Four contracts on chain 143, all verified through Sourcify, deployed by one repeatable script | Introduction | packages/ | deploy-dryrun.txt | Registry, Policy, Receiver (off the policy 2026-09-26, listed again 2026-10-02), Consent |
The P-256 precompile at 0x0100 is what makes a passkey a first class signer here. No server checks the WebAuthn assertion, and it cannot be added after the fact | Why Monad | packages/, through OpenZeppelin's WebAuthn library | test_, test_ | Every registration transaction in this page's rows |
| Gas, measured rather than estimated. The live registration cost 376,128 gas, and Monad bills the limit declared rather than the gas used | Why Monad | gas.txt | forge test --gas-report; a first statement measures 117,337 gas and a later one 91,036 | The 376,128 gas registration |
| Sub-second blocks, measured on one real pair of sends. The studio's statement landed 23 blocks, seven seconds, after the registration it belongs to | Register an image, where the receipt prints the milliseconds it measured in the browser | packages/ | studio-render.test.ts pins the receipt's confirmation line in every language | Registration at block 106392736, statement at block 106392759 |
| A live registry, not a demo fixture: 37 passports, 4 creators, 2 generators, 13 statements today | Explore | packages/ | The query above, run on 2026-09-22 | Every row reads from the four contracts |
What is deliberately not claimed
- No copyright proof. A passport proves who registered first. It does not prove who created the picture and it does not prove who owns the rights to it.
- No compliance claim. Registering a picture here does not make a provider or a deployer compliant with any law. Rules and standards sets what Moolam does beside what the C2PA specification and the EU texts actually ask for.
- Network consensus covered the verdict step only until 2026-10-07. Chainlink's network has run that
step since 2026-10-02, and the first verdict is in the table above. Every re-check before it came from Chainlink's
simulator run with
--broadcast, one machine and no quorum, and the passport page says so under each verdict. The look-alike step is in the network workflow from 2026-10-07, with the verify service's look-alike routes, its Google web check on the hosted service, and the hosted index rebuild that lets the live site's marks and counts see the network flag. - No watermark. Moolam writes nothing imperceptible into the picture. A product that needs machine-readable marking in the file itself still needs a watermarking layer beside this one.
- No crop resistance. Cutting about ten percent off each edge breaks both fingerprints: 0 of 24 copies matched in the sweep. Fingerprints has the numbers.
Try to break it
Reading a refusal is weaker than watching one. Try to break it sends
eleven of these attacks, and one control that has to pass, from the browser to the deployed
contracts on Monad mainnet as eth_call, so the refusal that comes back is the contract's own and
nothing is signed or spent.
The transcripts behind every refusal are in docs/security/attacks/, and the full sessions are in
prove-it.txt and consent-attacks.txt.