DAY 005 / CRYPTOPAL

CryptoPal: two blind-token hops from wallet to email to wallet

How I turned a 2022 PlantUML sketch into a local Solana demo that moves one cUSD from wallet to email to wallet through two independent blinded-token hops.

01 / THE PRODUCT IDEA

What if a wallet could pay an email address without learning the next wallet?

CryptoPal turns one local cUSD deposit into a one-time email claim, then lets the recipient withdraw to a different wallet. Two fresh blinded bearer credentials keep the sender wallet, email address, and receiver wallet out of one cryptographic identity chain.

The easiest product analogy is “PayPal with crypto”: the sender addresses a person by email instead of asking for a chain address. That analogy explains the interaction, not the product maturity. This milestone is a fixed-value, local-only privacy demonstrator. It does not implement PayPal’s accounts, buyer protection, disputes, compliance, recovery, risk system, or operational guarantees.

This is useful as a protocol laboratory for gifts, reimbursements, rewards, or onboarding flows where the sender knows an email address but should not need the receiver’s wallet in advance. The privacy goal is narrower: remove the direct cryptographic join at each handoff. It is not to make public chain activity or service metadata disappear.

02 / WHAT SHIPPED

The noun “anychain” became a boundary; Solana became the first implementation.

The build uses a disposable Agave validator, one classic SPL Token mint called cUSD, six decimals, and one denomination of 1_000_000 base units. A React wallet UI carries the flow. Rust compiled to WebAssembly owns client-side blinding, proof verification, unblinding, and bearer authorization. A TypeScript processor coordinates TurboPass, PostgreSQL, SMTP, and a custodial Solana vault.

1implemented chain
1 cUSDfixed denomination
2independent blind hops
7long-running local services
Complete disposable local labshell · source
git submodule update --init --recursive
docker compose up --build

# Browser UI       http://localhost:3000
# Processor API    http://127.0.0.1:3001
# Mailpit inbox    http://localhost:8025
# Solana JSON-RPC  http://127.0.0.1:8899

03 / FROM THE 2022 SPEC

The original PlantUML already contained the important two-hop insight.

The 2022 repository was an API and sequence sketch, not a complete cryptographic specification. Its overview named two separate “zero knowledge decoupling” groups: sender wallet to receiver email, then receiver email to receiver wallet. That is the key idea I preserved. I normalized spelling, wording, and PlantUML delimiters below while retaining every step in the original flow.

Original protocol sequence, step-preserving normalizationPlantUML · source
@startuml Overview
actor Sender
entity Chain
collections Cryptopals
actor Receiver

Sender -> Cryptopals: request a deposit with receipts
Cryptopals -> Sender: return deposit ID and chain address
Sender -> Chain: transfer assets and include deposit ID
Cryptopals -> Chain: listen and mark transaction ready
Sender -> Cryptopals: check whether deposit is ready

group Zero-knowledge decoupling: sender wallet ↛ receiver email
  Sender -> Cryptopals: send blinded tokens
  Cryptopals -> Sender: return signed transfer tokens
  Sender -> Cryptopals: transfer slips + receiver email
end

Cryptopals -> Cryptopals: increase receiver balance
Receiver -> Cryptopals: check available balance by email

group Zero-knowledge decoupling: receiver email ↛ receiver wallet
  Receiver -> Cryptopals: send address-transfer envelopes
  Cryptopals -> Receiver: return redemption coupons
  Receiver -> Cryptopals: redeem coupons to receiver wallet
end

Cryptopals -> Chain: send tokens to receiver wallet
@enduml

The object sketch also named Key, Issuer, and Redemption. The runnable system sharpens those nouns into two domain-separated issuers, browser-held bearer material, and an authoritative spent-token set inside TurboPass.

Receipt
The sender-side blinded point presented before the deposit; implemented as a slip issuance request.
Slip
The unblinded, one-use bearer that authorizes one exact transfer ID and normalized-email hash.
Envelope
The receiver-side fresh blinded point; implemented as a coupon issuance request behind the claim capability.
Coupon
The unblinded, one-use bearer bound to chain, genesis, mint, denomination, and destination wallet.

One blind credential could not satisfy both arrows. Reusing the sender’s slip at payout would join email handling to the receiver wallet. The implementation therefore makes the “envelope” a new preparation and the coupon a new bearer under a separate issuer domain.

04 / ARCHITECTURE

Four PlantUML boxes became a complete, inspectable local system.

Original component modelPlantUML · source
@startuml
[HTTP API] as api
[Processor] as processor
[Chain Vault] as vault
[ZKP microservice] as zkp

api --> processor : registers requests
processor --> vault : manages wallets
processor --> zkp : processes tokens
@enduml
Original CryptoPal component sketch mapped to the implemented local architectureThe original HTTP API, Processor, Chain Vault, and ZKP microservice expand into a React browser with Rust WebAssembly, a processor API, a Solana vault backed by a local Agave validator, TurboPass, PostgreSQL, DynamoDB Local, and Mailpit. Future chain adapters are marked as unimplemented.2022 PLANTUML SKETCHHTTP APIregisters requestsPROCESSORcoordinates transferCHAIN VAULTmanages walletsZKP SERVICEprocesses tokens2026 RUNNABLE DEMONSTRATORREACT + RUST/WASMblind · verify · unblindtab-scoped bearer secretsPROCESSOR APIcustody · state machinesSMTP + payout policySOLANA VAULTAgave + SPL poolimplemented nowFUTURE VAULTSnew adapter + issuer domainnot implementedalternative adapter seamsigned transaction broadcastMAILPITrecipient + claim URLPOSTGRESQLprocessor + issuer stateTURBOPASSblind issuer + redemptionDYNAMODB LOCALspent-preimage setThe client-side secret boundary and operational trust stores were absent from the sketch.
Four sketched components became a seven-service local labThe 2022 PlantUML named four responsibilities. The implementation makes the browser-side secret boundary, durable stores, local email service, and concrete Solana adapter explicit; future chain adapters remain a design seam, not delivered integrations.

The React application and Rust/Wasm module are not cosmetic frontend details; they are a security boundary. The processor may see a blinded point during issuance and a bearer preimage during redemption, but the browser keeps the seed and blind that connect those moments. PostgreSQL owns the product state machines. TurboPass owns issuance and the authoritative spent-preimage record, backed by PostgreSQL and DynamoDB Local. Mailpit makes the email handoff visible without contacting a real mailbox.

Temporal is intentionally absent from CryptoPal’s request path. The pinned TurboPass project can use Temporal for issuer-key rotation, but this demo creates bounded 30-day issuer windows and does not run a rotation worker. “Built with TurboPass” must not be misread as “every TurboPass operational component runs here.”

05 / THE INTERACTION

The interface makes an invisible protocol legible.

The sender screen presents one fixed transfer as four concrete steps: fund a burner wallet, lock one cUSD in the pool, prepare and verify the private slip, then deliver the email. It also puts “Local Solana” and “Demo only · not for real funds” in the primary frame. That wording matters because a polished wallet screen can otherwise imply economic safety the protocol does not provide.

CryptoPal local demo showing a one cUSD wallet-to-email transfer and its four-step progress rail
Owner-supplied CryptoPal sender screenThis screenshot documents the local interface and safety copy. It is not evidence of a completed payout; the browser walkthrough and test results provide that separately.
  1. 01

    Fund a disposable sender

    The local faucet adds SOL for fees and local cUSD with no real-world value.

  2. 02

    Prepare before depositing

    The browser creates a fresh slip seed and sends only its blinded point.

  3. 03

    Prove the exact deposit

    The wallet submits one checked token transfer plus the processor’s memo.

  4. 04

    Hand off by email

    The verified slip authorizes one email-bound transfer and one stable claim URL.

06 / HOP ONE · WALLET → EMAIL

A Solana signature is accepted only after the processor reconstructs its meaning.

The browser first creates a deposit intent with one blinded slip point. The response pins a pool token account, mint, amount, and memo. The wallet then constructs atransferChecked instruction and a Memo instruction in the same transaction.

Checked SPL transfer plus deposit memoTypeScript · source
const transfer = createTransferCheckedInstruction(
  source,
  mint,
  destination,
  wallet,
  BigInt(config.asset.denominationBaseUnits),
  config.asset.decimals,
  [],
  TOKEN_PROGRAM_ID,
);

const memo = new TransactionInstruction({
  keys: [],
  programId: memoProgramId,
  data: Buffer.from(new TextEncoder().encode(deposit.memo)),
});

const transaction = new Transaction({
  blockhash: latest.blockhash,
  feePayer: wallet,
  lastValidBlockHeight: latest.lastValidBlockHeight,
}).add(transfer, memo);

The processor does not trust the browser’s “confirmed” message. It loads the transaction from Solana and checks success, confirmation, exact memo, exactly one checked transfer, expected mint and decimals, sender signer and source account, destination pool account, exact amount, and token-balance deltas. A previously used chain signature or blinded-token hash cannot be assigned to another deposit.

Only then does TurboPass evaluate the blinded point under the slip issuer. The browser reconstructs its prepared point, requires the returned issuer key to equal the configured key, verifies the batch-DLEQ proof, and unblinds locally.

Browser verification before unblinding, condensedTypeScript · source
export async function verifyAndUnblind(
  seed: Uint8Array,
  expectedBlindedToken: string,
  issuance: Issuance,
  pinnedPublicKey: string,
): Promise<string> {
  if (issuance.publicKey !== pinnedPublicKey) {
    throw new Error("The issuer key changed unexpectedly.");
  }

  const prepared = prepareTokenBatch(seed, 1);
  try {
    if (prepared.blindedTokens[0] !== expectedBlindedToken) {
      throw new Error("Saved preparation data does not match this issuance.");
    }
    return prepared.finalizeIssuance(
      issuance.signedTokens,
      issuance.publicKey,
      issuance.batchProof,
      pinnedPublicKey,
    )[0];
  } finally {
    prepared.free();
  }
}

To send the value, the processor normalizes and hashes the email address and returns an exact payload. The browser HMAC-authorizes that payload with the slip bearer, so changing the transfer ID or email hash invalidates the authorization. TurboPass's separate spent-preimage record prevents a second redemption.

Domain-separated canonical payloadsTypeScript · source
export function depositMemo(depositId: string): string {
  return `cryptopal:deposit:v1:${depositId}`;
}

export function slipPayload(transferId: string, emailHash: string): string {
  return `cryptopal:slip:v1:${transferId}:${emailHash}`;
}

export function couponPayload(
  genesisHash: string,
  mint: string,
  amountBaseUnits: bigint,
  wallet: string,
): string {
  return `cryptopal:coupon:v1:solana-local:${genesisHash}:${mint}:${amountBaseUnits}:${wallet}`;
}

07 / HOP TWO · EMAIL → WALLET

The claim hands over capability, then a fresh coupon breaks the second join.

After the slip is spent, the processor derives an unpredictable 256-bit capability from a server secret, transfer ID, and current chain genesis. The derivation is deterministic so an email retry reproduces the same URL without storing the raw capability; PostgreSQL stores only its SHA-256 hash.

Retry-stable claim capabilityTypeScript · source
private deterministicClaimSecret(transferId: string, genesisHash: string): string {
  return createHmac("sha256", this.claimSecretKey)
    .update("cryptopal:claim-secret:v1\0")
    .update(genesisHash)
    .update("\0")
    .update(transferId)
    .digest("base64url");
}

private claimUrl(secret: string): string {
  return `${publicWebUrl}/#/claim/${encodeURIComponent(secret)}`;
}

The secret appears after # in the single-page application URL. Browsers do not include a fragment in the initial HTTP request, so the web server receives the page request without the capability. The application still uses it afterward, and the mailbox, browser, extensions, clipboard, or client telemetry can expose it. Possession of that URL is the claim authentication model.

The recipient creates new preparation material—never the sender’s slip—and asks for a coupon under the coupon issuer. After verifying and unblinding, the recipient authorizes a payload containing the exact Solana genesis, mint, amount, and destination wallet. The public redemption request carries only the wallet, bearer preimage, and HMAC. It does not carry the email, claim ID, claim secret, or transfer ID.

Wallet-bound coupon redemption, condensedTypeScript · source
const payload = couponPayload(
  binding.vault.genesisHash,
  binding.vault.mint,
  denominationBaseUnits,
  wallet,
);

let payout = await repository.createOrGetPayout({
  id: uuid(),
  tokenHash: sha256Hex(preimageBytes),
  wallet,
  couponPayload: payload,
});

await turboPass.redeem(
  binding.couponIssuerName,
  payload,
  preimage,
  signature,
);
CryptoPal wallet-to-email-to-wallet protocol sequenceThe sender prepares a blinded slip, deposits one cUSD to a Solana pool, verifies and unblinds the issued slip, and spends it for an email claim. The recipient prepares a fresh blinded coupon, verifies and unblinds it, and spends it for a payout to a chosen Solana wallet. Both issuance responses travel through the processor.SENDERPROCESSORTURBOPASSEMAIL / RECEIVERSOLANAHOP 1 · WALLET → EMAILcreate + blind slip locallyblinded slip + deposit intenttransfer 1 cUSD to pool + deposit memoload and verify exact confirmed transactionissue blinded slipsigned point + public key + batch proofissuance returnedverify key + proofunblind bearer locallyspend slip on email-bound payloadrecord slip spent onceSMTP claim URL with #capabilityHOP 2 · EMAIL → RECEIVER WALLETcreate + blind coupon locallyclaim capability + blinded couponissue blinded couponsigned point + public key + batch proofissuance returnedverify key + proofunblind coupon locallywallet + preimage + wallet-bound HMACredeem coupon oncecustodial payout of 1 cUSD
One transfer, two independent blinded-token hopsA sender-side slip breaks the cryptographic wallet-to-email join. A new receiver-side coupon breaks the email-to-wallet join. The processor still operates both paths and the public ledger still shows both pool transfers.

08 / WHAT “ZKP” MEANS HERE

The proof checks one relationship; blinding provides the unlinkability.

The original sketch called the token processor a ZKP microservice. More precisely, TurboPass returns a non-interactive batch discrete-log equality proof. The browser uses it to check that the issuer evaluated the blinded point consistently with the expected Ristretto public key. It does not prove the processor has funds, followed a business rule, sent an email, or paid a recipient.

The privacy property comes from the client choosing a secret and blinding scalar, sending only a blinded group element, verifying the result, and unblinding locally. At later redemption, the issuer sees the bearer preimage and redemption authenticator but cannot use the cryptographic transcript alone to match them to the earlier blinded point. Repeating that construction with fresh randomness creates the second boundary.

CryptoPal cryptographic and operational visibilityA sender wallet connects publicly to a custodial pool and the pool connects publicly to a receiver wallet. Separate blind slip and coupon hops protect the adjacent wallet-to-email and email-to-wallet cryptographic joins. The processor, email service, and network can still correlate metadata.CRYPTOGRAPHIC VIEWSENDER WALLETpublic on-chain addressBLIND SLIP HOPwallet ↛ email joinEMAIL CLAIMone-time capabilityBLIND COUPON HOPemail ↛ wallet joinRECEIVER WALLETpublic on-chain addressOBSERVABLE OR TRUSTED OUTSIDE THE BLINDING CLAIMSOLANAsender → pool and pool → receiver are publicPROCESSORcustody, timing, IP and adjacent operationsEMAILrecipient mailbox and full claim URLUnlinkable bearers are not anonymous operations, confidential transfers, or proof of reserves.
What the two privacy boundaries hide—and what they do notBlinding prevents two adjacent cryptographic joins. It does not hide the public Solana transfers or prevent the processor, mail service, network, or browser telemetry from correlating timing and identity metadata.

09 / THE BROWSER BOUNDARY

Private preparation data stays in the tab, but browser storage is not a vault.

The Rust crate wraps the same Ristretto primitives used by TurboPass and compiles them to WebAssembly. Its batch object keeps derived preimages and blinds in Wasm memory and zeroizes secret Rust values on drop. TypeScript receives blinded points, final bearer strings, and authorizations—but it also creates and temporarily persists the sensitive 32-byte deterministic preparation seed. The page stores that state in tab-scoped sessionStorage so it can survive navigation through the flow.

The application compares the current local chain genesis before restoring a session. A validator reset invalidates old preparations, claim capabilities, issuer names, and payout payloads rather than letting credentials minted against one pool incarnation spend from a new one.

10 / STATE + IDEMPOTENCY

One-use money movement needs convergence at every retry boundary.

Three explicit state machines separate chain observation, email availability, coupon issuance, and payout submission. PostgreSQL row locks and unique hashes choose one owner for each transition. TurboPass remains the authoritative one-use gate for bearer preimages.

CryptoPal issuers, state machines, and replay controlsSeparate slip and coupon issuer domains lead into deposit, email transfer, and payout state machines. Unique chain signatures, claim hashes, token hashes, and the TurboPass spent set make retries converge on one result.DOMAIN-SEPARATED ISSUERSSLIP ISSUERversion · genesis · mint · 1 cUSD · slipCOUPON ISSUERversion · genesis · mint · 1 cUSD · couponDEPOSITsignature + blinded-token hashAWAITING_CHAINISSUINGISSUEDEMAIL CLAIMemail + capability hashesAWAITING_SLIPAVAILABLECOUPON_ISSUINGCOUPON_ISSUEDPAYOUTcoupon hash + signed tx bytesREDEEMINGSUBMITTINGCONFIRMEDTURBOPASS SPENT-PREIMAGE SETauthoritative one-use gate
Retry safety is part of the protocol, not an HTTP afterthoughtThe original Key–Issuer–Redemption object sketch became two domain-separated issuers, three durable processor state machines, unique hashes and signatures, and TurboPass's authoritative spent-preimage record.
One payout row per coupon preimage, condensedSQL + TypeScript · source
INSERT INTO cryptopal_payouts
  (id, token_hash, wallet, coupon_payload, status)
VALUES ($1, $2, $3, $4, 'REDEEMING')
ON CONFLICT (token_hash) DO NOTHING;

SELECT ...
FROM cryptopal_payouts
WHERE token_hash = $1
FOR UPDATE;

-- A replay may reuse the exact same payout only.
if (row.wallet !== input.wallet || row.coupon_payload !== input.couponPayload) {
  throw conflict("COUPON_ALREADY_REDEEMED", "coupon is bound to another payout");
}

The payout path prepares and stores serialized, signed Solana transaction bytes before submission. A retry rebroadcasts the same bytes and signature. If the blockhash expires, a row-locked comparison replaces only the still-current prepared transaction. Once the chain confirms, later equivalent requests return the recorded signature; a changed wallet conflicts.

Email delivery is intentionally at-least-once. A retry may put another copy of the same message into Mailpit, but deterministic capability derivation makes the claim URL stable. Production still needs an outbox and delivery reconciliation because stable content does not make an SMTP side effect transactional with PostgreSQL.

11 / THE “ANYCHAIN” SEAM

The adapter is real; multi-chain support is future work.

The processor depends on a chain-vault interface for bootstrapping, health, faucet funds, independent deposit verification, payout preparation, and prepared-transaction submission. That is the right starting seam for another chain. It is not proof that another chain can be added by changing a URL.

