infrastructure · midnight

Proof Server — local ZK proving.

Every contract deploy and shielded transaction on Midnight generates a zero-knowledge proof against your private witness data. That proof is minted by the proof server — a service you run on your own machine so the witness never leaves it. There is no hosted equivalent, by design.

Why local?

The proof server needs your local secret key to build the proof. Exposing that endpoint publicly would defeat Midnight's privacy model. Run it on your laptop, your Codespace, or a trusted VM — never on shared infrastructure.

deploy status · both networks
preview○ awaiting deploy
Midnight Preview Testnet

Not yet deployed. Run bun scripts/deploy-midnight.mjs locally — the script writes the address + tx into src/data/midnight-contract.preview.json.

preprod○ awaiting deploy
Midnight Preprod Testnet

Not yet deployed. Run VITE_NETWORK_ID=preprod bun scripts/deploy-midnight.mjs locally — the script writes the address + tx into src/data/midnight-contract.preprod.json.

platform
network
step 01

Install Docker Desktop

The proof server ships only as a Docker image (midnightntwrk/proof-server). No native binary, npm package, or WASM build exists as of Ledger v8. Install Docker Desktop on macOS / Windows, or the Docker Engine on Linux.

step 02

Boot the proof server

One command. It listens on http://localhost:6300 — the default the Midnight SDK expects. The same container handles both preview and preprod proofs; only your VITE_NETWORK_ID changes.

docker run -d --name midnight-proof-server \
  -p 6300:6300 \
  midnightntwrk/proof-server:latest \
  midnight-proof-server -v

Check it's up: curl http://localhost:6300/health → should return {"status":"ok","timestamp":"..."}. First proof after boot is slow (~30–120s) while the container warms; subsequent proofs are fast.

verified · openclaw

Confirmed running Fri Jul 10 07:22 — container midnight-proof-server (id 265c73234164) live on port 6300:6300, image midnightntwrk/proof-server:latest (sha256 801bbc0340…4d531). Health endpoint returned status: ok.

You can inspect / stop / restart from Docker Desktop → Containers, or from the CLI: docker ps, docker logs -f midnight-proof-server, docker stop midnight-proof-server.

step 03

Fund your wallet with tDUST

The faucet dispenses tNIGHT, not tDUST. Paste your unshielded Lace address (starts with mn_addr_preprod1…) into the preprod faucet, then in Lace click Generate tDUST to delegate. Deploy spends tDUST.

Step-by-step Lace screenshots for funding tDUST live on the Wallet page.

step 04

Deploy TimestampLog.compact

Clone this repo, paste your funded 24-word mnemonic into .midnight-wallet.local (mode 0600, gitignored), then run the deploy script with the preprod network selected. It syncs the wallet, loads compiled ZK keys from contracts/managed/timestamp-log/, and calls deployContract — proving happens against your local proof server.

# in the repo root
bun install
echo "your twenty four word mnemonic here" > .midnight-wallet.local
chmod 600 .midnight-wallet.local

VITE_NETWORK_ID=preprod bun scripts/deploy-midnight.mjs

On success the script writes src/data/midnight-contract.preprod.json with address, deployTx, and verified: true, then prints the MidnightScan preprod URL.

tank cap ≠ required balance

You can deploy as soon as Lace shows any tDUST balance. The 5,000 figure is a tank ceiling, not a threshold — a deploy costs a fraction of one tDUST.

troubleshooting · sync stall

If the deploy script logs coins=0 tDUST=0 while Lace shows a real balance, just re-run. The Preview relay sometimes drops the initial WS sync; a fresh run usually catches it. Escalate only if two consecutive runs both stall at coins=0.

step 04b · preprod only

Programmatic DUST demo (optional)

A standalone tutorial that mirrors the official generating-dust-programmatically guide. Creates a scratch wallet, prints all three preprod addresses, waits for tNIGHT from the faucet, then calls registerNightUtxosForDustGeneration — the same step Lace runs when you click Generate tDUST. It then polls state.dust.availableCoins.length ≥ 1 (the correct readiness signal — balance alone is not spendable).

# preprod only — interactive
bun scripts/dust-demo-preprod.mjs

Seed persists to .midnight-wallet-preprod-demo.local (0600, gitignored). This wallet is intentionally separate from the deploy wallet in .midnight-wallet.local— treat it as a scratch demo.

when to run this

Run this before the deploy step if you want to see the DUST registration flow spelled out end-to-end, or if the deploy script logs coins=0 despite Lace showing a balance. If Lace already registered your NIGHT for you, the demo will detect a spendable coin and exit early.

step 05

Wire it back into the site

The deploy status panels above hydrate from the per-network JSON on next refresh — no code changes needed. If you deployed via a different path, paste address and deployTx straight into src/data/midnight-contract.preprod.json.

Why not in the browser?

Privacy

Proving needs your local secret key. A hosted proof server would see it.

Weight

ZK circuits are heavy. Multi-hundred-MB proving keys aren't a browser payload.

Trust

Local proving means the network verifies math, not a signed claim from a third party.