Skip to content

Built for ZECATHON 2026 · Private Markets

Rent a GPU without telling the world

Sealed requests, sealed bids and pay-per-interval in shielded ZEC.
No server in the middle ever reads your order, and you can prove it.

  • Sealed RFQs
  • Signed bids
  • Shielded pay
  • Leak Inspector
  • Agent-ready

How it works

Five steps, and no one in the middle.

Requests, bids, acceptances, payments and closes are signed frames inside encrypted Zcash memos. The job, its logs, heartbeats and results are too big for a memo, so they move over a relay that only ever holds ciphertext.

Buyer

silicr CLI · terminal

  • Shielded wallet
  • Ranks bids, picks one
  • Pays per interval

ZCASH · SHIELDED (IRONWOOD)

  • ① RFQ · sealed to each provider asked
  • ② BID · signed, to a one-time address
  • ④ ACCEPT, then one PAY per interval
  • CLOSE · from either side

Provider

silicr provide

  • Shielded wallet
  • Bids by policy
  • Docker sandbox, watchdog

③ Silicr relay: ciphertext only, no keys

Off the chain, to one side: buyer and provider swap the sealed job, logs, heartbeats and results here.

  1. Step 1: Sealed RFQ

    Your client picks matching providers from the public, signed directory and sends one shielded transaction. Each output carries your request in an encrypted memo only its recipient can read: GPU, count, duration, max price, a one-time reply address, a signing key made for this request and a channel key. Outputs are padded to 4, 8 or 16 with dummies to fresh addresses of your own, so the chain can't tell how many providers you asked within that bucket.

    Who can read it

    Only the providers you ask

  2. Step 2: Sealed bids

    Each provider answers with a BID memo signed by its directory key and sent to your one-time address. Your client checks every signature and drops bids that are late, above your max, or from providers you didn't ask. Each bid names a fresh payment address and its own channel key.

    Who can read it

    Only you

  3. Step 3: Encrypted job channel

    The channel keys in your RFQ and the winning bid meet in X25519; HKDF-SHA256 turns the shared secret into one XChaCha20-Poly1305 key. The job, logs, heartbeats and results are sealed with it and pass through a relay that stores only ciphertext, under mailbox ids derived from the same secret. No memo ever names a mailbox.

    Who can read it

    You and the winning provider

  4. Step 4: Metered settlement

    The ACCEPT transaction pays interval 0. After that your client pays each interval in shielded ZEC by its own clock, shortly before it begins, with random jitter. Heartbeats only confirm the job is alive, so a provider can't speed payments up by claiming progress. The provider runs only paid intervals: neither side can lose much more than one interval's worth.

    Who can read it

    You and the provider

  5. Step 5: Bonded providers

    Roadmap · not built

    Planned: providers lock a bond in a FROST 2-of-3 shielded address (provider, buyer pool, arbiter) that can be slashed on signed evidence of fraud. Not built yet: it is a stretch goal, and the arbiter would be a trusted party.

    Who can read it

    Bond holders

No matching engine

There is no order book on a server and no operator who sees every order. Bids land at your one-time address and your own client ranks them: by price per hour, then bond size (bonds are on the roadmap), then arrival. You accept one yourself, or set an auto-accept rule such as “the cheapest bid after waiting two blocks”.

Leak Inspector

Don't take the diagram's word for it.

Every screen in the terminal has a Leak Inspector toggle. Press L and the same moment appears from four viewpoints side by side: the public chain, the Silicr relay, the provider and the buyer.

Each column lists what that party actually received, recorded from real data rather than drawn: decoded memos, relay requests and public transaction views. What a party can read is marked; ciphertext stays ciphertext.

Leak Inspector · the moment you accept a bid

press L in the terminal

Public chain

A shielded transaction
v6 · 2 Ironwood actions
Fee
10,000 zats
Sender, recipient, amount, memo
hidden

Silicr relay

Stored opaque blob #1
48,212 B · mailbox 3f9ca1e2…
First 24 bytes
9b1e04c7d2…
Client IP
203.0.113.24

Provider

Bid won
ACCEPT for 144 intervals
Price
0.06 ZEC per 300 s
Buyer identity
not present in any message

Buyer

Job sealed and uploaded
command: python train.py
Paid now
0.06 ZEC (interval 0)
Relay mailbox
derived from the channel secret
Illustrative values. In the app each column is filled from what that party actually received: decoded memos, relay requests and public transaction views.

Who learns what

This is the table in THREAT_MODEL.md, the one the inspector fills in live. It also covers two parties the live view can't show: the light-wallet server your wallet syncs from, and the providers that bid and lost. Knows means the party can read it in the clear. The same file lists what Silicr does not protect: the winning provider sees the workload, and without Tor the relay and light-wallet server see your IP and timing.

  • Who the buyer is: identity, wallet

    Known to

    • Buyer

    Not known to: Public chain, Light-wallet server, Silicr relay, Providers asked, Winning provider

  • The buyer's IP address

    Known to

    • Light-wallet server
    • Silicr relay
    • Buyer

    Not known to: Public chain, Providers asked, Winning provider (talks only to the relay)

  • Which transactions that IP broadcasts, and when

    Known to

    • Light-wallet server
    • Buyer

    Not known to: Public chain (sees them, not who sent them), Silicr relay, Providers asked, Winning provider

  • Which transactions belong to one job

    Known to

    • Light-wallet server(your IP and the payment train)
    • Winning provider(its own payments)
    • Buyer

    Not known to: Public chain (not linkable on-chain), Silicr relay, Providers asked

  • Roughly how long the job ran

    Known to

    • Light-wallet server(from the payment count)
    • Silicr relay(from blob timing)
    • Winning provider
    • Buyer

    Not known to: Public chain, Providers asked

  • Blob sizes, upload and download times

    Known to

    • Silicr relay
    • Winning provider(its own)
    • Buyer(its own)

    Not known to: Public chain, Light-wallet server, Providers asked

  • The request: GPU, count, duration, max price

    Known to

    • Providers asked
    • Winning provider
    • Buyer

    Not known to: Public chain, Light-wallet server, Silicr relay

  • Which bid won, and at what price

    Known to

    • Winning provider
    • Buyer

    Not known to: Public chain, Light-wallet server, Silicr relay, Providers asked

  • Amounts paid

    Known to

    • Winning provider
    • Buyer

    Not known to: Public chain (only fees are public), Light-wallet server, Silicr relay, Providers asked

  • The workload: job bundle and its outputs

    Known to

    • Winning provider
    • Buyer

    Not known to: Public chain, Light-wallet server, Silicr relay (ciphertext only), Providers asked

  • When shielded transactions happen, their action counts and fees

    Public to everyone. Payments look like ordinary 2-action shielded payments; a padded RFQ (5–8, 9–12 or 17–20 actions) is a less common shape. Senders, recipients, amounts and memos stay hidden.

  • Light-wallet server (lightwalletd, the server your wallet syncs from): also learns each wallet's birthday height and any transaction the wallet fetches by txid. It can't tell which notes belong to the wallet; blocks are scanned locally.
  • Directory host: the public provider entries, and the IP of whoever publishes or reads them; nothing about buyers or trades. Entries are signature-checked by the relay on write and by every client on read, so the host can drop an entry but can't forge or replace one. In this build the relay serves the directory.
  • Winning provider: can't link the job to the buyer's other jobs, because each RFQ uses a fresh key and address.
  • Buyer: can't learn providers' other jobs or earnings.
  • Silicr arbiter (roadmap, FROST bonds): would see bond addresses and the evidence submitted in disputes, and nothing about undisputed trades.

Settlement

Pay as you go, in shielded ZEC.

Zcash has no escrow contract to lean on, so Silicr doesn't hold money at all. It caps what either side can lose instead: the buyer pays one interval at a time, and the provider runs only what has been paid for.

One job, then a buyer who stops paying
  1. running

    Interval 0

    paid by the ACCEPT; PAY 1 goes out shortly before it ends

  2. running

    Interval 1

    PAY 2 goes out shortly before it ends

  3. running

    Interval 2

    PAY 3 goes out shortly before it ends

  4. running

    Interval 3

    the buyer stops paying: no PAY 4

  5. paused

    Grace

    interval 4 unpaid: the job is paused for up to 3 min

  6. stopped

    After

    stopped, signed CLOSE (unpaid), no results

  1. Interval 0 is paid by the ACCEPT transaction, before any work starts. The job clock starts when the provider's first signed heartbeat arrives.
  2. Every later interval is paid by the buyer's own clock, shortly before it begins (1 min ahead by default on testnet), with random jitter so payments have no fixed rhythm.
  3. Heartbeats only confirm the job is alive. A missing or stale heartbeat holds a payment back, but no heartbeat can bring one forward: a provider can't speed up payments by claiming progress.
  4. The provider never computes an unpaid interval. At an unpaid boundary it pauses the job; if the payment hasn't arrived by the end of the grace period, it stops, sends a signed CLOSE (unpaid) and uploads no results.
  5. The buyer has a watchdog too: after 90 s without a signed heartbeat it stops paying and sends CLOSE (no heartbeat).

Defaults on testnet

Interval
5 min, quoted in each bid
Each payment goes out
about 1 min early, with random jitter
Heartbeat
every 15 s, signed and sealed
Grace before stopping
3 min
Buyer gives up after
90 s without a heartbeat

Providers set their own interval and grace. Mainnet uses a longer interval and lead, so fewer payments per hour; silicr status shows the live values.

Worked example

4 × A100 for 12 hours, max 0.8 ZEC/h. The winning bid is 0.72 ZEC/h, quoted as 0.06 ZEC per 5-minute interval.

Intervals
12 h ÷ 5 min = 144
Payment transactions
144: 1 ACCEPT + 143 PAY
Paid to the provider
144 × 0.06 ZEC = 8.64 ZEC
Network fees on payments
144 × 0.0001 ZEC = 0.0144 ZEC0.17% of the job
All fees, with the RFQ and your CLOSE
≈ 0.01495 ZECRFQ to 6 providers: 8 padded outputs + change = 9 actions, 45,000 zats. CLOSE: 10,000 zats.
Most you can lose
≈ 0.072 ZECThe rest of the interval in progress plus the one prepaid interval: about 1.2 intervals, or 6 minutes' worth (0.8% of the job), even against a provider that lies about its progress.
Most the provider can lose
no unpaid computeIt never computes an unpaid interval: it pauses at an unpaid boundary and stops after the grace period. It does act on payments while they are still in the mempool, since each is small.

Fees assume each payment spends one note and returns one change note: 2 logical actions, the ZIP 317 minimum. Spending more notes, or splitting change into extra notes, adds 5,000 zats per extra action.

Fees (ZIP 317)

fee = 5,000 zats × max(2, logical actions)

  • A PAY is one output plus change: 2 actions, 10,000 zats, or 0.0001 ZEC.
  • Each padding output in an RFQ adds 5,000 zats. That is the price of hiding how many providers you asked.

Why no smart contract is needed

  • Every payment is a plain shielded transaction carrying a signed PAY memo. Nothing is locked or pooled.
  • Nobody holds custody: not Silicr, not a contract, not the relay.
  • Instead of escrow, the risk is capped by the interval length. Shorter intervals mean less at stake and more fees.
  • An escrow on a transparent chain would publish the deal anyway.

Quick start

Try it on your machine.

You need Node 22 or newer, pnpm 9 and the Rust wallet in zec-sidecar/. The demo runs the relay, a buyer and a provider locally on Zcash testnet, with testnet coins. The real thing runs on Zcash mainnet with small balances.

demo · Zcash testnet
# from the repo rootpnpm install# relay, buyer and provider on Zcash testnet; runs the tiny-train dealpnpm silicr demo# in a second shell: the terminal, on http://127.0.0.1:7703pnpm --filter @silicr/web dev
Uses the funded testnet wallets in .silicr/wallets/test-buyer and .silicr/wallets/test-provider (create and fund them with silicr init --network test and a faucet). --network main is refused for the demo: real money. Open the terminal and press L to watch what each party sees.
buyer · Zcash mainnet
# keys + a shielded mainnet wallet; prints its u1… addresspnpm silicr init --role buyer --network main --data .silicr/wallets/main-buyer# in its own shell; syncs mainnetpnpm silicr daemon --data .silicr/wallets/main-buyer# sealed RFQ, bids, metered paypnpm silicr run --gpu a100 --count 1 --hours 1 --max 0.05 --auto ./job -- python3 train.py
Fund the buyer with a small amount of ZEC from any shielded wallet: send it to the u1… address init prints. It is spendable after 10 confirmations, about 12 to 13 minutes. On mainnet the daemon refuses to start without spend caps (per request and in total) in its config, and the terminal shows the amount and the cap before every send. Testnet: use --network test and a faucet. See docs/MAINNET.md.
provider · Zcash mainnet
# keys + a shielded mainnet walletpnpm silicr init --role provider --network main --data .silicr/wallets/main-provider# in its own shellpnpm silicr daemon --data .silicr/wallets/main-provider# publish a signed listing and bid by policypnpm silicr provide --gpu a100 --count 4 --vram 80 --min 0.6
Both sides need the Rust wallet (cargo build --release in zec-sidecar/) and a relay both can reach (pnpm silicr relay, default port 7700); the provider needs a little ZEC for bid fees. Install Docker for the sandboxed runner, plus the NVIDIA container toolkit to pass GPUs through. Without Docker it falls back to process mode, which is not a sandbox. See docs/ZCASH_SETUP.md.

FAQ

Straight answers.

The questions people ask first, including the uncomfortable ones.

Is it on mainnet?

It can be. Silicr runs on Zcash testnet or mainnet through its own light wallet, and the demo runs on testnet. Mainnet is opt-in: an explicit --network main, spend caps in the daemon config (per request and in total; the daemon refuses to start without them) and small balances.

This is unaudited hackathon software. Keep only what you are willing to lose in its wallets, back up the seeds under .silicr/ or treat them as throwaway, and read docs/MAINNET.md first.

Can the provider see my model and data?

Yes. The winning provider decrypts and runs your job, so it sees your code, your data and the outputs. Hiding the workload from the machine that runs it needs TEE execution with attestation, which is future work. Treat a provider as able to read everything you send it.

What it doesn't learn is who you are: no message carries your identity, your wallet or your IP address.

What stops a provider from taking payment and not computing?

Nothing prevents it outright; Silicr limits what it costs you. Your client pays each interval by its own clock, shortly before it begins, and stops as soon as signed heartbeats stop. Claiming progress can't speed payments up. In the 12-hour example on this page, a provider that walks away costs you at most about 0.072 ZEC, or 6 minutes' worth.

Heartbeats carry a step count and a checkpoint hash. They show the job is progressing, not that it ran on the claimed GPU or produced honest outputs. Provider bonds with slashing (FROST 2-of-3) are on the roadmap.

Does Silicr hold my funds?

No. There is no escrow, no custodian and no Silicr account. You pay the provider directly from your own wallet with ordinary shielded transactions, one interval at a time.

What does the relay know?

Blob sizes, upload and download times, and the IP addresses of the parties connecting. It stores only ciphertext, under mailbox ids derived from the buyer and provider's shared secret, so it can't tell which request a blob belongs to, what it contains, the price or who anyone is.

In this build the relay also serves the public provider directory. It is a small Node server that holds no keys, so you can run your own.

And the light-wallet server?

Your wallet syncs from the lightwalletd server you point it at (testnet.zec.rocks by default on testnet). It sees your IP address, every transaction you broadcast and when, and your wallet's birthday height. That is enough to link one job's transactions, a padded RFQ followed by a run of 2-action payments, and to estimate how long the job ran: random jitter blurs the cadence, but not the count. It can't read memos, amounts or recipients, and blocks are scanned on your machine. Tor or SOCKS support, batched payments, or running your own Zebra + Zaino/lightwalletd would reduce this; all are future work.

Who sees my request?

Only the providers you ask: your client picks up to 16 matching ones from the directory, or you name them. They learn the GPU type, count, duration and your max price, but not who you are, and the ones that lose never learn which bid won. The chain sees a padded transaction that doesn't show how many providers you asked, within its bucket.

Why Zcash?

A shielded Zcash transaction hides sender, recipient and amount, and every output carries a 512-byte encrypted memo. That gives Silicr a private payment rail and a private message channel in one, with no separate messaging network. Unified addresses make a fresh, unlinkable address per request cheap, and metered payments replace the escrow contract Zcash doesn't have.

Launch

See what each party learns, for yourself.

Open the terminal, send a request and press L. Every column shows what that party actually received.

Launch app opens app.silicr.xyz: the terminal runs on your machine, next to your Silicr daemons. The threat model is THREAT_MODEL.md in the repository.