Current Solana vault contractTypeScript · source
export interface SolanaVaultService {
  readonly memoProgramId: string;
  bootstrap(): Promise<VaultBootstrap>;
  currentGenesisHash(): Promise<string>;
  health(): Promise<void>;
  faucet(wallet: string): Promise<FaucetResult>;
  verifyDeposit(input: {
    signature: string;
    expectedMemo: string;
    expectedMint: string;
    expectedPoolTokenAccount: string;
    expectedAmountBaseUnits: bigint;
    expectedDecimals: number;
  }): Promise<DepositVerification>;
  preparePayout(wallet: string): Promise<PreparedPayout>;
  submitPreparedPayout(
    prepared: PreparedPayout,
  ): Promise<"CONFIRMED" | "PENDING" | "EXPIRED">;
}

A second implementation would need its own finality rule, asset and token-account policy, canonical address encoding, exact deposit interpretation, fee model, transaction replacement behavior, and recovery model. Chain identity, asset, denomination, protocol version, and hop must also enter distinct issuer names and payload domains so a pass from one network cannot be replayed on another.

12 / WHAT BROKE WHILE BUILDING

The difficult bugs sat at tool and trust boundaries.

01

Wasm was both generated and accidentally ignored

An initial wasm-pack attempt failed and a later attempt succeeded. Separately, a blanket ignore rule hid the generated browser package. The repository now explicitly allowlists the checked-in Wasm output so Docker and normal frontend builds do not require Rust.

02

Strict Rust checks exposed cleanup work

Clippy initially failed. The warnings were fixed instead of muted, then format, locked tests, and strict Clippy passed. The generated Wasm was ultimately verified against the Rust API.

03

A proof is meaningless against an untrusted key

The client originally needed a stronger expected-key check. The final flow compares the issuance key with configuration, reconstructs the original blinded point, then verifies before unblinding. The independent-manifest limitation remains explicit.

04

Interoperability language was too broad

Documentation was corrected to describe RFC 9497 components plus a custom TurboPass transcript—not a standards-compatible VOPRF or a generic zero-knowledge system.

05

Local orchestration still behaves like a system

Persistent volumes had to be reset while iterating, and port 3000 was already in use during the final walkthrough, so the browser run moved to port 3300. An initial automation step timed out; after inspecting the current page state, the walkthrough continued without weakening the protocol assertions.

13 / VERIFIED REPOSITORY RESULT

The pinned public commit builds and its committed checks pass.

85tracked paths
33 / 33non-load tests passing
550web modules built
3durable state machines
Independent verification at the pinned committext
$ npm --workspace apps/api test
# 3 files · 14 tests passed

$ npm --workspace apps/web test
# 2 files · 13 tests passed

$ cargo test --manifest-path crates/client-crypto/Cargo.toml --locked
# 6 tests passed

$ npm run check && npm run build
# both TypeScript workspaces passed; production build completed

I independently cloned public commit e41f723. The API suite passed 14 tests in three files, the browser-helper suite passed 13 tests in two files, and the Rust/Wasm crate passed six tests: 33 non-load tests with zero failures. Both TypeScript workspaces typechecked and the production web build completed.

The build produced a 161.80 kB Wasm asset (61.25 kB gzip) and a 737.17 kB main JavaScript bundle (233.31 kB gzip). Vite’s over-500 kB warning is a real performance follow-up: the wallet and Solana dependency tree should be split or modernized before treating this UI as a production frontend.

14 / LOAD + REPLAY TESTING

The load harness exercises journeys and races, not a headline TPS number.

The repository commits three guarded Artillery profile files exposed through four run commands. Every target must be loopback; redirects, proxies, a non-loopback processor target, or a non-loopback Solana RPC returned by /config stop the run. Stateful users create independent wallets, obtain faucet funds, touch the real local validator, issue and redeem through TurboPass, send SMTP to Mailpit, use the browser-compatible Wasm, and verify the receiver balance on-chain.

Committed local load profilesshell · source
npm run load:smoke          # 20 read-only virtual users
npm run load:protocol:solo  # one full journey for debugging
npm run load:protocol       # 10 independent full journeys
npm run load:idempotency    # concurrent replay assertions
Recorded profileObserved resultWhat it establishes
Read smoke20 users; 40/40 requests; zero failures; aggregate p95 and p99 about 7 ms.Local health/config shape and a quick dependency-read latency regression.
Full protocol10/10 independent users; 90/90 HTTP requests; zero failed users; aggregate p95 about 1.2 s and p99 about 1.3 s; final balances verified.A conservative local end-to-end regression through chain, crypto, SMTP, and stores.
IdempotencyA four-way deposit reservation produced one HTTP 201 and three expected 409s; four post-success replays at each remaining critical boundary returned identical stable results.Documented duplicate behavior after success plus one real reservation race.

15 / CURRENT TRUTH

Private bearer handoffs do not make the whole payment private or production-ready.

BoundaryWhat existsWhat must not be inferred
CustodyThe processor controls the pool and payout key.No proof of reserves, solvency, escrow, or willingness to pay.
On-chain privacyTwo blind-token hops remove adjacent cryptographic joins.Solana still publishes sender → pool and pool → receiver transfers; amount and timing can correlate them.
MetadataThe bearer payload omits the other side of each hop.Processor, SMTP, reverse proxy, IP logs, and browser telemetry may correlate activity.
Issuer trustThe browser verifies a batch-DLEQ proof against the configured key.That expected key comes from the same processor; no independent global key manifest prevents per-client tagging.
Value modelOne pass authorizes exactly 1 cUSD.No arbitrary amount, change, denomination hiding, refund, dispute, or recovery workflow.
Network scopeThe chain boundary is isolated behind an interface and issuer domain.Only a resettable local Solana/Agave ledger and classic SPL token are implemented.
Operational safetyState machines, row locks, unique constraints, and stable signed bytes make common retries converge.The email and chain submission paths still need production outboxes, reconciliation, rate limits, and abuse controls.
Cryptographic assuranceThe implementation reuses TurboPass and the pinned Ristretto crate.It is unaudited demonstrator code, not a standardized Privacy Pass deployment or production security claim.

The single-user demonstration is especially easy to correlate by amount and timing. Two cryptographic blind spots can coexist with obvious operational linkage. A production privacy design would need batching, randomized delay, relays, separated logs and duties, telemetry controls, and potentially an on-chain shielded or escrow construction—depending on the actual threat model.

16 / FILE GUIDE

Where to follow the implementation.

17 / WHAT IS NEXT

Move from a privacy demonstration to a threat-modelled payment system.

  1. 01

    Publish and authenticate one globally shared issuer-key manifest outside the issuing processor.

  2. 02

    Move pool custody to reviewed KMS/HSM or threshold controls, add reconciliation, and define refunds and recovery.

  3. 03

    Add a transactional outbox for SMTP and payout work, then test crash windows around submission and confirmation.

  4. 04

    Create a second chain adapter only after specifying finality, asset policy, canonical wallet encoding, and replay domains.

  5. 05

    Run production-shaped, retained benchmarks with pre-funded wallets, multiple denominations, and metadata defenses.

  6. 06

    Complete independent protocol, cryptographic, dependency, and application security reviews before real-value use.

The best next milestone is not another chain logo. It is an independently authenticated issuer manifest, durable side-effect workers, custody and reconciliation controls, and retained end-to-end evidence. Once those contracts exist, a second chain adapter can test whether “anychain” is genuinely architectural rather than aspirational.

18 / EVIDENCE LEDGER

Three immutable source pins and one clearly labelled session record.

  • CryptoPal implementation at e41f723 — the runnable local application, protocol, tests, and load harness.
  • Original CryptoPal spec at de7c055 — the 2022 PlantUML and API sketches.
  • TurboPass at f18da56 — the exact submodule commit providing issuance and spent-token enforcement.
  • The owner-supplied demo screenshot documents the interface. The private implementation task documents the browser walkthrough and local load observations; because it contains no committed machine-readable report, this note labels those measurements as session evidence rather than repository verification.