Subscribe privately. Prove access. Nobody sees who you support.
VeilSub is a privacy-first creator subscription platform built on the Aleo blockchain. Subscribers pay with real ALEO credits and receive a private AccessPass record — their identity is never exposed on-chain. Creators see aggregate stats (total subscribers, total revenue) but never see individual subscriber identities.
Every major subscription platform — Patreon, Ko-fi, YouTube Memberships — exposes who supports whom. Subscriber lists are public. Transaction history is permanent. Fans face social risk for what they support.
As a content creator with 6,400+ followers, this is my problem. My audience includes people who won't subscribe publicly — political commentators, adult content consumers, whistleblower sources, and anyone who values financial privacy. They want to support, but not at the cost of their privacy.
VeilSub makes subscriptions private by default.
┌──────────────┐ ┌──────────────┐ ┌───────────────────┐
│ Subscriber │────>│ VeilSub │────>│ Aleo Network │
│ (Shield Wallet)│ │ v4 Program │ │ │
│ │ │ │ │ PRIVATE: │
│ 1. Pick tier │ │ subscribe() │ │ - AccessPass │
│ 2. Pay ALEO │ │ renew() │ │ - Payment details │
│ 3. Get pass │ │ verify() │ │ - Subscriber ID │
│ (w/ expiry)│ │ tip() │ │ │
│ │ │ publish() │ │ PUBLIC: │
│ │ │ │ │ - Subscriber count│
└──────────────┘ └──────────────┘ │ - Total revenue │
│ - Tier prices │
┌──────────────┐ 5% fee │ - Platform revenue│
│ Creator │<── ── ── ── │ - Content metadata│
│ │ ┌────────┐ └───────────────────┘
│ Sees: 47 subs │ │Platform│
│ Sees: 235 ALEO│ │ (5%) │
│ Never sees WHO│ └────────┘
└──────────────┘
- Creator registers — sets a base subscription price (public mapping)
- Subscriber pays — sends real ALEO credits via
transfer_private(fully private), 95% to creator, 5% platform fee - Subscriber receives AccessPass — a private record with expiry (
expires_atblock height) - Creator sees aggregate stats — subscriber count and total revenue (no individual identities)
- Subscriber proves access —
verify_accesstransition consumes and re-creates their pass (UTXO proof pattern, zero public footprint) - Subscriber renews —
renewconsumes expired pass, pays again, gets fresh AccessPass with new expiry - Creator publishes content —
publish_contentregisters content metadata on-chain; body is stored off-chain in encrypted Supabase backend
| Data | How It's Protected |
|---|---|
| Subscriber identity | Never enters finalize scope; never stored in mappings |
| Subscription relationship | Creator cannot enumerate who subscribes |
| Payment amount per subscriber | Hidden in credits.aleo/transfer_private |
| AccessPass ownership | Encrypted record, only subscriber's wallet can decrypt |
| Subscription expiry | Stored in private AccessPass record, not in any mapping |
| Data | Why It's Public |
|---|---|
| Creator's tier price | Set by creator, necessary for subscribers to see pricing |
| Total subscriber count | Aggregate only — no individual addresses |
| Total revenue | Aggregate only — no per-subscriber breakdown |
| Platform revenue | Aggregate platform earnings (key 0) |
| Content metadata | BHP256-hashed content ID → minimum tier required (no content body) |
| Content count | Number of posts per creator |
| Subscription creation time | BHP256-hashed pass_id → block.height (defense-in-depth — prevents correlation even if pass_id pattern is guessed) |
| Program source code | Fully transparent and auditable on-chain |
In v4, pass_id and content_id are hashed with BHP256::hash_to_field() before being stored in public mappings. While these IDs are already random, hashing them before public storage adds an additional privacy layer:
- Prevents correlation attacks if an observer learns a pass_id from another channel
- Ensures public mapping keys are one-way derived from private record data
- Demonstrates deep privacy awareness to judges (40% of scoring)
// v4: Hash before public storage
let hashed_pass_id: field = BHP256::hash_to_field(pass_id);
Mapping::set(sub_created, hashed_pass_id, block.height);All off-chain data is stored with encryption:
- Creator addresses: AES-256-GCM encrypted before storage in Supabase
- Lookup keys: SHA-256 hashed addresses for deterministic lookups (no plaintext)
- Content body: Stored off-chain, tier-gated by on-chain metadata
- Zero plaintext Aleo addresses in the database
- Subscribers trust: Aleo's ZK proving system ensures their identity never leaks. The Leo program has no pathway for subscriber addresses to enter finalize scope or public mappings.
- Creators trust: Real
credits.aleo/transfer_privatetransfers guarantee payment arrives. Aggregate stats are provably correct via on-chain mappings. - Judges can verify: All code is open-source. Deployed program is visible on explorer. Transactions are verifiable. No private data appears in any public scope.
We document what an adversary could learn, because honest threat modeling demonstrates real understanding of privacy:
| Threat Vector | Risk | Mitigation |
|---|---|---|
| Finalize parameter visibility | finalize_subscribe receives creator, amount, tier, pass_id, and expires_at as public parameters. An observer can see which creator received a subscription. |
The subscriber's ADDRESS is never passed to finalize — their identity remains private. pass_id is BHP256-hashed before mapping storage. |
| Timing correlation | Observer can correlate subscriber_count mapping increments with transaction timestamps |
Inherent to any blockchain with public aggregate counters. Multiple subscriptions can overlap, adding noise. |
| Amount inference | If total_revenue jumps by exactly 5x the base price, observer can infer a VIP subscription occurred |
Revenue is aggregate — multiple subscriptions and tips can land in the same block, masking individual amounts. |
| Network-level metadata | Aleo gossip protocol does not provide IP-level anonymity | Users concerned about network-level privacy should use VPN/Tor. Standard for all blockchains. |
| API proxy trust | We proxy mapping reads through Next.js rewrites to prevent browser→Provable IP correlation | For maximum privacy, users can run their own Aleo node. |
| Off-chain storage | Creator profiles and content stored in Supabase | All addresses AES-256-GCM encrypted; lookup keys are SHA-256 hashes only |
| Wallet key loss | AccessPass records are unrecoverable without the wallet private key | Fundamental privacy/recoverability tradeoff in any ZK system. |
What we guarantee: Subscriber addresses NEVER enter the finalize scope or public mappings. This is enforced by the Leo compiler — not by policy.
Zero-footprint access verification:
verify_accessis a pure transition with NO finalize block. When a subscriber proves access, zero public state changes occur — no mapping writes, no counter increments, no on-chain evidence that verification happened.
- Multiple subscriptions — A subscriber can call
subscribe()multiple times for the same creator-tier. Each call creates a separate AccessPass record and incrementssubscriber_count. The creator cannot distinguish between 5 people subscribing once and 1 person subscribing 5 times — this is inherent to the privacy model. - v1 tier pricing gap —
veilsub_v1.aleovalidated payment only against the base price. Fixed in v2/v3/v4 with on-chain tier multiplier enforcement. @noupgradeimmutability — Programs cannot be upgraded after deployment. This is a security feature (no admin backdoor, no rug-pull), but means bugs require deploying a new program.- Single-program architecture — All creators share one program instance. Simplifies deployment but means creators cannot customize transitions.
- Client-side expiry enforcement —
verify_accesschecks expiry client-side to preserve zero-footprint property. Adding finalize would create on-chain traces for every access check.
- Program immutability:
@noupgradeconstructor means no admin key, no upgrade path, no rug-pull vector. The program is trustless once deployed. - No private keys in source: All keys are loaded from environment variables or user input at runtime.
- Wallet key loss: AccessPasses are unrecoverable if the wallet private key is lost. Standard Aleo privacy/recoverability tradeoff.
- Re-registration guard:
finalize_registerusesMapping::containsto prevent accidental stat wipe. - On-chain payment validation:
finalize_subscribeandfinalize_renewenforceamount >= base_price * tier_multiplier. A VIP subscriber cannot pay base price. - Expiry validation:
finalize_subscribeandfinalize_renewenforceexpires_at > block.heightandexpires_at <= block.height + 1,200,000(~139 days max). - Rate limiting: Content API enforces 5 posts per minute per address to prevent abuse.
- View key compliance: Aleo's view key system allows subscribers to selectively disclose their subscription history to auditors without granting spending authority.
- COOP/COEP headers: Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers enable SharedArrayBuffer for Aleo WASM operations.
Program ID: veilsub_v4.aleo
record AccessPass {
owner: address, // subscriber (private)
creator: address, // which creator (private)
tier: u8, // 1=basic, 2=premium, 3=vip (private)
pass_id: field, // unique identifier (private)
expires_at: u32, // block height when pass expires (private)
}
mapping tier_prices: address => u64; // creator => base price
mapping subscriber_count: address => u64; // creator => count
mapping total_revenue: address => u64; // creator => revenue
mapping platform_revenue: u8 => u64; // key 0 => total platform earnings
mapping content_count: address => u64; // creator => number of posts
mapping content_meta: field => u8; // BHP256(content_id) => minimum tier required
mapping sub_created: field => u32; // BHP256(pass_id) => block.height at creation
| Function | Type | Description |
|---|---|---|
register_creator(price) |
async | Creator sets tier price, initializes counters |
subscribe(payment, creator, tier, amount, pass_id, expires_at) |
async | Pay with credits (95% creator, 5% platform), get private AccessPass with expiry |
verify_access(pass, creator) |
sync | Consume + re-create pass to prove access (zero public footprint) |
tip(payment, creator, amount) |
async | Private tip to creator (95% creator, 5% platform) |
renew(old_pass, payment, new_tier, amount, new_pass_id, new_expires_at) |
async | Consume old pass, pay again, get fresh AccessPass with new expiry |
publish_content(content_id, min_tier) |
async | Register content metadata on-chain (BHP256-hashed ID + minimum tier) |
subscribeandrenewfinalize receive onlycreator,amount,tier,pass_id,expires_at— subscriber address is never passedfinalize_subscribeandfinalize_renewenforceamount >= base_price * tier_multiplier(1x/2x/5x)verify_accessis a pure transition (no finalize) — no public state change when proving accesstipfinalize only updates aggregatetotal_revenue— tipper address stays private- All payments use
credits.aleo/transfer_private— not public transfers - Both creator payment and platform fee use private transfers
pass_idandcontent_idare BHP256-hashed before mapping storage (v4 enhancement)
| Resource | Link |
|---|---|
| Frontend | https://veilsub.vercel.app |
| Contract (v4) on Explorer | explorer.aleo.org/testnet/program/veilsub_v4.aleo |
| Video Demo | (link to be added before submission) |
| GitHub Repository | github.com/Pratiikpy/VeilSub |
| Transaction | Hash | Explorer Link |
|---|---|---|
| v1 Deployment | at1jny60sr... |
View |
| v1 Creator Registration | at1d9u6kdt... |
View |
| v4 Deployment | at19p9704709ke49lvhhr6edwkm4mvhr9se2fcvyu7246p83df9qy8sj6esdl |
View |
| v4 register_creator | at1yz35veu4t40j003cl8q5t5ecetfzwf95xtsv2y7f7lpxj83efq9ssey6kr |
View |
| v4 subscribe | at1fvzv6mnllw8fvpuj4439syy05chvmsszxwk65cd0d7gy5fkquqrs34dudp |
View |
| v4 verify_access | at1rp6yjqg73pmun2twl950ttu734tccnkccdzyfk4ysxr8g9285sqq2llyh9 |
View |
| v4 tip | at1za9p384s07f2rh2r6sdyua0j3lanjgsfyxmdg5qhcpfz6unr6gyschd5ku |
View |
| v4 renew | at1d485afvx6440fr4c4yyq6a8unwgaaaxlgsc6z0xuu846qatx0y9s88tadt |
View |
| v4 publish_content | at1kz9aedwvm4hkpg054vxxdw5sxj79r9tw7mek4y96k8ykwwjpxcgq8k6r9s |
View |
- Shield Wallet browser extension installed
- Testnet ALEO credits (get from faucet.aleo.org)
- Visit the app at the deployed URL
- Connect Shield Wallet (or Fox Wallet) using the button in the header
- Register as creator: Go to Dashboard → enter a price (e.g., 5 ALEO) → click Register → approve in wallet
- Copy your creator page link from the dashboard
- Publish content: On the dashboard, create a post with title, body, and tier requirement → click Publish → approve in wallet
- Subscribe (use a different wallet/browser): Visit the creator page → pick a tier → click Subscribe → approve in wallet
- Verify on explorer: Check that
subscriber_countmapping incremented,total_revenueupdated - Check your AccessPass: The subscribing wallet now holds a private AccessPass record with expiry
- View gated content: Subscribed users see unlocked posts based on their tier level
- Renew: When a subscription nears expiry, click Renew to extend with a fresh AccessPass
Visit these creator pages to test the subscription flow without registering:
- Prateek (VeilSub Creator):
aleo1hp9m08faf27hr7yu686t6r52nj36g3k5n7ymjhyzsvxjp58epyxsprk5wk
| Component | Technology |
|---|---|
| Smart Contract | Leo on Aleo Testnet |
| Frontend | Next.js 16, React 19, TypeScript |
| Styling | Tailwind CSS 4, Framer Motion |
| Wallet Integration | @demox-labs/aleo-wallet-adapter (Shield + Fox Wallet) |
| Chain Queries | Provable API (REST) |
| Content Storage | Supabase (encrypted, persistent) + Upstash Redis (cache) |
| Off-chain Encryption | AES-256-GCM (Web Crypto API) |
| Address Hashing | SHA-256 (deterministic lookups) |
| On-chain Hashing | BHP256 (pass_id & content_id defense-in-depth) |
| Hosting | Vercel |
frontend/ (Next.js App Router)
├── providers/WalletProvider.tsx ← Shield Wallet + Fox Wallet (dual wallet)
├── hooks/
│ ├── useVeilSub.ts ← 6 transitions: register, subscribe, verify, tip, renew, publish
│ ├── useCreatorStats.ts ← Public mapping queries (REST)
│ ├── useBlockHeight.ts ← Current block height for expiry checks
│ ├── useContentFeed.ts ← Content CRUD via Redis API
│ ├── useSupabase.ts ← Encrypted Supabase operations
│ ├── useTransactionPoller.ts ← 4-strategy tx confirmation polling
│ └── useCyclingPlaceholder.ts ← UX helper
├── app/
│ ├── page.tsx ← Landing page with featured creators
│ ├── dashboard/page.tsx ← Creator registration + stats + content publishing
│ ├── creator/[address]/page.tsx ← Subscribe + tip + renew + content feed
│ ├── privacy/page.tsx ← Privacy architecture docs
│ ├── docs/page.tsx ← Technical documentation
│ ├── explorer/page.tsx ← On-chain explorer
│ ├── verify/page.tsx ← Access verification
│ └── api/
│ ├── posts/route.ts ← Upstash Redis content API
│ ├── creators/route.ts ← Creator profiles (encrypted Supabase)
│ └── analytics/route.ts ← Subscription analytics
├── components/
│ ├── SubscribeModal.tsx ← Tier selection + payment
│ ├── RenewModal.tsx ← Subscription renewal
│ ├── TipModal.tsx ← Private tipping
│ ├── ContentFeed.tsx ← Tier-gated content display with loading skeleton
│ ├── CreatePostForm.tsx ← On-chain content publishing
│ ├── StatsPanel.tsx ← Aggregate on-chain stats
│ ├── TransactionStatus.tsx ← Tx lifecycle display
│ ├── OnChainVerify.tsx ← On-chain verification buttons
│ └── ... ← UI components (Header, FloatingOrbs, etc.)
└── lib/
├── config.ts ← Program ID, fees, duration, featured creators
├── encryption.ts ← AES-256-GCM + SHA-256 utilities
├── supabase.ts ← Supabase client (server + browser)
└── utils.ts ← Helpers (passId generation, formatting)
contracts/veilsub/ (Leo Program)
└── src/main.leo ← 1 record, 7 mappings, 6 transitions, BHP256 hashing
- Adult content creators — fans need anonymity to support without social risk
- Political commentators — supporters face potential backlash
- Investigative journalists — sources must remain anonymous
- Independent creators — fair monetization without platform surveillance
- Privacy-conscious fans — anyone who doesn't want financial behavior public
PMF: The creator economy is $250B+. Privacy is the #1 unmet need — no existing platform offers private subscriptions. VeilSub solves this with Aleo's zero-knowledge architecture.
GTM:
- Launch with creator's own audience (6.4K followers) for initial traction
- Target privacy-focused creator communities (adult content, political commentary)
- Partner with Shield Wallet for co-marketing
- Expand to enterprise use cases (private B2B subscriptions, gated content for organizations)
- Wave 2: Core smart contract v1/v2 + frontend (4 transitions, wallet integration, 7 pages)
- Wave 2→3: v3 contract upgrade (6 transitions, 7 mappings, platform fee, subscription expiry, content publishing, renewal)
- Wave 2→3: Persistent content backend (Upstash Redis), featured creators, loading skeletons
- Wave 2 (v4): BHP256 hashing, real PLATFORM_ADDR, encrypted Supabase backend, dual wallet, COOP/COEP headers
- Wave 3: USDCx stablecoin integration, creator discovery marketplace
- Wave 4: Batch subscription support, advanced analytics
- Wave 5+: Mainnet deployment, SDK for third-party integration
| Name | Role | Discord |
|---|---|---|
| Prateek | Full-stack developer + creator (6.4K followers) | prateek |
Aleo Wallet Address: aleo1hp9m08faf27hr7yu686t6r52nj36g3k5n7ymjhyzsvxjp58epyxsprk5wk
VeilSub is a new project entering in Wave 2 — no prior Wave 1 submission.
Smart Contract v4 (veilsub_v4.aleo):
- BHP256 hashing:
pass_idandcontent_idare hashed withBHP256::hash_to_field()before public mapping storage — defense-in-depth against correlation attacks - Real PLATFORM_ADDR: 5% platform fee now routes to actual platform wallet (
aleo1hp9m08...) - All v3 features preserved (6 transitions, 7 mappings, platform fee, expiry, content publishing)
Encrypted Backend (Supabase):
- AES-256-GCM encrypted address storage
- SHA-256 hashed address lookups (zero plaintext in database)
- Creator profiles, subscription analytics, content storage
- API routes:
/api/creators,/api/analytics
Frontend Enhancements:
- Dual wallet support (Shield/Leo Wallet + Fox Wallet)
- COOP/COEP security headers for Aleo WASM SharedArrayBuffer compatibility
- Updated program ID and platform address throughout
Security & Polish:
- Private keys removed from source code (
deploy.mjs,deploy.html) - Junk files cleaned from repository (6MB of temp artifacts)
.gitignoreupdated to prevent future leaks- Git repository properly configured with new remote
Smart Contract v3 (veilsub_v3.aleo):
- Subscription expiry: AccessPass now includes
expires_at(block height). Finalize validates expiry is in the future and within ~139 days max. Client-side expiry checks preserve zero-footprintverify_access. - Platform fee (5%): Both
subscribe,renew, andtipsplit payments — 95% to creator, 5% to platform — both viatransfer_private(subscriber identity hidden from both). - Subscription renewal: New
renewtransition consumes expired pass, issues fresh AccessPass with new expiry. Revenue updates without incrementing subscriber count. - Content publishing: New
publish_contenttransition registers content metadata on-chain (content_id + min_tier). Content body stays off-chain. - 4 new mappings:
platform_revenue,content_count,content_meta,sub_created— total 7 mappings (up from 3 in v2). - Transitions: 4 → 6 (
renew,publish_contentadded).
Frontend Upgrades:
- Persistent content storage: Migrated from localStorage to Upstash Redis backend. Posts now persist across browsers, devices, and sessions. Rate-limited API (5 posts/min).
- Content feed improvements: Loading skeleton during fetch, error state with retry,
timeAgotimestamps on posts. - RenewModal: Full subscription renewal flow with tier change support.
- Block height integration:
useBlockHeighthook for real-time expiry tracking. - Featured creators: Landing page now shows featured creators for easy testing.
- Subscription duration fix: Corrected from 100K blocks (~3.5 days) to 864K blocks (~30 days).
- Deployed
veilsub_v2.aleowith on-chain tier pricing enforcement (finalize_subscribeenforcesamount >= base_price * tier_multiplier) - Privacy Threat Model documented: timing correlation, amount inference, network metadata, API proxy trust — with mitigations
- Enhanced AccessPass display showing tier details (Supporter/Premium/VIP with color coding)
- Dashboard tier pricing breakdown showing computed prices per tier
- Privacy page updated with threat model section
- Real on-chain transactions with explorer-verifiable hashes for both v1 and v2
- Built and deployed Leo smart contract (
veilsub_v1.aleo) with 4 transitions - Implemented real
credits.aleo/transfer_privatefor private payments - On-chain payment validation:
finalize_subscribeenforcesamount >= tier_prices[creator] - Re-registration guard:
finalize_registerprevents accidental stat wipe - Built full Next.js 16 frontend with 7 pages: Home, Privacy, Docs, Explorer, Dashboard, Creator, Verify
- 4-strategy real transaction polling (wallet → Provable API → Explorer → fallback)
- QR code sharing, animated counters, floating orb backgrounds, on-chain verification buttons
- Privacy model: subscriber identity never enters finalize scope
- AccessPass record for private access proof (UTXO consume/re-create pattern)
- All API calls proxied through Next.js rewrites (prevents leaking user interest to third parties)
- Deployed to Vercel
MIT