What a Swap Site Actually Logs — and What It Can't Avoid
We opened our own database and web server and counted: nineteen order columns with no IP, nine thousand log lines in ten hours with the amount sitting in the URL, and one path where an IP meets an order id.
Every exchange says it keeps no logs. The claim is cheap to make and cheap to check, so we checked ours: at 10:01 UTC on 28 October 2026 we opened the database and the web server of the site you are reading, counted rows and log lines, and wrote down what one order actually leaves behind. The answer is less flattering than a slogan and more useful than one. A swap can leave a trace in three places — the edge that terminates your connection, the application database, and the counterparty that fills the order — and "no logs" is true of one layer at a time, never all three at once.
Three layers, three different answers. The edge is whatever terminates your connection: Cloudflare, then nginx on our origin. It writes one line per HTTP request — URL, status, bytes, user agent, and two IP addresses. The application is a SQLite file with one row per order. The counterparty is the liquidity provider that actually swaps the coins: it receives the payout address, the refund address and the amounts, and applies its own screening. A site that answers "we log nothing" without naming the layer has not answered. The useful question is narrower: what is written, where, for how long, and can it be tied to a specific order. (recovering misdirected funds)
Layer two: the order row, all nineteen columns. Our orders table has exactly nineteen fields: id, corridor, from_asset, to_asset, from_amount, to_amount, to_address, refund_address, provider, provider_order_id, deposit_address, deposit_extra_id, markup_bps, status, provider_status, created_at, updated_at, affiliate_status, commission_est_usd. There is no column named ip, user_agent, email or session; we checked the schema, not the documentation. At 10:01 UTC it held 115 rows created between 24 September and 27 October — 90 expired, 19 paid, 5 failed, 1 refunded, split across three pools (92, 22, 1) — in a 771 KB database. Two neighbours matter as much: a 97-row table of rejected scanner orders, kept because the pattern is how you block the next wave, and a routing table with 1,173 rows that stores corridor, amount and which pool won, purged at 60 days. Support chat is the only place free text persists: 51 messages when we counted, which is why we say do not paste an address into it.
Layer one: what the edge writes, line by line. The nginx format is one line: remote address, a literal CFIP= plus Cloudflare's client IP, the timestamp, the request line, the status, bytes sent, and the full user agent. A real one from today: `GET /api/quote?from=usdt-tron&to=xmr-xmr&amount=1337.4242 HTTP/1.1" 200 157 "curl/8.5.0`. The current file reached 9,748 lines by 10:01 — 16.2 a minute — and those lines carried 2,410 distinct client IPs. Rotation is daily with fourteen compressed copies kept, so the 14 archives we counted hold 392,163 lines between them. Logging is disabled for exactly one location, the news image directory, because a static image says nothing you need. Everything else is written, order pages and every API call included.
The query string is the leak nobody audits. Look at what that line contains: the amount, verbatim, in the URL. A quote is a GET, so every quote anyone requests becomes a fourteen-day record of a pair and a size sitting next to an IP and a user agent — 15 such lines today, 982 across the archived fortnight. Addresses never travel that way: an order is a POST, its fields are in the body, and nginx is not configured to log bodies. We verified it by posting an order with the literal string 4INVALID as the payout address, getting a 400, then grepping the log: zero matches. The order appears as `POST /api/order HTTP/1.1" 400 130` and nothing else. One detail for people reading logs elsewhere: the first field is the Cloudflare edge (172.71.x and 104.22.x in our samples) and the real client hides behind CFIP=, so counting the first column measures a CDN, not visitors.
The one place an IP meets an order id. Order pages live at /order/<uuid>, and that path is logged like any other. Today's file contains two such lines, both from the same client at 00:13:43 and 00:13:44 on one order id. That is the only join in our stack between a network address and a specific order, and its horizon is the same fourteen days. We are not going to pretend the join does not exist: our privacy page says server logs are not linked to orders, and this is the exception that makes the sentence imprecise. The practical consequence is small but real — open the order link over Tor, and do not paste it into a chat app or an email thread that outlives the swap. (Is Tor safe for swaps?)
What we never receive, and what still loads in your browser. An order needs five fields: from-asset, to-asset, amount, payout address, refund address. No account, no email, no wallet connection, no signature. The Telegram notification our operator gets reads like "order 70455957, 100 USDT-TRC20 to 0.14460093 XMR, pool changenow" — eight characters of the id, the assets, the amounts, the pool, no address. On analytics the split is deliberate: session replay is absent from the order page HTML entirely (two matches on the home page, zero on an order page), while the aggregate visitor counter does load there, because it counts a visit and does not read a field. The trading component also carries an explicit mask attribute, so text is not recorded even if something else is loaded later. (recovering misdirected funds)
What the pool writes about you is not ours to erase. The moment an order exists we hand the provider a payout address, a refund address and an amount, because that is what an order is. The provider keeps its own records, applies its own screening and answers to its own regulator; nothing we do changes that. This is the honest boundary of no-KYC: no identity is collected here, but a well-formed address and a size exist at the counterparty, tied to a timestamp. For scale, quotes measured at 10:02 UTC today: 100 USDT-TRC20 bought 0.14460093 XMR (691.56 USDT per XMR), 1,000 bought 1.60620148 (622.59), and 1,337.4242 bought 2.1533691 — the 11.1% gap between the small and the large order is per-order fixed cost, not price movement. 1 XMR sold for 587.75537 USDT. Live quotes move; the page is the reference. (recovering misdirected funds)
How to audit any swap site in five minutes. 1. Open a quote and look at the address bar: if the amount is in the URL and the request is a GET, it is in that site's access log next to your IP. 2. Open an order page, view source, search for clarity, hotjar, fullstory, sentry — a replay tool on an order page watches the address field. 3. Read the privacy page for a number of days, not an adjective: "short-lived" is not a retention policy, "fourteen days" is. 4. Test whether the support chat persists text: paste a deliberately wrong address and reload. 5. Read the response headers — a `server: cloudflare` line means a second company holds your IP with a retention policy you cannot read. Four mistakes we keep seeing: treating no-KYC as no-record; reusing one payout address across every order, which links them at the pool; pasting addresses into chat or email; and assuming Tor covers a browser extension that runs at document_start. (Is Tor safe for swaps?)
None of this is a scandal; it is a boundary map. Ours reads like this: no identity, no address book, no request bodies, order rows kept for reconciliation and refunds, a fourteen-day edge log with exactly one joinable path, a sixty-day routing table that never sees an address, and a counterparty holding its own file. That is what "we keep no logs" has to contain to be true. Ask the next site you use for the same list in the same units, and see whether its answer has numbers in it.