Skip to content

Repository files navigation

Sealed

Sealed is a Stellar Testnet marketplace for encrypted digital drops. Creators publish sealed content, buyers purchase contract-native Access Keys, and each key can reveal content or move through resale when the creator allows it.

Current Testnet Status

  • Soroban marketplace contract deployed on Stellar Testnet
  • Freighter browser signing for create, buy, multi-buy, reveal, resale, offers, votes, pause, and resume flows
  • Real XLM Testnet payment settlement for create fees, primary sales, resale, and escrowed offers
  • One-use secrets consume the selected key after reveal
  • Reusable digital assets can be opened and later resold
  • Encrypted IPFS storage through Pinata when configured, with local encrypted fallback
  • Pinata-backed public drop index for fresh-browser marketplace loading
  • Server-side content key release after Stellar reveal verification

ZK Status

Sealed now generates a Noir Sealed Access Proof before reveal.

Implemented:

  • Noir circuit at circuits/sealed_access
  • Noir Pedersen hash constraints for the access commitment and nullifier relation
  • Barretenberg UltraHonk proof generation in the browser
  • browser-side proof verification before reveal_key
  • deterministic proof-derived reveal nullifier
  • proof hash shown in Judge Demo after browser reveal
  • nullifier anchored on Stellar through the existing Soroban reveal transaction

Boundary:

  • the deployed Soroban contract does not verify the full Noir proof bytes on-chain
  • the contract receives the proof-derived nullifier, enforces ownership and key state, then blocks nullifier replay
  • a dedicated Soroban verifier contract is a future hardening step, not simulated here

What The Proof Shows

Private input:

  • local access witness for the wallet, drop, key, and action

Public inputs:

  • contract id commitment
  • network commitment
  • action commitment
  • action
  • drop id
  • key id
  • content commitment
  • access commitment
  • nullifier

The witness is not sent to Stellar. Stellar stores the nullifier and contract state.

Judge Demo Steps

  1. Connect Freighter on Testnet.
  2. Create or open a sealed drop.
  3. Buy an Access Key.
  4. Reveal the key.
  5. Open Judge Demo and review the ZK Evidence section.
  6. Confirm the Noir proof stack, proof hash, public input summary, nullifier, and Stellar transaction link.

Encrypted Storage

Public files:

  • cover artwork
  • public listing images
  • public listing PDFs

These may be uploaded as normal public IPFS files because they are visible before purchase.

Private files and hidden content:

  • private article or guide text
  • one-use secret codes
  • private links
  • private image attachments
  • private PDF attachments

These are encrypted in the browser with AES-GCM before IPFS upload. Opening the raw IPFS CID should show encrypted JSON or encrypted bytes, not the readable hidden content. Buyer reveal decrypts content in the app only after access is verified.

Shared Marketplace Index And Key Release

Published drops are indexed through a Pinata-backed public manifest index.

  • GET /api/drop-index returns public listing manifests for the configured Testnet contract.
  • POST /api/publish-drop-manifest pins a public manifest, stores the server-side release record, and republishes the shared index.
  • POST /api/release-content-key verifies Stellar Testnet key ownership and reveal state before returning decrypt material.
  • GET /api/health checks server Pinata config, index reachability, Stellar RPC, and contract config.

The public manifest contains public listing metadata, public artwork/file references, encrypted payload references, hashes, and Stellar transaction references. It does not contain hidden plaintext, private links, one-use codes, private file plaintext, decrypt keys, wallet secrets, or PINATA_JWT.

localStorage is used as optimistic cache and wallet-scoped UI state. It is not the production source of truth for published listings.

Local Fallback Mode

Use this for plain Vite development:

npm run dev

.env.local:

VITE_STORAGE_MODE=local

Private content uses the local encrypted fallback. Public and private files are stored in IndexedDB, not localStorage.

Pinata IPFS Mode

Pinata mode uses a serverless endpoint so the Pinata JWT is never bundled into frontend code.

.env.local:

VITE_STORAGE_MODE=pinata
VITE_IPFS_GATEWAY=https://gateway.pinata.cloud/ipfs/
PINATA_JWT=your_server_side_pinata_jwt
VITE_SEALED_CONTRACT_ID=your_testnet_contract_id
VITE_STELLAR_RPC_URL=https://soroban-testnet.stellar.org

PINATA_JWT must be server-side only. Do not prefix it with VITE_.

Run Pinata mode with Vercel's local runtime:

vercel dev

Plain npm run dev does not provide the /api/ipfs-upload serverless endpoint, so the app falls back to local encrypted storage.

Storage Verification

  1. Create a drop with a private article plus a private image or PDF.
  2. Use Pinata mode with vercel dev.
  3. Confirm Create shows encrypted IPFS ready.
  4. Publish the drop.
  5. Open an incognito browser and confirm the drop appears from /api/drop-index.
  6. Buy an Access Key with a buyer wallet.
  7. Reveal the key and confirm the Noir proof is generated and verified.
  8. Confirm Stellar stores the reveal nullifier.
  9. Confirm /api/release-content-key succeeds only after reveal verification.
  10. Confirm the app decrypts and displays hidden content only after reveal.
  11. Open the raw IPFS CID or gateway URL for the private payload and confirm it exposes ciphertext, not the readable secret.

Documentation

Commands

npm run build
npm run contract:test
npm run contract:build

Deploy command when the contract source changes:

powershell -ExecutionPolicy Bypass -File scripts/deploy-testnet.ps1 -Source sealed-admin -Network testnet

About

Encrypted digital drops marketplace on Stellar Testnet with Noir access proofs.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages