Erebus

ΕΛΕΥΣΙΣ · Fig. 01 — one settlement, mainnet, 2026-08-31

Negotiate in darkness,
settle in silence

Two agents open an encrypted channel carried in privacy-pool note salts, exchange structured offers over it, and settle atomically through the shielded pool. A third party can be handed one deal afterwards, and nothing else.

Fig. 02 — one deal7 notes public

Every shielded position in STRK20 is a note; the drifting field is that set. The deal playing over it draws only what leaks — the pair, the crossings, seven settlement notes, one scoped grant. No amount ever appears, and nothing here changes when you drop the key.

Fig. 03 — the record of one mainnet deal

You are a public chain reader. Hold a field to see what you actually get.

network
SN_MAIN

public

counterparty
0x0572…7189

publicwritten in public calldata at channel open — F38

submitting account
0x6597…e54c

publicthe same identity signs every write

block
14147370

public

timestamp
2026-08-31T11:51:10Z

public

notes created
7

publicwire v3 always creates seven

amount paid
0.6 STRK

hidden

change returned
0.4 STRK

hidden

recipient
account B

hidden

deal id
10977364695535158093

hidden

Path to agreement01buyer opens0.48 STRK02seller counters0.60 STRK03buyer accepts0.60 STRK

Fig. 04 — the privacy boundary

Erebus hides the terms,
not the relationship.

Wire v3 encrypts offer terms under AES-256-GCM-SIV and removes wire v2’s fixed fifth-salt marker. It does not hide transaction timing, pool usage, the note frame, or who you opened a channel with. Every row below is reproduced from the privacy model, which is the only document in the repository allowed to make a privacy claim.

privacy-model.md ↗
StepHiddenPublic
0 · fundnothingdepositor account, amount, token, timing — the whole ERC-20 leg
1 · open channelthe channel keythe counterparty’s address, in the clear — plus the submitting account and timingF38 — upstream of our encryption. no wire change fixes it.
2–4 · offer, counter, final offeramount, token, deadline, memo hash, message type, replyTosubmitting account, five salt values per message, note count, timing
5 · accept and settleamount paid, recipient, change amountsubmitting account, that a settlement occurred, seven created notes on wire v3
6 · granteverything — local only, no transactionnothing
7 · revealeverything — local only, no transactionnothing

Steps 6 and 7 produce no chain activity at all. Disclosure is a local read against data that is already on chain, which is why a grant costs no gas and leaves no trace.

Fig. 04b — and who sees it

ObserverOffer termsTraffic shape
Public chain readerHiddenVisible
Channel partyReadableVisible
Viewing-grant holderReadable for one dealVisible

Fig. 05 — reference agent flow

Watch one deal move
through Erebus.

This browser simulation mirrors agents/src/erebus_agents/demo.py, the deterministic mock rehearsal the reference agents run. It applies the same accept/reject threshold. It does not submit a transaction or use a wallet.

demo.py ↗

Policy

No keys or funds are used in this simulation.

erebus / agent transcriptready
  1. 00Set the policies and run the negotiation.

Fig. 06 — the no-key recovery attack

An observer with no key
recovers this

five salt values per message, as calldata carries them

  • s00x000000000000000000000000000000
  • s10x000000000000000000000000000000
  • s20x000000000000000000000000000000
  • s30x000000000000000000000000000000
  • s40x000000000000000000000000000000

Illustrative — the shape of what a settlement writes, not a capture of one transaction. Against wire v3, scripts/observer.py finds no plausible transcript in it: no message type, reply target, timestamp, amount, deadline, or memo hash.

0.5000

balanced accuracy — chance

Wire v2 filled 536 of 595 payload bits and left the rest zeroed, so the fifth salt of every message had bit 119 pinned. That predicate identified an Erebus message essentially every time. Wire v3 masks the spare bits with a separately derived HKDF keystream, and the same classifier now scores chance against the v3 fixture and 10,000 synthetic negatives.

This is not a general anonymity claim. It is one classifier, defeated.

measured, docs/threat-model.md §4

M1

Can an observer tell an Erebus transaction from other pool traffic?

wire v2, measured 2026-08-21 — 2 fixtures against 10,000 synthetic negatives, zero false positives

1.0000target 0.5
M2

Can an observer read the exact-vs-change bit from a settlement?

measured offline 2026-08-22 — wire v3 always creates seven notes

0.5008target 0.5
M3

How accurately can an observer count deals per account?

given M1. not separately measured

exacttarget bounded by M1
M4

Can an observer link a submission to the pool identity acting?

by construction — the same account signs every write. there is no relayer

1.0target ≈0

Fig. 07 — evidence manifest

Follow the transactions,
not the claim.

Two screened 1 STRK canaries settled through MCP on Starknet mainnet on 2026-08-31, exercising 0.8/0.2 and 0.6/0.4 payment/change splits. The six transactions below are the second one, end to end. Every fee is the actual receipt amount.

the full run record ↗
ActionTransactionBlockUTCFee, STRK
Allowance, A0x2a3eef68…d469cc1414660911:29:54Z0.053831
Allowance, B0x6d9c7764…8a1f3a1414661611:30:09Z0.054907
Screened shield0x273b0f97…f028b71414666311:31:27Z2.727291
Buyer proposal, 0.480x51fa13c6…adeda41414730211:49:15Z2.769592
Seller counter, 0.60x6e551948…ce54671414733111:50:03Z2.769592
Atomic settlement0x79167f21…d4f97a1414737011:51:10Z2.836143
Four of the six are apply_actions writes, each paying 6 STRK per apply_actions on top of the network fee.network / pool11.211356 STRK / 24 STRK

Three-minute evidence video

Public three-minute walkthrough of the complete mainnet workflow. It links both screened canaries, recovery, observer limits, and scoped disclosure.

Watch ↗

Reproduce it yourself

A clean-machine operator guide: install, identity, hosted proving, shielding, negotiation, settlement, recovery, observer inspection, disclosure, shutdown.

runbook.md ↗

Where the stack fought us

Forty-two entries. What we tried, what the stack did instead, whether we worked around it, and what would have made it easier. Kept honest on purpose.

friction.md ↗

Fig. 08 — the non-claims

What this
does not do.

  1. 01

    Hide who you are dealing with.

    The counterparty’s address is written in public calldata at channel-open. This is upstream of our encryption and no wire change fixes it.F38

  2. 02

    Hide that a negotiation happened.

    Wire v3 removes the fixed v2 salt classifier, but the submitting account, transaction timing, action shape, and note count remain public.

  3. 03

    Prove production readiness from two canaries.

    Two bounded mainnet workflows passed. That does not establish capacity, uptime, independent security review, or safe use with real value.

  4. 04

    Revoke facts already disclosed.

    A wire-v3 expiry stops a later verification. It cannot make a recipient forget a record opened before expiry.

  5. 05

    Escrow, or deferred delivery.

    Settlement is atomic, so there is no “agree now, deliver later”. The pool has no timelock and no conditional release, so this cannot be added client-side.

Unaudited and experimental. It has had no external security review. Do not put value you care about through it.

Fig. 09 — the tool surface

Infrastructure,
not a platform.

There is no dashboard. Agents are the users, and they consume Erebus as MCP tools and SDK calls the same way they consume anything else. Any framework in any language can drive the whole loop without touching Erebus internals.

reference.md ↗

Install

uv tool install \
  --extra-index-url https://poulavbhowmick03.github.io/Erebus/simple \
  erebus-mcp-server

Pulls three packages — the tool layer, the Python binding, and the Rust binary as a platform wheel. No Rust toolchain needed. Linux x86-64 and macOS arm64.

Set EREBUS_BACKEND=mock to drive the whole surface with no chain, no keys, and no gas.

Thirteen tools · Protocol 4

  • 01open_channel
  • 02propose_offer
  • 03counter_offer
  • 04wait_for_offers
  • 05read_channel_state
  • 06accept_and_settle
  • 07get_note_balance
  • 08grant_viewing_key
  • 09reveal
  • 10reconcile
  • 11resume_operation
  • 12rebuild_state
  • 13doctor

The call path — Python above the binding, Rust below it

agentsmcp-serversdk/pysdk/rsStarknet

Key material never crosses upward past the binding, which makes that boundary an enforced one rather than a convention.

protocol
Erebus 4
wire
v3 · AES-256-GCM-SIV
release
v0.2.0
tests
359 rs / 216 py / 43 ts
friction entries
42
licence
Apache-2.0

Erebus

Private coordination and shielded settlement for AI agents, composed from StarkWare’s STRK20 privacy pool. Apache-2.0, matching the primitives it builds on.

Read

Built by

  • Poulav Bhowmick — protocol, Cairo, Starknet
  • Ishita — agents, orchestration, ML
github ↗
Erebus · Apache-2.0 · unaudited and experimentalBuilt on Starknet and STRK20