paranoid mode
cover stream steady at 1 packet per second

ParanoidMode

On October 16, Vitalik Buterin posted paranoid mode, a wrapper that hides who you are from the AI you talk to, and named what it still lacked: a mixnet built for AI requests, with cover traffic that never stops. So we built PM: Paranoid Mode. Your connection sends one packet every second, all day, whether you use it or not. A prompt just takes the place of one of those cover packets, so there is nothing for anyone watching to notice.

One packet in this field is a real request. The rest are cover. Click any packet to inspect it, or hold the button to find yours.
CApending
scroll
live from X@VitalikButerin, Oct 16, 2026
01

Where it starts

On October 16, 2026, Vitalik Buterin posted paranoid mode, an open-source wrapper for his AI agent that hides who you are and what you ask from the model provider. He tested it by asking for the best vegan restaurants in Tallinn. It stacks five protections:

  • A Tor circuit per chatEach conversation leaves through a different Tor route, so separate chats cannot be tied together by address.
  • Cover trafficRandom extra requests are mixed in with the real ones.
  • zkAPI paymentsYou deposit into an Ethereum vault once, then pay with zero-knowledge proofs. The provider gets paid without learning who paid.
  • Local rewritingA small model on your own machine rewrites each prompt so your writing style does not travel with it.
  • A sandboxThe agent runs locked inside bubblewrap, so it cannot reach the network except through Tor.

The missing piece

Tor was built for browsing, not for this. Cover traffic stops someone who is only listening. But a provider that works with the Tor entry relay can delay or speed up packets and watch for the same pattern on its side, and the protection mostly falls apart.

His suggested fix was a mixnet built for AI requests, with cover traffic that never stops. Paranoid Mode is that mixnet, run by relays that are paid in $PM and lose their stake if they cut corners.

Reporting: ChainCatcher, U.Today, crypto.news. Paranoid Mode is an independent project and is not affiliated with Vitalik Buterin or the Ethereum Foundation.

Run the full Paranoid Mode stack and this is what the AI provider gets when you send a request.
your IP address
203.0.113.42The address of the last relay in your path.
hidden by this mixnet
when you asked
14:32:07.418 burst, 9 packetsNothing. The traffic looked the same before, during and after.
hidden by constant-rate cover traffic
who paid
0x71c4...b9a2, card ending 4417A short-lived credit that cannot be traced to a deposit.
hidden by zkAPI
how you write
"hey quick q lol, best vegan spots??"A rewritten prompt with your phrasing and habits removed.
hidden by a local model on your machine
02

Timing gives you away

Encryption hides what a packet says. It does not hide when it was sent or how many followed it. Both monitors below are watching the same person send the same requests, live. On the left, every burst lines up with a request reaching the provider. On the right, there is nothing to line up.

Plain Tor packets per second, your side

Traffic spikes every time you ask something, and a request reaches the provider a moment later.

requests matched to you0linkable

Paranoid Mode packets per second, your side

The rate never moves. Real packets take the place of cover that would have been sent anyway.

requests matched to you0nothing to match
03

One request, three hops

Keep scrolling to send one. Your agent talks to a small client on your machine like any OpenAI-compatible API. The client does the rest.

  1. step 1

    Pick a path

    The client reads the relay list from Ethereum and picks three in three different regions. Relays with more stake are picked more often.

  2. step 2

    Wrap it

    The request is cut into packets of one fixed size. Each packet gets three layers of encryption, one per relay.

  3. step 3

    Blend it

    Your client is already sending cover packets at a steady rate. The real packets take their place, so the rate stays flat.

  4. step 4

    Mix it

    Each relay peels off its own layer, holds the packet a short random time, shuffles it with everyone else's and sends it on.

  5. step 5

    Hand it over

    The third relay passes the request to the provider. The answer comes back the same way, in packets of the same size.

04

The relays

Relays are run by anyone who locks up $PM. They are paid each day from the fees users pay. These rules are written into the contracts and nobody can change them.

Stake to join0PM
Stake that counts toward rewardsup to0
Packets each relay must send per day01 per second
Waiting period to leave0days
Bond to challenge a relay0PM
Penalty for a proven false report0%of stake
day N

Relays carry traffic. Fees build up in that day's pool.

day N+1

Each relay reports how many packets it sent.

day N+2

Reports can be challenged and voted on.

day N+3

Rewards for day N can be claimed.

Catching a liar

A relay that stops sending cover traffic when nobody is using it saves bandwidth and leaks timing for everyone behind it. So any report can be challenged.

  1. i
    Anyone posts a 1,000 PM bond, names a relay and a day, and attaches a hash of their evidence, such as packet captures.
  2. ii
    Active relays vote for one day, each with the weight of its stake. The accused relay's reward for that day is frozen.
  3. iii
    If upheld, the relay loses that day's reward and 20% of its stake. Half goes to the challenger, half is burned, and the bond comes back.
  4. iv
    If rejected, the bond goes to the relay that was wrongly accused. If too few relays vote, the bond comes back and nothing changes.
0
relays online
0
PM staked
0
current day
0
in today's reward pool
05

Run a relay

You need a server that can stay online, an onion address for it, and at least 50,000 PM. Joining takes two wallet confirmations: one to approve the stake and one to lock it.

Before you join

  • Set up the relay software on your server and let it run for a day. It has to keep up one packet per second without gaps.
  • Paste its onion address and public key here. Only a hash of them goes onchain.
  • Pick the region your server is actually in. Clients use it to spread paths out.
  • Your stake can be slashed for 14 more days after you start leaving, so leave cleanly.

Full steps are in the relay docs.

waiting for your details
06

$PM

The full supply is created once at launch. There is no mint after that, no owner and no pause. Holders can burn their own tokens.

An ERC-20 on Ethereum
85% to relays
15% burned

how every fee paid for traffic is split

Users pay with it

Fees for sending traffic through the network are paid in $PM. With zkAPI-style credits, the payment cannot be traced back to the person who made the request.

Relays lock it up

A relay needs 50,000 locked to join. It is the deposit that makes lying expensive.

Relays earn it

Each day's pool is shared between relays that kept up the full rate, in proportion to their stake, up to 250,000.

Part of every fee is burned

15% of fees and half of every slashed stake are destroyed.

07

What it does not hide

Being honest about the edges is part of the design.

limit 1

What you ask

The provider still reads the request so it can answer it. The local rewrite removes how you write, not what you want. A request that names you will still name you.

limit 2

All three relays together

If every relay on your path is run by the same person, they can follow you. Paths cross regions and are picked again for every conversation to make that unlikely and short-lived.

limit 3

Speed

Mixing adds delay, mostly when a conversation starts. Paranoid Mode trades a second or two for not being watched. It is not built for anything that needs an instant reply.

Paranoid Mode docs

PM, token $PM, at paranoid.cash

Paranoid Mode is a mixnet for traffic between AI agents and the model providers they call. It does one job: make it impossible for someone watching the network, including the provider, to link a request to the person who sent it by looking at addresses or timing.

It does this with three things working together:

  • Three-hop paths. Every request crosses three relays run by different people in different regions. Each relay only knows the hop before and the hop after.
  • Constant-rate traffic. Every client and every relay sends packets of one fixed size at a fixed rate, all the time. Real packets replace cover packets, so the flow never changes shape.
  • Staked relays. Relays lock $PM on Ethereum, get paid daily for keeping the rate up, and are slashed if they are caught skipping cover traffic.

The project takes its name and its starting point from the paranoid mode Vitalik Buterin described for his AI agent on October 16, 2026. Read his post. Paranoid Mode is independent and not affiliated with him.

Where it fits

Vitalik's paranoid mode has five layers. This network replaces one of them and leaves the rest as they are.

LayerWhat it hidesHandled by
Local prompt rewriteYour writing styleA local model on your machine, unchanged
SandboxAnything the agent might leak on its ownbubblewrap or a microVM, unchanged
zkAPI paymentsWho paid for the requestzkAPI, unchanged
Cover trafficWhen you are activeParanoid Mode, as constant-rate traffic instead of random extras
Tor circuit per chatYour IP addressParanoid Mode, as a three-hop mixnet built for this traffic

Random cover traffic on top of Tor still leaves bursts that an active attacker can find. Moving both jobs into one network built for constant rate is what closes the timing gap he described.

Architecture

There are four parts.

  1. The client runs on the user's machine next to the agent. It looks like an OpenAI-compatible endpoint on localhost. It picks paths, wraps packets and keeps the constant-rate stream going.
  2. Relays receive packets, remove one layer, wait a short random time, reorder and forward. Each relay also keeps its own outgoing rate constant by adding cover packets.
  3. The exit is the third relay on a path. It rebuilds the request from its packets and sends it to the provider over TLS, then sends the answer back the same way in fixed-size packets.
  4. Ethereum holds the list of relays, their stake, the daily reward pools and the challenge votes. Nothing about individual requests ever goes onchain.
agent ── localhost ──▶ client ══▶ relay 1 ══▶ relay 2 ══▶ relay 3 ── TLS ──▶ provider
                         │          (eu)        (asia)      (na)
                         └─ reads relay list and keys from Ethereum

══ fixed-size packets at a constant rate, three layers of encryption

Packets and timing

Every packet on the network is the same size. A short request might fill one packet and a long answer might take many, but from the outside each one looks identical. Cover packets are encrypted the same way as real ones, so a relay in the middle of a path cannot tell them apart either. Only the client that made a packet knows whether it was real.

Rate

The minimum rate for a relay is one packet per second, every second of the day. That is 86,400 packets per day, and it is the number the rewards contract checks. Busier relays send more, but they never go below the minimum and they never vary with demand.

Mixing delay

Each relay holds a packet for a short random time before sending it on, and sends out packets in a different order than they arrived. This breaks the link between "packet in" and "packet out" even for someone watching one relay closely.

What an observer sees

Someone watching your connection sees a flat stream to one relay, all day. Someone watching the provider sees requests arriving from many exit relays. There is no burst on your side that lines up with an arrival on theirs.

Choosing a path

The client reads every active relay from ParanoidMixRelayRegistry: its endpoint hash, its region and its stake. It then picks three relays with these rules:

  • All three must be in different regions.
  • Within those limits, a relay's chance of being picked grows with its stake, up to the 250,000 cap.
  • A new path is picked for every new conversation, so conversations cannot be linked to each other through the relays they used.

The endpoint hash onchain lets the client check that the onion address and key a relay announces are the ones it registered with. If they do not match, the client skips that relay.

Sending traffic

Point your agent at the local client instead of at the provider. Anything that talks to an OpenAI-compatible API works without other changes.

OPENAI_BASE_URL=http://127.0.0.1:8462/v1
OPENAI_API_KEY=<your zkAPI credential>

The client keeps the cover stream running whether or not the agent is active. Expect a small amount of steady bandwidth use while it is on, and an extra second or two of delay when a conversation starts while the path is built.

Fees for traffic are paid in $PM and collected by ParanoidMixFeeSplitter. Pair this with zkAPI credentials so the payment itself cannot be traced back to you.

Running a relay

What you need

  • A server that can stay online all day with steady bandwidth.
  • An onion address and an ed25519 public key for the relay.
  • At least 50,000 PM in the wallet you will join from.

Joining

  1. Run the relay for a day first and make sure it never drops below one packet per second.
  2. Hash the onion address and public key together. The join form on the home page does this for you with SHA-256.
  3. Approve the registry to take your stake: approve(registry, amount) on the token.
  4. Call join(endpointHash, region, amount) on the registry. It returns true if you are in.

Region codes

CodeRegionCodeRegion
1North America5Middle East
2South America6Asia
3Europe7Oceania
4Africa

Leaving

Call beginExit(). Your relay stops being picked right away and your stake stops counting toward rewards. After 14 days call finishExit() to get the stake back. Your stake can still be slashed during those 14 days, which stops a relay from misbehaving and leaving at the same time. You can call cancelExit() at any point before you finish.

If a slash leaves you with less than 50,000, the registry starts your exit for you.

Days and rewards

Rewards work in days counted from the moment the rewards contract was wired. currentEpoch() returns the current day number.

DayWhat happens
NRelays carry traffic. Fees arrive and are added to day N's pool.
N+1Each relay calls report(N, packets). A report below 86,400 is refused.
N+1 to N+2Anyone can challenge a day N report. Votes last one day.
N+3 onwardRelays call claim(N) to collect their share.

A relay's share of day N's pool is its weight divided by the total weight of every relay that reported. Its weight is its stake at the time it reported, capped at 250,000. A relay that missed its report window gets nothing for that day.

If nobody earned anything for a day, anyone can call rollover(N) after day N+3 to move that pool into the current day.

Challenges

A relay that quietly drops its cover traffic when nobody is using it saves money and leaks timing for everyone routed through it. Challenges are how the network catches that.

  1. Open. During day N+1, anyone approves 1,000 PM to the court and calls open(relay, N, evidenceHash). The evidence itself is published off-chain; the hash pins it. The relay's day N claim is frozen.
  2. Vote. For one day, active relays call vote(id, support). Each vote counts with the voter's full stake.
  3. Resolve. After the vote closes, anyone calls resolve(id).
OutcomeRuleResult
UpheldVotes cast reach 10% of active stake and yes beats noDay N reward wiped. 20% of stake slashed, half to the challenger, half burned. Bond returned.
RejectedQuorum reached and no is equal to or above yesBond goes to the accused relay. Claim unfrozen.
No quorumVotes cast below 10% of active stakeBond returned. Claim unfrozen.

What counts as evidence

The useful kind is a capture taken from the relay's neighbours: if a relay's peers saw long gaps in what it sent them, it was not keeping the rate it reported. Voting relays can check this against what they saw themselves, since they were the ones receiving its packets.

Fees and burn

Fees paid for traffic are sent to ParanoidMixFeeSplitter. Anyone can call split(), which sends 85% of the balance to the rewards contract for the current day and burns the other 15%. The split is fixed in the contract and there is no owner who can change it.

Half of every slashed stake is also burned, by the registry.

$PM

NameParanoid Mode
SymbolPM
Ticker$PM
Websiteparanoid.cash
Decimals18
Supply1,000,000,000, created once by open()
NetworkEthereum mainnet
Addresspending
ExtrasEIP-2612 permit, burn, burnFrom, sendMany

$PM is the token of Paranoid Mode. Relays stake it to join, earn it each day for keeping the rate up, and users pay their traffic fees in it. No owner, no mint after launch, no transfer fee, no pause, no blocklist.

Contracts and wiring

None of the contracts take constructor arguments. Each is deployed bare and then connected once by the deployer with wire. After wiring, the deployer has no further powers. No function in any of the contracts reverts: anything that cannot be done returns false or 0.

1. deploy ParanoidToken, call open()
2. deploy ParanoidMixRelayRegistry
          ParanoidMixEpochRewards
          ParanoidMixChallengeCourt
          ParanoidMixFeeSplitter
3. registry.wire(token, court)
   rewards.wire(token, registry, court)
   court.wire(token, registry, rewards)
   splitter.wire(token, rewards)
ContractJobAddress
ParanoidTokenThe $PM tokenpending
ParanoidMixRelayRegistryRelays, stake, exits, slashingpending
ParanoidMixEpochRewardsDaily pools, reports, claimspending
ParanoidMixChallengeCourtChallenges and votespending
ParanoidMixFeeSplitter85/15 fee splitpending

Contract reference

ParanoidMixRelayRegistry

FunctionWhoDoes
join(bytes32 endpoint, uint16 region, uint256 amount)anyoneLocks at least 50,000 and registers the relay. Needs approval first.
addStake(uint256 amount)relayLocks more stake.
beginExit()relayLeaves the active set and starts the 14 day wait.
cancelExit()relayComes back before the wait is over, if stake is still at least 50,000.
finishExit()relayReturns the stake after the wait.
slash(address relay, uint256 bps, address challenger)court onlyTakes up to 50% of stake, half to the challenger, half burned.
isActive(address), stakeOf(address)viewRelay status and stake.
relays(address), relayAt(uint256)viewFull relay record, and relays by join order starting at 1.
activeCount(), totalActiveStake()viewSize of the active set.

ParanoidMixEpochRewards

FunctionWhoDoes
fund(uint256 amount)anyoneAdds tokens to today's pool. Needs approval.
sync()anyoneAdds tokens that were sent straight to the contract to today's pool.
report(uint256 epoch, uint256 packets)active relayReports the previous day. Only during the next day, at least 86,400 packets.
claim(uint256 epoch)relayPays out its share from day N+3 on. Returns the amount.
rollover(uint256 epoch)anyoneMoves an unclaimed pool with no earners into today.
hold, forfeitcourt onlyFreezes or wipes a relay's share for one day.
currentEpoch(), claimable(epoch, relay), pool(epoch), weightOf(epoch, relay)viewDay number, payout, pool size, weight.

ParanoidMixChallengeCourt

FunctionWhoDoes
open(address relay, uint256 epoch, bytes32 evidence)anyonePosts the 1,000 bond and opens a case. Returns the case id, or 0.
vote(uint256 id, bool support)active relayVotes once per case with its stake.
resolve(uint256 id)anyoneSettles the case after the vote closes.
cases(uint256), caseFor(epoch, relay)viewCase details, and the case id for a relay and day.

ParanoidMixFeeSplitter

FunctionWhoDoes
split()anyoneSends 85% of its balance to rewards and burns 15%.
totalToRelays(), totalBurned()viewRunning totals.

Reading the registry

const reg = new ethers.Contract(REGISTRY, [
  "function relayCount() view returns (uint256)",
  "function relayAt(uint256) view returns (address)",
  "function relays(address) view returns (uint256 stake, uint64 joinedAt, uint64 exitAt, uint16 region, bytes32 endpoint)",
  "function isActive(address) view returns (bool)"
], provider);

const n = await reg.relayCount();
for (let i = 1n; i <= n; i++) {
  const addr = await reg.relayAt(i);
  if (await reg.isActive(addr)) console.log(addr, await reg.relays(addr));
}

Parameters

All of these are constants. None can be changed after deployment.

ConstantValueContract
MIN_STAKE50,000 PMRegistry
EXIT_DELAY14 daysRegistry
MAX_SLASH_BPS5,000 (50%)Registry
EPOCH1 dayRewards
MIN_PACKETS86,400 per dayRewards
STAKE_CAP250,000 PMRewards
BOND1,000 PMCourt
VOTE_TIME1 dayCourt
QUORUM_BPS1,000 (10% of active stake)Court
SLASH_BPS2,000 (20%)Court
TO_RELAYS_BPS8,500 (85%)Fee splitter

Threat model

Protected against

  • A provider that wants to know which IP address sent a request.
  • A provider working with the first relay on your path to match timing. This is the attack Vitalik pointed out against Tor, and constant rate is the answer to it.
  • Someone watching your home connection who wants to know when you use AI.
  • Linking two of your conversations through the network, since each one gets a new path.

Not protected against

  • The content of a request. The provider has to read it to answer it.
  • All three relays on your path being run by the same person. Region rules and stake weighting make this unlikely, not impossible.
  • An agent that leaks things on its own. That is what the sandbox layer is for.
  • Payment tracing if you pay fees straight from your main wallet. Use zkAPI-style credits.

FAQ

Is this Vitalik's project?

No. It builds the missing piece he described in his post. It is not affiliated with him or the Ethereum Foundation.

Why not just use Tor?

Tor sends traffic when you send traffic. That is what makes it fast for browsing and what makes it weak here: bursts can be lined up. Paranoid Mode is slower to start but sends the same amount at all times.

Does the client use a lot of bandwidth?

It uses a small, steady amount all the time it is on, instead of nothing most of the time and a lot in bursts. That steady amount is the cost of privacy.

Can a relay fake its packet count?

It can write any number in its report, but its neighbours saw what it actually sent. That is what the challenge court is for, and a proven lie costs 20% of its stake.

Who controls the contracts?

Nobody. The deployer connects them once and has no powers after that. Every parameter is a constant.