-
-
1. wellcom page
-
2. instruction / wallets connection / standard explanation page
-
3. connect 1am and phantom wallets
-
4. building page (with drag-n-drop components and sdn config + copilot basic checking and goal judging components)
-
5. simulation for incidents page
-
6. grading page
-
7. generating midnight zk proof and pass to sepolia chain
-
8. midnight zk proof generated and passed to sepolia chain
-
9.1 use cloudflare workers as ai agent to allocate ksn(reward) tokens
-
9.2 focus on the process on 9.1
-
10. reward received
-
12.1 1 am wallet preview chain token transaction status
-
12.2 1 am wallet preview chain token transaction status
-
13. on chain transaction successed
Inspiration
A data center's real numbers — its PUE, its cooling topology, its rack layout — are commercial secrets. But every certification scheme on earth asks an operator to hand exactly those numbers to an auditor in order to earn a badge. That asymmetry bothered me: you have to reveal the thing in order to prove something about the thing.
At the same time I wanted the actual design work to feel like play rather than a spreadsheet. Anyone who has built anything in Minecraft already understands adjacency, capacity, and redundancy — they just don't know they're doing TIA-942. So the project became two ideas welded together: a Lego-style 3D data center builder that scores you against real international standards, and a zero-knowledge proof that lets you publish the verdict without ever publishing the design.
What it does
You drag blocks into a $32 \times 8 \times 32$ voxel grid — 47 block types across structure, site, power, cooling, IT, safety, and network. Then:
- Score. 37 rules, each citing a real clause from Uptime Tier, TIA-942, EN 50600, ASHRAE TC 9.9, NFPA 75/2001, ISO/IEC 27001, EU EED, SG DIA, DE EnEfG, CN PUE, SOC 2, and NIST SP 800-30. Six axes roll into one overall number:
$$ S \;=\; \Big\lfloor 0.20\,S_{\text{red}} + 0.20\,S_{\text{pow}}
- 0.15\,S_{\text{cool}} + 0.15\,S_{\text{safe}}
- 0.15\,S_{\text{eff}} + 0.15\,S_{\text{sec}} \Big\rceil $$
plus an Uptime tier (I–IV or F) and a cert level (Bronze → Platinum).
- Simulate. The same build state renders as a SimCity-like sim — NPCs walk the floor, scheduled events fire, power and thermal gauges oscillate, and deterministic projections cover staffing, OPEX, carbon and water.
- Prove. The build is compiled into a knowledge graph, digested, and fed to a Noir circuit that proves one sentence and nothing more.
- Mint. A client-side SVG certificate, mintable as a Soulbound Token on Polygon, Ethereum, BSC, Base, Arbitrum, or Optimism.
The proof
circuits/noir/src/main.nr proves exactly:
I know a facility design whose knowledge-graph digest is $D$, which rule pack $V$ scored at or above threshold $T$.
| Public | Private |
|---|---|
| A blinded commitment $C$ | The digest $D$ — and so the entire design |
| The rule pack version $V$ | The exact score $s$ — only that $s \ge T$ |
| The threshold $T$ (default 85) | PUE, layout, rack counts, every asset and edge |
The circuit asserts $s \ge T$ and returns the commitment as a public output:
$$ C \;=\; \mathrm{Pedersen}\big(D_{hi},\, D_{lo},\, r_{hi},\, r_{lo},\, V,\, \texttt{DOMAIN}\big) $$
Three design decisions in that one line, each of which I got wrong first:
- $C$ is an output, not an input. If the prover supplies the commitment, they can publish one that has nothing to do with the witness they proved about. As an output, the verifier reads the value the circuit itself derived.
- $V$ is inside the hash. Without binding the rule pack, a proof produced under a lax pack replays as though it had cleared a strict one.
- The digest is split. SHA-256 gives 256 bits; BN254's field holds 254. I carry it as two 128-bit halves. Truncating would have "worked" and silently weakened the binding — the worst class of bug, because nothing fails.
The blinding $r$ matters too: without it, anyone holding a guess at your design can confirm it by recomputing the hash.
How I built it
Pure-frontend Next.js 15 (App Router) with React Three Fiber for the voxel world, Zustand + zundo for undo/redo, IndexedDB for autosave. Auth is wallet-only — SIWE for EVM, a hand-rolled SIWS for Solana over tweetnacl — with iron-session httpOnly cookies. No email, no password, no user table.
The scoring engine is deliberately pure: no Date.now(), no Math.random()
anywhere in the core. It has to be, because a proof about a score is worthless if
two runs over the same build disagree.
The ZK path is Noir 1.0.0-beta.20 + @aztec/bb.js 4.2.0 (UltraHonk over BN254).
The knowledge-graph pipeline — extract → fuse → gate → digest → serve — produces
the canonical digest the commitment binds to, so any verifier can reproduce it
from a disclosed graph if the owner ever chooses to disclose one.
Measured on an M-series laptop: witness ~90 ms, prove ~1.4 s warm, verify ~0.4 s, 16 KB proof. Fast enough to sit inside a live demo.
Challenges
The proof that was true but vacuous. DEFAULT_THRESHOLD was 85, and for a
while the witness carried competitionScore, which is on a 0–1000 scale — not
the 0–100 rating shown on the result page. Every proof verified. Every proof was
also meaningless: a bar cleared by a factor of ten. Nothing crashed, no test went
red, and the cryptography was flawless. It taught me that in ZK the dangerous
bugs live entirely outside the circuit, in what you chose to put on each scale.
Midnight didn't ship. I built the whole thing on Midnight first. The Compact
circuit is real and compiles — but no released combination of the Compact
compiler and the Midnight proof server could actually prove it. I kept
docs/MIDNIGHT_ZK.md in the repo rather than deleting it, because "here is
precisely where the toolchain generations fail to line up" is a genuine finding,
and the path reopens the day they align. Then I ported to Noir in a weekend.
WASM vs. the edge. bb.js ships a multi-megabyte WASM module and needs a
Node host. A static import drags it into the edge bundle and breaks the
Cloudflare build; without serverExternalPackages, Next rewrites the .wasm to
a static asset URL and the loader dies with Failed to parse URL from
/_next/static/media/noirc_abi_wasm_bg.*.wasm. Everything is lazily imported
inside the request handler. Real proving genuinely cannot run on Workers, so I
made that an explicit, documented limit rather than a mystery.
Version pinning as a safety property. bb.js 5.x rejects beta.20's ACIR with
error converting into field Circuit::opcodes. Every ZK dependency is pinned
exactly — no caret ranges. A silent minor bump is exactly how the Midnight path
made that an explicit, documented limit rather than a mystery.
Version pinning as a safety property. bb.js 5.x rejects beta.20's ACIR with
error converting into field Circuit::opcodes. Every ZK dependency is pinned
exactly — no caret ranges. A silent minor bump is exactly how the Midnight path
broke.
Making a mock proof impossible to mistake for a real one. A MockProver
exists for tests, and it is forgeable. It is refused in production unless
ZK_ALLOW_MOCK=true, the console prints MOCK PROVER — proof is simulated and
forgeable in red, and the certificate drops the "proven in zero knowledge"
wording and carries a machine-readable Proof Backend attribute. That document
gets written to a public chain forever, so the sentence printed on it has to be
true.
What I learned
- State the boundary out loud. The circuit does not prove the score was computed honestly — it can't, since re-running 37 rules inside a circuit would mean ingesting the whole private build, which defeats the point. That's in the docs as a heading, not buried for a reader to discover. A ZK system that overstates itself is worse than none.
- Determinism is a cryptographic requirement, not a code-style preference.
- Test the attacks, not the happy path.
noir-prover.test.tsruns against the real prover with no stubs, and asserts four separate forgeries fail: a below-threshold build, an inflated threshold claim, a swapped rule pack, and tampered proof bytes.
What's next
396 unit/integration tests plus 13 workerd tests, all green. Next up: an on-chain UltraHonk verifier so the SBT contract checks the proof at mint time rather than trusting the minter, community block plugins with scoring hooks, and reopening the Midnight path the moment its toolchain lines up.
Built With
- 1am-wallet
- aztec-barretenberg
- cloudflare-workers
- ethereum
- hardhat
- indexeddb
- knowledge-graph
- midnight
- next.js
- noir
- phantomjs
- playwright
- react
- react-three-fiber
- solidity
- soulbound-token
- tailwind-css
- three.js
- typescript
- vitest
- wagmi
- webassembly
- zero-knowledge-proofs
- zk-snarks
- zustand
Log in or sign up for Devpost to join the conversation.