Skip to content

cl-fi/clearbox

Repository files navigation

ClearBox

Transparent AI Judging with On-Chain Attestation on Sui


"The AI gave your project a 72. But who scored the AI?"

More and more competitions use AI to judge submissions. But participants are left wondering: Were the judging criteria really what they claimed? Could the organizer have tweaked the prompt after seeing the results?

ClearBox makes AI judging verifiable. Every score is cryptographically signed and recorded on the Sui blockchain. Anyone can independently prove that the judging followed the published criteria — no trust required.


How It Works

  Organizer (Sui Wallet)       ClearBox                         Sui Testnet
  ────────────────────         ────────                         ───────────
      │                            │                                 │
      │  Connect wallet            │                                 │
      │  Define criteria + model   │                                 │
      │  Sign create_competition   │                                 │
      │ ───────────────────────────────────────────────────────────► │
      │                            │                                 │
      │  Add repos + Gemini key    │                                 │
      │ ─────────────────────────► │                                 │
      │                            │  ┌─────────────────────────┐    │
      │  Sign update_status(1)     │  │   Isolated Judging TEE  │    │
      │ ───────────────────────────│──│──────────────────────► │    │
      │                            │  │  1. Fetch README        │    │
      │                            │  │  2. Score with Gemini   │    │
      │                            │  │  3. Sign with Ed25519   │    │
      │                            │  └─────────────────────────┘    │
      │                            │                                 │
      │                            │  Submit signed scores on-chain  │
      │                            │ ──────────────────────────────► │
      │                            │       (signature verified       │
      │  Sign update_status(2)     │        by Move contract)        │
      │ ───────────────────────────────────────────────────────────► │
      │                            │                                 │
  Participant (no wallet)      │                                 │
  ───────────────────────      │                                 │
      │  Browse competitions       │                                 │
      │  View scores + verify      │                                 │
      │ ─────────────────────────► │  Read on-chain data             │
      │                            │ ──────────────────────────────► │

What gets verified on-chain:

Check How
Prompt integrity SHA-256 hash of the judging prompt is stored on-chain. Hash the published prompt yourself — it must match.
Score authenticity Every score is Ed25519 signed by the judging service's ephemeral keypair.
Tamper-proof The Move contract verifies the signature before accepting any result. Invalid signatures are rejected.

The Vision: Nautilus TEE

In production, the judging service runs inside a Nautilus Nitro Enclave (Trusted Execution Environment) on Sui. The enclave generates its keypair in hardware-isolated memory, and AWS provides a cryptographic attestation proving exactly what code is running — down to the byte.

For this demo, we implement the same verification pattern with a simulated TEE. The architecture is identical; swapping to a real Nautilus enclave requires zero code changes.


Architecture

┌─────────────────────┐      ┌──────────────────────┐      ┌─────────────────┐
│   React SPA         │◄────►│   Web Backend (API)   │─────►│  Sui Testnet    │
│   Tailwind CSS      │      │   Express/TypeScript   │      │  Move Contracts │
│   @mysten/dapp-kit  │──────│──────────────────────────────►│                 │
│   (wallet signing)  │      └──────────┬─────────────┘      └─────────────────┘
└─────────────────────┘                 │
                                        ▼
                              ┌──────────────────────┐
                              │   Judging Service     │
                              │   (Simulated TEE)     │
                              │                       │
                              │  Ephemeral Ed25519    │
                              │  keypair (in-memory)  │
                              │  ────────────────     │
                              │  Fetches README       │
                              │  Calls Gemini API     │
                              │  Signs every result   │
                              └──────────────────────┘

Two views:

  • Organizer — connects Sui wallet, creates competitions on-chain, adds repos, provides their own Gemini API key, triggers judging
  • Participant — browses competitions and verifies scores publicly (no wallet needed)
Component Tech
Smart Contracts Sui Move 2024
Judging Service TypeScript, Express, @noble/ed25519, Gemini API
Web Backend TypeScript, Express, @mysten/sui SDK
Frontend React, Vite, Tailwind CSS, @mysten/dapp-kit
Monorepo pnpm workspaces

