Updates
Every release of GigaDungeon and what changed in it, newest first. The version you are playing is at the bottom of the game screen.
1.0.0 — 2026-10-16
GigaDungeon 1.0: the public dungeon as a complete turn-based roguelike on the 3.5 System Reference Document — a 256 × 256 maze of 128 × 128 levels generated from a seed; eight classes with skills, feats, spells and saving throws; monsters, bosses, Wardens and the Core Gate; merchants, people and their jobs; gathering, crafting, hunger, thirst, light, and hirelings; private worlds, squads, PvP arenas, leaderboards and achievements; the Hero subscription; an admin console with telemetry, content overrides, sprites, health and editable pages. No code changes from 0.29.0.
What comes next, as 1.x releases inside this deployment: GigaDungeon Frontier — a ruleset for private worlds with destructible walls, building, tool durability, resource tiers and a trades tree.
0.29.0 — 2026-10-16
Launch hardening, part two. Migration 031 (billing_events.event_created/object_id, subscriptions.past_due_since).
- Load test:
npm run loadtestsimulates up to hundreds of players against the in-memory database, in-process or through the HTTP layer, and reports latency per operation. 100 players: p95 2.5 ms (engine), 70 ms (HTTP) on a single process. The per-address API rate limit was too tight for several players behind one address; raised to 3000/min. - Stripe: late, older webhook events can no longer overwrite newer subscription state; a failed renewal keeps Hero for 7 days from the failure (not a whole extra period), then locks the extra characters until it is paid, and unlocks them the moment it is.
- Server errors older than 90 days are trimmed daily, as the Privacy Policy promises.
docs/RUNBOOK.mdanddocs/LAUNCH-CHECKLIST.md.
0.28.1 — 2026-10-16
- Admin Pages tab: previewing or saving the Updates page failed with "request entity too large" — the whole changelog is bigger than the API's 32 KB body limit. The pages routes now accept up to 1 MB; everything else keeps the small limit.
0.28.0 — 2026-10-16
- Updates page at
/updates.html, linked from the footer: every release and what changed in it, newest first. Its default text is the changelog shipped with the running version, so it keeps itself current; it is also editable from the admin Pages tab like the legal pages (Reset to default picks up new releases after an edit). - The page shell no longer inherits the game's two-column layout at desktop widths (the 0.26 legal pages did).
0.27.0 — 2026-10-16
- Pages tab in the admin console: the Terms of Service, Privacy Policy, Open Game License and About pages are now Markdown you edit in the console — title, the line under it, and the body — with a live preview, Save, and Reset to the shipped text. The live page changes within a minute, no deploy. Fill in the governing-law state and paste the OGL text there. Migration 030 is a marker only.
0.26.0 — 2026-10-16
Launch hardening, part one (the planned 0.26 and 0.27 together). Migration 029 adds server_errors; the app runs without it (errors are then kept in memory only).
- Legal pages. Terms of Service, Privacy Policy and About/credits at
/legal/, with the OGL page, linked from the footer, the sign-up form and the Hero page. The Terms describe the $3.00/month Hero plan, cancellation, locked characters, refunds and price-change notice; the Privacy Policy lists exactly what is stored (email, names, sign-up and last IP, Stripe ids, play telemetry, error logs) and how to delete it. Two things to fill in before launch: the governing-law state in Terms §11 (highlighted), and the full OGL 1.0a text on/license.html. - Deleting an account closes billing: the Stripe subscription is cancelled and the customer deleted before the account goes, whether the player or an admin deletes it.
- Rate limits on every API route, by player or by address, with a 429 and
Retry-Afterover the limit; the Stripe webhook is exempt. Editable on the new admin Health tab without a restart. - Request ids on every API response and in the error message players see ("ref 9e48d65e"), one JSON log line for every failed or slow call, and a server error log (ring + table) with stack traces on the Health tab.
/healthzreports database reachability, Stripe mode/price state, request and error counters and the schema doctor, so an uptime monitor sees something real.
0.25.1 — 2026-10-16
- Maps panel (M): the explanatory lines under the level map and the maze map are gone; the same notes remain as hover titles on the two maps.
0.25.0 — 2026-10-16
Company. Migration 028 is a marker only — nothing new to store.
- Hirelings. Half the safe rooms with someone in them also hold a person for hire: a sellsword, a shield-bearer (+1 AC at your side), an acolyte (prays over your wounds between fights, three times a rest) or a torchbearer (five tiles of light in the dark). Walk up for the pitch, Y to hire — 20 + 5 × ring gold, once. They take the companion's slot, fight with the companion's rules, scale with your level, die for good. Dismiss from the toolbar.
- Camps. Rest in a safe room with someone in it and nothing comes for you. A bedroll (1 gp) halves the odds elsewhere and adds your level in hit points.
- Foul water. A pool is Survival DC 15 to drink clean; otherwise Fortitude or filth fever.
- Four achievements (101 in all). Sprite prompts for the four hirelings.
0.24.1 — 2026-10-16
- Artisan's tools work from the pack; the pack no longer offers to equip them (0.23 did, and then refused), and the item and the refusal both say so.
0.24.0 — 2026-10-16
Survival: hunger, thirst and light. Migration 027 (characters.needs) — run it before deploying. Every existing character is handed three rations, a waterskin and three torches the first time they load.
- Hunger and thirst on the SRD's clocks, a turn a minute: a day plus Con hours without water, three days without food, then hourly (or daily) Con checks or 1d6. It never kills outright — it stops at one hit point — but past the limit you are fatigued and rest heals nothing until you eat and drink. "Hungry"/"Thirsty" warn at three-quarters; the HUD shows both and your light.
- Provisions from every merchant: trail rations (K to eat), waterskins with four drinks (V), torches; the chandler and deeper merchants add hooded lanterns, oil, sunrods and, in the deep, the everburning torch. Any pool is free water and refills your skins.
- Light in lightless worlds: nothing lit, you see one tile; a torch five, a lantern or sunrod seven. O lights or douses. A doused torch keeps its minutes, a lantern its oil, and resting douses the light for you. Standard worlds are lit as before.
- Rest is eight hours of all three clocks.
- Admin: the Quests tab carries a catalogue (the four kinds, pay by ring, and "jobs around a square" — every job the people near any square in any world would hand out); a new Recipes tab lists all 79 recipes and 24 materials with how many players have made each.
- Four achievements (97 in all): Well Provisioned, Firekeeper, Cistern Water, A Light in the Dark.
0.23.0 — 2026-10-15
Crafting. Migration 026 is a settings marker only — no new columns, nothing to backfill.
- Somewhere to work. Every merchant's chamber has a bench; artisan's tools (50 gp, merchants stock them) are a bench you carry. Without one or the other the sheet says where to find them.
- 79 recipes across five craft skills, listed on the sheet with a skill filter and an "only what I can make now" toggle: Craft (weapons) and Craft (bows) make every weapon in the game, plain (DC 12 simple, 15 martial) and masterwork (DC 20, silver in the steel or giant sinew in the bow: +1 to hit); Craft (armor) every armour and shield (masterwork lightens the check penalty by one); Craft (alchemy) brews sixteen potions and a bone-dust salve from reagents and monster parts (DC 15 + 5 × spell level); Spellcraft, casters only, enhances a weapon or armour +1 to +5 one step at a time (DC 20–40, 100–2500 gp) and imbues one ability per weapon — flaming, frost, keen, holy (DC 25–30).
- Failure makes nothing; a failure by 5 or more ruins half the materials, as the SRD has it.
- The five craft skills are class skills for every class. Four achievements: Smith, Master smith, Brewer, Enchanter.
- API:
POST /api/game/craft {recipe, item_id?}; the state'scraftblock carries bench/tools, your gear, your craft bonuses and every recipe with what stands in its way.
0.22.0 — 2026-10-15
Resource gathering. Migration 025 (characters.materials) — run it before deploying.
- Nodes in the rooms. Ore veins in caves, reagent beds in cisterns, bone piles in catacombs, collapsed timbers in halls — one in roughly every third room, drawn as a glyph until the Sprites tab makes
tiles/node_*. Walking onto one is the first try (Knowledge for ore, Survival for reagents, Search for bone and salvage; DC 10 + 3 per tier); H tries again. A success takes 1–3 units — iron, silver or mithral ore, cave moss, glowcap or black lotus, bone, grave dust or ancient cloth, timber, scrap iron or old rope, richer kinds deeper — and empties the node until the square respawns. Nodes roll on their own random stream, so nothing about the squares you already know has moved. - Monster parts. A Survival check on a kill (DC 10 + CR) takes something by family: hide or fang, chitin, a beast pelt, giant sinew, brimstone, ooze residue, an elemental core, a strange gland, fibrous stalk, undead ash — and a dragon scale. Bosses always leave two or three.
- Carrying. Every material weighs something; the limit is 20 + 4 per point of Strength over 10. Past it you are laden and nothing more fits. The sheet's Materials list shows what you hold with weight and worth, and at a merchant each kind has Sell all (value × (50% + Appraise)).
- Five achievements: Gatherer (100 units), Mithral! and Scaled (hidden), Laden, Collector (twelve kinds). 89 in all.
- Crafting comes next (0.23) and spends all of this.
0.21.0 — 2026-10-15
Quests. Migration 024 (quests table) — run it before deploying.
- Work on offer. The people in the safe rooms hand out jobs, each hashed from the giver and the world and always about the dungeon within two squares: clear a named room, open a particular chest (it fails if someone beat you to it), hunt a kind of monster, or carry a package to another person nearby. Talking to someone shows their offer with pay; J (or the badge button) takes it. Three jobs in hand at most; three jobs per giver.
- Progress and pay. Jobs are judged after every action and the log says when one is done. A finished job pays when you talk to the giver again — gold and XP scaled to the ring, an item for clears and hunts — and a delivery pays at the recipient. Each job finished raises your standing with that person.
- Quest log on the character sheet (Work) with progress, giver, pay and a Drop button; target squares are marked
!on the maze map. - Four achievements: Hired Hand, Reliable (ten jobs), Courier (five deliveries), Work in the Deep (hidden, ring 100+). 84 in all.
- Admin → Quests: jobs taken, paid, in hand, failed and dropped; completion rate and median time to finish; by kind, by ring band, the busiest givers, and the last fifty.
- Room names no longer read "a echoing reservoir".
0.20.2 — 2026-10-15
Sprite generation that pays off, and a way to recover what was already paid for. No migration.
- Why "Generate missing" charged you and delivered nothing. When Retro Diffusion's queue is busy it accepts a request as an asynchronous task and answers at once with a task id instead of an image. The server read that as "returned no image", marked the sprite failed and moved on to the next one — twelve requests in three seconds, all charged, all finished a minute later on Retro Diffusion's side, none fetched. The server now recognises a task id, polls the task every five seconds (up to seven minutes), and then fetches the retained image.
- Recover. Retro Diffusion keeps each finished image for a day. A Recover panel on the Sprites tab lists the account's recent finished generations with a preview of each; pick which sprite it is from a list (missing ones first) and Claim puts it in the bucket — no new generation, no new charge. For the batch that went astray that is the gate, the three terrain tiles and the eight people.
GET /api/admin/sprites/recent,/sprites/preview/:id,POST /api/admin/sprites/claim.
0.20.1 — 2026-10-15
Textures back; sprite generation runs in the background. No migration.
- Tile textures were painted over. 0.19's terrain overlay was inserted between the "draw the sprite" line and the "else paint a flat colour" line, so the flat colour landed on top of every floor, wall and corridor texture. Monsters and classes still drew. Fixed; the browser test now checks that a texture survives rendering.
- Generate missing no longer hangs. A batch takes seconds per sprite, longer than a request may stay open behind Hostinger's proxy, so the page used to wait on a connection that had already been cut. The request now starts the batch and returns at once; the Sprites tab polls every three seconds and shows *Generating 7/25: monsters/roper — $1.26 so far*, refreshes the grid as sprites land, and reports the result (and anything that failed) when it ends. You can leave the tab; it carries on. One batch at a time.
GET /api/admin/sprites/job. - The
npcsgroup is listed in the bucket and the Sprites filter. - Self-test gains a sprites step: how many sprites the bucket lists, how many the manifest wants, whether a sprite is publicly readable (a HEAD request to one), and whether generation is enabled.
0.20.0 — 2026-10-15
Shops and people. Migration 023 (characters.social) — run it before deploying.
- Merchants with names, trades and memories. Every merchant is a named weaponsmith, armorer, alchemist or chandler whose trade leans the weekly stock, with a line of their own at the top of the shop panel. They remember you: 5% off once you have spent 100 gold with them, 10% at 500, 15% at 2,000 — and Haggle (Diplomacy, DC 15 + 2 per tier, once a week per merchant) takes a further tenth off until the stock turns over. Prices on the panel are what you pay.
- People in the safe rooms. About one square in six without a merchant has a hermit, sage, pilgrim, mapmaker, gravedigger, prospector, deserter or relic-seller sitting in its first chamber, drawn with their name. Walk into them or press G. Diplomacy gets them talking (DC 10 + ring/8), Gather Information draws out a second and third rumour, Sense Motive tells true from false — and deserters and relic-sellers do lie, turning directions around. Ten gold buys one rumour from anyone. Rumours are real: the nearest lair and its vault, a Warden's bearing, the nearest merchant, the make of the dungeon one square over, the gate. What you were told is remembered and repeated, never re-rolled.
- Three new skills — Diplomacy, Gather Information, Sense Motive (25 in all) — on the SRD classes that have them.
- Four achievements: Good Company (five people talked to), Silver Tongue (five haggles won), Regular Custom (2,000 gold with one merchant), Not Born Yesterday (hidden: see through a liar). 80 in all.
- Arriving in a square names the merchant or the person in its first chamber. New action
talk({pay:true}to pay);POST /api/game/haggle;shop.merchant/discount/loyalty/haggleandsquare.npcin the state. - Sprite manifest:
npcs/<role>andtiles/pit|water|rubble, for the admin Sprites tab to generate.
0.19.0 — 2026-10-15
Every skill does something. No migration (skills are ranks in characters.skills; terrain is generated).
- Eleven new skills: Appraise, Use Magic Device, Knowledge (dungeoneering), Climb, Jump, Swim, Tumble, Escape Artist, Concentration, Intimidate, Bluff — 22 in all — and class skill lists now follow the SRD. Nothing already allocated is invalidated.
- The three that were only descriptions now work. Spot: thirteen ambushers (giant and phase spiders, darkmantles, cloakers, ropers, assassin vines, gricks, gelatinous cubes, grey oozes, carrion worms, stirges, shadows, chuuls) start hidden and off the map; Spot reveals one in view, otherwise it springs on whoever walks up to it with a surprise blow. Listen: entering a room, you may hear what waits in rooms nearby; the log gives a direction and a
?marks the room on the map until you see it. Spellcraft: magic found in the dungeon is unidentified until a Spellcraft check on pickup or after a rest, or a merchant's fee (25 gp + 25 per plus); until then it gives no bonuses and shows as "(unidentified)" with an Identify button at a merchant. - Terrain. Caves have chasms, cisterns have pools, halls and catacombs have rubble, drawn over the floor on the map and the level map. Jump clears a chasm (fall in on a failure; Climb out), Swim crosses water (armor drags), Climb crosses rubble. Terrain lies only on a room's own floor, leaves a way around, and never cuts a room off; monsters never cross it, which makes a pool or a chasm a place to fight from.
- In the fight. Intimidate (I) shakes an adjacent foe; Feint (N) sets up a flat-footed blow; Escape Artist (U) wrenches free of paralysis; Tumble avoids the parting blow on a step away; Concentration is rolled when you cast with a foe at your side — fail and the spell is lost; Use Magic Device lets anyone with ranks try wands and scrolls; Knowledge gates what
looktells you about a monster; Appraise raises what merchants pay. - Tiles are now sent one base-36 character each so tile values above 9 fit. Monster view carries
shaken,feinted,known; the state carriessquare.heard. New actionsescape,intimidate,feint;POST /api/game/identify. - Tests: terrain placement and reachability across 400 squares, every new skill mechanic, veiled loot and the merchant, Appraise at the counter.
0.18.0 — 2026-10-14
The map is remembered. Migration 022 (explored table) — run it before deploying; without it the game plays as before and simply forgets the map again.
- Explored tiles survive leaving and returning. Until now the fog of war lived only in the browser tab: your position, hit points, gear, the state of every square (what you killed, what you opened) and your discovered doors were all saved, but what you had seen of each level was not, so coming back meant a dark map around you. Each square's seen-bitmap (2 KB) is now sent to the server a few seconds after it changes, when you leave the square, and when the tab goes to the background; the server merges it with what it already has, and hands it back whenever it sends a square's tiles. Per character, so two characters in the same world each remember their own exploring.
PUT /api/game/explored;square.exploredin the state.
0.17.12 — 2026-10-14
An Achievements tab in the admin console. No migration.
- Achievements tab. Tiles for the catalogue size, how many have been awarded, how many players hold at least one (of all non-guest accounts), the average per player, the most held by one player, and how many have never been earned. Then every achievement with its group, description, hidden flag, how many accounts have it (and what share of players), and who earned it first and most recently with the character they were playing; filter by group or to the never-earned ones, and click a row for the complete list of earners in order. Below: the ten most decorated players and a feed of the last fifty awards.
GET /api/admin/achievements,GET /api/admin/achievements/:key. - The players' side was already there since 0.16 (the profile page shows all 76, hidden ones as ???); this is the operator's view of the same data.
0.17.11 — 2026-10-14
Admin Subscriptions tab loads again. No migration.
- "Could not find a relationship between 'subscriptions' and 'profiles'." The tab asked Supabase to embed each subscription's profile, which only works when a foreign key links the two tables; both point at
auth.usersinstead, so the query had never worked on a real database (the in-memory test database was more forgiving). The list now reads the rows and the profiles separately and joins them in code; search by name, username or email still works.
0.17.10 — 2026-10-13
Ranged attacks read as shots. No migration.
- "You swing at the kobold with your light crossbow" is now "You shoot at the kobold with your light crossbow", and a hit reads Your bolt from your light crossbow strikes the kobold for 7 (arrow for bows, stone for a sling). The attack itself was already resolved as a ranged attack — Dexterity to hit, the weapon's range, the Point Blank / Precise / Rapid Shot feats — only the wording came from the melee path. Blind shots and shots through an incorporeal foe use the same words.
0.17.9 — 2026-10-13
Complimentary Hero from the Players tab. No migration.
- Hero button on every player row. Asks for a number of days,
forever(a hundred years), or 0 to take it away, and grants the Hero plan with no Stripe customer and no charge. It is the same complimentary mechanism the Subscriptions tab has had (Grant Hero days by username, +30 days per row), now reachable where you are looking at the player; that tab's input acceptsforevertoo, and the old ten-year cap is gone.
0.17.8 — 2026-10-13
A live/test switch for Stripe in the admin console. No migration (uses the settings table from 020).
- Two sets of Stripe variables.
STRIPE_TEST_SECRET_KEY,STRIPE_TEST_WEBHOOK_SECRET,STRIPE_TEST_PRICE_MONTHLYandSTRIPE_TEST_PRICE_YEARLYsit beside the liveSTRIPE_*set. The Subscriptions tab gains a Payment gateway panel with Live / Test radios showing what each set has configured; switching takes effect immediately, no restart, and is remembered across restarts. The price and subscription caches are dropped on a switch. - Test mode is safe for players. While test is selected, non-admin players see *Subscriptions are briefly offline for maintenance* and checkout refuses them (test keys decline real cards). Admins see the Become a Hero button regardless of their tier, with a banner saying test mode is on and which card to use.
- One webhook URL for both modes. The signature is checked against whichever secrets are configured, so the test-mode and live-mode endpoints in Stripe can both point at
/api/billing/webhook; every verified event is processed. - Self-test and the Subscriptions note now say which set is in use.
0.17.7 — 2026-10-13
Stripe API version. No migration.
- Checkout works on accounts with Managed Payments. Stripe refused to open Checkout — *Managed Payments is not supported on API version 2024-06-20* — because the server pinned that version on every request. It now pins
2025-03-31.basil, the first version such accounts accept. The server already reads the period end from either the subscription or its items, which is the one change in that version that touches the game, so nothing else moves.
0.17.6 — 2026-10-13
A Stripe step in the admin self-test. No migration.
- Run self-test → stripe reports what the server sees: the key's mode (live/test) and kind (restricted/secret), whether the webhook secret and
APP_URLare set, and for eachSTRIPE_PRICE_*id either the price Stripe returned (livemode, active, amount, interval) or Stripe's error. It fails loudly when a price is missing, archived, not recurring, or from the other mode than the key, and it clears the price cache so the Hero page re-reads straight after you fix a variable. Also checks that the key can read customers. The key itself is never shown. - The Subscriptions tab shows the error instead of staying blank when its data cannot be loaded.
0.17.5 — 2026-10-13
The Hero page says why it has no plan to offer. No migration.
- "You are on the free Adventurer plan" with no button happened whenever Stripe could not return the prices — a test-mode
price_…id with a live key, a restricted key without Products read, or a price archived in Stripe — and, worse, the empty answer was cached for an hour, so fixing the variables looked like it had not worked. The page now shows *Plans cannot be offered right now* (admins see Stripe's reason after the dash), the admin Subscriptions tab shows the same, and a failed lookup is retried after thirty seconds instead of an hour. A missingSTRIPE_PRICE_MONTHLYis reported the same way.
0.17.4 — 2026-10-13
Admin account deletion and a full guest purge. No migration.
- Delete account on every non-admin player row (type DELETE to confirm): removes the auth user, so the profile, characters, inventory, subscription row and everything else go with it. Admin accounts must have admin removed first; your own account is deleted from your profile page, not here.
POST /api/admin/delete-account. - Purge all guests next to Purge now: deletes every guest account at once, including trials still in progress (those players are signed out and lose the sample fighter), after a confirmation that says how many. Purge now still honours the chosen delay.
POST /api/admin/guest-purge {run:true, all:true}.
0.17.3 — 2026-10-13
Switching Stripe from test to live no longer strands the accounts that subscribed in test mode. No migration.
- Records from the other Stripe mode reset themselves. When Stripe answers *No such customer/subscription … a similar object exists in test mode*, the server clears that player's Stripe ids (status back to none, complimentary days kept) and tells them: That plan is no longer on file with Stripe … Subscribe again from this page if you want Hero. Checkout checks the stored customer first and starts a fresh one if Stripe no longer knows it, so "Become a Hero" works straight away for those accounts instead of failing on a dead customer id.
- Admin → Subscriptions → Unlink Stripe does the same on demand for any row with a customer id (
POST /api/admin/unlink-stripe).
0.17.2 — 2026-10-13
Billing errors say what happened. No migration.
- Stripe's own message reaches the player. A failed Stripe call used to come back as a generic "Something broke on our side" (it was a 502 and the error handler hid everything above 500). It now reads Stripe said: … with Stripe's wording, so a missing portal, a wrong key or a subscription that no longer exists is obvious at a glance. Admins still get the stack hint on real server errors.
- Cancel repairs an incomplete subscription row. A Hero whose row has the customer but no subscription id (checkout done before 0.16.4, or the
customer.subscription.*webhooks never arrived) can still cancel: the server asks Stripe for the customer's live subscription, stores it, then applies the change. - Manage billing without the portal activated explains that the customer portal must be switched on in the Stripe dashboard and that cancelling still works from the page, instead of failing with a bare error.
0.17.1 — 2026-10-13
Client addresses for abuse control. Migration 021 (profiles.signup_ip/last_ip/last_seen_at, ip_blocks) — run it before deploying; without it nothing is recorded or blocked and the cap is not enforced.
- Addresses recorded. Guest trials and sign-ups store the address they came from; every authenticated request refreshes the last-seen address and time (at most once a minute per account). The admin Players tab shows "from" and "last" addresses on each row; clicking one searches for every account seen from it, and the search box accepts an address.
- Trials per address. A second radio row in the guest panel caps guest trials per IP per day — 1, 2, 3, 5, 10 or no cap (default 3). Over the cap, "Try it now" answers *Too many trials from this address today. Create an account to keep playing.* Sign-ups are not capped.
- Block IP. Each player row has a Block IP button (with an optional reason); a blocked address can start no guest trial and make no sign-up, but accounts that already exist keep working — a shared address should not lock out a household, and the ban button is there for the accounts themselves. The guest panel lists blocked addresses with Unblock.
GET /api/admin/ip/:ipreports the accounts behind an address;POST/DELETE /api/admin/ip-blocks. - Integration test: the cap (third trial refused, sign-up allowed, another address allowed, cap off), block/unblock, the address report, and IPv4-mapped address normalisation.
0.17.0 — 2026-10-13
Guest purge control and plan-locked characters. Migration 020 (settings table) — run it before deploying; without it the purge still runs at the 24-hour default and the admin panel says the setting cannot be saved.
- Guest purge delay (admin). The top of the Players tab has a radio row — 4, 8, 12 or 24 hours — for how long a guest account lingers after its ten-minute trial before the server deletes it (auth user, profile and sample fighter). The purge now runs on a timer (thirty seconds after boot and every fifteen minutes) as well as when a new guest starts, and Purge now runs it on demand. The panel shows guests on the books, how many the next run will remove, and the last run. Guest accounts are flagged in the player list, with "trial over" once their ten minutes are up.
GET/POST /api/admin/guest-purge;src/settings.jsis a small cached key/value store for future runtime settings. - Characters wait for the Hero plan when a subscription lapses. The Adventurer limits used to apply only when creating things, so a lapsed Hero could keep playing twelve characters. Now the two oldest unretired characters stay playable and the rest are locked: the profile shows them with a 🔒 Hero plan button instead of Play/Travel, and loading one directly answers with
needs_hero(the client opens the plan page). Nothing is deleted; everything unlocks the moment the account is a Hero again, including through complimentary days.GET /api/charactersand the profile list carrylocked. - Integration test: locked characters on the free plan and the unlock; guest purge delay, counts and a purge against the fake database (which now has
auth.admin.deleteUser).
0.16.4 — 2026-10-13
Two fixes from the first Stripe test run. No migration.
- Duplicate emails are caught before the account is touched. "Keep this character" (guest → account) and sign-up now check whether another account already uses the email and answer *That email already has an account. Sign in with it instead, or use a different email.* Supabase's admin update does no duplicate check of its own, so a clash used to surface as a bare "Error updating user"; if it ever gets that far again, the same message is shown instead of the raw error.
- Cancel from the game. The Hero page shows Cancel my plan (with a confirmation that says exactly what changes) and Resume my plan while a subscription exists; both call the new
POST /api/billing/cancel, which setscancel_at_period_endon the Stripe subscription. Hero lasts to the end of the paid period either way. Manage billing (the Stripe portal, for receipts and card changes) now shows whenever a subscription exists rather than only once a period end was known. - Newer Stripe API versions. Since Stripe API 2025-03,
current_period_endlives on the subscription items, not the subscription; webhooks from an account on a new default version therefore never gave the game a renewal date, which hid "Manage billing" and the "renews on" line. The server reads either shape, andcheckout.session.completednow fetches the subscription itself so the renewal date is known as soon as checkout finishes. - Integration test: the fake DB understands
ilike; new billing block (duplicate email, items-level period end, cancel/resume round-trip through a mocked Stripe).
0.16.3 — 2026-10-13
A difficulty switch for new worlds. Migration 019 (worlds.difficulty, characters.found) — run it before deploying.
- Lightless worlds. The private-world form has a difficulty next to the mode: Standard (as before: the maze map shows every door around you and the Core Gate is marked from the start) or Lightless: the maze map shows a door only after your character has seen it from inside a square or walked through it, and the Core Gate is unmarked — and not even drawn — until you have stood where you can see it. Discoveries are kept on the character on the server, so they survive sessions and cannot be peeked at from the client; walking through a door finds it on both sides, and what you can see from where you stand is checked on every action and on login. Deeper (+N) dungeons inherit the parent's difficulty. The world list and the HUD mark lightless worlds.
0.16.2 — 2026-10-13
Phones. No migration.
- The direction pad is now an overlay on the map — bottom-right corner, translucent, with the tiles showing through — instead of sitting below the log. On narrow screens the map is a little taller (22 rows) and the camera keeps your character up and to the left of centre, so the pad never covers you. The pad toggle in the action bar still hides it; hold a direction to keep walking as before.
0.16.1 — 2026-10-13
Maps panel. No migration.
- The two maps are stacked instead of side by side, and This level now fills the panel's width — about twice its old size. A Zoom row beneath it (1×, 2×, 4×, 8×; the mouse wheel over the map steps it too) magnifies the level centred on you; the gold frame is still what the main map shows. The zoom is remembered on the device. The Core Gate shows on the level map as a gold mark. The maze around you is unchanged.
0.16.0 — 2026-10-13
The GigaDungeon has an ending: the Core Gate. Migration 018 (keys and retirement on characters, deeper worlds, gate passages, achievements, the bump_stats counter function) — run it before deploying.
- The Core Gate. The centre square — where everyone begins — holds a sealed gate of black iron with four keyholes, in its first chamber, visible from the first minute. Four Wardens hold the keys: hardened, named CR-20 bosses at the four edges of the world — an old red dragon at the far north, a pit fiend east, a balor south, a nightwalker west — each in its seat with a vault beside it. Slaying a Warden hands its key to everyone standing in the square. With four keys the gate opens for that character; they are 128 rings from it, and the dungeon knows what they carry (rests are broken far more often). Stepping through is the win: a gate passage is recorded, the account's legend counts an escape, and the character chooses — retire with honours (sealed, name kept on the legend) or **walk back down** into a deeper dungeon.
- Deeper dungeons (+1, +2, …). The first character to pass a world's gate creates its +1 world beneath it: generated from the parent's seed, same mode and ownership, named GigaDungeon +1 (and so on), reachable only through the gate — no fresh characters, no Travel. Everything in it is harder: area CR is ×(1 + depth/4) + 2·depth, so a +1 dungeon runs from CR 2.3 at the centre to 27 at the rim and its Wardens have half again the hit points and +1 to everything per depth. Keys are cleared on the way in; find the Wardens again.
- Achievements. Seventy-six of them, judged on the server from the account's lifetime counters and the facts of each action, so they cannot be faked: descent milestones, bosses and Wardens, the keys and the gate (and passing it from a +3 dungeon), combat feats (Dragonbane, Ghostbreaker, By a Hair, Shrapnel), every class to 5/10/20, every affliction suffered, loot and gold, squads and PvP, and a set of oddities. Twenty-four are hidden until earned. They land as a line in the log the moment they happen and live on the profile in groups with dates.
- Leaderboards: Through the Gate — every finisher in order, with class, level and the depth of the dungeon they escaped. Profile: escapes on the legend line, keys and gate state on each character, ✦ Escaped on retired ones.
- Fix: the Perform skill showed an undefined modifier on the sheet (0.15); skills are now derived from the table, so any skill added later is covered.
- Sprites:
tiles/gateis in the manifest (generate it from the Sprites tab); until then the gate draws as a gold Ω.
0.15.0 — 2026-10-13
The deep dungeon: the rim is CR 20, every ring from 60 out has a named boss guarding a vault, and the game keeps leaderboards. Migration 017 (boss kills, ring firsts, a bosses counter on the legend; it replaces the add_renown function) — run it before deploying.
- CR curve to 20. Rings 0–64 are exactly as before (CR 1/4 to 5); from there the curve bends up to CR 10 at ring 96 and 20 at the rim. Loot tiers, merchant stock, trap DCs and chest gold all follow the area CR as they did.
- 22 new monsters, CR 10–20 (110 in all, 16 families). Clay, stone and iron golems; adult black, green, blue and red dragons and an old red dragon; hezrou, glabrezu, nalfeshnee, marilith and balor; ice devil, horned devil and pit fiend; lich and nightwalker; storm giant; roper, purple worm and a twelve-headed hydra. Two new specials: breath weapons (a cone over everyone within six tiles, Reflex — or Fortitude/Will — for half, then a few turns to recharge: all the adult dragons, the iron golem's poison gas, the ice devil's frost, the lich's wail, the storm giant's lightning) and the balor's death burst (10d6 fire on everyone within three tiles when it dies, Reflex half). The carrion crawler is now the carrion worm (the old name is Product Identity, not OGL).
- Bosses and vaults. From ring 60 outward one square per ring — hashed from the world seed, so it is the same lair for everyone in that world — holds a named boss: a monster from the ring's CR band with double hit points, +2 attack and AC, +2 damage and at least DR 5, with a seeded name like Ysolde, Queen of Ash or Kragmaw the Unburied. It lairs in the room farthest from the entrance, which is otherwise cleared, beside the ring's vault: three chests with 150–300 gold × (1 + CR), a potion and a guaranteed item rolled a tier deeper than the ring. Killing it always drops an item too. A slain boss and its emptied vault stay down for seven days, whoever comes by; everything else in the square respawns on the usual clock. Entering a lair square announces it; the HUD names the lair; the boss is boxed in gold on the map and named in Look.
- Leaderboards. A new Leaderboards page in the account menu: deepest ring, bosses slain, monsters slain and renown across all accounts (top ten, your own row highlighted), and for the world you are in, the first character to reach each ring (10 and out), each lair cleared with who got there first and how many times it has fallen, and the world's boss hunters. Boss kills credit everyone standing in the square; the legend counts them.
- Admin dashboard: total boss kills and the latest five. Sprites: prompts for all 22 new monsters are in the manifest (the Sprites tab generates them with
RETRO_DIFFUSION_KEY). - Fix: an animal companion standing on the far side of its druid now circles round to reach a foe at the druid's side instead of waiting for it to come.
0.14.0 — 2026-10-12
The game has a face and a voice: a full set of Retro Diffusion pixel sprites and synthesised sound. Migration 016 (the sprites storage bucket and its policies) — run it before deploying; then, once the sprite set is in the bucket, run supabase/lock-sprites.sql so only the server can write to it.
- 170 sprites, 32×32, one consistent top-down style (RD Pro
rd_pro__topdown, backgrounds removed): the eleven classes, all 88 monsters, 43 items (every weapon, armor, shield, ring, amulet, cloak, helm, glove, boot and belt, plus potion, scroll, wand and gold), 16 seamless floor/wall/corridor/exit tiles for the four level styles, chest, open chest and trap, and nine effect sprites (hit, miss, fire, cold, lightning, holy, heal, sleep, arrow). They live in the public Supabase bucketsprites/<group>/<key>.png; the whole set was generated and uploaded for this ship. - The renderer draws images. Tiles are drawn from the sheet and tinted by depth (hue and brightness drift with the ring), monsters, players, companions and chests are drawn as sprites, and anything missing from the bucket falls back to the old glyph — a half-finished or custom set is fine.
GET /api/spritestells the client where the bucket is and which files exist. The camera window is 40×25 tiles (24 px at 960 px wide; 32×32 sprites scale cleanly). - Sound.
public/audio.jssynthesises everything with WebAudio — no audio files to host. Hits, misses, kills, being hurt, death, spells by school, doors, chests, traps, arrows, sleep and level-ups, and a looping ambience that changes with the level style (halls, caves, catacombs, cistern). "Sound: on/off" in the account menu; the choice is remembered on the device. - Admin › Sprites. Shows the whole set with what exists in the bucket, and with
RETRO_DIFFUSION_KEYset in hPanel lets an admin generate a missing sprite or redo one ($0.18 each, charged to your RD balance; the key never leaves the server).src/sprites/manifest.jsholds every entry with its prompt, so new monsters or items get art the same way. - Config:
RETRO_DIFFUSION_KEY(optional).
0.13.0 — 2026-10-12
Druid and bard: all eleven SRD base classes are in. Migration 015 (animal companion on the character; the two classes in the class constraint) — run it before deploying.
- Druid (d8, medium BAB, Wisdom casting, prepares). Own spell list to level 5 — entangle, produce flame, shillelagh, barkskin, call lightning, plus the cures, bull's strength, bear's endurance, lesser restoration, neutralize poison, remove disease, flame strike, ice storm, baleful polymorph and stoneskin. Animal companion: a wolf from level 1 (black bear at 4, dire wolf at 7, brown bear at 10, dire bear at 13) that stays at your side, bites anything adjacent that is after you, is targeted by monsters like any squad-mate, and if it falls, another answers your call after a rest. Wild shape from level 5 (1/day, up to 6/day at 18): wolf, black bear, dire wolf, brown bear or dire bear — the beast's Strength, Dexterity, Constitution, natural armor and natural attacks replace your own; armor and weapons are set aside and you cannot cast until you change back. Leather or studded only; no steel shields.
- Bard (d6, medium BAB, Charisma casting, spontaneous). Learns a few spells per level like a sorcerer from a list that mixes cures with arcane tricks (sleep, shield, false life, haste, dimension door, greater invisibility, hold monster, mass cure light, phantasmal killer); a listed "0" in the bard's slot table still earns bonus slots from Charisma, as the SRD says. Inspire courage (needs 3 ranks of Perform — a new skill — and a use per level per day): +1 to attacks and damage for everyone in the square with you (+2 at 8, +3 at 14), ten turns, and it lifts fear.
- Companions appear on the map in green and in the HUD with their hit points; Look names them.
0.12.0 — 2026-10-12
Feats wave two, ranged weapons, weapon and armor special abilities, metamagic. No migration.
- Twenty-five new feats (40 in all). Improved Initiative, Combat Expertise, Mobility, Spring Attack, Whirlwind Attack, Blind-Fight; the Two-Weapon chain (Two-Weapon, Improved, Greater — any class may now fight with a weapon in each hand via the new Off hand button; rangers keep it free); Point Blank Shot, Precise Shot, Rapid Shot, Far Shot; Lightning Reflexes (and Iron Will and Great Fortitude now actually apply); Alertness, Stealthy, Investigator, Nimble Fingers, Self-Sufficient, Track (non-rangers track as well as rangers); Greater Spell Focus, Improved Turning, Extra Turning; and metamagic — Empower, Maximize, Extend. Prerequisites now include Int, a casting class and a turning class.
- Ranged weapons. Sling, shortbow, light and heavy crossbows, longbow (plus handaxe and rapier). The Shoot action (T, or the new bar icon) fires at a foe in range; walking into a foe with a bow fires point-blank; shooting into a melee costs −4 unless you have Precise Shot. Bows add Dexterity to hit and no Strength to damage; the HUD shows the ranged numbers while a bow is in hand.
- Parting blows. A foe you step away from gets one swing at you (the SRD's attack of opportunity). Mobility gives +4 AC against it; Spring Attack avoids it entirely. Shriekers, sleepers and held foes don't get one.
- Weapon and armor special abilities. Flaming, frost and shock (+1d6), keen (threat range doubled), holy (+2d6 against undead, outsiders, goblinoids and aberrations) and bane (+2d6 against one family), on weapons; light fortification (one blow in four halved), shadow and silent moves on armor. They appear on enhanced loot from tier 2 on; enhancements now climb to +3 at tier 3 (CR 6–9) and +4/+5 at the new tier 4 (CR 10+). Names read as the SRD does: "+2 holy longsword", "+1 undead-bane battleaxe".
- Metamagic. Toggle Empowered (×1.5 damage and healing, +2 slot levels), Maximized (every die at maximum, +3) or Extended (double duration, +1) in the spellbook before casting. Wizards and clerics prepare the metamagic version in the higher slot; sorcerers apply it on the fly.
- Removed the placeholder Combat Casting feat (it never did anything; characters who took it keep the slot filled).
0.11.5 — 2026-10-12
Patch: boot hardening. No migration.
/healthzalways answers 200 (the schema state is in the body); a 503 there could make a hosting platform's health probe take the app out of rotation.- The in-memory test database is imported statically instead of through a top-level
await, so the module graph has no asynchronous module at startup.
0.11.4 — 2026-10-12
Patch: the "Something broke on our side" bug. No migration.
- Every action since 0.10.0 failed with
Cannot read properties of null (reading 'length')atgame.js:377. The consumable bookkeeping added in 0.10.0 inserted two promises at the front of the save batch, which shifted the loot result out of its slot; the code then read.lengthon the "nothing consumed" null. Movement, waiting, resting, casting — everything that went through an action — broke. Fixed, and the loot slot is now read defensively. - New
npm run integration: the whole server-side loop (load, state, single and batched moves, rest, prepare, cast, potions, wands, equip, sell, feats, skills, the PvP arena) runs against an in-memory stand-in for Supabase. It fails on exactly this class of bug, and it is now part ofnpm run checkso no build ships without it.
0.11.3 — 2026-10-12
Patch: find the 500. No migration.
- Admins now see the real error inside "Something broke on our side." (message and the first stack frames), so a failing request can be reported verbatim. Players still get the plain message.
- Admin console: Run self-test walks the player code paths as you — schema, profile row, plan, legend, worlds, your characters and each one's full game state — and prints which step fails and why. The server log also tags every 500 with the method and path.
0.11.2 — 2026-10-12
Patch: admin world controls. No migration.
- Worlds tab: Reset (clear every visited square so monsters, chests and traps return; end fights and squads; every character back on their own spawn at full health), Regenerate (reset plus a brand-new maze from a fresh seed, with new spawn squares for everyone), Wipe characters (delete every character in the world; confirmed by typing the world's name) and Delete (private worlds only, typed confirmation; members, invites, characters, squares, squads and logs cascade). The public GigaDungeon can be reset or regenerated but not deleted. The tab also shows each world's mode (editable), owner and visited-square count. Endpoints:
POST /api/admin/world/reset,DELETE /api/admin/world/:id.
0.11.1 — 2026-10-12
Patch: a schema doctor. No migration.
- On boot and every ten minutes the server probes one marker per migration. If the database is behind the code, the server log says exactly which migrations are missing,
/healthzanswers 503 with the list, and the player-facing "Something broke on our side." gains " The database is missing migration 013, 014 — run the migrations SQL from this release." A deploy whose roll-up was not run is now self-explaining instead of a bare 500.
0.11.0 — 2026-10-12
Bestiary wave two and the condition system. No migration.
- Fifty more monsters (88 in all, 15 families). Elementals (air, earth, fire, water, small to huge), oozes (gelatinous cube, gray ooze, ochre jelly, black pudding), plants (shrieker, assassin vine, shambling mound), lycanthropes (wererat, werewolf, werebear), more undead (ghast, allip, vampire spawn, spectre, mohrg, devourer), the young white, green, blue and red dragons, swarms (spiders, rats, centipedes), darkmantle, stirge, cockatrice, medusa, gorgon, cloaker, otyugh, chuul, gibbering mouther, grick, imp, hell hound, bearded devil, vrock, bone devil, frost and cloud giants, dire bear, manticore, phase spider and a five-headed hydra.
- Conditions. Monsters now inflict what the SRD says they do: poison and touch attacks deal ability damage (Str, Dex, Con, Wis…), bites carry disease (filth fever, mummy rot: no natural healing and 1d3 Con lost every rest), undead drain levels (−1 to attacks, saves and skills and −5 hit points per level; as many negative levels as you have levels is death), dragons and cloakers cause fear (−2 attacks and saves), vrocks and mouthers stun, darkmantles blind (sight drops to the adjacent tiles, half your swings miss, −2 AC), and cockatrices, medusas, gorgons and basilisks petrify in two steps: the first failed save stiffens you for 20 turns, a second while stiff turns you to stone. Elementals and hell hounds burn, oozes corrode.
- Monster traits. Swarms take half weapon damage and cannot be sneak-attacked; shadows, allips, wraiths and spectres are incorporeal (half of non-magical blows pass through); grick hide turns 10 points of every blow; trolls, hydras and vampire spawn regenerate; a shrieker wails and wakes everything within 15 tiles; a hydra bites five times. Look (L) now lists each monster's traits.
- Cures. Poison, disease, energy drain and ability damage survive a rest; they are lifted by new cleric spells (lesser restoration at 2, remove disease and remove blindness at 3; neutralize poison and restoration now cure what their names say) or by the healer's care at any merchant (25 gold per level, 50 minimum). Death still resets you.
- Afflictions show in red on the HUD with their numbers; the shop lists the healer only when something ails you.
- Engine:
specialon a monster is now a generic schema (paralysis, poison, abil, disease, drain, fear, stun, blind, petrify, burn, acid, swarm, incorporeal, dr, regen, heads, alert, noAttack); wight, shadow, wraith, mummy, basilisk, the young black dragon and the troll gained the specials they were missing.
0.10.0 — 2026-10-12
Spell preparation, the sorcerer, spell levels 4–5, scrolls and wands, natural healing. Migration 014 (prepared spells and spells known on characters; the sorcerer in the class constraint) — run it before deploying.
- Preparation. Wizards and clerics now prepare spells into their slots from the spellbook drawer ("Change" under Prepared spells; takes no time, enemies must be out of sight). Casting spends a prepared copy; rest refreshes them. Anyone who has never prepared gets a sensible automatic fill, so existing characters keep casting. Clerics can turn any prepared spell into a cure of that level or lower on the spot.
- Sorcerer (d4, poor BAB, Charisma casting): the wizard list, more slots, spontaneous casting, but only a handful of spells known — chosen in the spellbook as you level, like feats. Cannot wear armor.
- Spells 4–5. Wizard: stoneskin, ice storm, fire shield, dimension door, phantasmal killer, enervation, greater invisibility, cone of cold, hold monster, baleful polymorph, cloudkill — and haste at level 3. Cleric: cure critical, divine power, neutralize poison, restoration, flame strike, slay living, righteous might, mass cure light wounds. New effects behind them: damage reduction and absorption, a retaliatory fire shield, invisibility (half of attacks miss, foes do not see you cast), an extra hasted attack, negative levels on monsters, held monsters, and a polymorph that persists in the square.
- Scrolls, wands and more potions. Potions of every self-target spell of levels 1–3 (anyone may drink), scrolls of every spell up to level 5 and wands up to level 3 (scrolls and wands need a caster with the spell on their list). Loot is 40% consumables now; merchants always stock three potions of level 1–2 plus the odd scroll or wand. Drink, Read and Use buttons in the pack.
- Rest heals naturally instead of fully:
level × (2 + Con mod) + Heal rankshit points per rest, every rest still risking a wandering monster. Spells and daily powers come back in full. Cure spells, potions and the Heal skill matter accordingly. - Legend perks Prepared and Learned now apply at character creation (they were defined but not wired in 0.9.0).
- Engine:
resolveActiongainsprepare,learnanduse; the view carries aspellbookblock; monster negative levels, holds and polymorphs persist in square deltas; monsters killed by a fire shield during their own turn are recorded.
0.9.0 — 2026-10-12
Subscriptions, the account-wide legend, world modes, and content editing from the console. Migration 013 (content overrides, subscriptions, renown and perks on profiles, worlds.mode, the add_renown RPC) — run it before deploying.
- Hero subscription (Stripe). No advertising; the Hero plan funds the game. Checkout, the billing portal and webhooks are plain HTTPS calls (no SDK); signatures are verified; events are idempotent. Free Adventurer accounts get the public world, squads, invited worlds, two characters and one legend perk; Heroes host private worlds in any mode, carry characters between worlds, hold every perk and keep twelve characters. Gated actions answer
402 needs_heroand the client opens the plan page. New environment variablesSTRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET,STRIPE_PRICE_MONTHLY,STRIPE_PRICE_YEARLY,APP_URL; with the key unset, billing is off and every account has Hero entitlements, so nothing changes until you turn it on. Admins always have Hero entitlements. A "Hero plan" page sits in the account menu and on the profile. - Legend. Renown accrues from every XP point any of your characters earns; each legend level (2000·L² renown) grants a permanent perk chosen on the profile page — Hardy, Keen, Lucky, Prepared, Learned, Vigilant, Sturdy, Deathless — that applies to every character you play in every world. Lifetime counters: monsters slain, adventurers bested, deaths, deepest ring.
- Travel. A character can move to another world you belong to from the profile page, keeping sheet, gear and feats; they arrive at that world's entrance and leave their squad behind.
- World modes. The world builder asks PvE / PvP / PvPvE, fixed at creation. PvP worlds spawn no monsters (and none wander in on a rest); in PvP and PvPvE, walking into an adventurer outside your squad attacks them under the usual d20 rules, and a kill is worth 50 XP per level and a quarter of their gold. Hostile adventurers are drawn in red; being struck or slain between your own turns shows up in your log.
- Admin: Bestiary and Archetypes tabs. Every monster's name, family, CR, HD, HP, AC, attack, damage, weapon, glyph and undead flag, and every class's name, hit die, skill points, starting abilities and description, can be edited, switched off (out of spawn pools / off the create screen) or reset to the SRD values. Overrides live in
content_overridesand apply in place without a redeploy. - Admin: Subscriptions tab. Active / canceled / complimentary counts, the live prices, a searchable subscriber list, and complimentary Hero days by username. The stats row shows Hero subscribers.
- Engine:
derive()takes legend perks;resolveAction()takespvpandmonstersflags; PvP deaths merge with monster-turn deaths; the smoke test covers the legend curve, perk bonuses, a duel, squad safety and PvE safety.
0.8.0 — 2026-10-12
Four more classes and a bigger bestiary. Migration 012 (class constraint, ranger favored enemy; run before deploying).
- Barbarian (d12, full BAB, greataxe): Rage — +4 Str and Con, +2 Will, −2 AC for 3 + Con-mod turns, then 10 turns of fatigue (−2 Str and Dex). Rages per day grow with level. Temporary hit points from the Con boost come and go with the rage.
- Paladin (d10, full BAB, sword and shield): Smite — one attack adds Charisma to hit and level to damage; Lay on hands — a daily healing pool of Cha-mod × level; Divine grace — Charisma to all saves from level 2; a short divine spell list (bless, cure light/moderate, shield of faith, bull's strength) and turning from level 4.
- Ranger (d8, full BAB, two swords): fights with both weapons (off-hand at −2 with a light weapon); chooses a favored enemy family once, for +2 damage rising with level; Track reads nearby rooms. Survival is a new skill; anyone with ranks can Track at a harder DC.
- Monk (d8, medium BAB, no kit): unarmed strikes 1d6 → 2d10 with level; flurry of blows adds a second strike; Wisdom and level add to AC when unarmored; good saves all round; evasion from level 2.
- Bestiary: 38 monsters, up from 19, each tagged with a family (humanoid, goblinoid, undead, giant, animal, vermin, magical beast, monstrous, aberration, dragon). New: dire rat, wolf, worg, dire wolf, black and brown bear, giant spider, giant scorpion, carrion crawler, lizardfolk, troglodyte, shadow, wraith, mummy, gargoyle, basilisk, chimera, young black dragon, ogre mage.
- Action bar icons for rage, smite, lay on hands, track and favored enemy; a themed picker for the favored family; the HUD shows extra attacks.
- Fixed the real stutter. The action route was not forwarding
steps, so every multi-step move since 0.7.0 reached the server as a single wait: the client drew three steps, the server moved none, and the reconcile snapped the @ back. Steps are forwarded now and verified end to end. ROADMAP.md: the shipment plan from here to 1.0, with what the SRD still offers.
0.7.3 — 2026-10-12
Stutter, round two. Three real causes, all fixed; no migration.
- Lock contention with yourself. The square lock was released fire-and-forget after each reply, so the next keystroke often arrived while the previous request's lock was still on the books and slept 100 ms per retry. The lock holder is now the character itself, which re-acquires its own lock instantly; other characters are still kept out.
- Batches that stopped after one step. The multi-step walk treated "a monster within nine tiles" as an event, and with up to a hundred monsters per level that was nearly every step. It now stops only when the set of monsters in actual line of sight changes, an aware monster is within two tiles, you enter a room, or the rules write a real log line.
- Prediction that switched itself off. The client refused to predict a step whenever any aware monster was visible — including one fleeing or lagging eight tiles behind — so after the first fight every step waited a full round-trip. It now predicts unless an aware monster is within two tiles, and the predicted step recomputes line of sight locally, so the lit area moves with the @ instead of lagging behind it. Queued keys are dropped only for things that matter (square change, encounter, combat/loot/room lines, an adjacent aware monster).
- The direction-pad toggle no longer logs "Unknown action", and every browser confirm/alert (sell, delete character, delete account, errors) is replaced by a themed dialog with Enter/Esc.
0.7.2 — 2026-10-12
- Touch direction pad: a 3 × 3 pad (eight directions plus wait) sits beneath the chat bar on the right. It appears automatically on touch devices (coarse pointer, no hover, or touch points), can be toggled anywhere from a new bar icon, and remembers the choice. Hold a direction to keep walking (one step every 180 ms through the same queue as the keyboard). Each press is a move; nothing changes about the rules.
- Phone layout: the camera shows 24 × 18 tiles instead of 48 × 30 so tiles stay legible; the header hides the location line and shortens the trial pill; the HUD is two columns; drawers are full-width; the keyboard hint is hidden on touch devices.
- The character sheet nudges open once per session when a feat is waiting, not on every move.
0.7.1 — 2026-10-11
- Fixed the snap-back when pressing a direction several times. A reply from an earlier request was replacing the whole view, including steps that had been drawn but not yet sent. Replies are now reconciled: the server's confirmed position is taken, then the steps still waiting in the queue are redrawn on top of it. If the reply carried an event (a monster, a door, a room, anything non-trivial), the queued steps are discarded instead, since they were typed for a picture that no longer holds. Verified with a browser test: four quick presses with a 400 ms server now trace 31 → 30 → 29 → 28 → 27 with no reversal.
0.7.0 — 2026-10-11
Faster movement. No migration.
- Server: an action used to make eight to ten database round-trips in a row (character, world, inventory, feats, squad, lock, square state, party, save, save). Now the world (60 s) and the character's inventory and feats (30 s, invalidated on any change) are cached in memory, generated squares are cached before the overlay, the lock is acquired in parallel with the square loads (the version check on save still catches a stale read), and the saves run in parallel. The serial chain is about three round-trips.
- Multi-step moves:
POST /api/game/actionwithaction: "move", steps: [{dx,dy}, …](up to 12) walks several tiles in one request and stops at the first event — a monster noticed, a door, a chest, a trap, a new room, anything that writes a non-system log line. Not used during encounters. - Client: keys are no longer dropped while a request is in flight; they queue (up to 8) and go out as one multi-step request. Plain floor steps are drawn immediately, before the server answers, and the server's reply replaces the guess. Nothing is predicted next to monsters, exits, chests, traps or other players.
- Tip for the biggest remaining factor: put the Supabase project in the same region as the Hostinger server. Every round-trip is a network hop between them; same-region is ~5 ms, cross-continent ~100 ms.
0.6.1 — 2026-10-11
- A first visit opens straight into the trial: no sign-in screen, no button. The page shows a one-line splash while the fighter is rolled up, then the dungeon. The sign-in screen is reachable from the guest's account menu ("Sign in to an existing account"), from the upgrade dialog ("I already have an account"), or at
/#signin; a browser that has already had a trial lands on the sign-in screen, which still offers the Try it button. - Fixed two client errors introduced in 0.6.0 (a missing button handler and a drawer variable used before it was declared) that could leave the page on the splash.
0.6.0 — 2026-10-11
Try it now. Migration 011 (guest columns; run before deploying). Supabase: turn on Authentication → Sign In / Providers → Allow anonymous sign-ins, or the button answers "Guest play is not enabled on this server yet."
- A Try it now — ten minutes, no account button on the sign-in screen. One click creates an anonymous Supabase user, a guest profile (
Guest XXXXXX,@guest_xxxxxx) and a ready-made character in the public world: a level-3 fighter with a +1 longsword, chain shirt, heavy shield, ring of protection, five potions, 120 gold, Weapon Focus, Power Attack and Toughness. The visitor lands in the dungeon straight away. - A trial clock in the header counts down ten minutes (red for the last two). When it reaches zero the server refuses further play (
403, codetrial_over) and the client shows the upgrade dialog, which cannot be dismissed. A Keep this character button beside the clock, and an entry in the account menu, open the same dialog early. - Upgrading keeps the character.
POST /api/auth/upgradeturns the anonymous user into a real account (email, password, username, profile name) with the service role, flips the profile out of guest mode, and renames "Sample Adventurer" to the new profile name. Nothing is copied or lost. - Guests cannot create worlds or squads or change profile names. Guests whose trial ended more than a day ago are deleted (20 per new trial), which cascades their character and inventory.
0.5.1 — 2026-10-11
- Admins can take staff names (DM, DungeonMaster…) for their own account: the reserved-name check is skipped for admins editing their own profile, so the super admin's names save. Uniqueness still applies. A failed save on the profile page now also raises an alert, not only the small note beside the button.
- "Delete this character" in the account menu rendered red-on-red; it is red text on the menu background again.
- Admin console has the same header (account pill with Back to the dungeon, Profile, Sign out) and footer as the game, signs in by username / profile name / email, and the game's account menu shows an Admin console entry only for admins (the path comes from the profile API, so it is never sent to non-admins).
0.5.0 — 2026-10-11
Reserved and blocked names. Migration 010 (creates and seeds reserved_names; run before deploying).
- Usernames and profile names are checked against
reserved_namesat sign-up and on every change. Matching folds case, separators and leetspeak, soD-M,d_mandDungeonMa5terall hitdungeonmaster. Rules are exact / prefix / contains, scoped to usernames, profile names or both, with an optional owner: the one account allowed to use the name. - Seeded with staff and system impersonation terms (DM, DungeonMaster, GM, admin, moderator, support, official, system, root, bot, GigaDungeon…) and a starter set of hate, sexual-violence, child-safety, harassment and profanity terms. Ethnic and other slurs are deliberately not shipped as a word list; add your community's set from the console, where it lives as data rather than in the code.
- Admin console → Names tab: list, add/update, remove rules; a tester that shows how a name normalises and whether it is blocked; an audit of existing accounts whose names match a rule, with a one-click "make owner" for the legitimate holder (use it on the site DM's account for
dmanddungeonmaster). - Existing accounts are never renamed automatically; a reserved name already in use stays until the holder changes it or an admin acts.
0.4.0 — 2026-10-11
World is 256 × 256. Migration 009 — resets every world's explored state; run it before deploying.
- The maze is 256 × 256 squares (65,536 levels of 128 × 128 tiles: 2^30 tiles, 32,768 on a side), centre at (128, 128). Coordinates 0–255 fit a byte and every dimension is a power of two.
- Because the maze is generated from the centre, every existing world's layout changes. Migration 009 truncates
square_deltasandsquare_locks(monsters, chests, traps, encounters). Characters keep levels, gear, gold and squads; anyone whose tile is now inside a wall is moved to the arrival room of their square on next load, and the server now does that check on every load as a general safety net. - Difficulty curve unchanged in shape: CR ¼ at the centre rising to about 10 at the edge.
0.3.0 — 2026-10-11
Accounts. Migration 008 (usernames; run it before deploying).
- Sign in with a username, profile name or email plus password. The lookup and password check happen on the server (
POST /api/auth/login), so the browser never learns which email a name belongs to; only session tokens come back. Sign-up asks for email, username (lowercase handle, unique), profile name (unique, case-insensitive) and password, with live availability checks. "Forgot password" works from any identifier and always answers the same way; the reset link lands on the sign-in screen's new-password form. - Profile page replaces the modal: profile name and username, email change (confirmation links), password change, every character across every world with Play and Delete, account stats (characters, total XP, deepest ring), and a delete-account section that requires the username typed back. Accounts created before 0.3.0 are asked for a username and profile name on their next sign-in.
- Header pill shows the profile name only (the menu shows @username).
- Character names are required (2–24 characters) and may repeat freely across the dungeon.
- Keys: WASD moves again (search had collided with S); search is now F, Tab opens the character sheet.
- Admin: player search matches name, username or email; list shows both names.
0.2.1 — 2026-10-11
- Migrations are now idempotent end to end: every
create policyis preceded bydrop policy if exists, so the rolled-upmigrations-<version>.sqlcan be re-run on a project that already has earlier migrations applied (the 0.2.0 roll-up failed withpolicy "own profile" already existson such a project). This is the one exception to "never edit a shipped migration": the change is mechanical and does not alter any schema.
0.2.0 — 2026-10-11
Interface rework for desktop play.
- Header with an account pill (top right): avatar, display name, dropdown with Enter the dungeon, Create a character, Delete this character, Change world, Profile, Sign out. Profile modal sets a display name (
profiles.display_name, migration 007) and can change the password. Footer with version and licence link. - Directional pad removed; movement is keyboard only (arrows / WASD / QEZC).
- Action bar under the map with SVG icons and hover tooltips: potion (with count), look, search, wait, rest, and contextual icons for open lock, disarm trap, turn undead (with attempts left), follow leader; panel toggles for character sheet (badge when a feat or skill points are pending), spellbook, squad (member count), merchant (when one is present) and maps. Keys: P L S Space R, B spellbook, M maps, Tab sheet, Esc closes.
- Panels open in a right-hand drawer, one at a time, instead of stacking in the sidebar. Squad is a drawer, not a permanent section. Spells are an icon grid; hover for the description, click to cast (auto-targets a lone foe).
DELETE /api/characters/:id,GET/PUT /api/profile./license.htmlcarries the OGL Section 15 notice with a placeholder for the full licence text.
0.1.1 — 2026-10-11
- Admin console moved out of
public/into its own directory, served at/<ADMIN_PATH>/(default/admin/). SetADMIN_PATHto something unguessable in production. Never indexed (robots.txt+X-Robots-Tag). - Optional hardening in front of the console and
/api/admin:ADMIN_GATE=user:passwordadds an HTTP basic-auth prompt before the Supabase sign-in;ADMIN_IP_ALLOWLISTrestricts who can load it at all (404 to everyone else). Both are on top of the existing Supabase JWT +is_admincheck, which remains the real authorisation. - Each ship now includes
migrations-<version>.sql, every migration rolled up for a fresh Supabase project.
0.1.0 — 2026-10-11
First deployable build.
- 255 × 255 maze of 128 × 128 levels in four styles (halls, caves, catacombs, cistern), three doors per shared wall, all regenerated from
WORLD_SEED_SECRET + world.seed; only player changes are stored. - D&D 3.5 SRD rules: fighter, rogue, cleric, wizard; 19 monsters; 30 items with 3.5 body slots plus an ear slot; 15 feats as a prerequisite tree; 9 skills; 21 spells (levels 0–3); saves; traps, locks, stealth, sneak attack, turning, rest.
- Co-op: shared squares, party-targeted monsters, split XP, encounter mode with initiative and 30 s turns, per-square lease lock, Supabase Realtime presence and chat.
- Squads of up to four with a shared spawn, follow-the-leader through doors, squad chat; public-world arrivals spawn in a random inner square.
- Respawn after six idle hours (chests after two days), merchants in one square in five, admin console at
/admin.html. - Express on Hostinger managed Node; all configuration from environment variables; Supabase migrations 001–006.