ACE Deep Dive

ACE (Access Control Encryption) encrypts data and lets a Move smart contract decide who can decrypt it. Threshold decryption with smart contract access conditions, built natively on Aptos using Identity-Based Encryption (IBE). Encrypt now, let the contract decide who decrypts.

The encrypted data lives on Shelby CDN, onchain as a Move resource, in a WebSocket stream, or in a transaction payload. ACE controls who gets the key.

Architecture

Three Components

1. The Committee A set of worker endpoints that collectively manage decryption keys. Each worker holds an IBE key share. A fragment of the master IBE secret for a given domain. No single worker holds the complete key. Only a threshold (k-of-n) of workers cooperating can derive the decryption key for a specific piece of content.

2. The ContractID An identifier for the onchain contract (any Move #[view] function returning bool) that manages decryption permission. The ContractID specifies: (a) the Aptos address of the Move module, (b) the function name, (c) what arguments to pass (typically the requester's address and the content identifier).

If the view function returns true, the requester has permission. Workers will release their key shares. If it returns false, permission is denied. Workers refuse.

3. ProofOfPermission A signed proof that a specific user has onchain permission at a specific time. Workers verify the proof before releasing their key shares. The proof is generated by the ACE SDK from the user's Aptos signature and the onchain permission state.

Why This is Better Than Token Gating

Traditional token gating: a server checks if you hold the token, then decides to serve the content. The server is the access control. Hack the server, get the content. Subpoena the server operator, get the content. The server can be misconfigured.

With ACE: the content is encrypted before it ever reaches a server. The server (Shelby) stores ciphertext. Even if Shelby is fully compromised, the attacker gets useless ciphertext. The access control lives in the Move contract and the IBE committee. Both of which are decentralized and require a threshold of cooperating parties.

The platform never holds the decryption key. It cannot leak what it doesn't have.

ContractID as Arbitrary Onchain Logic

The most powerful design choice in ACE: the access condition is any #[view] Move function. This means access conditions can encode any onchain state.

Simple Conditions (existing Onchain State)

Active CA payment stream:

#[view]
public fun has_active_stream(subscriber: address, creator: address): bool {
    payment_streaming::is_active(subscriber, creator)
}

If the subscriber is paying the creator right now, return true. Stop paying, return false. Decryption access is live, and it revokes automatically when the stream lapses.

Token holding:

#[view]
public fun holds_token(user: address, token_id: u64): bool {
    token::balance_of(user, token_id) > 0
}

Standard NFT or fungible token gate.

Vault membership:

#[view]
public fun is_vault_member(user: address, vault_id: u64): bool {
    vaults::get_stake(user, vault_id) >= MINIMUM_STAKE
}

Stake in the vault above a threshold unlocks decryption of the vault's strategy configuration.

Time-locked Conditions

#[view]
public fun after_block_height(requester: address, unlock_height: u64): bool {
    block::get_current_block_height() >= unlock_height
}

This is the most powerful condition for signal callers. Before the unlock block height, nobody (including the creator) can read the content. After the unlock, anyone can request decryption. The content commitment is cryptographic and irrevocable. The exact call content was sealed before the trade.

Multi-condition (composite Logic)

#[view]
public fun premium_vault_access(user: address, vault_id: u64): bool {
    let is_member = vaults::get_stake(user, vault_id) >= MIN_STAKE;
    let is_active = payment_streaming::is_active(user, vault_id);
    let is_verified = identity::is_kyc_verified(user);
    is_member && is_active && is_verified
}

Vault member AND active payment stream AND KYC-verified. All must be true simultaneously. No single condition is sufficient. This is the access pattern for institutional vault strategy configurations.

Revocation

The elegant property of ACE: revocation is free and automatic. When an access condition moves from true to false (payment stream lapses, vault stake withdrawn, token transferred), no revocation event is needed. Workers simply re-check the current onchain state and return false. The encrypted content remains inaccessible to the former holder.

This is fundamentally different from token gating or JWT-based access control, where revocation requires invalidating tokens, updating databases, and hoping all caches are cleared. With ACE, the onchain state IS the access state.

Shared IBE Infrastructure With UTT

Both ACE and UTT use IBE:

  • ACE workers use IBE master keys for threshold decryption key derivation
  • UTT uses IBE for the send-to-identity mechanism (sending to max.whop without knowing the wallet address)

Both systems come from Alin Tomescu's research group at Aptos Labs. A unified committee infrastructure serving both protocols. A single set of workers with two separate key spaces. Is architecturally plausible. This would reduce operational overhead significantly.

The key distinction: ACE encrypts arbitrary content and controls decryption via smart contracts. UTT encrypts coin ownership (the note containing sn and v) and sends it to an IBE identity. Different use cases, same underlying cryptographic primitive.

Production Worker Setup

Public test workers (for development only):

https://ace-worker-0-646682240579.europe-west1.run.app
https://ace-worker-1-646682240579.europe-west1.run.app

Why you can't use these for production: Using shared RPCs introduces a trust dependency. A malicious provider could return false permission results or log decryption requests. Production requires running your own workers with their own fullnodes.

Trust architecture for Whop:

ProductWho runs the workersWhy
Feed metered contentWhop 2-of-2 internal workersLow-stakes content, operational simplicity
Time-locked signal pre-commitmentsAptos Labs + Whop + neutral 2-of-3Trustless proof of prior commitment requires no single party to control
Vault strategy configurationWhop + vault manager 2-of-2Both parties have interest in maintaining confidentiality
Premium chart replay contentWhop internal workersPlatform-controlled premium content
Institutional vault strategyk-of-n institutional committeeCustom threshold for institutional clients

The 2-of-3 model for signal pre-commitments is the most important trust architecture decision. The signal caller, Whop, and a neutral third party (Aptos Labs is ideal) each hold a key share. For time-locked content to unlock as claimed, all three must cooperate, but also, any two of the three can verify the unlock happened correctly.

Detailed Product Integration Specs

Whop Feed. Metered Article Reads

Encryption structure: Each article is divided into paragraphs. Each paragraph is a separate ACE domain. Its own encryption key with its own ContractID. This enables per-paragraph charging.

Article: "Trading BTC in a bear market"
  - Paragraph 1: ACE domain "article-abc123/p1" → ContractID: FeedPayment(reader, "article-abc123", 1)
  - Paragraph 2: ACE domain "article-abc123/p2" → ContractID: FeedPayment(reader, "article-abc123", 2)
  - Paragraph 3: ACE domain "article-abc123/p3" → ContractID: FeedPayment(reader, "article-abc123", 3)

FeedPayment Move module:

#[view]
public fun can_read_paragraph(
    reader: address,
    article_id: vector<u8>,
    paragraph_index: u64
): bool {
    payment_streaming::is_active(reader, article_creator(article_id))
    || single_payment::has_paid(reader, article_id, paragraph_index)
}

A reader who has an active payment stream with the creator can read the whole article continuously. A reader who makes a one-time payment for a specific paragraph gets that paragraph and only that paragraph.

The 5¢/paragraph economics:

  • Creator sets price per paragraph at listing
  • Default recommendation: 5 cents
  • Premium articles (time-sensitive analysis, major market events): 10-25 cents
  • Content Rewards attribution fires on each paragraph decryption event

Time-locked Signal Pre-commitment

This is ACE's most commercially significant application for Whop.

The flow:

  1. 02Signal caller writes the full call: asset=BTC, direction=long, entry=82K, target=90K, stop=80K, position_size=10 BTC
  2. 04Signal caller encrypts this content with ACE, using a time-locked ContractID: unlock_after_block(caller, CURRENT_BLOCK + 30). Locked for approximately 30 seconds
  3. 06The ACE ciphertext is posted onchain (small, just the encrypted call content)
  4. 08Signal caller executes their trade on the Spot orderbook
  5. 10The UTT nullifier records the exact block height of their entry
  6. 1230 seconds (30 blocks) after posting the pre-commitment, the time lock releases
  7. 14The pre-commitment decrypts. Anyone can now verify the exact call content was written before the entry
  8. 16The call goes to followers in the signal group

What ACE adds beyond UTT:

UTT proves when the caller entered. ACE proves what the caller committed to before entering. These are different claims:

  • UTT only: "I entered my trade, then published the call 30 seconds later." The trade happened first, but the caller could have written the call after seeing the price move. UTT proves timing, not content.
  • UTT + ACE: "I encrypted my call, posted the ciphertext onchain, then entered the trade, then the time lock released and revealed my call said BTC long at $82K, which matches the trade." The call content was locked before the trade. No way to change it after seeing the price.

Security against precommitment manipulation: Once the encrypted call is posted onchain, no party (including the caller) can change it. The decryption key is held by the ACE committee and only released after the time lock. The caller cannot see the price action after posting the commitment, change their mind, and modify the commitment. The ciphertext onchain is immutable.

Vault Strategy Configuration

What gets encrypted: Indicator parameters (specific moving average lengths, RSI thresholds, signal conditions), position sizing rules (max leverage, max concentration, stop-loss levels), entry/exit logic (exact conditions for opening and closing positions).

Why this matters: A vault that proves a 1.9 Sharpe over 18 months is valuable. But if the strategy parameters are visible onchain, competitors can copy it. ACE keeps the strategy itself private while the performance statistics are fully public on Whop Explorer.

ContractID: Vault membership contract. Stake above minimum → ProofOfPermission → decryption keys released.

On version updates: When the vault strategy is updated, a new ACE encryption is created with a new ContractID pointing to the updated vault membership contract. Old subscribers who haven't renewed don't get the new strategy. Current members get both the current version and the update.

Chart Replay Premium Content

Structure: Each chart replay session produces:

  • Standard clip (free, always public, stored as plaintext on Shelby): 60-second highlight
  • Extended replay (ACE-encrypted, premium): full trading session recording
  • Post-trade commentary (ACE-encrypted, premium): creator's analysis of the trade

ContractID for premium content:

#[view]
public fun has_contentrewards_subscription(viewer: address, creator: address): bool {
    contentrewards::has_active_subscription(viewer, creator)
}

The economics: Standard clip views generate Content Rewards attribution. Premium content views (ACE-unlocked) generate higher Content Rewards rates because they represent higher engagement. The creator earns from both tiers, but the premium tier earns more per view.

Terminal Premium Streams

WebSocket content ACE-encrypted before push. Even if a non-subscriber intercepts the WebSocket connection, they receive ciphertext that cannot be decrypted without a valid onchain subscription.

1. Terminal backend generates content (market signal, news summary, QVAC analysis)
2. Content encrypted with ACE before WebSocket push
3. ContractID: active CA stream check for that stream type
4. Subscriber client receives ciphertext
5. Subscriber client requests ProofOfPermission from ACE committee
6. If stream is active, committee releases key shares
7. Subscriber client decrypts and displays content
8. Non-subscriber: receives ciphertext, cannot decrypt

Revocation timing: When a subscriber cancels their Terminal stream, the CA payment stops immediately. The next time their client requests a ProofOfPermission, the committee checks the onchain stream status, finds it inactive, and refuses. The latency between stream cancellation and content revocation is approximately one block height (~0.1 seconds).

Private .whop Name Messaging

A feature that falls naturally out of the ACE + .whop names combination:

  1. 02Alice encrypts a message with ACE using an allowlist contract she controls: only_authorized(sender, "bob.whop")
  2. 04She posts the ciphertext as a transaction payload or on Shelby
  3. 06Bob requests decryption from the ACE committee
  4. 08Committee checks the allowlist contract: only_authorized("bob.whop.apt_address", "bob.whop") returns true
  5. 10Bob decrypts the message

Result: encrypted DMs between .whop names. No Shelby needed for short messages (payload fits in a transaction). Alice's message is permanent onchain, decryptable only by Bob, without either party revealing a wallet address.

Storage Architecture. ACE + Shelby

ACE and Shelby are designed to work together, but they are architecturally independent.

The separation of concerns:

LayerResponsibility
ShelbyStore bytes. Serve bytes. No access control.
ACEControl who can decrypt. No storage.
Move contractDefine access conditions.
ACE committeeEnforce the conditions. Distribute key shares to authorized parties.

This separation is intentional and important. A Shelby outage doesn't compromise ACE's security. You can't decrypt content you can't access, but the access control logic itself (the committee) remains secure. An ACE committee compromise doesn't affect Shelby's ability to serve ciphertext. The bytes are still there, just now potentially decryptable without authorization.

Content addressing: Every piece of ACE-encrypted content has a content hash committed onchain at the time of encryption. If someone swaps the Shelby-stored ciphertext for different content (trying to change what gets revealed when the time lock releases), the content hash mismatch is detectable onchain. Tamper detection is built into the content addressing scheme.

ACE as a Platform Primitive for Whop

Across the entire Whop stack, ACE appears in:

  1. 02Feed: Paragraph-level metered access
  2. 04Chart replay: Premium replay content
  3. 06Signal pre-commitment: Time-locked call content
  4. 08Vault strategy: Encrypted parameters for members only
  5. 10Indicator backtest data: Full dataset for bonding curve funders
  6. 12Terminal streams: Real-time premium data feeds
  7. 14PnL card detailed metadata: Selective audit disclosure
  8. 16Private .whop messaging: Encrypted identity-based communication
  9. 18Course sections: Pay-per-section unlocking

In every case, the pattern is the same: creator encrypts before uploading, ContractID specifies the access condition, committee enforces it, buyer's onchain state determines access. The creator never needs to implement custom access control logic. The committee never needs to know what the content is. The platform never holds decryption keys.