diff --git a/apps/web/content/blog/wownerogue-launch.mdx b/apps/web/content/blog/wownerogue-launch.mdx
new file mode 100644
index 0000000..2c9893a
--- /dev/null
+++ b/apps/web/content/blog/wownerogue-launch.mdx
@@ -0,0 +1,55 @@
+---
+title: "Wownerogue: A Provably-Fair Roguelike on Block Time"
+date: "2026-06-01"
+excerpt: "A browser roguelike where the clock is the blockchain. How the commit-reveal fairness, random block-timing deadline, and Monero payout batching actually work."
+draft: false
+---
+
+**Wownerogue** is a multiplayer roguelike that runs in the browser. You drop into a dungeon, and you have until the next block to reach the exit. A monster is hunting you. There is a treasure bag worth grabbing on the way out. That is the whole game, and the catch is in the clock: the deadline is a new block on the Monero or Wownero chain, so it is genuinely random.
+
+It runs in two places. [play.wowne.ro](https://play.wowne.ro) is the Wownero instance: free to play, with paid entry feeding a separate leaderboard, and no crypto payouts. [monerogue.app](https://monerogue.app) is the Monero stagenet instance, where escaping pays out stagenet XMR from a house wallet. Stagenet coins are worthless, which makes it a safe place to run the full payment-and-payout loop in the open.
+
+
+
+## The clock is the point
+
+Most timed games give you a countdown. Wownerogue gives you a block. Monero and Wownero target roughly two-minute block times, but proof-of-work timing is a Poisson process: the next block might land in fifteen seconds or in five minutes, and nothing on the server can predict which. Your run starts when a block arrives, and a later block ends it.
+
+I had one early instinct to "fix" this by adding a minimum fairness floor so nobody got a fifteen-second round. That was wrong, and I tore it out. The random deadline is not a bug to smooth over: it is the entire reason the game is interesting. The only timing guard left is a two-second grace at the very start, so a block landing in the same instant your run begins cannot kill you before you have moved.
+
+## Provably fair, the boring honest way
+
+A game that holds money has to prove it is not cooking the board after you pay. Wownerogue uses a standard commit-reveal:
+
+1. Before your run, the server generates a 256-bit seed and shows you `SHA-256(seed)`: the commitment. You can copy it.
+2. The seed drives a deterministic RNG that lays out the whole dungeon: walls, the exit, the treasure, and the monster's behaviour.
+3. When the run ends, the server reveals the seed.
+4. You hash the revealed seed and confirm it equals the commitment you were shown up front.
+
+Because the commitment is published before you move, the server cannot reroll a dungeon it does not like once the round is live. Feed the seed into the verification page and it regenerates the exact board you played, then prints a layout fingerprint so you can compare. No trust required, just a hash.
+
+## Two instances, one codebase
+
+The split between the two sites is config, not forks. The same server runs both; environment variables decide the currency, the network, whether payouts are on, and the branding. Mainnet Wownero runs free-to-play with payouts switched off. Stagenet Monero runs with payouts on. That meant being careful that a single flag actually gated the money path end to end, and not just part of it: an early version of the payout toggle disabled credits payouts but not direct ones, which is exactly the kind of gap you do not want in code that moves funds.
+
+## Paying out without tripping over Monero
+
+The interesting backend problem is payouts. When a Monero wallet spends, the change output (and the spent output) get locked for about ten blocks. Fire payouts naively and you will try to spend funds that are still locked, and the transfer fails.
+
+So payouts batch, and the batcher checks unlocked balance before it sends. If the wallet does not have enough unlocked balance to cover the batch right now, it does not fail the payouts: it defers them back to pending and retries on the next block tick and the next interval. Insufficient-funds errors are treated as safe to retry, because they happen before anything is broadcast. Anything ambiguous (an error that might mean the transaction already went out) gets parked for manual review instead of being auto-retried, so a flaky RPC response can never cause a double payout.
+
+## Keeping the free board honest
+
+Free play is the obvious abuse target: spin up sockets, farm the leaderboard. Scores are computed server-side from real game state, so you cannot just post a number. The rate limiter already capped new connections and game starts per IP, but nothing stopped one IP from holding a pile of sockets open, so I added a per-IP concurrent-socket cap. Not bulletproof against a determined botnet, but it raises the cost of the cheap attacks well past worthwhile.
+
+## Stack
+
+Node.js and Socket.io for real-time game state, chat, and live spectating. PostgreSQL for everything durable, with money handled in integer atomic units and idempotent, transactional writes. Monero and Wownero wallet-RPC for payments and payouts. The client is plain JavaScript with a tile renderer; no framework, because the game is a grid and a render loop.
+
+The whole thing is MIT-licensed and public: [github.com/Such-Software/wownerogue](https://github.com/Such-Software/wownerogue). If you want to see exactly how the fairness check, the block-timing loop, or the payout batching work, the answer is in there rather than in this post.
+
+## Play it
+
+
+
+Queue up, escape before the block lands, and check your seed afterward. If you escape with the bag on the Monero instance, the stagenet XMR is real (and worthless, which is the point).
diff --git a/apps/web/public/images/products/moonplace-HiRes.png b/apps/web/public/images/products/moonplace-HiRes.png
deleted file mode 100644
index 2d28982..0000000
Binary files a/apps/web/public/images/products/moonplace-HiRes.png and /dev/null differ
diff --git a/apps/web/public/images/products/moonplace.png b/apps/web/public/images/products/moonplace.png
deleted file mode 100644
index e623c4a..0000000
Binary files a/apps/web/public/images/products/moonplace.png and /dev/null differ
diff --git a/apps/web/public/images/products/r-cryptocurrency.png b/apps/web/public/images/products/r-cryptocurrency.png
deleted file mode 100644
index 76313a0..0000000
Binary files a/apps/web/public/images/products/r-cryptocurrency.png and /dev/null differ
diff --git a/apps/web/src/app/blog/[slug]/page.tsx b/apps/web/src/app/blog/[slug]/page.tsx
index a4a162a..fd2bac6 100644
--- a/apps/web/src/app/blog/[slug]/page.tsx
+++ b/apps/web/src/app/blog/[slug]/page.tsx
@@ -6,6 +6,7 @@ import {
SuchoiceStoreButtons,
BauhausEchoStoreButtons,
SuchMoonLaunchStoreButtons,
+ WownerogueLinks,
} from "@/components/store-buttons";
import Link from "next/link";
import type { Metadata } from "next";
@@ -71,6 +72,7 @@ export default async function BlogPost({ params }: Props) {
SuchoiceStoreButtons,
BauhausEchoStoreButtons,
SuchMoonLaunchStoreButtons,
+ WownerogueLinks,
}} />
diff --git a/apps/web/src/app/products/custom-websites/page.tsx b/apps/web/src/app/products/custom-websites/page.tsx
index f2f6d94..e88ac57 100644
--- a/apps/web/src/app/products/custom-websites/page.tsx
+++ b/apps/web/src/app/products/custom-websites/page.tsx
@@ -6,12 +6,12 @@ import Link from "next/link";
import type { Metadata } from "next";
export const metadata: Metadata = {
- title: "Custom Websites | Such Software",
- description: "Real-time platforms and interactive web experiences. See our work on rcryptocurrency.com and moonplace.io.",
+ title: "Custom Web Apps & Real-Time Platforms | Such Software",
+ description: "We build fast, accessible real-time web apps: multiplayer games, live dashboards, and crypto platforms with WebSocket sync. Next.js, Socket.io, WebRTC.",
alternates: { canonical: "/products/custom-websites" },
openGraph: {
- title: "Custom Websites | Such Software",
- description: "Real-time platforms and interactive web experiences. See our work on rcryptocurrency.com and moonplace.io.",
+ title: "Custom Web Apps & Real-Time Platforms | Such Software",
+ description: "Fast, accessible real-time web apps: multiplayer games, live dashboards, and crypto platforms with WebSocket sync. Next.js, Socket.io, WebRTC.",
type: "website",
},
};
@@ -38,31 +38,32 @@ export default function CustomWebsitesPage() {
diff --git a/apps/web/src/app/products/page.tsx b/apps/web/src/app/products/page.tsx
index 04be49f..8dc124c 100644
--- a/apps/web/src/app/products/page.tsx
+++ b/apps/web/src/app/products/page.tsx
@@ -7,8 +7,8 @@ import Link from "next/link";
import type { Metadata } from "next";
export const metadata: Metadata = {
- title: "Products | Such Software",
- description: "Tools and applications built by Such Software. Neroswap, Webshops, Custom Websites, Smirk Wallet, Bauhaus Echo, Suchoice, Vegan IQ, and Such Moon Launch.",
+ title: "Our Products & Apps: Crypto Tools, Games & Commerce | Such Software",
+ description: "Explore Such Software's products: Wownerogue Monero roguelike, Neroswap crypto aggregator, Smirk tipping wallet, headless Webshops, and our mobile games.",
alternates: { canonical: "/products" },
openGraph: {
title: "Products | Such Software",
@@ -18,6 +18,13 @@ export const metadata: Metadata = {
};
const products = [
+ {
+ slug: "wownerogue",
+ title: "Wownerogue",
+ description: "A provably-fair multiplayer roguelike synced to Monero and Wownero block times. Play free in the browser, escape the dungeon, earn on-chain payouts.",
+ color: "orange",
+ icon: "/images/products/privacy-labs.png",
+ },
{
slug: "neroswap",
title: "Neroswap",
@@ -35,9 +42,9 @@ const products = [
{
slug: "custom-websites",
title: "Custom Websites",
- description: "Real-time platforms and interactive experiences. See rcryptocurrency.com and moonplace.io.",
+ description: "Real-time web apps, multiplayer games, and live dashboards. Built on Next.js, Socket.io, and WebRTC.",
color: "purple",
- icon: "/images/products/r-cryptocurrency.png",
+ icon: "/images/products/neroswap.png",
},
{
slug: "smirk-wallet",
diff --git a/apps/web/src/app/products/wownerogue/page.tsx b/apps/web/src/app/products/wownerogue/page.tsx
new file mode 100644
index 0000000..549dd57
--- /dev/null
+++ b/apps/web/src/app/products/wownerogue/page.tsx
@@ -0,0 +1,186 @@
+import { Header } from "@/components/layout/header";
+import { MobileNav } from "@/components/layout/mobile-nav";
+import { Card } from "@repo/ui/components/card";
+import { WownerogueLinks } from "@/components/store-buttons";
+import Link from "next/link";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Wownerogue: Provably-Fair Monero Multiplayer Roguelike | Such Software",
+ description: "Play Wownerogue: a provably-fair, blockchain-synced multiplayer roguelike. Every run is verifiable, every round ends on a new block. Free in-browser, no download.",
+ alternates: { canonical: "/products/wownerogue" },
+ keywords: [
+ "provably fair game",
+ "monero game",
+ "wownero game",
+ "crypto roguelike",
+ "blockchain multiplayer game",
+ "browser roguelike",
+ "monerogue",
+ "wownerogue",
+ ],
+ openGraph: {
+ title: "Wownerogue: Provably-Fair Monero Multiplayer Roguelike",
+ description: "A provably-fair, blockchain-synced multiplayer roguelike. Escape the dungeon before the next block. Free in-browser at play.wowne.ro.",
+ type: "website",
+ url: "/products/wownerogue",
+ },
+};
+
+// VideoGame structured data: the highest-value rich result for a game query.
+const jsonLd = {
+ "@context": "https://schema.org",
+ "@type": "VideoGame",
+ name: "Wownerogue",
+ alternateName: "Monerogue",
+ url: "https://such.software/products/wownerogue",
+ description:
+ "A provably-fair, blockchain-synced multiplayer roguelike. Escape the dungeon before the next block, avoid the monster, grab the treasure, and earn on-chain payouts.",
+ applicationCategory: "Game",
+ genre: ["Roguelike", "Multiplayer"],
+ gamePlatform: "Web browser",
+ operatingSystem: "Any (web browser)",
+ playMode: "MultiPlayer",
+ author: { "@type": "Organization", name: "Such Software" },
+ publisher: { "@type": "Organization", name: "Such Software" },
+ offers: {
+ "@type": "Offer",
+ price: "0",
+ priceCurrency: "USD",
+ url: "https://play.wowne.ro",
+ },
+};
+
+const features = [
+ { title: "Provably Fair", body: "Each round commits to a SHA-256 hash before you play and reveals the seed after. That seed deterministically generates the entire dungeon, so you can verify nothing was changed once the round began." },
+ { title: "Synced to Block Time", body: "Rounds start and end on new Monero or Wownero blocks. Because block timing is random, your escape deadline is genuinely unpredictable: that is the whole game." },
+ { title: "Live Spectating", body: "Watch any in-progress run in real time from the lobby, with a global feed announcing escapes as they happen." },
+ { title: "Two Leaderboards", body: "Free runs feed the Pleb board; paid runs feed the Hall of Champions. Rankings are time-windowed (day, week, all-time)." },
+ { title: "On-Chain Payouts", body: "On the payout instance, escaping pays out real funds from a house wallet over wallet-RPC, batched and verified against the chain." },
+ { title: "Open Source", body: "The full server and client are MIT-licensed and public. Read exactly how the fairness, payments, and payouts work." },
+];
+
+const steps = [
+ { n: "1", title: "Queue", body: "Join the queue. Your run starts when the next block lands." },
+ { n: "2", title: "Escape", body: "Move with WASD or arrow keys. Reach the exit before the next block, or the monster, ends your run." },
+ { n: "3", title: "Grab the bag", body: "Collecting the treasure bag is optional but pays a higher multiplier on escape." },
+ { n: "4", title: "Verify", body: "After the run, the seed is revealed. Check it against the pre-game commitment on the verification page." },
+];
+
+export default function WowneroguePage() {
+ return (
+
+
+
+
+
+ ← Back to Products
+
+
+
+ Wownerogue
+
+
A provably-fair roguelike, synced to the blockchain.
+
+
+
+ Wownerogue is a multiplayer dungeon-crawler that runs entirely in your browser. Every
+ round is provably fair and synced to Monero or Wownero block times: you have until the
+ next block to escape the dungeon, avoid the monster, and grab the treasure. Random block
+ timing means your deadline is never the same twice.
+
+
+
+
+
+
+
+
Two Worlds
+
+
+
play.wowne.ro
+
+ The Wownero mainnet instance: free to play, with paid entry for the Hall of Champions.
+ No crypto payouts, all the tension. The friendly place to learn the game.
+
+
+
+
monerogue.app
+
+ The Monero stagenet instance, where escaping pays out stagenet XMR from a house wallet.
+ Stagenet coins have no real value, so it is a safe sandbox for the full payout loop.
+
+
+
+
+
How a Run Works
+
+ {steps.map((s) => (
+
+
{s.n}. {s.title}
+
{s.body}
+
+ ))}
+
+
+
Features
+
+ {features.map((f) => (
+
+
{f.title}
+
{f.body}
+
+ ))}
+
+
+
How “Provably Fair” Works
+
+
+ Before your run begins, the server publishes a SHA-256 hash, the commitment, of a secret
+ random seed. You can record it. When the run ends, the server reveals the seed itself.
+ Anyone can hash the revealed seed and confirm it matches the commitment they were shown
+ earlier, which proves the seed was fixed before you started moving.
+
+
+ That same seed deterministically generates the dungeon layout, the treasure and exit
+ positions, and the monster’s behaviour. Feed the seed back in and you regenerate
+ the exact dungeon you played. There is no way to re-roll a bad board after the fact, and
+ no hidden house edge in the level itself. The verification page does this check for you.
+
+
+
+
Under the Hood
+
+
+
Real-time: Node.js and Socket.io drive game state, chat, and spectating.
+
Fairness: SHA-256 commit-reveal with a seeded RNG for deterministic dungeons.
+
Payments: Monero and Wownero wallet-RPC for entry payments and batched payouts.
+
Storage: PostgreSQL with atomic, idempotent money handling in integer units.
+
Open source: MIT-licensed. The whole thing is on GitHub.
+
+
+
+
+ Want the build notes? Read{" "}
+
+ the launch write-up
+
+ {" "}for how the fairness, block-timing, and payout logic came together.
+
-
-
);
-};
\ No newline at end of file
+};
diff --git a/apps/web/src/components/store-buttons.tsx b/apps/web/src/components/store-buttons.tsx
index 15b569b..6f94f38 100644
--- a/apps/web/src/components/store-buttons.tsx
+++ b/apps/web/src/components/store-buttons.tsx
@@ -88,6 +88,20 @@ export function SuchMoonLaunchStoreButtons() {
);
}
+export function WownerogueLinks() {
+ // Wownerogue is a browser game, not a store app: link the two live instances + source.
+ return (
+
+ );
+}
+
export function BauhausEchoStoreButtons() {
return (