diff --git a/apps/web/content/blog/bauhaus-echo-launch.mdx b/apps/web/content/blog/bauhaus-echo-launch.mdx index 54a7e4f..8c92247 100644 --- a/apps/web/content/blog/bauhaus-echo-launch.mdx +++ b/apps/web/content/blog/bauhaus-echo-launch.mdx @@ -1,40 +1,144 @@ --- -title: "Bauhaus Echo is Live" +title: "Building Bauhaus Echo: A Cross-Platform Puzzle Game from Scratch" date: "2026-03-04" -excerpt: "A visual memory puzzle inspired by Bauhaus design, now on every platform" +excerpt: "How we built a Bauhaus-inspired memory puzzle with libGDX, shipped it to five platforms, and added global leaderboards with HMAC-signed score submissions" --- Bauhaus Echo is now available on the [App Store](https://apps.apple.com/us/app/bauhaus-echo/id6759010657), [Google Play](https://play.google.com/store/apps/details?id=com.suchsoftware.bauhausecho), [itch.io](https://suchsoftware.itch.io/bauhaus-echo), and [the web](https://bauhaus.such.software). It is a visual memory game where you memorize tile arrangements, watch them shuffle, and reconstruct the original pattern. -## The Concept - -The game shows you a grid of colorful geometric tiles inspired by Bauhaus design. You get a few seconds to memorize their positions. Then the tiles spin, shrink, and scatter to the bottom of the screen. Your job is to drag them back to where they belong. - -## Four Game Modes - -- **Classic**: the core experience. Memorize and reconstruct. -- **Speed**: tiles appear one at a time. Place each before the next drops. -- **Endless**: speed mode that loops forever. See how long you last. -- **Memory**: traditional card-matching pairs. - -Each mode supports 2×2 through 5×5 grids and three difficulty levels. Global leaderboards let you compete against other players across all platforms. - -## 500 Bauhaus Tiles - -The tile collection features 500 hand-curated geometric designs drawn from the Bauhaus aesthetic: bold shapes, primary colors, clean lines. Each tile has beveled 3D edges and the game board is styled like a wood-and-felt craft table. - -## Tech Stack - -Built with [libGDX](https://libgdx.com/), a cross-platform Java game framework. One codebase runs on Android, iOS (via RoboVM), desktop (LWJGL3), and web (via GWT). The tile assets are packed into a TextureAtlas for efficient GPU rendering. - -## Monetization - -The mobile versions are free with AdMob banner ads. A one-time in-app purchase removes ads permanently. Desktop is a paid download ($2) with no ads. The web version is free. - -## Get It - - **Mobile (free, ads removable via IAP)**: [App Store](https://apps.apple.com/us/app/bauhaus-echo/id6759010657) | [Google Play](https://play.google.com/store/apps/details?id=com.suchsoftware.bauhausecho) - **Desktop ($2, no ads)**: [itch.io](https://suchsoftware.itch.io/bauhaus-echo) - **Web (free)**: [bauhaus.such.software](https://bauhaus.such.software) +## The Concept + +The game shows you a grid of colorful geometric tiles inspired by Bauhaus design. You get a few seconds to memorize their positions. Then the tiles spin, shrink, and scatter to the bottom of the screen. Your job is to drag them back to where they belong. + +It started as a simple prototype: a 3x3 grid, a shuffle, and a timer. But the Bauhaus aesthetic gave the project a visual identity early on, and from there it grew into four distinct game modes with difficulty scaling, global leaderboards, and a full cross-platform release. + +## The Tiles: Bauhaus Blocks by art101 + +The tile collection features 500 hand-curated geometric designs from the [Bauhaus Blocks collection by art101](https://art101.io/). Each tile is a 256x256 pixel sprite drawn in the Bauhaus tradition: bold primary shapes, clean geometry, and striking color contrasts. The tiles are packed into a libGDX TextureAtlas spread across three PNG sprite sheets for efficient GPU batching. + +We chose art101's Bauhaus Blocks because they capture the movement's aesthetic perfectly — circles, triangles, and rectangles in red, blue, yellow, black, and white — while providing enough variety across 500 designs that the game rarely repeats tiles in a session. We plan to add more tile packs from art101 in the future, introducing new visual themes while keeping the same core gameplay. + +Each tile in-game has beveled 3D edges and the game board is styled like a wood-and-felt craft table, giving the abstract designs a physical, tactile quality. + +## Four Game Modes + +- **Classic**: the core experience. Memorize the full grid, watch the tiles scatter, then reconstruct the pattern before time runs out. +- **Speed**: tiles appear one at a time in their correct position for a shrinking window of time, then drop to the tray. Place each before the next one arrives. The display time decays exponentially — starting at 28% of the Classic base time and shrinking with a 0.40 decay factor per tile. +- **Endless**: Speed mode that never ends. Placed tiles have a lifespan of three more spawns, after which they fade out and rejoin the queue. Your score is total correct placements before you run out of lives. +- **Memory**: traditional card-matching. All tiles face down. Flip two to find pairs. Matched pairs spin away, mismatches cost a life. + +Each mode supports 2x2 through 5x5 grids and three difficulty levels (Easy at 1.7x time, Medium at 1.35x, Hard at 1.0x). Lives scale with grid size and difficulty so larger grids give more room for error. + +## How We Built It + +### The Stack + +Bauhaus Echo is built with [libGDX](https://libgdx.com/), a Java-based cross-platform game framework. A single shared codebase in the `core` module runs on all platforms, with thin launcher modules for each target: + +- **Android** via the standard Android SDK (compileSdk 35, minSdk 21) +- **iOS and iPad** via [RoboVM/MobiVM](https://mobivm.com/) — an ahead-of-time compiler that translates Java bytecode to native ARM +- **Desktop** via LWJGL3, packaged with [Construo](https://github.com/nicklaus-dev/construo) and bundled JRE via jlink for a self-contained native app +- **Web** via [GWT](http://www.gwtproject.org/) (Google Web Toolkit), which compiles the Java source to JavaScript + +This means we write game logic once and it runs everywhere. Platform-specific code (ads, billing, leaderboard networking) is abstracted behind interfaces that each launcher implements. + +### Development Timeline + +The project started with a Scene2D UI menu and basic tile grid rendering. From there, development followed a roughly additive pattern: + +1. **Core gameplay**: grid rendering, tile dragging, snap-to-grid placement, and the show-shuffle-reconstruct loop for Classic mode +2. **Configuration and polish**: a config screen for grid size and difficulty, custom fonts, tile selection tooltips, and extended memorization timers +3. **New animations**: tile spin, shrink, and slide animations using libGDX's Action system — parallel and sequential action compositions for smooth transitions +4. **Speed Mode**: the conveyor-belt mechanic where tiles arrive one at a time with decaying display windows +5. **Endless Mode**: building on Speed with tile cycling, lifespan tracking, and an in-game settings overlay for pausing mid-run +6. **Memory/Pairs Mode**: face-down card-matching with flip animations and pair detection +7. **Audio**: a music system with randomized track selection from a pool of five game tracks, plus sound effects for pickup, drop, rejection, and UI interactions +8. **Statistics and scores**: per-mode, per-grid tracking of wins, losses, and best times, displayed in an expandable stats UI +9. **Monetization**: AdMob banner ads on mobile with a one-time "Remove Ads" in-app purchase, and a paid desktop build on itch.io +10. **Cross-platform fixes**: GWT build compatibility, iOS RoboVM quirks, Android 48-second startup time reduction (pre-building the UI texture atlas), and desktop jlink module resolution + +One of the more satisfying fixes was smart tile snapping. Early on, dragging a tile near an occupied cell would try to place it there, causing overlap. The fix was straightforward: `Level.getClosestOpenTargetIndex()` finds the nearest unoccupied cell instead of just the nearest cell, so tiles always snap to valid positions. + +### Tile Mechanics Under the Hood + +Each tile is a `Tile` actor extending libGDX's `Image` class with custom drag handling. When you pick up a tile, it: + +1. Plays a pickup sound effect +2. Renders a procedural drop shadow — a 64x64 soft-edge texture drawn at 35% alpha with an (8, -12) pixel offset, shared across all tile instances +3. Tracks your finger position and updates the tile's screen coordinates + +When you release it above the split line (the boundary between the grid and the tray), the game checks `Level.getClosestOpenTargetIndex()` to find the nearest unoccupied grid cell. If one exists, the tile snaps into place with a 0.15-second MoveToAction and plays a drop sound. If all positions are occupied, the tile rejects — spinning 360 degrees, shrinking back to tray size, and sliding to the bottom in a 0.13-second parallel animation. + +### The GWT Challenge + +Getting the web build working was one of the trickier parts of the project. GWT compiles Java to JavaScript, but it only supports a subset of Java — no `javax.crypto`, no reflection, limited standard library. This became a problem when we added HMAC-SHA256 score signing for the leaderboard. + +The solution was a pure JavaScript SHA-256 implementation embedded via GWT's JSNI (JavaScript Native Interface). The GWT leaderboard client uses `RequestBuilder` instead of libGDX's `Gdx.net` API for proper CORS handling, and the HMAC secret is embedded at compile time so it doesn't appear in the distributed JavaScript source in plain text. + +## Global Leaderboards + +Every mode, grid size, and difficulty combination has its own leaderboard. Scores are submitted to our backend at `api.such.software` and ranked globally across all platforms. + +### Backend Architecture + +The leaderboard runs on a Flask API behind Nginx on a VPS, backed by PostgreSQL. The stack is deliberately simple: + +- **Flask** with Gunicorn (2 workers) handles API requests +- **PostgreSQL 17** stores scores with composite indexes for fast per-mode/per-grid queries +- **Nginx** terminates SSL (Let's Encrypt) and enforces rate limits: 10 requests/second globally, 6 requests/minute on score submission endpoints +- **flask-limiter** adds a secondary rate limit of 100 requests/hour per IP + +No ORM — just parameterized SQL queries via `psycopg2`. The schema is straightforward: each score row stores the device UUID, nickname, mode, grid, difficulty, primary score value, an optional secondary tiebreaker, platform, and timestamp. + +### Scoring Logic + +Different modes rank differently: + +| Mode | Primary Score | Tiebreaker | Best = | +|------|--------------|------------|--------| +| Classic | Completion time | — | Lowest | +| Speed | Completion time | Lives remaining | Lowest time, then highest lives | +| Endless | Tiles placed | Survival time | Highest tiles, then longest time | +| Memory | Lives remaining | Completion time | Highest lives, then fastest time | + +A UNIQUE constraint on (device_uuid, mode, grid, difficulty, score_value, score_secondary) prevents exact duplicate submissions. Conflicts upsert the nickname and platform fields so a player's display name stays current. + +### Anti-Cheat and Score Verification + +Score submissions are signed with HMAC-SHA256. The client computes `HMAC(secret, timestamp + request_body)` and sends it in the `X-Signature` header alongside an `X-Timestamp` header. The server verifies the signature using constant-time comparison and rejects any request with a timestamp older than 5 minutes (replay protection). + +Beyond signature verification, the server validates score bounds per mode: + +- Classic mode times must fall within plausible ranges for the grid size — you can't clear a 5x5 in 0.5 seconds +- Speed mode lives are capped per grid and difficulty (1–6 depending on configuration) +- Endless mode tile counts have per-grid maximums (e.g., 500 for 2x2, 200 for 5x5) +- Memory mode lives can't exceed the configuration maximum (3–16 depending on grid and difficulty) + +Scores that fail validation are rejected outright. An admin endpoint (protected by a separate API key with constant-time comparison) allows manual moderation of suspicious entries. + +### Privacy + +Device UUIDs are never returned in API responses. When fetching a leaderboard, the client sends its own UUID as a query parameter, and the server returns an `is_self` boolean flag on matching entries. This lets the game highlight your own scores without exposing any player's device identifier to others. + +### Client Integration + +The game uses a `LeaderboardClient` interface with two implementations: + +- **HttpLeaderboardClient** for Android, iOS, and Desktop — uses libGDX's `Gdx.net.sendHttpRequest()` with Java's `javax.crypto.Mac` for HMAC computation +- **GwtLeaderboardClient** for the web build — uses GWT's `RequestBuilder` with the pure-JS SHA-256 implementation + +All network calls are asynchronous with callbacks posted back to the rendering thread. Score submission is fire-and-forget: if the network call fails, the game continues normally. Local high scores always work regardless of connectivity. + +## Monetization + +The mobile versions (Android and iOS) are free with AdMob banner ads. A one-time in-app purchase removes ads permanently. The desktop build is a paid download ($2) on itch.io with no ads. The web version is completely free with no ads. + +## What's Next + +We're looking at adding more tile packs from [art101](https://art101.io/) to introduce new visual themes — different art movements, color palettes, and geometric styles — while keeping the same core gameplay mechanics. The infrastructure is already there: the TextureAtlas system supports swappable tile sets, so new packs are primarily an art curation effort. + See the full [Bauhaus Echo product page](/products/bauhaus-echo) for more details. diff --git a/apps/web/content/blog/neroswap-aggregator.mdx b/apps/web/content/blog/neroswap-aggregator.mdx index 216555c..647ce8d 100644 --- a/apps/web/content/blog/neroswap-aggregator.mdx +++ b/apps/web/content/blog/neroswap-aggregator.mdx @@ -1,61 +1,118 @@ --- title: "Neroswap: a Privacy-First DEX & CEX Aggregator" -date: "2026-03-17" -excerpt: "How we built a real-time orderbook aggregator across 6 decentralized and centralized exchanges — with no KYC, no custody, and no tracking." +date: "2026-03-22" +excerpt: "How we built a real-time orderbook aggregator across 6 decentralized and centralized exchanges — scraping atomic swap networks, gRPC observer nodes, and public APIs every 10 minutes. No KYC, no custody, no tracking." --- ## The Problem -If you want to trade Monero by DEX or Wownero at all today, your options are scattered across half a dozen exchanges — each with a different UI, different pair formats, and wildly different prices. Some are decentralized atomic swaps; others are no-KYC centralized platforms. Comparing prices means opening six browser tabs and doing mental math. +If you want to buy Monero on a decentralized exchange today, you need to check at least three different platforms — each with its own node, its own UI, and its own pair format. Want to compare XMR/BTC on an atomic swap DEX versus a P2P exchange versus a no-KYC centralized platform? That's six browser tabs and a spreadsheet. -[Neroswap](https://neroswap.com) fixes that. +Wownero is even worse. Liquidity is split across a handful of small exchanges with no way to compare prices without visiting each one individually. -## What Neroswap Does +[Neroswap](https://neroswap.com) solves this by pulling live orderbook and ticker data from six exchanges into a single dashboard, with USD reference pricing, historical sparklines, and per-DEX liquidity tracking. -Neroswap is a real-time aggregator that pulls orderbook and ticker data from six exchanges and displays it in a single, unified dashboard. You can see best prices at a glance, filter by exchange type (DEX vs CEX) or coin (XMR vs WOW), and click directly through to the source exchange to trade. +## The Six Exchanges -The exchanges currently tracked are: +Neroswap tracks three decentralized exchanges and three centralized (no-KYC) exchanges. Each one works differently under the hood, which means each one requires a completely different scraping approach. -- **BasicSwap** — SMSG-based atomic swaps for BTC, LTC, XMR, PART, WOW, FIRO, DASH, and more -- **Retoswap** — P2P exchange for XMR fiat/crypto pairs, trades settle over Tor -- **Eigenwallet** — BTC ↔ XMR trustless atomic swaps via Tor rendezvous -- **Nonlogs** — No-KYC Wownero-focused exchange with WOW/BTC and WOW/USDT -- **CexSwap** — Spot markets and AMM liquidity pools across 40+ coins -- **AltQuick** — No-KYC altcoin exchange with 25+ BTC-quoted markets +### BasicSwap DEX -Kraken reference prices are used to compute approximate USD values for every pair. +[BasicSwap](https://basicswapdex.com/) is a decentralized exchange built on the Particl network's SMSG (SecureMessaging) protocol. Offers are broadcast across a peer-to-peer network and settled via cross-chain atomic swaps — no central server, no custody, no counterparty risk. It supports 14 coins including BTC, XMR, LTC, BCH, WOW, FIRO, DASH, PIVX, and Decred. -## How the Data Pipeline Works +**How we scrape it:** We run a local BasicSwap full node with a custom "God Mode" patch. Normally, BasicSwap only shows offers for coins you have wallets configured for. Our patch injects a synthetic interface that bypasses the native coin client check, letting us query offers for all 14 supported coins without actually syncing all those blockchains. The scraper hits the node's local REST API at `127.0.0.1:12700/json/offers` and maps internal coin IDs to human-readable names. -Neroswap runs a fleet of Python scrapers on a dedicated server. Each scraper targets one exchange and runs every 10 minutes via cron: +BasicSwap currently has the widest pair coverage of any DEX we track — around 30 active trading pairs and 100+ offers at any given time. -1. **Scraping** — Each script queries its exchange via REST API, gRPC, HTML-over-Tor, or direct RPC. BasicSwap and Retoswap are scraped via local full nodes; Eigenwallet via its Tor onion; Nonlogs, CexSwap, and AltQuick via public APIs. +### Retoswap (Haveno) -2. **Normalization** — Raw data is transformed into a common JSON schema with fields like `pair`, `price`, `amount`, `type` (bid/ask), and `source`. +[Retoswap](https://retoswap.com/) is a Haveno fork — a P2P exchange where all trades settle over Tor. It supports XMR against fiat currencies (USD, EUR, GBP, CHF, AUD, CAD) and crypto (BTC, ETH, LTC, BCH, and others). Offers include detailed payment method info, fee structures, and maker deposit requirements. -3. **Sync** — The JSON files are rsync'd to a CDN edge server over SSH every 10 minutes, right after the scrapers finish. +**How we scrape it:** We run a Haveno observer node locally and connect via gRPC on port 9998 with password authentication. The scraper auto-creates an observer account if needed, then pulls the full offer list including price, amount ranges, payment methods, and trade state. Haveno's gRPC interface gives us much richer data than a typical REST API — we get maker addresses, security deposits, and fee breakdowns for every offer. -4. **Serving** — The [neroswap.com](https://neroswap.com) frontend is a Next.js app using ISR (Incremental Static Regeneration). It fetches the JSON data from the CDN on each request with a 5-minute revalidation window, so the data is always fresh. +Retoswap typically has 40-300+ active offers and is the primary source for XMR/fiat pricing on Neroswap. -5. **Rendering** — The frontend computes best bid/ask prices across all sources, applies Kraken USD reference rates, and renders sortable, filterable tables. Every row links back to the source exchange. +### Eigenwallet + +[Eigenwallet](https://eigenwallet.org/) provides trustless BTC-to-XMR atomic swaps routed through Tor. Market makers run persistent onion services and publish quotes (price, min/max swap amounts, refund policy) to a set of rendezvous nodes. + +**How we scrape it:** This was the most challenging integration. Eigenwallet has no public API — their dev team confirmed their internal liquidity scripts are private. So we built a custom Rust binary that connects directly to the Eigenwallet libp2p network. + +Our scraper spins up a libp2p node, bootstraps through Tor via an embedded Arti client, discovers market makers through 7 hardcoded rendezvous points, and negotiates the `/comit/xmr/btc/bid-quote/2.0.0` protocol to fetch `BidQuote` structs containing price (in satoshis), min/max quantities, and refund policy terms. + +The binary runs as a persistent systemd daemon rather than a cron job. Since Tor bootstrap takes 10-20 seconds and peer discovery is gradual, keeping a persistent connection lets us maintain warm connections to market makers and accumulate quotes continuously. Every 10 minutes, the daemon dumps its current orderbook snapshot to JSON. It also maintains a peer address cache so it can dial known market makers directly on restart instead of waiting for rendezvous discovery. + +Eigenwallet typically has 3-6 active market makers with total XMR liquidity ranging from $300K to $500K+ USD. + +### Nonlogs + +[Nonlogs](https://nonlogs.io/) is a no-KYC centralized exchange focused on Wownero. It offers WOW/BTC, WOW/USDT, and several other privacy coin pairs (GRIN, BEAM, RYO, MWC). **Scraped via public REST API** — straightforward ticker data with 24h volume, bid/ask spreads, and price change percentages. + +### CexSwap + +[CexSwap](https://cexswap.cc/) is a Wownero-focused CEX with both spot markets and AMM liquidity pools. It supports 40+ assets across 100 spot pairs and 90+ AMM pools. **Scraped via two public API endpoints** — one for spot tickers (OHLCV data) and one for AMM pools (TVL, liquidity depth). CexSwap is the largest source of WOW trading pairs on Neroswap. + +### AltQuick + +[AltQuick](https://altquick.com/) is a no-KYC altcoin exchange with 25+ BTC-quoted markets including WOW, DASH, DOGE, BCH, and several smaller coins. **Scraped via public ticker API** with 24h OHLC, volume, and trade counts. + +## The Data Pipeline + +The pipeline runs on a dedicated scraper server and pushes data to the web server every 10 minutes. + +### Scraping + +An orchestrator script runs seven scrapers in a staggered sequence — Kraken reference prices first, then each exchange with 90-second gaps to avoid overwhelming any single source. The full cycle takes about 6 minutes. + +``` +T+0s Kraken (USD ref prices: BTC, XMR, LTC, BCH) +T+0s BasicSwap (local node REST API) +T+90s Haveno/Retoswap (local node gRPC) +T+180s Nonlogs (public API) +T+270s CexSwap (public API) +T+360s AltQuick (public API) +T+365s Merge all sources → combined.json +``` + +Eigenwallet runs independently as a persistent daemon, writing its own JSON file. + +### Historical Data + +Every time the frontend revalidates (every 30 seconds), it records an aggregated snapshot to a SQLite database on the web server: + +- **Price snapshots** — best/worst/last price per source per trading pair, with USD-converted volume +- **Liquidity snapshots** — total available USD liquidity per DEX per coin (XMR and WOW), with outlier filtering to exclude garbage offers + +This powers the sparkline charts on the dashboard and the DEX Liquidity overview. At roughly 80 price snapshots and 5 liquidity snapshots per cycle, the database grows at about 30-40 MB per month. + +### Freshness Detection + +Each JSON file carries a `fetched_at` timestamp. The frontend checks data age against a 30-minute threshold — if a source hasn't been updated in over 30 minutes, it's marked as "Stale" (orange badge) instead of "Live" (green). Stale data is excluded from the Best Prices summary, the DEX Liquidity calculations, and the history database to prevent polluting charts with old numbers. + +### Serving + +The JSON files are synced via rsync over SSH to the production web server. The frontend is a Next.js app using ISR (Incremental Static Regeneration) with 30-second revalidation — so data is always at most 30 seconds behind the latest scrape. ## Privacy by Design -Neroswap has no user accounts, no cookies, no tracking pixels, and no analytics. There is nothing to log and nothing to sell. The entire site is a read-only dashboard — it never touches your funds or your identity. +Neroswap has no user accounts, no cookies, no tracking pixels, and no analytics. The site is a read-only dashboard — it never touches your funds or your identity. There is nothing to log and nothing to sell. -All the DEX sources (BasicSwap, RetoSwap, Eigenwallet) are fully decentralized with on-chain or cross-chain atomic settlement. The CEX sources (Nonlogs, CexSwap, AltQuick) are all no-KYC platforms. Retoswap trades happen entirely over Tor. +All three DEX sources (BasicSwap, Retoswap, Eigenwallet) are fully decentralized with on-chain or cross-chain atomic settlement. Retoswap trades happen entirely over Tor. Eigenwallet swaps are routed through Tor onion services. The three CEX sources (Nonlogs, CexSwap, AltQuick) are all no-KYC platforms. ## The Stack -- **Frontend**: Next.js (App Router), React, TypeScript, Tailwind CSS -- **Scrapers**: Python scripts with requests, grpc, and stem (Tor) -- **Data transport**: rsync over SSH to CDN, served as static JSON -- **Infrastructure**: Systemd services, cron scheduling, PM2 process management +- **Frontend**: Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4 +- **Scrapers**: Python (requests, gRPC, BeautifulSoup) + Rust (libp2p, Arti/Tor) +- **Data storage**: SQLite for historical snapshots, JSON files for live data +- **Infrastructure**: Systemd services, cron scheduling, PM2 process management, rsync sync +- **Reference pricing**: Kraken public API for BTC, XMR, LTC, BCH in USD + +## WOW Price API + +Neroswap also provides a public API endpoint for Wownero's USD price at [`/api/price/wow`](https://neroswap.com/api/price/wow). It computes a volume-weighted average price (VWAP) across all WOW trading pairs on Nonlogs, CexSwap, and AltQuick — using Kraken BTC and direct USDT prices as the USD reference. Pairs with zero volume are included at equal weight as a fallback. ## Try It Visit [neroswap.com](https://neroswap.com) to see the live dashboard. -The project is built and maintained by [Such Software](/#contact). If you need a custom aggregator, scraping pipeline, or privacy-first web application, [get in touch](/#contact). - -See the full [Neroswap product page](/products/neroswap) for the complete feature list and exchange descriptions. +The project is built and maintained by [Such Software](https://such.software). If you need a custom aggregator, scraping pipeline, or privacy-first web application, [get in touch](/#contact).