An AI bouncer for your NFT whitelist. Each bouncer is an ERC-7857 iNFT you own. Before mint, its exact private intelligence must pass an eight-scenario Bouncer Safety Report through 0G Compute. It then interviews applicants inside a TEE, records attested decisions on 0G Chain, and exports a Merkle root for any EVM mint contract.
The whole point: projects can't hand-screen 4,400 applicants for 200 spots. Forms get gamed in an afternoon. Captchas get solved by mechanical-turk farms. Hanami runs the first pass (a private conversation with a character your project defines), and the founders only have to review borderline cases.
Hanami is a Zero Cup tournament entry, built AI-native on 0G. It cleared the Group Stage and advanced to the Round of 32. See What's new for the Round of 32 below.
| Track | AI-native on 0G (agent / companion) |
| 0G modules | Compute (TEE inference + image), Storage (persona, lorebook, portrait, transcripts), Chain (ERC-7857 iNFT + decision log) |
| Status | Live on 0G mainnet, chain 16661, both contracts verified |
Most entries lean on a single 0G primitive. Hanami's trust model only works because all three are load-bearing: the bouncer reasons inside a TEE on 0G Compute, its persona and every transcript are content-addressed on 0G Storage, and each decision lands on 0G Chain carrying the attestation hash that proves the inference ran in a sealed enclave. Remove any one and the product stops being verifiable.
Hanami is also submitted to the 0G Bridge Buildathon by AKINDO, a 10-week, 5-Wave program for builders shipping AI × Onchain applications on 0G. It arrived with all three 0G pillars already integrated and live on mainnet (chain 16661), having previously advanced to the Round of 32 in the Zero Cup 2026 tournament.
| Category | AI Agents / Trust & Safety |
| 0G modules | Compute (TEE inference + image), Storage (persona, lorebook, portrait, transcripts), Chain (ERC-7857 Agentic ID + decision log) |
| Status | Live on 0G mainnet, both contracts verified, existing user base |
| Previous | Zero Cup 2026: Top 32 |
- 0G mainnet contract addresses:
BouncerRegistryat0x764883319e51e46F683aB54D93F26bcBb74A7030,CampaignFactoryat0xfe6b2417407595Ad4d1F8D4D8c95860881d539d4. Both verified on Chainscan. - Live demo: hanami-hazel.vercel.app. Certify and mint a bouncer, apply as an applicant, verify its decision, and export a Merkle root.
- Architecture diagram:
docs/architecture.pngwith full data-flow walkthrough below. - Demo script:
demo/script.md. A 3-minute walkthrough covering mint, chat, adversarial, admin, Merkle export. - X post template:
demo/x-post.md. Ready with Bridge hashtags and tags. - Reproduction: see Local deployment. Two
npm install && npm run devand you're screening applicants locally against 0G mainnet.
The Wave 2 judge asked for genuine new development built on Hanami's existing 0G foundation. Wave 3 is that product change.
- Bouncer Safety Report before mint. The owner signs one gasless request for the exact persona and lorebook. Hanami runs eight fixed scenarios through the existing 0G Compute TEE path. Mint stays locked unless all eight decisions match and every response is TEE verified.
- Persistent, recoverable execution. Runs checkpoint after every scenario, use at most two workers with global provider pacing, survive backend restarts, and resume technical interruptions without repeating completed checks.
- Privacy-safe evidence on 0G Storage. A pass uploads the content hash, scenario names, decision results, and TEE states. Reports and routine logs exclude private inputs, full simulated replies, hidden instructions, and reasoning.
- Backend-enforced publication. New campaigns start private. Admin requires a matching campaign report before publication. Campaigns that were already public keep working as clearly labeled legacy campaigns; making one private permanently enables the new gate.
- Production redesign. Landing, Create, Applicant, Admin, Gallery, and Mine now use a shared responsive shell, persisted light/dark themes, clear async states, visible focus, touch-safe controls, reduced motion, and certification labels.
The new implementation is isolated in three commits: 5922490 defines the build contract, 138a0c8 adds the resumable safety engine, and 130905c ships the enforced gates and redesigned product. Wave 3 submission notes separate this work from Hanami's pre-existing contracts and demo.
The tournament rewards improving between rounds. Since the Group Stage:
- Verify on 0G: every decision screen has a one-click check that pulls the inference's
x_0g_trace, recomputeskeccak256(requestId, provider, tee_verified)in the browser, and shows it matching the hash on chain byte-for-byte. The attestation claim went from "trust the README" to a 15-second proof anyone can run, no wallet needed. - Signed-enclave decision path, wired and ready: alongside the live Router path, the decision turn can run through the 0G Compute Direct broker, which returns the provider's raw TEE signature over the response. The on-chain attestation hash becomes
keccak256(signature), and the Verify-on-0G panel recovers that signature to the provider's on-chain-registeredteeSignerAddressin the browser. Cryptographic proof the enclave signed the decision, with the Router out of the trust base. The code is wired end to end and verified against 0G mainnet (provider discovery, signer status, offline recovery) but ships disabled (OG_DIRECT_ENABLED=false): activating it requires a standing 3 0G deposit in the Compute ledger, which we've chosen not to lock up for now. When enabled it's a drop-in, same0GM-1.0-35B-A3Bmodel, transparent fallback to the Router if the ledger is unfunded or a provider is unreachable. See Direct broker for the decision turn. - On-chain reputation, live: each approval now calls
incrementRepon the bouncer iNFT, so a bouncer accrues a verifiable track record that travels with the token across every campaign it runs. Previously declared on the contract but inert; now wired end to end. - Transcripts pinned to 0G Storage: the full conversation (not just the reasoning hash) is now content-addressed on Storage at decision time, with its rootHash on the decision record.
- Privacy gate: a private campaign's applicant feed (wallets + decisions) now requires an owner signature; aggregate counts stay public.
- Hardening: the
tee_verifiedflag is persisted per turn, a keepalive keeps the backend warm for judges, and the copy now matches the contract. The iNFT is a tradable ERC-721 whose reputation and history travel with it; the ERC-7857 sealed-key transfer path is a documented v1 limitation.
| Contract | Address |
|---|---|
| BouncerRegistry (ERC-7857 iNFT) | 0x764883319e51e46F683aB54D93F26bcBb74A7030 |
| CampaignFactory | 0xfe6b2417407595Ad4d1F8D4D8c95860881d539d4 |
Both verified on chainscan.0g.ai. Each project's bouncer iNFT is minted directly to the project owner's wallet. Hanami never custodies the token.
| Bouncer | Token | Safety evidence | Campaign |
|---|---|---|---|
| Sakura Society | #17 | 8/8 report · 0x9f2550…cabf6 |
Open |
| Material Memory | #21 | 8/8 report · 0x9e1529…a7972 |
Open |
| Slow Collectors Circle | #22 | 8/8 report · 0x710677…3295 |
Open |
Tokens #21 and #22 are owned by 0x34b0Ba20669f3ec4F1056853780c381e5e35F724, authorize the production backend on chain, and began private as required by the certification publication gate.
- Bouncer Safety Report: eight fixed, TEE-required simulations certify the exact private draft before mint and produce a privacy-safe 0G Storage report root.
- AI bouncer you own: each bouncer is an ERC-7857 iNFT minted to the project owner's wallet, not a service account Hanami controls.
- Persona-driven screening: the owner writes the voice, taste, and fit criteria. The bouncer holds a 3–6 turn private conversation, then approves or rejects.
- TEE-attested decisions: every approve and reject tx carries a
bytes32attestation hash proving the inference ran inside a sealed enclave. - Verify on 0G (in-app): any decision screen recomputes
keccak256(requestId, provider, tee_verified)from thex_0g_tracein the browser and shows it matching the on-chain attestation hash byte-for-byte. A 15-second proof anyone can run, no wallet needed. (A stronger signature-recovery path via the Direct broker is wired and ready behind a flag; see Direct broker for the decision turn.) - On-chain receipt: decision, reasoning hash, and attestation hash are recorded per applicant, inspectable on Chainscan without a wallet. The full conversation transcript is pinned to 0G Storage at decision time.
- On-chain reputation: each approval increments the bouncer iNFT's
repScore, so a verifiable track record accrues to the token and travels with it across campaigns. - Chain-agnostic Merkle export: close a campaign and export a root plus copy-paste Solidity that drops into an existing mint contract on Ethereum, Base, Arbitrum, OP, or 0G.
- Anti-gaming by design: criteria stay sealed in storage, one attempt per wallet is enforced at the API and contract layer, prompt-injection attempts are rejected, and expensive endpoints are rate-limited.
- Privacy gate: a private campaign's per-applicant feed (wallets + decisions) requires an owner signature; aggregate counts stay public.
- Owner-revocable delegation: the backend can sign decisions only because the owner granted it through
authorizeUsage, and that grant can be revoked anytime.
A project owner:
- Writes the bouncer's persona and optional project context, then signs a gasless eight-scenario safety request for that exact content.
- Mints only after the report reaches 8/8 with verified TEE responses. The new campaign begins private.
- Shares the applicant URL when the campaign is ready to publish.
An applicant connects a wallet, the bouncer greets them in character, they talk for 3–6 turns. The bouncer issues approve or reject. Decision + reasoning are written on chain with the TEE attestation hash that proves the inference ran inside a sealed enclave.
At campaign close, the owner exports a Merkle root and copy-paste-ready Solidity. That root drops into the project's existing mint contract on Ethereum, Base, Arbitrum, OP, or 0G itself. Hanami does not require the mint to happen on 0G.
The MVP ships with three demo bouncers (Kenji, Bad Frogs, Sakura Society). Their persona documents and lorebooks live on 0G Storage and their iNFTs live on 0G Chain, all inspectable on Chainscan. AI-generated portraits are pinned to 0G Storage and mirrored in a durable store (see Portraits) so they keep rendering even if a storage node drops the blob.
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, wagmi 3, MetaMask |
| Backend | Hono, TypeScript, Node 22, Turso/libSQL (read cache + durable portrait store) |
| Contracts | Solidity 0.8, Foundry, ERC-7857 iNFT, OpenZeppelin |
| 0G Compute | Router API (0GM-1.0-35B-A3B chat, z-image portrait), verify_tee:true |
| 0G Storage | @0gfoundation/0g-storage-ts-sdk, content-addressed rootHashes |
| 0G Chain | Mainnet chain 16661, verified on chainscan.0g.ai |
| Hosting | Vercel (frontend), Render (backend) |
The editable Excalidraw source lives at docs/architecture.excalidraw. Drop it into excalidraw.com (File > Open) to tweak it. The text version below mirrors the same flow for quick reading inline.
┌─────────────────────────────┐
│ /create (Next.js) │
│ exact persona + lorebook │
└──────────────┬──────────────┘
│ gasless owner signature
┌──────────────▼──────────────┐
│ POST /api/safety-runs │ persists exact inputs on 0G,
│ owner-authorized │ checkpoints after each scenario
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ 0G Compute · TEE │ eight fixed scenarios,
│ 0GM-1.0-35B-A3B │ expected vs actual decisions
└──────────────┬──────────────┘
│ strict 8/8 + tee_verified:true
┌──────────────▼──────────────┐
│ 0G Storage safety report │ content hash + safe summaries,
│ reportRoot │ never prompts/replies/reasoning
└──────────────┬──────────────┘
│ certified to mint
┌──────────────▼──────────────┐
│ POST /api/campaigns/prepare│ reuses certified text roots,
│ backend-enforced gate │ generates only z-image portrait
└──────────────┬──────────────┘
│ owner signs three txs in MetaMask:
│
┌──────────────▼──────────────┐
│ BouncerRegistry │ mintBouncer(personaURI,
│ ERC-7857 iNFT │ lorebookURI, imageURI, 0x0)
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ BouncerRegistry │ authorizeUsage(tokenId,
│ delegation │ backendAddress, 0x)
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ CampaignFactory │ createCampaign(tokenId, cap)
│ (factory deploys Campaign) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ POST /api/campaigns/index │ backend verifies on-chain
│ (DB write only) │ ownerOf + isAuthorized, then
│ │ writes index row for the UI
└──────────────┬──────────────┘
│
───── applicant arrives ─────
│
┌──────────────▼──────────────┐
│ /c/[slug] chat (Next.js) │ bouncer greets in character
└──────────────┬──────────────┘
│ per turn
┌──────────────▼──────────────┐
│ 0G Compute · 0GM-1.0-35B │ verify_tee:true
│ (TEE-attested chat) │ → x_0g_trace with attestation
└──────────────┬──────────────┘
│ attestationHash =
│ keccak256(requestId, provider, tee_verified)
┌──────────────▼──────────────┐
│ Campaign.recordDecision │ backend (authorized executor)
│ signs on chain │ writes each decision tx
└──────────────┬──────────────┘
│ campaign close
┌──────────────▼──────────────┐
│ Merkle root (sortPairs) │ exports JSON + Solidity snippet
│ → any EVM mint contract │
└─────────────────────────────┘
The mint is user-signed. Three signatures from the project owner (mint, authorize, create campaign) put the iNFT in their wallet and make them the owner of the Campaign contract. The backend has no key access to either. It can call recordDecision only because the owner explicitly granted it through authorizeUsage, and that grant can be revoked at any time.
TEE attestation lands on chain per decision, not just at mint. Every approve and reject tx carries a bytes32 attestationHash computed from the Router's x_0g_trace (request_id, provider, tee_verified). Anyone watching the contract can prove the inference ran inside a sealed enclave, even without seeing the conversation.
The Merkle root is chain-agnostic. The export bundles a bytes32 root, per-applicant proofs, and a Solidity verifier snippet. The actual NFT mint can happen on Ethereum, Base, Arbitrum, OP, or 0G itself. We don't care where the project decides to deploy.
SDK: @0gfoundation/0g-storage-ts-sdk@1.2.9.
What we pin on storage:
- Bouncer persona text (the system prompt the bouncer reasons inside).
- Project context document (what the bouncer knows about the project's art, story, what makes a fit).
- Bouncer portrait PNG (1024×1024, generated by z-image).
- Passing Bouncer Safety Report (content hash, eight decision summaries, TEE states, and no private source text or replies).
- Decision reasoning for every approve/reject (a paragraph in the bouncer's voice).
- Full conversation transcript per applicant.
The persona, lorebook, and portrait roots go on chain at mint (as the iNFT's URIs); the reasoning hash goes on chain per decision. The transcript root is pinned to 0G Storage and recorded in the index DB (applicants.transcript_uri), not passed on-chain. 0G Storage is the canonical home; the Turso/libSQL database is a read cache so the admin dashboard renders without round-tripping storage on every load.
We pass finalityRequired:false on upload: the default (true) polls for network-wide finalization and can hang 10+ minutes on larger blobs (the ~700KB portrait PNG hit this consistently), so we return as soon as a node accepts the file and let finalization happen in the background.
finalityRequired:false has a sharp edge: a holding node can drop a blob before it replicates, and the indexer then returns File not found for that root. The original demo portraits were lost this way: the only surviving copy lived in Render's .image-cache/, which is wiped on every restart. So portraits now have a durable home: each generated PNG is stored base64 in a portraits table (Turso survives restarts) keyed by its rootHash. The /api/image/:root proxy resolves disk cache → Turso → 0G indexer, backfilling caches on every hit, and the frontend <Portrait> falls back to a procedural SVG on any load error, so a card never shows a broken image. (The three demo portraits were regenerated after this fix; their displayed root is the durable one and may differ from the now-dead root recorded on-chain at the original mint, since BouncerRegistry has no setImageURI in v1.)
Endpoint: https://router-api.0g.ai/v1. OpenAI-compatible. All requests use verify_tee:true and we reject any reply where x_0g_trace.tee_verified is not true.
Two models in use:
0GM-1.0-35B-A3Bfor the bouncer chat. TEE-attested. Thinking mode is disabled viachat_template_kwargs: { enable_thinking: false }because the bouncer doesn't need chain-of-thought. It needs to stay in character and decide.z-imagefor the bouncer portrait. TEE-attested. Same TEE provider (0xE29a…F974) attests the image generation, so a freshly minted Hanami portrait carries provable TEE provenance. (The three demo portraits were regenerated after a storage-durability fix, so their current bytes are a re-pin whose root differs from the one recorded on-chain at the original mint. See Portrait durability.)
The Wave 3 safety runner uses the chat model for eight fixed multi-turn simulations. It passes only when every expected decision matches and every response has tee_verified:true. Network, provider, storage, rate-limit, or attestation failures interrupt the run instead of being counted as product verdicts.
Each chat response comes back with an x_0g_trace object. We compute the on-chain attestation hash as keccak256(abi.encode(requestId, provider, tee_verified)) and pass that as a parameter to Campaign.recordDecision. No off-chain trust required; anyone can recompute the hash from the trace and match it against the on-chain log.
The Router verifies the enclave server-side and returns a boolean. That's convenient, but it puts the Router in the trust base. For the decision turn only (the inference that emits a verdict and lands on chain), the code can instead route through the 0G Compute Direct broker (@0gfoundation/0g-compute-ts-sdk). The broker talks to a TEE provider directly and exposes the provider's raw signature over the response, plus the provider's on-chain-registered teeSignerAddress. We verify the signature recovers to that signer, set the on-chain attestation hash to keccak256(signature), and store the { text, signature, signingAddress } bundle so the Verify-on-0G panel can re-recover it in the browser. This is strictly stronger than the boolean: cryptographic proof the enclave signed the decision, verifiable offline with recoverAddress(hashMessage(text), signature).
Status: wired and ready, shipped disabled. The full path is implemented and verified against 0G mainnet. Provider discovery, TEE signer status, and the offline recovery all check out against the live 0GM-1.0-35B-A3B provider (0x4870CbC4…a4E9, signer 0x0038F716…DB2e8). It ships off (OG_DIRECT_ENABLED=false) because a Direct-broker ledger requires a standing minimum deposit of 3 0G, which we've chosen not to lock up. When enabled, only turns that can carry a verdict (turn 3+) use the broker; earlier turns stay on the fast Router. To turn it on: npx tsx scripts/direct-broker-setup.ts fund=3 to create the ledger, then set OG_DIRECT_ENABLED=true and OG_DIRECT_PROVIDER. If it's disabled or a call fails (unfunded ledger, provider down), the deciding turn transparently falls back to the Router path and still produces a wallet-free hash-match proof, so the product never blocks on broker availability.
Chain ID 16661. Two contracts, both verified on chainscan.0g.ai:
-
BouncerRegistry: ERC-7857-compliant iNFT. Each token holds(encryptedPersonaURI, lorebookURI, imageURI, oracleConditions, repScore). Transferable as a standard ERC-721: the token, itsrepScore, and its on-chain decision history all travel with the new owner (everything is keyed bytokenId). The ERC-7857 sealed-keytransfer/clonepath (which re-encrypts the persona for the new holder) is disabled in v1 and reverts; a plain ERC-721transferFromis how the token actually moves. ImplementsauthorizeUsageso the iNFT owner can delegate write access to one or more executors (the Hanami backend, in the standard flow), and that grant is revocable. -
CampaignFactoryandCampaign: one campaign per whitelist round.Campaign.recordDecisionrequires the caller to be authorized by the bouncer iNFT, enforces one-attempt-per-wallet, respects the WL size cap, and stores(reasoningHash, attestationHash, status, timestamp)per applicant.finalizeMerkleRootis owner-only and one-shot.
16 Foundry tests in contracts/test/, including a separate MerkleConsumer.t.sol that simulates an external EVM mint contract consuming the exported root. Five tests prove approved addresses can mint, rejected addresses revert with NotApproved, and double-mint reverts with AlreadyMinted. The registry suite proves both transfer paths: the ERC-7857 sealed-key transfer reverts (TransferDisabled) while standard ERC-721 transferFrom works and carries the token's stored data.
The product is "an AI bouncer projects own, that screens applicants in private, with a verifiable receipt." Each module above does one specific job.
A bouncer's persona and project context document are the project's competitive edge. If the criteria leak, the screen is dead within a day. Applicants memorize the right answers, farms script them. We needed a store that's cheap, content-addressed, large enough for multi-page documents, and not a centralized service we operate. 0G Storage gave us all four. The rootHash gets pinned on the iNFT, so the persona doc lives on the same rail as the token that points to it.
The whole pitch falls apart if the bouncer can be bribed or read. The TEE solves both. The persona text unseals only inside the enclave; no provider operator can read it. The attestation receipt proves to anyone watching the chain that the reply came out of that enclave. We don't have to trust the AI provider, the project owner doesn't have to trust us. The same TEE rail also generates the portrait, so the visual identity of each bouncer carries the same provenance as its decisions.
This is where the bouncer iNFT lives and where decisions are recorded. Each screen decision is an indexed event tied to a TEE attestation hash. The Merkle root finalizes on chain too. Other EVMs could have hosted the contracts, but ERC-7857 is native to 0G. encryptedPersonaURI is a first-class field, not a metadata workaround. Picking 0G means the iNFT primitive matches what the product needs without us reinventing it.
Projects rarely want to mint on the same chain they screen on. The Merkle root, the per-applicant proofs, and the Solidity verifier let them deploy the mint contract wherever their audience already is. Hanami sits in front of the existing mint, not in place of it.
You need: Node 22+, Foundry, a wallet with mainnet OG, and a funded 0G Compute Router account at pc.0g.ai. The chat and image endpoints both bill from the Router escrow (separate from the wallet balance shown in the dashboard, with a "Deposit" button on the Router tab).
# 1. clone + install
git clone <this repo> hanami && cd hanami
git submodule update --init --recursive
# 2. contracts. Already deployed to 0G mainnet. Run tests locally if you want.
cd contracts && forge test --offline
# 16 tests pass: BouncerRegistry + Campaign + MerkleConsumer
# 3. backend
cd ../backend && npm install
cp .env.example .env
# fill in DEPLOYER_PRIVATE_KEY (wallet that signs Campaign.recordDecision on
# behalf of bouncer owners, needs a small mainnet OG balance for gas)
# fill in OG_ROUTER_API_KEY (from pc.0g.ai → Dashboard → API Keys,
# created while connected with the wallet that funded the Router escrow)
npm run dev # → :8787
# 4. frontend
cd ../frontend && npm install
npm run dev # → :3000Open http://localhost:3000. The contract addresses, RPC, and indexer endpoints in backend/.env.example already point at 0G mainnet. No extra configuration needed.
End-to-end checks against the live network. From backend/:
npx tsx scripts/check-balance.ts # deployer wallet balance on 0G mainnet
npx tsx scripts/hello-inference.ts # one Router call against 0GM-1.0-35B-A3B
npx tsx scripts/hello-tee.ts # confirms tee_verified:true in x_0g_trace
npx tsx scripts/hello-storage.ts # upload + download by rootHash, byte-match
npx tsx scripts/smoke-chain.ts # full mint flow against the deployed contracts
npx tsx scripts/adversarial.ts # pre-Wave manual adversarial scriptThe current deterministic Wave 3 gate runs from the repository root with ./verify.sh safety. It covers strict 8/8 behavior, TEE interruption handling, checkpoints, owner signatures, exact-content invalidation, preparation, and publication policy without spending Router funds.
The repository-level verifier keeps deterministic, UI, release, and deployed evidence separate:
./verify.sh safety # backend/frontend safety contracts and privacy scan
./verify.sh ui # frontend tests, lint, build, and UI Revamp audit
./verify.sh release # backend + frontend + 16 unchanged Foundry tests + docs
./verify.sh live # deployed UI/API, report root, and 0G mainnet bytecode./verify.sh live only passes after a real owner-signed safety run has been recorded in the Wave 3 submission notes. Local fixtures are never treated as deployed evidence.
hanami/
├── frontend/ # Next.js 16 app: /create (owner) + /c/[slug] (applicant chat)
├── backend/ # Hono API: 0G Storage, 0G Compute, chain indexing, Merkle export
│ ├── src/ # og-storage.ts, og-compute.ts, og-image.ts, server.ts
│ └── scripts/ # smoke + adversarial end-to-end checks
├── contracts/ # Foundry: BouncerRegistry (ERC-7857) + CampaignFactory + Campaign
│ └── test/ # 16 tests incl. MerkleConsumer.t.sol
├── docs/ # architecture diagram (Excalidraw source + PNG)
└── demo/ # demo script + assets
A funded reviewer wallet for the demo is documented in the submission form (separate from this public repo). It holds enough mainnet OG to:
- Mint a bouncer (3 tx signatures: mint, authorize, create campaign; totals about 0.005 OG).
- Apply as an applicant against an existing public bouncer (1 tx for the decision; gas only).
- Finalize a Merkle root.
0G mainnet OG faucet: see official 0G docs at docs.0g.ai. Mainnet OG can also be acquired through 0G ecosystem exchanges if the faucet is rate-limited.
You can read the full state without signing anything:
https://chainscan.0g.ai/address/0x764883319e51e46F683aB54D93F26bcBb74A7030: every bouncer ever minted, with persona/portrait URIs in the constructor calldata.https://chainscan.0g.ai/address/0xfe6b2417407595Ad4d1F8D4D8c95860881d539d4: every campaign deployed by the factory.- Each
Campaign.recordDecisiontx on Chainscan shows theattestationHashparameter. That is the TEE receipt.
- 0G Compute Router API keys are wallet-bound. A key you generated while connected to wallet A won't see funds you deposited from wallet B. Create the key while connected with the wallet that funds the Router escrow.
- Storage uploads use
finalityRequired:false. Documented inbackend/src/og-storage.ts. The file is pinned immediately and becomes downloadable through the indexer once the network finalizes, but a holding node can drop it first, after which the indexer returnsFile not found. Portraits are insulated from this by the durableportraitsstore in Turso plus the procedural-SVG fallback (see Portrait durability); other blobs (persona, lorebook, reasoning) are small text and finalize reliably. - Chain ID enforcement. The mint flow calls
switchChainAsync({ chainId: 16661 })unconditionally before any signing prompt. If your wallet sits on Sepolia, Ethereum mainnet, or anywhere else, MetaMask will pop a "switch to 0G" prompt first. This is intentional. wagmi v2'suseChainId()can report stale values when the wallet's actual chain isn't in the config'schainsarray, so we don't trust it for gating.
These four scenarios are implemented in backend/scripts/adversarial.ts:
- Thoughtful, specific applicant → approve. Passes.
- Generic enthusiasm / hype words only → reject. Passes.
- Prompt-injection attempts (
ignore previous instructions, fake admin tags, scripted insider claims) → reject. Passes after a frame-rule tightening; the bouncer no longer parrots scripted persona language. - Retry after rejection → blocked at the API layer and at the
Campaigncontract layer.
The old manual script's last recorded full run was 6/8. Both misses were false negatives: genuinely thoughtful applicants (T1, T2) that the bouncer rejected for being understated rather than for any flip/hype/manipulation signal. That run predates the Wave 3 certification gate and is not presented as a passing safety report.
For the Round of 32 we added a frame rule that tells the bouncer not to over-reject sincere, specific-but-quiet applicants (genuine specificity outweighs a low word count), while leaving the auto-reject defenses for hype, flip intent, and manipulation untouched. The new product gate is stricter: a bouncer cannot mint or newly publish unless its current, exact content produces a fresh 8/8 report.
MIT. Built on 0G. Zero Cup 2026 Top 32 · 0G Bridge Buildathon by AKINDO.

