Skip to content

Repository files navigation

Cutout

Cutout is a wallet-native signing guard that uses deterministic public STRK20 evidence to help users avoid predictable deposit amounts before they sign.

Before a user signs a shield transaction, Cutout checks whether the proposed token and exact amount have meaningful public cohort cover. If the user has explicitly allowed amount flexibility, Cutout can recommend the smallest permitted amount change that reaches a healthier public cohort. Cutout can recommend, but it cannot sign.

Cutout is not a privacy guarantee. It does not claim anonymity, untraceability, unlinkability, or a probability of deanonymization.

Built for the STRK20 Private Sprint, 14 to 31 August 2026.

The problem

STRK20 protects private state inside the protocol, but deposits and withdrawals remain public at the edges. Exact amounts can therefore become operational fingerprints even when the cryptography is sound.

Cutout's reproducible pool census found that the median proposed shield had only three prior same-token, exact-amount deposits in the trailing 24 hours. Thirty-five percent had no prior exact match. Pool traffic exists, but it is fragmented across amounts and does not automatically become cover for the amount a user is about to sign.

Why Starknet and STRK20

STRK20 makes the wallet signing boundary programmable through the Starknet Wallet API while keeping the pool's deposit events and block provenance publicly verifiable. Cutout uses those Starknet-native boundaries directly: public RPC data for evidence, WalletAccountV6 for simulation and submission, and an independently queried receipt for post-transaction verification.

No Cutout contract is required. The public evidence and recommendation logic are deterministic offchain checks, while Ready X remains the only component that can request a signature and submit the STRK20 action.

How Cutout works

typed shield intent
    -> canonical public STRK20 snapshot
    -> deterministic CUTOUT-v1.3 evaluation
    -> GUARD_POLICY-v1 decision and evidence
    -> optional in-bounds amount recommendation
    -> final exact-amount preflight
    -> wallet simulation
    -> explicit user confirmation
    -> wallet-owned submission
    -> independent public receipt verification

The only supported transaction action is one typed deposit:

{
  "type": "deposit",
  "token": "0x...",
  "amount": "0x..."
}

Transfer, withdraw, mixed actions, arbitrary invoke calldata, stale evidence, corrupt snapshots, wrong-network wallets, and unsupported tokens fail closed.

Architecture

Starknet RPC primary/secondary
           |
           v
supervised incremental indexer
           |
           v
persistent canonical SQLite read model
           |
           v
POST /api/preflight -> CUTOUT-v1.3 -> GUARD_POLICY-v1
           ^
           |
Next.js signing client <-> Ready X / WalletAccountV6

The indexer stores only public Deposit and ViewingKeySet observations plus block, cursor, and replay provenance. Snapshots are complete, canonicalized, hashable, freshness-checked, and withdrawn during reorg or schema uncertainty.

The browser owns user intent and signing state. The backend cannot sign or broadcast. Ready X remains the only signing authority.

See the submission-ready system diagram in ARCHITECTURE.md.

CUTOUT-v1.3

CUTOUT-v1.3 is a frozen deterministic model. The same intent and canonical snapshot produce the same band, signals, cohort evidence, recommendation, and decision ID. It does not use an LLM.

It reports public candidate-cohort evidence under the passive-public-observer threat model in THREAT_MODEL.md. A candidate cohort is not an anonymity set, and private wallet, exchange, or RPC telemetry can give an adversary more information than Cutout observes.

Operational policy is separately frozen in GUARD_POLICY.md:

CUTOUT band Guard decision
HIGH DENY
MEDIUM WARN
LOW ALLOW

Mainnet evidence

Milestone 3 completed one explicitly authorized Ready X mainnet transaction. This is historical evidence, not a Milestone 5 smoke transaction.

Item Verified value
Action one 0.01 STRK STRK20 deposit
Transaction 0x50f81ee1b9e90576d51dde9dd73bdd89f481b677ceb9b6d244027cbe0499c9e
Status SUCCEEDED / ACCEPTED_ON_L2
Block 13,427,531
Preflight snapshot 0xbe131fcf5f63309e40b8e3656465e56bac7f5b8a1b8d5336974071bfabc34acf
Receipt artifact 0x09adfe7664898666360052a35a9e3551e1c343c14fff37ea6d6fafb9046d643c

Reproduce the full public receipt binding through an independent RPC:

npm run verify:milestone3-receipt

The v0.1.1 release verification separately used Ready X 5.33.8, Wallet API 0.10.3, and fresh mainnet snapshot 0x494e624cf0afd9ba4d2abd52ab61ded11f597052ec782cc42836b7cde54e1cb8 at block 13,448,562. One 0.01 STRK deposit was prepared with wallet_strk20PrepareInvoke(..., simulate: true) and reached READY_FOR_CONFIRMATION. No confirmation, invoke/broadcast method, transaction hash, or transaction submission occurred.

The v0.1.2 patch is presentation-only. It improves the Propose -> Verify -> Review -> Sign hierarchy, evidence disclosure, receipt presentation, responsive layout, focus states, and reduced-motion behavior. It does not change the engine, policies, API, indexer, guard, receipt verifier, or wallet execution boundary, and it submits no new transaction.

The v0.1.3 patch retains that workflow and adds strict client-side receipt artifact structure validation plus receipt-ID recomputation before verified evidence can render. It also adds mutation-rejection and narrow-viewport receipt tests and refreshes the production/demo documentation. The independent public receipt verifier and every signing, policy, API, indexer, and transaction boundary remain unchanged.

The v0.1.4 patch is presentation-only. It adds Lenis 1.3.26 smooth scrolling, GSAP 3.15.0 with @gsap/react 2.1.2, a reusable motion system, clearer Propose -> Verify -> Review -> Simulate -> User Wallet progression, stronger evidence and receipt hierarchy, and responsive, keyboard-focus, and reduced-motion improvements. No signing authority, autonomous execution, or transaction semantics were added, and CUTOUT-v1.3, GUARD_POLICY-v1, and FRESHNESS_POLICY-v1 remain unchanged.

Wallet-native security boundary

  • Cutout never receives private keys, seed phrases, viewing keys, private notes, proof secrets, or shielded balances.
  • The API and indexer are read-only with respect to blockchain execution.
  • strk20PrepareInvoke(actions, true) is used before confirmation.
  • strk20InvokeTransaction(actions) exists only in the isolated browser wallet adapter and requires final validation plus explicit user approval.
  • Receipt verification uses a public RPC independently of the signing wallet.
  • Application telemetry omits raw account addresses and requested amounts.

See SECURITY.md and RELEASE_AUDIT.md.

Integrator package

packages/guard builds @cutout/guard@0.1.4. Its single root export contains only stable action, preflight, guard, amount, version, and public receipt interfaces. It does not export wallet submission, the indexer, SQLite, RPC ingestion, or operational runtime modules.

npm run package:verify
npm pack --workspace @cutout/guard
import {
  CUTOUT_MAINNET,
  createGuardedDepositPlan,
  requestPreflight,
  validateSingleDepositAction,
  verifyDepositReceipt,
} from "@cutout/guard";

The isolated TypeScript consumer is in examples/guard-consumer. The reviewed API and versioning contract are in INTEGRATOR.md.

Deployment

The hackathon deployment is two processes on one persistent host: one supervised indexer writer and one read-only Next.js/API process sharing a SQLite volume. Primary and secondary RPCs are explicitly configured and cross-checked.

cp .env.example .env
docker compose up --build -d
curl --fail-with-body http://127.0.0.1:3000/api/health

The current production deployment is live at https://cutout.rouma.online, serving the v0.1.4 application release. The endpoint is HTTPS-terminated, exposes the read-only API through the reverse proxy, and has been verified with a current complete snapshot and a non-submitting public preflight. See DEPLOYMENT.md for the host architecture and verification boundary.

Testing

npm ci
npm run ci:verify
npm run test:milestone5
CUTOUT_ENV_FILE=.env.example docker compose config --quiet

Live public-data smoke, with no wallet call and no submission:

CUTOUT_DB_PATH=data/cutout-mainnet.sqlite npm run indexer:once
CUTOUT_DB_PATH=data/cutout-mainnet.sqlite npm run smoke:mainnet

The command must end with NO TRANSACTION WAS SUBMITTED.

Current limitations

  • One reviewed STRK20 mainnet pool and four configured tokens.
  • One supported action: shield/deposit.
  • SQLite assumes one persistent host and one indexer writer.
  • Public RPC cross-checking reduces provider failure modes but is not a claim of decentralized or malicious-provider-proof infrastructure.
  • Cutout does not protect against private wallet, exchange, browser, or RPC telemetry outside the published threat model.
  • Registry publication remains a separate release action and is not inferred from the tested Git tag or local container. The current public deployment is recorded independently at https://cutout.rouma.online.

Documentation

Apache-2.0. Nothing here is an audit or a privacy guarantee.

About

A privacy-liquidity router for STRK20. Before you sign, it checks whether your amount has meaningful public cover and routes flexible transactions toward healthier exact-amount cohorts.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages