ai newsDo., 29. Okt.
EilmeldungOpenAI and Anthropic Both Claim Their AI Refuted a 27-Year-Old Math "Law" — And the Math World Is Reeling

ModelProof: Proving an AI Model Hasn't Been Tampered With, Stored Forever on Arweave

ModelProof hashes AI models in your browser, signs a provenance manifest and stores it free and forever on Arweave, so anyone can verify the weights.

HackerNoonDonnerstag 08:14 · 29.10.4 Min. Lesezeit
Bild: HackerNoon

Signup Write New Story ModelProof: Proving an AI Model Hasn't Been Tampered With, Stored Forever on Arweave by Lawal Ridwan by Lawal Ridwan | @ lawalridwan Builder & web3 enthusiast based in Port Harcourt.

Builder & web3 enthusiast based in Port Harcourt.

TL;DR: ModelProof hashes any AI model in your browser, signs a provenance manifest with Ed25519 and stores it permanently on Arweave for free, so anyone can later prove a copy of the weights is authentic, or see exactly which chunks were changed.

I'm Lawal Ridwan, a frontend and web3 developer from Nigeria. Most of the models I run are ones I downloaded: a GGUF from a mirror, a fine-tune a friend shared on Telegram, an ONNX export someone uploaded "with fixes". Every time, I'm trusting a filename, and I don't like that.

So for the HackerNoon Decentralize AI Hackathon I built ModelProof, a small open-source PWA that gives AI weights the kind of provenance we already expect from software releases, with no server in the middle.

There are three ways things go wrong today.

Decentralised AI needs a provenance layer that is permanent, publicly checkable and doesn't ask you to trust whoever hosts the file.

model file ─► Web Worker ─► SHA-256 per 4 MiB chunk ─► Merkle root │ metadata + dataset/config hashes ───────────────────────►│ ▼ canonical JSON ─► Ed25519 signature (WebCrypto) ▼ ANS-104 data item (RSA-PSS, local Arweave JWK) ─► upload.ardrive.io (free < 100 KiB) ▼ Arweave tx id ─► arweave.net/<id> Two design choices are worth explaining.

A domain-separated Merkle tree. Leaves are SHA-256(0x00 ‖ chunkHash) and inner nodes are SHA-256(0x01 ‖ left ‖ right). An odd node is promoted rather than duplicated, which avoids the classic ambiguity where [a, b, c] and [a, b, c, c] share a root. Storing every chunk hash in the manifest is what lets the verifier say "chunk #812 changed" instead of just "no".

No Arweave SDK. I wanted the app to stay tiny and free of Node polyfills, so I implemented the ANS-104 data-item format directly on WebCrypto: Avro-encoded tags, Arweave's SHA-384 "deep hash", RSA-PSS signing and the binary layout. It comes to about 120 lines. Here's the core:

export async function createDataItem(data: Uint8Array tags: Tag[] jwk: JsonWebKey) { const owner = fromB64url(jwkn!); // 512-byte RSA modulus const tagBytes = serializeTags(tags); // Avro array of {name, value} const empty = new Uint8Array(); const msg = await deepHash([utf8('dataitem') utf8('1') utf8('1') owner empty empty tagBytes data]); const key = await cryptosubtleimportKey('jwk' { jwk alg: 'PS256' } { name: 'RSA-PSS' hash: 'SHA-256' } false ['sign']); const signature = new Uint8Array( await cryptosubtlesign({ name: 'RSA-PSS' saltLength: } key buf(msg))); const raw = concat(new Uint8Array([ ]) signature owner new Uint8Array([]) new Uint8Array([]) u64le(tagslength) u64le(tagByteslength) tagBytes data); return { id: b64url(await sha('SHA-256' signature)) raw }; } Then it's one fetch to https://upload.ardrive.io/v1/tx with Content-Type: application/octet-stream. Turbo checks the signature itself, so an item that's even slightly malformed gets rejected. That made Turbo accepting it a good end-to-end test of my encoder.

The repo ships a tiny demo model, tiny-mlp.safetensors (a 256→48→10 MLP, 51,720 bytes), and a copy where I changed one float32 weight at byte 25,860.

I hashed the original with 16 KiB chunks (4 chunks, so the demo is easy to see), signed the manifest and published it through Turbo. This is the real transaction:

Querying the gateway's GraphQL returns the tags exactly as signed: Content-Type: application/json, App-Name: ModelProof, Merkle-Root: 9942b76f…57bf and Model-Name: tiny-mlp (ModelProof demo).

The same scenario runs in CI as a Vitest test, alongside tests for the Merkle tree, canonicalisation, Ed25519 signing (any edit to the manifest, including changing the licence, breaks the signature) and ANS-104 signing round-trips. There are 18 tests and all of them pass.

Provenance for weights is step one. Next I want to run a verified model on Nosana 's decentralised GPU network and have each job publish a signed attestation of {merkleRoot, inputHash, outputHash} to Arweave next to the manifest. That would link "these exact weights" to "this exact output", which is the missing piece for verifiable inference. After that: automatic cross-checks against Hugging Face's LFS hashes, discovery by Merkle-Root tag ("who else has signed this model?"), and a GitHub Action that publishes a manifest with every model release.

If you ship models, try it on your next release and pin the tx id in your model card. It's free, it's permanent and it takes about a minute.

Quelle: HackerNoon (HackerNoon). Original öffnen