Quick Start

Prerequisites

1. Install dependencies

pnpm install

2. Deploy the Move contract

./scripts/deploy.sh

This publishes the contract to Sui Testnet and prints a PACKAGE_ID.

3. Configure environment

cp .env.example .env

Fill in:

SUI_PRIVATE_KEY=your-base64-encoded-sui-private-key
PACKAGE_ID=0x-from-deploy-script

SUI_PRIVATE_KEY — This is the backend server's Sui private key, used only for submitting judging results on-chain (submit_result). The Move contract verifies the judging service's Ed25519 signature before accepting results, and the backend is the transaction sender. This key is not used for creating competitions or updating status — those are signed by the organizer's own wallet in the browser.

To export your private key:

# List your addresses
sui client addresses

# Export the private key (base64-encoded)
sui keytool export --key-identity <your-address> --json

Copy the key field (the base64 string starting with suiprivkey...) into SUI_PRIVATE_KEY.

Make sure this address has testnet SUI for gas fees (sui client faucet).

Note: GEMINI_API_KEY is no longer needed in the server .env — organizers provide their own API key through the UI when they trigger judging.

4. Run

pnpm dev

This starts all three services in parallel:

5. Try it

As an organizer:

  1. Open http://localhost:5173
  2. Click I'm an Organizer
  3. Connect your Sui wallet
  4. Click + New Competition, enter a name, judging criteria, and select a model
  5. Click Create Competition (signs a transaction with your wallet)
  6. Add GitHub repository URLs on the manage page
  7. Enter your Gemini API key and click Start Judging (signs status update transactions)
  8. View the scores once judging completes

As a participant:

  1. Open http://localhost:5173
  2. Click I'm a Participant
  3. Browse all competitions with organizer addresses and status
  4. Click a completed competition to view scores
  5. Click Verify on Chain to inspect the on-chain attestation

Project Structure

clearbox/
├── contracts/           # Sui Move smart contracts
│   └── sources/
│       ├── clearbox.move          # Competition, Submission, signature verification
│       └── clearbox_tests.move    # Move unit tests
├── judging-service/     # Simulated TEE — isolated scoring + signing
│   └── src/
│       ├── crypto.ts              # Ed25519 keypair, signing, payload hashing
│       ├── github.ts              # GitHub API README fetcher
│       ├── gemini.ts              # Gemini API scoring (accepts API key per request)
│       └── index.ts               # Express server
├── backend/             # API server — orchestration + Sui interaction
│   └── src/
│       ├── store.ts               # In-memory competition store
│       ├── sui.ts                 # Sui transaction building (submit_result only)
│       └── index.ts               # Express server (registration, repos, judging)
├── frontend/            # React SPA with wallet integration
│   └── src/
│       ├── layouts/               # OrganizerLayout, ParticipantLayout
│       ├── pages/                 # LandingPage, OrganizerDashboard, CreateCompetition,
│       │                          # ManageCompetition, CompetitionDirectory, CompetitionResults
│       ├── components/            # ScoreCard, VerifyPanel, WalletGuard, CompetitionCard
│       ├── providers.tsx          # dapp-kit + React Query provider stack
│       ├── config.ts              # Shared constants (PACKAGE_ID)
│       └── api.ts                 # API client + TypeScript interfaces
└── scripts/
    └── deploy.sh                  # One-click contract deployment

On-Chain Verification

Every result stored on Sui contains:

  • prompt_hash — SHA-256 of the exact judging prompt
  • enclave_pubkey — the judging service's Ed25519 public key
  • signed_payload — the canonical JSON that was signed (includes scores, prompt hash, timestamp)
  • signature — Ed25519 signature, verified by sui::ed25519::ed25519_verify before storage

The Move contract rejects any submission with an invalid signature. If the judging service is tampered with, the signature won't match and the results cannot be recorded.


Built With


License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors