Seedelf-Wallet

Privacy by default (2026-09-27)

This review tests the web wallet against the owner’s principle of 2026-09-27: “The Seedelf web wallet should be the most private by default, but with the ability to turn it down when applicable.” Six lenses tested branch web-wallet/crypto-review at HEAD 96682e8 (some traced at its parent 3cbfda9):

A verifier re-traced every finding against the committed code. The settled decisions are taken as given. So are the accepted items, which aren’t reported again: giveme.my as a single point of failure, Koios’s keyless public tier, the unbuilt restore scan, the vault KDF, and the same session index being the same key on both networks.

Status (2026-09-27)

Since: an independent review found more, and fixed it on the same branch: see independent-review.md. Its M8 finished §2.9 for the paths this round missed.

Everything in §2 is done, and so is most of §3 and §4.1, as the owner decided below. The owner declined §3.5, §3.8, §3.9, §4.2 and §4.3. §3.10 and §4.4–§4.9 are still open, with the smaller items under Still open. §5’s limits are said plainly in privacy.md and the privacy policy, and §6’s docs are corrected.

Two parts were built in parallel worktrees, marked (connector) and (small items) below, and are merged into web-wallet/crypto-review (the hashes below are the merged commits).

The owner’s decisions (2026-09-27), which the work follows where they differ from the review:

Checked on web-wallet/crypto-review once everything was merged (2026-09-27):

§ Status What was done Commits
2.1 Done (connector) A signature checks only its own account’s Lovejoin chain; another account’s UTxO takes the stranger’s path and words. 0012e2f
2.2 Done (connector) A site finds only the outputs that pay its own account among the wallet’s recent sends; the rest go through its budget and Koios. A site’s own submits are kept for it alone, so it still chains on them (launch review #13). 8ec902b
2.3 Done, best effort Each private UTxO has a history class from the sealed Seedelf history (made private, each payment received, each box back from Lovejoin, each session, Unknown). WebAssembly tries one UTxO alone, then one class, then merges, boxes last and one at a time, ordered by purpose (Send and Make public, a stealth mint, a funding). It never refuses: a merge gets a plain note in the review, and a funding names another session’s money. The UTxOs screen tags each UTxO. No gate and no switch (the owner). 366cd5c, d5710a2, 055fea0
2.4 Done The welcome line, Collateral, Settings’ About and Lovejoin section, and Lovejoin’s page say what’s hidden is hidden on chain, and that Lovejoin hides boxes from people reading the chain, not from Koios or giveme.my. The policy, privacy.md and the listing say Koios sends every transaction from the IP that reads the public account. adbe02e, 4fb0ad8
2.5 Done, two checks open giveme.my is named as ours (Logical Mechanism’s) in the policy, About, Collateral, the listing, its host justification, the test instructions and the root README, with what it sees and what its logs leave out. Open: check the host’s own log retention before promising more; decide the Web Store’s Location answer (store README). adbe02e, 4fb0ad8
2.6 Done “isn’t tied”, “(1 in 9)”, “nothing ties it”, “the mixes hide which boxes are yours” and the floor’s “so yours hide among enough others” are gone. One helper says how far a box hides (lovejoinHides), and the public mix’s review says its box can be picked out while few people bring boxes into a Seedelf. chunk-16 notes that “1 in 9” assumes the exits look alike. be6024b, 3e0a795, adbe02e
2.7 Done A swap’s approval reads the pool once at Review (kept five minutes), caps the boxes, and says when the ADA would come back directly. b972461
2.8 Done An ADA→token swap’s approval prices what a stop or refund sends through Lovejoin, with the no-audit note; Stop shows the live estimate and offers Stop and bring it back directly. Capping a refund’s chain at the approved boxes wasn’t built (optional). b972461
2.9 Done (small items) A private spend that lands marks only the private side behind; the next reading keeps the account’s. Home’s tx_status polling stays (the review’s “better” option, spotting it in the contract scan, wasn’t built). be26bc0
2.10 Done A public mix’s boxes are mixed again by Mix again from my public account, paid by the account; Mix my boxes again leaves them out; Pay from my private balance anyway warns. 1c054c3, afb87d4
2.11 Done (connector) isEnabled answers whether the site is connected, locked or not; reads while locked are refused in a stranger’s words, with no window; the unlock window names the sites waiting, and closing it answers “The user declined.” 9973116
2.12 Done The connect window, a site’s session, the Minswap and dApps pages, the funding review and Disconnect say what the site is given and what it can still learn; the policy, privacy.md, the listing and the plans say it too. The optional warning for a site that once had a private session wasn’t built. 3e0a795, edc20c8 (connector), 4fb0ad8
2.13 Done Half and Max round down to a whole unit and say they tell Minswap roughly what’s held; minswap.ts, privacy.md and the policy say Minswap groups swaps by IP. f9093a3, 4fb0ad8
2.14 Done (small items) Every service fetch has credentials: "omit" and referrerPolicy: "no-referrer", with a unit test. The e2e check that no Cookie header goes out wasn’t added. 5f3dd4b
2.15 Done A return takes the collateral its funding paid; with none left it says Lovejoin was left out. d319d9f
2.16 Done Hide balances masks how many Lovejoin boxes there are. 47c7504
2.17 Done (small items) The own-account warning matches the account’s payment keys too. bbde341
2.18 Done The unlock reads the pool only on a network with something open, and status() writes no empty record. 57c767e
2.19 Done (small items) Spell check is off on <body>, so on every field; the public note stays off on purpose. fdc7dc2
2.20 Done (connector) Settings’ connector note says every https site, and its scripts, can see the wallet once it’s on. edc20c8
2.21 Done (small items) The private CSV’s note says what the file ties together. c06572d
3.1 Done The unlock’s run sends nothing: each box due waits a fresh draw inside the unlocked stretch, a swap’s step too; no withdraw in a run that sent anything else, or within 5 minutes of the wallet’s own send. d74677a, 1860244, c58be9c
3.2 Done (small items) Remove defaults to who paid (mintedBy.<network>, sealed, or worked out from what’s held); unknown, nothing is chosen. 8edd702
3.3 Done (connector), option (a) Nothing preselected; each option says its cost; Connect waits for the public account to be chosen. b80497b
3.4 Done (small items) One ExplorerLink; private-side links carry the warning. cb5109b, e6e0d9e
3.5 Declined The delay stays “1-6” (the owner). chunk-16 and the docs say so.  
3.6 Done The box that goes is one someone else’s mix has moved since, when there is one; leaves are kept while their boxes sit. 5364a60
3.7 Done A Seedelf spend’s outputs are shuffled. 366cd5c
3.8 Declined The currency stays “usd” (the owner). privacy.md names CoinGecko as the one default that asks a third party for a convenience, and the policy says it’s asked when Home or the swap form opens. 4fb0ad8
3.9 Declined Hide balances stays off by default (the owner).  
3.10 Open Not built: the account-paid mint’s draft and a Lovejoin chain’s first mix still go to Ogmios at review. The docs-only minimum is done: privacy.md, the policy, the listing’s Koios justification and CreateSeedelf’s header say so. adbe02e, 4fb0ad8
3.11 Done The swap picker searches the wallet’s own list first, and Minswap only when nothing matches or on Search Minswap. Telling a pending order from utxo_info (optional) wasn’t built. 87c8bbc
3.12 Done (connector) A disconnected site’s session record goes, unless something was left behind at its account. 6bef75b
3.13 Done (small items), one part left Sealed records padded; no vault.createdAt; prices in session storage; Remove wallet clears the pools and old prices. Sealing every record empty at creation was left out: the “has used” checks (§2.18) must read content first. 74a0ffc
4.1 Done Settings’ Bring private sessions back through Lovejoin (on by default); a swap’s approval switch (auto.direct), Stop’s direct option, each return’s Bring it back directly instead, and Bring everything back’s Bring them back directly instead. be6024b, b972461, 40f7c3f
4.2 Declined No switch and no per-payment confirmation (the owner): §2.3 is best effort.  
4.3 Declined Not needed: the connect window preselects nothing (§3.3).  
4.4 Open The owner’s call after launch. The connector is off by default, and §2.20’s copy is done.  
4.5 Open Longer waits, and “bring boxes back when I ask”, weren’t built.  
4.6 Open Clearing private history wasn’t built this round.  
4.7 Open A user-set Koios, to be built with the own backend.  
4.8 Open Spreading Bring everything back out wasn’t built; its review now says what sending them together ties, and that bringing each back from its own page, hours apart, avoids it. 3749080
4.9 Open None of the smaller controls were built.  
5 Done Each limit is said plainly in privacy.md and the policy (§5.1–§5.10), with the wording given, adjusted to what was built. 4fb0ad8
6 Done, two parts left The docs in the table are corrected, and Settings’ Lovejoin section says what counts as spare, what bringing a box back costs, and that a lock stops a chain. Left: LockCountdown’s “A Lovejoin chain is being sent: locking now stops it partway” (it needs the worker to say a chain is going), and a rough send time for a chain. 3749080, and the docs commit with this Status

Still open, beyond the table: the two worktree branches’ merge, then an end-to-end run (npm run e2e) on the merged branch and on the packaged store build; §2.5’s two checks; §3.10; §4.4–§4.9.

1. Verdict

The wallet is private by default in its primitives: fresh one-time keys, shared collateral, re-randomized registers, an ownership check on the device, sealed records, Lovejoin on for returns, and the connector off.

It falls short in the choices it makes on top of them:

Turning privacy down is only half there. The owner’s belief that the Lovejoin settings turn it down is partly true: Settings can make Lovejoin cheaper (depth 1) but can’t turn it off. The 1–6 h delay is already the shortest. A swap that runs itself (Stop included) and Bring everything back have no direct option.

Before mainnet: §2.1–§2.6. The rest can follow in the order below.

2. Fix now (privacy bugs and broken promises)

# Severity Kind What Sources
2.1 High Code A connected site can test any UTxO against the wallet’s Lovejoin chains dapps-1
2.2 High Code A connected site can tell whether a recent transaction is the wallet’s dapps-2
2.3 High Code Coin selection ignores where each private UTxO came from defaults-1, onchain-1, defaults-2, dapps-3
2.4 High Copy “Koios can’t tell which Seedelf UTxOs or Lovejoin boxes are yours” defaults-3, observers-1, observers-2, onchain-7, docs-1
2.5 High Copy “We collect nothing”, yet giveme.my is ours observers-3, docs-2
2.6 High Copy Lovejoin copy promises unlinkability it doesn’t reach defaults-10, defaults-11, onchain-2, docs-3, docs-4
2.7 Medium Code The swap approval promises Lovejoin without looking at the pool defaults-7, docs-6
2.8 Medium Code A stopped or refunded ADA→token swap mixes its whole principal, undisclosed defaults-6, docs-5
2.9 Medium Code Confirming a private spend re-reads the public account in the same second observers-5
2.10 Medium Code “Mix my boxes again” ties a public mix’s boxes to the private balance onchain-5
2.11 Medium Code Sites can learn when the wallet is locked or unlocked device-1, dapps-7, defaults-15, observers-15, docs-17
2.12 Medium Copy “Your public account never appears”, “the site sees only it” onchain-3, dapps-4, defaults-2, dapps-8, observers-13, docs-10
2.13 Medium Copy + code “Minswap never sees your private balance”, but Max and Half send it docs-8, observers-14
2.14 Low Code Service fetches don’t opt out of cookies docs-12
2.15 Low Code A session whose collateral was spent comes back directly without saying so onchain-9
2.16 Low Code Hide balances leaves the Lovejoin holding readable device-5
2.17 Low Code Make public’s own-account warning misses enterprise addresses docs-16
2.18 Low Code Every unlock reads Lovejoin’s pool on every network ever opened docs-13
2.19 Low Code The contact name is spell-checked device-10
2.20 Low Copy The connector’s “That’s all it adds” defaults-15, observers-15, dapps-6, device-8, docs-17
2.21 Low Copy The Activity CSV’s note says only “isn’t encrypted” device-3

2.1 High: a connected site can test any UTxO against the wallet’s Lovejoin chains

What leaks, and to whom

The change

Files: dapp.ts:913-928, :1094; spent.ts; tests/dapp.test.ts:1161, plus three new cases:

Cost: none. A holder’s keys can’t sign for another account’s UTxO, a Seedelf UTxO or a pool box anyway. On a patched scratch copy all 39 dApp tests pass.

2.2 High: a connected site can tell whether a recent transaction is the wallet’s

What leaks, and to whom

The change

Files: dapp.ts:835-853, :935-941, :1238-1248; sent-txs.ts; tests/dapp.test.ts:

Cost: none for normal flows. Send change and a session’s funding or top-up still chain before they land.

2.3 High: coin selection ignores where each private UTxO came from

What leaks, and to whom: anyone reading the chain, plus the site or Minswap that knows the session’s one-time account.

This causes three links:

  1. Lovejoin boxes get spent together.
    • A box back from Lovejoin (about 9.71 ₳ into a register) is easy to spot. Spending two leaves of one 9-leaf tree, or a box with its session’s funding change, turns 1 in 9 into near certainty. It ties the boxes to each other and back to the session or the public account.
    • After a private token buy, the main UTxO carries the token and sorts after every pure-ADA box, so the next payment reaches for the boxes first (reproduced offline).
  2. Sessions fund from the move-in and chain together.
    • A funding spends the largest UTxO, usually the user’s own Make private, one hop from the public account, and shows the site or Minswap its full value.
    • The return merges into that funding’s change (sessions.ts:1714-1729). The change stays the largest UTxO and funds the next session, so swaps and sessions at different sites get tied together.
  3. A stealth mint can spend moved-in money even when received money could pay.

The change

  1. Selection, in seedelf-core and the WebAssembly crate. Give each row a class; the CLI passes Unknown and keeps today’s behaviour behind a parameter. The analysts agree on these rules:
    • First try a single UTxO that pays on its own (the smallest that does; a token-bearing one counts).
    • Then add inputs within one class before mixing classes.
    • Never spend two Lovejoin boxes together by default (each box is its own class), and never a box with anything else without the user’s consent.
    • Order by purpose:
      • a session funding prefers a received payment, or a single box that pays alone, and takes another session’s change or return last;
      • a stealth mint prefers received money;
      • Send and Make public spend own money before a box.
    • Return what was merged (exitsSpent, classesMixed).
  2. Classes, in the extension. Build them locally from the sealed history, matched by the transaction that made each UTxO. Never ask Koios.
    • Each lovejoin-withdraw is its own class, noting whether it came from a public mix or which session.
    • A session’s funding change and merged return belong to that session.
    • A move-in belongs to the public account.
    • Each received payment is its own class.
    • A spend’s change inherits its inputs’ classes.
    • Restored or unknown history is one class, which is no worse than today.

    A cheaper first step for fundings, in TypeScript only: build without other sessions’ UTxOs first, fall back to all of them, and return linkedSessions.

  3. Reviews.
    • Under “Private UTxOs spent” (Transfer.tsx:130, Withdraw.tsx:133, and the session, mix and mint reviews), say what merging ties together. Example: “Spends 3 boxes back from Lovejoin together. This ties them to each other and back to your public account’s mix.” Keep Send disabled until the user picks Spend them together.
    • The funding reviews (Swaps.tsx:739-742, DappApprovals.tsx:484-487, SiteSessions.tsx:449, Lovejoin.tsx:617) always show “Spends private UTxOs worth X ₳: the site (or Minswap) and anyone can see this”, and name any linked session.
  4. UTxOs screen (Utxos.tsx:28-34): tag each UTxO’s origin (Back from Lovejoin, Received, Made private, Private session N, Unknown), so locking by hand is an informed choice.
  5. Settings switch: see §4.2.
  6. Tests: pin the probe’s cases in seedelf-core: cases 1 and 3; case A (12 boxes against a token-bearing main UTxO); case C (a stealth mint with moved-in and received money).

Cost to the user

2.4 High: “Koios can’t tell which Seedelf UTxOs or Lovejoin boxes are yours”

The claims: privacy-policy.md:32, :16 (“Nobody is told”), :44 (“A VPN hides your IP address”), :25, :48; privacy.md:83, :84, :132; Onboarding.tsx:30.

What’s true

The change

Cost: copy only, plus a store policy update.

2.5 High: “We collect nothing”, yet giveme.my is ours

The claims:

What’s true

The change

Cost: copy only. The headline “we collect nothing” becomes a qualified claim.

2.6 High: Lovejoin copy promises unlinkability it doesn’t reach

The claims

What’s true

The change

Cost: copy only. Lovejoin looks less protective at launch, which is accurate.

2.7 Medium: the swap approval promises Lovejoin without looking at the pool

What leaks: chain observers and Minswap can tie the swap’s proceeds to the session and its funding.

The change

Files: sessions.ts:583-605, :620; lovejoin.ts; Swaps.tsx:674, :721, :733, :1005-1016, :1042-1057; a sessions.test.ts case and a screens test.

Cost: one pool read per Review. Koios sees it at return time anyway.

2.8 Medium: a stopped or refunded ADA→token swap mixes its whole principal, undisclosed

What happens

Nothing new leaks. The user pays for privacy they were told wouldn’t apply.

The change

Cost: one flag, one estimate, and copy.

2.9 Medium: confirming a private spend re-reads the public account in the same second

What leaks, and to whom: Koios.

The change

Files: pending.ts:261, :337, :344, :366; Home.tsx:147, :156-158; balances.ts.

Cost: separate caches. A Max withdraw’s banner confirms later. Koios requests stay the same or drop.

2.10 Medium: “Mix my boxes again” ties a public mix’s boxes to the private balance

What leaks, and to whom: anyone reading the chain.

The change now (copy): when a not-mixed box came from a mix with no session (LovejoinStatus.chains[].session), say so in the NotMixed callout (170-174), the modal (557-559) and AgainReview (661-664).

The change next

Cost: the account pays those mixes (about 4.5 ₳ a box at depth 2) and needs its collateral. There are two chains, not one.

2.11 Medium: sites can learn when the wallet is locked or unlocked

What leaks, and to whom

The change

Cost: a connected dApp that polls while locked hears “call enable()” until the user presses Connect. A connected site can still tell locked from unlocked by whether it’s answered; say so at privacy.md:104.

Files: dapp.ts:264-285, :420, :618; shared/dapp.ts (READ_METHODS); Unlock.tsx:75-78; tests/dapp.test.ts; chunk-15 plan :51; flows.md step 4.

2.12 Medium: “Your public account never appears”, “the site sees only it”

The claims

What’s true

The change (reuse CreateSeedelf.tsx:166’s wording)

Cost: copy only.

2.13 Medium: “Minswap never sees your private balance”, but Max and Half send it

What’s true

The change

Cost: up to just under 1 ₳ (or one token unit) left unswapped, still in the private balance.

2.14 Low: service fetches don’t opt out of cookies

2.15 Low: a session whose collateral was spent comes back directly without saying so

2.16 Low: Hide balances leaves the Lovejoin holding readable

2.17 Low: Make public’s own-account warning misses enterprise addresses

2.18 Low: every unlock reads Lovejoin’s pool on every network ever opened

2.19 Low: the contact name is spell-checked

2.20 Low: the connector’s “That’s all it adds”

2.21 Low: the Activity CSV’s note says only “isn’t encrypted”

3. Change the default

3.1 High: box returns and automatic session steps go out at the moment of unlock

Sources: defaults-8, onchain-12, observers-6, device-1.

Today

Most private

Cost: boxes come back a few minutes later, at most one lock period (15 min by default). A brief unlock may leave a box for the next one. This needs the owner’s sign-off: chunk-16-lovejoin.md:18 recorded “at the first unlock”.

Files:

3.2 High: Remove a Seedelf defaults to the public account

Sources: defaults-4, onchain-6, docs-7.

Today

Most private

Cost: none for a known mint. One tap otherwise.

Files: activity.ts:182-183 or mint.ts; rpc.ts:118-125; balances.ts:167; RemoveSeedelf.tsx:1-5, :28, :118-122; Choice accepting value?; privacy.md:62-64; flows.md:302-305.

3.3 Medium: the connect window preselects the public account

Sources: defaults-9, dapps-5, docs-11.

Today: useState<Connection>("public") (DappApprovals.tsx:278). One press of Connect gives the site the account’s addresses, stake key, balance and UTxOs, and the choice sticks (switching needs Disconnect, dapp.ts:225).

Most private: never start on public. The analysts offer two versions:

Either way:

Cost: one press for dApps that need the public account. A user who keeps the private default pays a network fee, keeps 5 ₳ of collateral in the session for its life, sends a request to giveme.my, waits about a minute, and pays Lovejoin’s return (about 3.5 ₳ at depth 2, and hours).

Files:

Sources: observers-7, device-2, docs-15.

Today

Most private

Cost: one line of text, or one tap.

Files: format.ts; a new ui/components/ExplorerLink; the five places above; PendingBanner.tsx; e2e extension.spec.ts:566, :700; docs in §6.

3.5 Medium: Lovejoin’s delay defaults to the shortest range

Sources: defaults-12, onchain-10, docs-9.

Today: “1-6” of [“1-6”, “2-12”, “6-24”] (preferences.ts:30, :67). The least a box waits after its last move is the range’s low end (lovejoin.ts:1331-1340).

Most private

Cost: money comes back later (plus the wait for an unlock) and sits longer in an unaudited contract. No ADA cost. The gain grows with pool activity and is small while the pool is quiet. This reverses chunk-16 decision 4.

Files: preferences.ts:67; Settings.tsx:349-364; privacy.md:129; architecture.md:488-489; chunk-16-lovejoin.md:17, :162; tests preferences.test.ts:27/38/57 and staking.test.ts:272.

3.6 Low: the box brought back is the one nobody else has mixed since

Source: onchain-11.

3.7 Low: a transfer’s change is always the last output

Source: onchain-8.

3.8 Low: ADA’s price is asked of CoinGecko by default

Sources: defaults-13, observers-9, docs-12.

3.9 Low: balances are shown by default

Sources: defaults-14, device-4.

3.10 Low: drafts go to Koios’s Ogmios at review

Source: observers-11.

3.11 Low: token search asks Minswap before the shipped list

Source: observers-14.

3.12 Low: a closed site session keeps its origin forever

Source: device-7.

3.13 Low: unsealed storage shows what was used and when

Source: device-6.

4. Add a control to turn it down (or up)

4.1 Medium: turn Lovejoin returns down

Sources: defaults-5, onchain-4, docs-5, defaults-17.

What exists today

Control Where Default
“Bring it back through Lovejoin” Swap approval, beside LovejoinCost (Swaps.tsx:733). Stored as auto.direct (sessions.ts:995), passed at :1388 and :1413. When off, hide the cost and say the money comes back at once, tied on chain to the session and its funding. On (from the Settings switch)
“Stop and bring it back directly” The Stop dialog, beside “Stop” (§2.8) Stop through Lovejoin
“Bring them back directly instead” Bring everything back: calls session-claim-build {direct:true}. Render LOVEJOIN_UNAUDITED there too. Through Lovejoin
“Returns go through Lovejoin” (lovejoinReturns) Settings, Lovejoin section. Checked in buildBack (sessions.ts:1622) only when !record?.mix, so a tile Mix stays a mix. lovejoinCost returns nothing when off. Depth and wait are disabled when off, and the section says what’s lost. On

4.2 Medium: keep money with different histories apart

Source: defaults-1.

4.3 Medium: which account new sites start on

Sources: defaults-9, dapps-5.

4.4 Low: which sites see the wallet

Sources: defaults-15, observers-15, dapps-6, device-8, docs-17.

Where: Settings → Sites, under the connector switch: “Only sites I allow” (the default once the connector is on) or “Every https site” (the way down).

The analysts disagree on how to build it:

Approach Cost to the user
Register the scripts only for allowed origins, with persistAcrossSessions:false, rebuilt from the sealed record at unlock (dapps-6, observers-15) dApps don’t see the wallet until it’s unlocked and the page is reloaded
“Connect this tab” through scripting.executeScript, nothing stored (device-8) A click on every visit
A per-origin permission and a persistent list (defaults-15) A Chrome dialog per dApp, and the allowed origins sit unsealed in Chrome’s profile

docs-17 rejects per-site injection for those reasons.

4.5 Low: longer Lovejoin waits (turning it up)

Sources: defaults-12, onchain-7.

4.6 Low: clear private history

Source: device-7.

4.7 Low: read and send through my own Koios

Sources: observers-8, defaults-3, docs-1.

4.8 Low: spread out Bring everything back

Source: defaults-16.

4.9 Low: smaller optional controls

Control Default Source
“Bring boxes back as soon as I unlock”, once §3.1 lands Off device-1, onchain-12
“Wait for Lovejoin’s pool”, per swap (§2.7) Off (opt-in) defaults-7, docs-6
“Hide again each time the wallet locks” Off defaults-14
“One-click Cardanoscan links on private transactions” Off observers-7, device-2
CSV: “Leave out private sessions and Lovejoin” (the private balance then won’t add up in a tax tool) Off device-3

5. Inherent limits to say plainly

5.1 Koios sees both sides from one IP

Sources: defaults-3, observers-1, observers-2, docs-1.

5.2 giveme.my sees every private transaction

Sources: observers-4, docs-2, onchain-7.

5.3 Lovejoin hides a box among Seedelf Wallet users, not among 30 boxes

Sources: defaults-11, onchain-2, docs-4.

Why

Ways to grow the crowd

Wording for privacy.md:125-126 (and the same under the public mix, :130): “A box’s way out shows where it goes. The wallet’s go into the Seedelf contract under a register; Lovejoin’s own app writes no datum. So a box hides only among the fan-out’s leaves whose owners also bring them back into a Seedelf around the same time, not among all 3^depth leaves or the 30 boxes the floor counts. While few people do, a box coming back a few hours after its mixes can be picked out: for a session’s return that ties it to the session, for a mix from the public account, to the account. 30 boxes means enough to mix with, not 30-way hiding.”

5.4 Box return times follow when you use the wallet

Source: defaults-8.

5.5 Fixed-size boxes, and a single private UTxO

Sources: defaults-1, defaults-2, onchain-1.

5.6 A session account looks like a Seedelf session

Sources: onchain-9, dapps-9.

5.7 Sites see the browser

Sources: dapps-8, observers-13, docs-10.

5.8 Minswap groups swaps by IP

Source: observers-14.

5.9 The connector’s presence while it’s on

Sources: docs-17, observers-15.

5.10 The browser and the device

Sources: device-11, device-9.

What the wallet can’t prevent

Wording for the policy’s “Your control” (after :61): “The Chrome Web Store and Chrome itself know this extension is installed in your browser, and Chrome Sync, if it syncs your extensions, installs it in your other signed-in browsers. Only the extension is synced, never your wallet. While the wallet is unlocked, anyone using your browser can see your balances and history, and someone who knows Chrome’s developer tools can read your keys. Lock the wallet (or close the browser) before leaving it.”

Also:

6. Docs to correct

These are the corrections not already named in §2–§5.

Doc Change Source
privacy.md:85 (“never polls in the background”); architecture.md:250; privacy.md:115, :129; archive/roadmap-v1.md:501 Say what really runs. The balance is read only when Home opens or on Refresh. While unlocked, a one-minute alarm reads only what’s running: a swap’s account and its Minswap orders, a chain, a box that’s due, a maybe-sent payment every 2 min. At unlock, the pool is read once on each network with Lovejoin open. Home polls tx_status every 15 s while waiting. Locking stops all of it, and a VPN must stay on until the work is done. observers-12, docs-13
keys-and-accounts.md:64 Probes the next index alone, and a window of 20 only if that index was used (sessions.ts:2162-2173; launch review #22). Add a pointer at crypto-review.md:17. docs-14
privacy.md:74-77; flows.md:273-274; architecture.md:128 Describe coin selection as it’s actually implemented (after §2.3), not “should”. onchain-1, defaults-1
architecture.md:445 “On preprod, spare ADA goes through Lovejoin first” → “on mainnet, once the pool floor is met”. defaults-17
Settings’ Lovejoin note (Settings.tsx:326-331); LockCountdown; LovejoinNote and LovejoinCost; flows.md:352 Add: a swap’s ADA proceeds count as spare; about 0.3 ₳ to bring each box back; at 3 waves the mixes cost more than the box (about 10.7 ₳ for 10 ₳); a chain sends only while unlocked, and locking cuts it; how to turn Lovejoin down (§4.1); a rough send time. LockCountdown: “A Lovejoin chain is being sent: locking now stops it partway.” Qualify flows.md’s “Pause and resume at any point”. defaults-17
privacy.md:134; ClaimAll.tsx:246-247 With Lovejoin, the chosen sessions’ deposits land in the same block or two and their mixes share blocks. A shallow pool serves the first sessions, and the rest come back directly. Bringing each back from its own page, hours apart, avoids both. defaults-16
flows.md:350; privacy.md A stopped or refunded swap’s ADA goes through Lovejoin unless the user chooses direct. A direct return joins the funding’s change, already tied to the session. defaults-6, onchain-4
privacy.md:124; LovejoinReturn.tsx comment List where the no-audit note appears, adding ClaimAll and the ADA→token approval. docs-5, defaults-6
store README.md:41 and “What it doesn’t hide”; Settings.tsx:152 (MOVE_TO.mainnet); the welcome screen’s NETWORK_NOTE.preprod; README.md:11 The preprod line becomes “…with a recovery phrase you don’t use on mainnet: the same phrase has the same keys on both networks, so anyone comparing them can tell they’re one wallet’s”. If the unlisted item is updated, migrate installs that have a vault and no seedelf.network to preprod in onInstalled (sw.ts), rather than landing testers on mainnet. docs-18
privacy.md Network; flows.md:86, :160, :346; privacy policy Explorer links open Cardanoscan in the browser’s own profile, only when clicked. Cardanoscan then sees the IP, its cookies and what was opened. docs-15, device-2
architecture.md storage table Add sessions.<net> and lovejoin.<net> to the sealed row; dappPassword, lovejoinDepth and lovejoinDelay to preferences; rows for seedelf.prices and seedelf.openIn; say the pools’ time shows when staking was last browsed. privacy.md: sealing hides a record’s contents, not whether it exists or its rough size, and Remove wallet isn’t a secure erase. device-6
Policy :20 (settings row) The currency default, the new switches, the 5-minute price cache, and what survives Remove wallet. defaults-13, device-6
Policy rows 17, 24, 26; privacy.md:117, :122 The sessions record keeps each session’s transactions and a site’s origin until cleared (after §4.6: “You can clear it in Settings”). device-7
Plans and comments chunk-15c:10 and chunk-15-dapp-connector.md:216: “unlinked” → “not linked on chain”. chunk-16-lovejoin.md:16 (“1 in 9” assumes exits look alike), :18 (unlock timing), :17 and :162 (delay), :411-414 (box preference, once built). Header comments at sessions.ts:3, Swaps.tsx:4, and CreateSeedelf.tsx:6 (the draft goes to Ogmios at review). onchain-3, dapps-8, onchain-2, onchain-11, observers-11

7. Already private by default