Private Data Transfer & Access Control - Encrypted digital goods marketplace built on Solana + Arcium.
Apps need cryptographic access control without trusted servers. With Arcium, keys, policies, metering, and licensing are enforced in encrypted shared state over arbitrary storage (IPFS/S3), enabling revocation, pay-to-decrypt, and time-bound access.
Arxcess brings this directly to digital commerce: sellers publish encrypted files to IPFS, Arcium's MXE confidential compute network takes custody of the content key, and buyers receive buyer-bound re-encrypted delivery payloads - all verifiable on Solana Devnet with zero server trust.
- Why Arxcess
- How It Works
- Architecture
- Repository Structure
- Tech Stack
- Arcium Integration
- Solana Program
- Frontend Surfaces
- Getting Started
- Environment Variables
- Supabase (Optional)
- Verifying On-Chain State
- Security & Privacy
- Development Workflow
- Judging Notes
- Team
- License
Traditional digital marketplaces require buyers to trust the platform to deliver what they paid for, and sellers to trust the platform not to leak their content. Both parties implicitly trust a centralized server.
Arxcess eliminates that trust requirement:
- No trusted server for decryption. The content key is never stored on any server. Arcium's MXE network - a network of confidential compute nodes - holds it under encrypted shared state.
- Buyer-bound delivery. Arcium re-encrypts the content key specifically for the buyer's ephemeral delivery keypair, generated in-browser at checkout. The re-encrypted payload lives on-chain - no courier, no intermediary.
- Policy enforcement in confidential compute. Payment verification, product status, revocation status, and delivery finality are all evaluated inside the Arcium circuit before any key material is released.
- Verifiable on Solana. Every custody and delivery event emits Anchor events and on-chain state transitions that anyone can inspect on Solana Explorer.
The core protocol separates encryption, custody, and delivery into distinct on-chain steps:
| Step | Actor | Action |
|---|---|---|
| 1 | Seller | Encrypts file in-browser (AES-GCM, Web Crypto API), uploads ciphertext to Pinata/IPFS |
| 2 | Seller | Signs create_product → registers listing on-chain with ciphertext CID and hash |
| 3 | Seller | Signs request_deposit_product_key → encrypts content key + IV to the Arcium MXE public key using Rescue cipher over x25519 shared secret, queues confidential compute job |
| 4 | Arcium MXE | Executes deposit_key_v3 circuit, verifies inputs, stores key under MXE custody, fires deposit_key_v3_callback → sets arcium_custody_ready = true on ProductState |
| 5 | Seller | Signs activate_product → listing becomes publicly purchasable |
| 6 | Buyer | Generates ephemeral x25519 delivery keypair in-browser, signs purchase_product → payment transferred on-chain, delivery pubkey recorded in PurchaseState |
| 7 | Seller | Signs request_evaluate_and_seal → triggers Arcium evaluate_and_seal_v4 circuit |
| 8 | Arcium MXE | Evaluates all policy conditions (payment verified, product active, not revoked, not yet delivered), re-encrypts content key to buyer's delivery pubkey, fires evaluate_and_seal_v4_callback → writes ciphertext payload into PurchaseState, sets arcium_delivery_ready = true |
| 9 | Buyer | Reads on-chain ciphertext payload, decrypts using delivery private key + Arcium MXE shared secret via Rescue cipher, recovers content key + IV, downloads and decrypts the IPFS ciphertext |
The seller never has access to the buyer's decrypted content. Arcium's MXE network performs the re-encryption inside a confidential compute environment - the result is cryptographically verifiable on-chain.
┌─────────────────────────────────────────────────────────────────┐
│ BROWSER (Seller) │
│ AES-GCM encrypt file → upload ciphertext to IPFS/Pinata │
│ Rescue-encrypt content key + IV → MXE x25519 public key │
│ Sign: create_product, request_deposit_product_key, │
│ activate_product │
└───────────────────────────┬─────────────────────────────────────┘
│ Solana transactions
▼
┌─────────────────────────────────────────────────────────────────┐
│ SOLANA PROGRAM (Anchor) │
│ ProductState ← create_product, activate_product │
│ PurchaseState ← purchase_product, finalize_delivery │
│ Arcium CPIs: request_deposit_product_key │
│ request_evaluate_and_seal │
│ Callbacks: deposit_key_v3_callback │
│ evaluate_and_seal_v4_callback │
└──────────┬──────────────────────────────────┬───────────────────┘
│ CPI to Arcium │ Callbacks
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ ARCIUM MXE NETWORK │
│ deposit_key_v3: custody wrap (key → MXE encrypted state) │
│ evaluate_and_seal_v4: policy check + re-encrypt to buyer pubkey│
│ BLS-signed outputs, on-chain delivery commitment verified │
└─────────────────────────────────────────────────────────────────┘
│
▼ arcium_delivery_ready = true
┌─────────────────────────────────────────────────────────────────┐
│ BROWSER (Buyer) │
│ Read PurchaseState ciphertexts from chain │
│ Decrypt via delivery keypair + MXE public key (Rescue cipher) │
│ Recover content key + IV → decrypt IPFS ciphertext │
│ Preview / Download decrypted file │
└─────────────────────────────────────────────────────────────────┘
Storage layer - Ciphertext and metadata JSON are stored on Pinata/IPFS. Supabase (optional) holds listing metadata and purchase intents for cross-browser discoverability. localStorage is the fallback for single-browser use.
arxcess/
├── frontend/ # Next.js 14 application
│ ├── app/ # App Router pages and API routes
│ │ ├── page.tsx # Home / landing page
│ │ ├── explore/ # Explore (buyer storefront)
│ │ ├── launch/ # Launch (seller workbench)
│ │ ├── library/ # Library (purchases + delivery)
│ │ └── api/
│ │ ├── listings/ # GET/POST listings (Supabase-backed)
│ │ └── purchases/ # GET/POST purchases (Supabase-backed)
│ ├── components/
│ │ ├── marketplace/ # AppShell, TopBar layout
│ │ ├── purchase/ # ProductCatalog (Explore), PurchasesList (Library)
│ │ ├── upload/ # SellerWorkbench (Launch)
│ │ └── ui/ # Button, Input, Badge, etc.
│ ├── hooks/ # use-purchases, use-products, use-delivery-keys
│ └── lib/
│ ├── arcium/ # Arcium client: prepare custody, decrypt delivery
│ ├── anchor/ # Anchor provider + program helpers
│ ├── crypto/ # content.ts (AES-GCM), delivery.ts (x25519 keypair)
│ ├── ipfs/ # Pinata upload client
│ ├── solana/ # RPC helpers, transaction utilities
│ ├── supabase/ # Supabase client (listings, purchases)
│ └── storage/ # Marketplace listing storage abstraction
├── sdk/ts/ # Shared TypeScript types (@arxcess/sdk)
├── contracts/
│ ├── Anchor.toml # Anchor workspace config
│ └── programs/arxcess/src/
│ ├── lib.rs # Program entry point + instruction routing
│ ├── state/
│ │ ├── product_state.rs # ProductState account layout
│ │ └── purchase_state.rs# PurchaseState account layout
│ ├── instructions/ # One file per instruction handler
│ ├── errors.rs # Custom error codes
│ └── events.rs # Anchor event definitions
├── encrypted-ixs/src/ # Arcium circuit definitions (Rust + arcis)
│ ├── lib.rs # deposit_key_v3, evaluate_and_seal_v4 circuits
│ ├── deposit_key.rs # Vault handle + key commitment derivation
│ └── evaluate_and_seal.rs # Policy evaluation + buyer-bound sealing
├── build/ # Compiled Arcium circuit interface files (.idarc)
├── scripts/ # CLI smoke-test: smoke-arcium-flow.mjs
├── supabase/ # SQL schema files
├── Cargo.toml # Rust workspace manifest
├── Arcium.toml # Arcium cluster config (devnet offset 456)
└── package.json # npm workspace root
| Layer | Technology |
|---|---|
| Frontend | Next.js 14, React 18, TypeScript, TailwindCSS |
| Wallet | Solana Wallet Adapter, @coral-xyz/anchor |
| On-chain program | Solana Devnet, Anchor 0.32.1 |
| Confidential compute | Arcium MXE (live on Devnet) |
| Arcium client SDK | @arcium-hq/client (x25519, Rescue cipher, MXE pubkey fetch) |
| Storage | Pinata/IPFS (ciphertext + metadata JSON) |
| Client-side crypto | Web Crypto API - AES-GCM 256-bit |
| Delivery keypair | TweetNaCl x25519 key generation |
| Shared state (optional) | Supabase (listings + purchase history) |
Arcium is the core privacy layer of Arxcess. It replaces the need for a trusted key-management server by performing cryptographic operations inside a Multi-Party Execution (MXE) confidential compute network.
Defined in encrypted-ixs/src/lib.rs:
pub fn deposit_key_v3(input_ctxt: Enc<Shared, DeliveryMaterial>) -> Enc<Mxe, DeliveryMaterial>- Input:
DeliveryMaterial(content key + IV, 44 bytes) encrypted by the seller using the MXE x25519 public key - Output: Same material re-encrypted under MXE custody
- Effect: Content key is now held exclusively in Arcium's encrypted shared state; the seller's one-time encryption key is discarded
pub fn evaluate_and_seal_v4(
input_ctxt: Enc<Shared, DeliveryMaterial>,
payment_verified: bool,
product_active: bool,
purchase_not_revoked: bool,
delivery_not_yet_finalized: bool,
buyer: Shared,
) -> (bool, Enc<Shared, DeliveryMaterial>)- Input: MXE-encrypted delivery material + four on-chain policy flags + buyer delivery pubkey
- Policy: All four conditions must be true; if any fails, a zero payload is re-encrypted instead
- Output:
(approval_flag, buyer-encrypted DeliveryMaterial) - Effect: If approved, buyer can decrypt the content key using only their delivery private key
Located in frontend/lib/arcium/client.ts:
Publish path (prepareListingCustody):
- Fetches MXE x25519 public key from chain via
getMXEPublicKey - Packs content key + IV into 44-byte
DeliveryMaterial - Generates a one-time x25519 keypair, derives shared secret with MXE pubkey
- Encrypts material using Rescue cipher with a random 16-byte nonce
- Returns
sellerEncryptionPublicKey,encryptedKeyCiphertexts,encryptedKeyNonce- passed torequest_deposit_product_key
Reveal path (revealArciumDeliveryMaterial):
- Reads
arcium_delivery_encryption_key,arcium_delivery_nonce,arcium_delivery_ciphertextsfromPurchaseState - Verifies the encryption key matches the buyer's delivery public key (ownership check)
- Derives shared secret between buyer delivery private key and MXE public key
- Decrypts using Rescue cipher → recovers 44-byte
DeliveryMaterial - Unpacks content key + IV → used to AES-GCM decrypt the IPFS ciphertext
#[arcium_callback(encrypted_ix = "deposit_key_v3", auto_serialize = false)]
pub fn deposit_key_v3_callback(...) -> Result<()>
#[arcium_callback(encrypted_ix = "evaluate_and_seal_v4", auto_serialize = false)]
pub fn evaluate_and_seal_v4_callback(...) -> Result<()>Callbacks are called by the Arcium program after computation completes. They write the output into ProductState or PurchaseState and set the arcium_custody_ready / arcium_delivery_ready flags.
| Property | Mechanism |
|---|---|
| Content key never exposed server-side | AES-GCM encryption happens in-browser; key is handed to Arcium MXE, never to a backend |
| Seller cannot read buyer's delivery | Re-encryption is done by Arcium MXE, not the seller; seller only queues the computation |
| Buyer delivery is bound to one keypair | MXE encrypts to buyer's x25519 public key recorded at purchase time; the payload is useless without the matching private key |
| Policy enforced in confidential compute | Payment, status, revocation, and delivery flags are checked inside the Arcium circuit - not in client-side JavaScript or a server |
| Delivery commitment verifiable on-chain | delivery_commitment is a SHA-256 hash over all delivery inputs; published on-chain and can be independently verified |
| Revocation | Seller can call revoke_purchase on revocable listings; the circuit checks purchase_not_revoked before releasing key material |
| Time-bound access | expires_at and max_access_count enforced in consume_access on-chain |
Program ID: sDNRRyCwQptaRZHATCha4nSJCFCwpcDWH2NvJCCAwFk
Source: contracts/programs/arxcess/src/
| Instruction | Signer | Description |
|---|---|---|
create_product |
Seller | Initializes ProductState with listing metadata, ciphertext CID/hash, price, license params |
request_deposit_product_key |
Seller | Encrypts content key + IV to MXE; queues Arcium deposit_key_v3 computation |
deposit_key_v3_callback |
Arcium | Sets arcium_custody_ready = true, stores vault handle and key commitment |
activate_product |
Seller | Transitions listing from Draft → Active after custody is ready |
pause_product |
Seller | Pauses an active listing |
purchase_product |
Buyer | Initializes PurchaseState; transfers payment on-chain; records buyer delivery pubkey |
request_evaluate_and_seal |
Seller | Queues Arcium evaluate_and_seal_v4 computation for a specific purchase |
evaluate_and_seal_v4_callback |
Arcium | Writes delivery ciphertext into PurchaseState; sets arcium_delivery_ready = true |
consume_access |
Buyer | Increments access_count, enforces expiry and reveal limit |
revoke_purchase |
Seller | Revokes buyer access on revocable listings |
ProductState (state/product_state.rs)
Stores listing metadata plus Arcium custody state:
ciphertext_cid,ciphertext_hash- IPFS location and integrity hash of the encrypted filearcium_vault_handle,key_commitment- identifiers for MXE custodyarcium_key_nonce,arcium_key_ciphertexts- MXE-encrypted delivery materialarcium_custody_ready- set totrueby thedeposit_key_v3callbacklicense_duration_seconds,max_access_count,revocable- access policy
PurchaseState (state/purchase_state.rs)
Stores purchase and delivery state:
buyer_delivery_pubkey- buyer's x25519 public key recorded at checkoutsealed_key_box- Arcium-encrypted delivery payloadarcium_delivery_encryption_key,arcium_delivery_nonce,arcium_delivery_ciphertexts- Arcium Rescue-cipher outputdelivery_commitment- on-chain SHA-256 commitment over all delivery inputsarcium_delivery_ready- set totrueby theevaluate_and_seal_v4callbackaccess_count,max_access_count,expires_at,revoked_at- entitlement tracking
Home - frontend/app/page.tsx
Landing page. Introduces the encrypted marketplace concept, outlines the seller and buyer workflows, and links to Launch and Explore.
Searchable, filterable storefront of active listings.
- Listings pulled from Supabase (or
localStoragefallback) - Filter by category, search by title, sort by price/date/popularity
- Each card shows price, reveal limit, license duration, and revocability badge
- Checkout: triggers
purchase_producton-chain; buyer delivery keypair auto-generated in-browser and stored inlocalStoragekeyed by purchase ID
Step-by-step seller workspace for publishing encrypted listings.
- Fill metadata: title, description, category, price (SOL), license duration, reveal limit, revocable flag
- Select file → AES-GCM encrypted in-browser via Web Crypto API
- Ciphertext + metadata JSON uploaded to Pinata/IPFS
- Three sequential wallet transactions:
create_product- registers product on-chainrequest_deposit_product_key- queues Arcium MXE custody; passes Rescue-encrypted content keyactivate_product- activates listing after custody settles
- Listing synced to Supabase and/or
localStorage
Wallet-gated hub showing all purchases associated with the connected wallet.
Assets tab - active purchases with delivery state:
| Action | Shown To | Condition |
|---|---|---|
| Finalize delivery | Seller | Purchase exists, delivery not yet sent |
| Reveal | Buyer | arcium_delivery_ready = true |
| Download | Buyer | After successful reveal |
| Revoke | Seller | Listing marked revocable |
Each card shows: delivery status, reveal count (used/max), price, purchase date, expiry, and expandable transaction trail (Purchase tx, Publish tx, Delivery tx).
History tab - full chronological purchase history with expandable per-entry transaction details.
- Node.js 20+, npm 10+
- A Solana wallet extension (e.g. Phantom) set to Devnet
- Devnet SOL - get some from the Solana faucet
- A Pinata account for IPFS uploads (free tier works)
- (Optional) A Supabase project for cross-browser listing/purchase sync
For smart contract development only: Rust toolchain, Solana CLI, Anchor CLI 0.32.1
# Clone the repository
git clone https://github.com/your-org/arxcess.git
cd arxcess
# Install all npm workspace dependencies (frontend + SDK)
npm install
# Configure environment (see Environment Variables below)
cp frontend/.env.example frontend/.env.local
# Edit frontend/.env.local with your values
# Start the development server
npm run dev
# → http://localhost:3000npm run dev # Start Next.js dev server
npm run build # Production build (frontend)
npm run lint # ESLint
npm run typecheck # tsc --noEmit (must pass with zero errors)
# Contract / Arcium scripts (requires full Rust + Anchor + Arcium CLI setup)
npm run build:anchor # Build Anchor program
npm run build:arcium # Build Arcium circuits → .idarc files
npm run test:arcium # Run Arcium circuit tests on devnetCreate frontend/.env.local - never commit this file:
# Solana
NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
NEXT_PUBLIC_PROGRAM_ID=sDNRRyCwQptaRZHATCha4nSJCFCwpcDWH2NvJCCAwFk
NEXT_PUBLIC_TREASURY_WALLET=<your_devnet_treasury_pubkey>
# Arcium
NEXT_PUBLIC_ARCIUM_CLUSTER_OFFSET=456
# Pinata (required for Launch uploads)
PINATA_JWT=<your_pinata_jwt>
# Supabase (optional - enables shared listing and purchase history)
NEXT_PUBLIC_SUPABASE_URL=<your_supabase_project_url>
SUPABASE_SERVICE_ROLE_KEY=<your_supabase_service_role_key>| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_SOLANA_RPC_URL |
Yes | Solana JSON-RPC endpoint |
NEXT_PUBLIC_PROGRAM_ID |
Yes | On-chain Arxcess program address |
NEXT_PUBLIC_TREASURY_WALLET |
Yes | Protocol fee destination |
NEXT_PUBLIC_ARCIUM_CLUSTER_OFFSET |
Yes | Arcium devnet cluster offset (456) - required for publish and delivery finalization |
PINATA_JWT |
For Launch | Authenticated Pinata uploads (ciphertext + metadata) |
NEXT_PUBLIC_SUPABASE_URL |
Optional | Enables cross-browser listing and purchase sync |
SUPABASE_SERVICE_ROLE_KEY |
Optional | Server-side Supabase writes via API routes |
When NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are set:
- Listings published via Launch are persisted to the
listingstable - Purchases made via Explore are persisted to the
purchasestable - Library and Explore query Supabase first, falling back to
localStorage
SQL schema files: supabase/
Server-side API routes:
| Route | File | Description |
|---|---|---|
GET /api/listings |
frontend/app/api/listings/route.ts |
Fetch all active listings |
POST /api/listings |
same | Persist a new listing |
GET /api/purchases |
frontend/app/api/purchases/route.ts |
Fetch purchases by wallet |
POST /api/purchases |
same | Persist a new purchase record |
Without Supabase, state is fully browser-local via localStorage. Cross-browser and cross-device visibility requires Supabase.
After publishing or finalizing delivery, the Library shows Publish tx and Delivery tx links. Open them on Solana Explorer (Devnet) and inspect:
- CPI calls to the Arcium program
- Anchor events:
ArciumProductKeyComputationRequested,ArciumProductKeySettled,ArciumDeliverySettled
# Read raw ProductState account
solana account <PRODUCT_STATE_PUBKEY> --url devnet --output json | \
node -e "
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
const buf = Buffer.from(d.account.data[0], 'base64');
console.log('raw hex:', buf.toString('hex').slice(0, 160));
"node scripts/smoke-arcium-flow.mjsThe script runs a full end-to-end Arcium flow:
- Creates a product → calls
request_deposit_product_key→ printscustody_tx - Polls until
arcium_custody_ready = trueonProductState - Creates a purchase → calls
request_evaluate_and_seal→ printsdelivery_tx - Polls until
arcium_delivery_ready = trueonPurchaseState - Decrypts on-chain payload and asserts the commitment hash matches the original content
A passing run is cryptographic proof that Arcium performed real confidential computation on Devnet.
| Property | Implementation |
|---|---|
| In-browser encryption | window.crypto.subtle AES-GCM 256-bit; content key is never sent to any server |
| Arcium MXE custody | Content key + IV packed into 44-byte DeliveryMaterial, encrypted to MXE x25519 pubkey using Rescue cipher over ECDH shared secret; MXE holds it under encrypted shared state |
| Buyer-bound re-encryption | Arcium circuit re-encrypts delivery material exclusively to the buyer's x25519 public key recorded at purchase_product time |
| On-chain delivery commitment | delivery_commitment is SHA-256 over (product_id, purchase_id, buyer_pubkey, buyer_delivery_pubkey, approval_flag, sealed_key_box) - written on-chain and verifiable by anyone |
| Policy enforcement | Payment, product status, revocation, and delivery finality evaluated inside the Arcium circuit - not in JavaScript or a server |
| Revocation | Seller can call revoke_purchase on revocable listings; future evaluate_and_seal calls will output a zero payload |
| Time-bound access | expires_at enforced in consume_access; max_access_count limits total reveals |
| Supabase stores no secrets | Supabase holds only listing metadata and purchase intents (no content keys, no ciphertext); guarded by service role key in server-side API routes |
1. Configure frontend/.env.local
2. npm run dev → http://localhost:3000
3. Connect Phantom (Devnet mode) - get Devnet SOL from faucet
4. Open Launch (Wallet A / seller):
- Fill metadata, select a file, click Publish
- Approve 3 wallet transactions: create_product, request_deposit_product_key, activate_product
- Wait for Arcium custody callback (arcium_custody_ready = true on-chain)
5. Open Explore (Wallet B / buyer):
- Find the listing, click Buy
- Approve purchase_product transaction
6. Open Library (Wallet A / seller):
- Find the purchase, click Finalize delivery
- Approve request_evaluate_and_seal transaction
- Wait for Arcium delivery callback (arcium_delivery_ready = true on-chain)
7. Open Library (Wallet B / buyer):
- Find the asset, click Reveal → Download
Run npm run typecheck before every push - zero TypeScript errors required.
This project was built as a submission for the Private Data Transfer & Access Control track.
- Functional Solana project integrated with Arcium - Program deployed on Devnet at
sDNRRyCwQptaRZHATCha4nSJCFCwpcDWH2NvJCCAwFk; Arcium circuits live on Devnet (cluster offset 456); full publish + delivery flow executed via real MXE nodes - Clear explanation of how Arcium is used and the privacy benefits it provides - See Arcium Integration above
- Open-source GitHub repository - This repository
- Submission in English - All documentation and UI in English
| Criterion | Implementation |
|---|---|
| Innovation | Trustless pay-to-decrypt marketplace - buyers receive content keys re-encrypted by Arcium MXE, eliminating server-side trust entirely |
| Technical Implementation | End-to-end Arcium integration with custom circuit logic, Anchor callbacks, x25519/Rescue cipher key transport, and on-chain commitment verification |
| User Experience | Single-flow marketplace: seller publishes in 3 clicks, buyer purchases in 1 click, delivery and reveal handled from the Library |
| Impact | Applicable to digital goods, research data, media licensing, and any scenario requiring cryptographic access control without a trusted key server |
| Clarity | README covers full architecture, Arcium circuit logic, privacy properties, and a step-by-step test flow for judges |
1. Wallet A → Launch → publish any file → wait for custody ready
2. Wallet B → Explore → buy the listing
3. Wallet A → Library → Finalize delivery
4. Wallet B → Library → Reveal → Download
5. Click any tx link → Solana Explorer → verify Arcium CPI + events
| Handle |
|---|
| @EL3NG |
| @0xZucho |
MIT