Product
The Map Is a Renderer, Not a Second Company
Muchiround Republic mounts a Three.js world at /republic — but every shop, wallet debit, and chat line is a projection of the existing platform. How we refused the second economy.

The first design meeting for a multiplayer map almost always invents a second product.
Someone says “game economy.” Someone else says “game users.” A third branch opens a second chat stack “just for proximity.” Six weeks later you are reconciling two ledgers, two auth cookies, and a support queue that cannot tell whether the customer bought land or a delivery.
We refused that path for Muchiround Republic.
The Republic is a renderer for Muchiround, not a game with a Muchiround integration.
This is the field note from locking that sentence into the decision log and the SPEC — what we project, what we forbid, and how you can apply the same cut on your own stack.
The temptation
Republic is a multiplayer Three.js world at /republic inside the zim-platform Rails + Inertia monolith. Freehold parcels. Buildings. Presence. Later, land tax and build tools. It looks like a game.
Games have habits:
| Habit | Why it feels reasonable | What it actually costs |
|---|---|---|
game_users |
Faster avatar prefs | Second identity, second ban list, second KYC story |
game_wallet |
Isolate sink math | Permanent reconciliation + fraud surface |
| In-world SKU catalogue | “Feels native” | Parallel checkout you will never finish |
| Second chat | Proximity needs special casing | Two moderation graphs, two unread models |
| Separate deployable | “Keep the monolith clean” | Auth drift, double ops, dual CI |
Every row above is a strong process in the Bennett sense we wrote about in Weakest Sufficient Beats Clever Process: it fits one imagined demo and fails the next CEO ask. The weak move is uglier on a whiteboard and cheaper forever: project existing records into space.
What “projection” means in practice
An in-world entity is not a second truth. It is a spatial view of something the platform already owns.
| In the world | On the platform |
|---|---|
| Player | Identity::User (signed-in; no game_users) |
| Shop / storefront building | Projection from Identity::BusinessProfile |
| Money you spend on claim, tax, materials | Markets::Wallet / RBX ledger only |
| Chat in district | Messaging:: (proximity later; not a second chat product) |
| Buy something from a stall | Existing Commerce:: + Inertia UI as a DOM overlay |
| Real-world courier pin | Stays on Geo::Place / logistics — not lat/lng stuffed into the Republic grid |
Republic gets new tables only for genuinely spatial facts — parcels, occupancy, pose that must survive a refresh. Ephemeral presence rides Solid Cable + short TTL cache. Durable ownership and value stay in Postgres + the event store the rest of the product already trusts.
The Cartesian district is fictional. We do not mirror Harare PostGIS into the walkable grid. Logistics already owns real geography; overloading Geo::Place with game cells would poison both products. Tenure (who owns the lot) is the bridge — not a coordinate transform.
Money: one wallet or you are lying
We already treat the money gem as the single source of truth for custom currencies and rates. Republic sinks — parcel claim, monthly property tax, Town Council materials, later ads or NPC inference — must debit that same RBX path.
Locked economics we actually wrote down (SPEC-013 era):
- Land claim = fixed RBX debit (fail-closed ledger)
- Retention = property tax (monthly), not a daily drain minigame copied from a reference video
- Materials from a Council shop first — a real sink without a survival-sim skill tree
- No second
game_wallet, no off-ledger “build points”
If the map can invent balances the wallet page cannot explain, you did not ship a world. You shipped a second company with worse audits.
UI: canvas for space, DOM for money
Commerce and identity flows already exist as Inertia/React pages. Rebuilding checkout in 3D is a year of work that teaches users nothing they do not already know on mobile.
Rule we locked: WebGL for the district; DOM overlay (portal / panel / existing routes) for buy, wallet, messaging, profile. The map frames attention. The money path stays the path we already hardened for Zimbabwe and diaspora rails — including fail-closed webhooks and one fiat process path.
Same discipline on chat: Muchiround Messaging is the text spine. Proximity and voice can layer later. A greenfield “district Discord” is how you double moderation forever.
Phasing that protects the cut
We did not “build the MMO, then integrate.” We ordered proof:
- P0 — static walkable district, render budget on mid-range Android WebGL. No durable economy tables.
- P1 — multiplayer presence (ephemeral).
- P2 — first product-shaped ship: parcels, auto-buildings from business profiles, overlay commerce + messaging.
- P3+ — editor, tax automation, terraform, ads, NPCs — only after LNR capacity and spend go.
P0 isolates the hard unknown (can this run on the phones we care about?) without inventing schema debt. P2 is the first moment a stranger would call it a product. Everything after is density, not a second GTM engine — company north star stays LNR (leads and related revenue), not land cosplay.
Build tech evolved the same way: kit-of-parts first, then free-voxel CSG when the CEO locked concrete claim/tax numbers and “build CSG now.” The constant was never “Minecraft clone.” The constant was no dual economy while the mesh gets interesting.
Forbidden list (print this)
If you are mounting a world on an existing marketplace or social product, ban these by name:
FORBIDDEN in Republic-shaped work
- game_users / game_wallets / game_orders
- second auth cookie or SSO just for the map
- second chat, AI, or moderation stack
- in-world SKU catalogue parallel to Commerce::
- WebGPU-first or a separate realtime microservice for v1
- real-city PostGIS mirror as the walkable grid
- “temporary” off-ledger balances for land or materials
Write the ban in the decision log once. Agents and humans both re-litigate less when the reject list is boring and public.
How to apply this without our stack
You do not need Rails, Three.js, or RBX. You need a prime directive and a reuse map.
- Write one sentence that forbids the second product. Put it at the top of the SPEC.
- Inventory the platform you already trust: identity, wallet/ledger, chat, checkout, moderation, realtime bus.
- For every in-world noun, fill a two-column table: world concept → existing record. Empty right column means you are inventing a company.
- Split UI by concern: spatial canvas vs money/identity DOM you already ship.
- Phase for the unknown. Prove render and presence before durable land math if the GPU budget is the real risk.
- One ledger. Sinks and sources share the wallet the user already opens on their phone.
# Shape, not our exact constants — one debit path for spatial sinks
def claim_parcel!(user:, parcel:, price_rbx:)
Markets::WalletService.debit!(
user.rb_wallet,
amount: price_rbx,
purpose: "republic_parcel_claim",
reference: parcel
)
Republic::Parcel.transaction do
parcel.lock!
raise AlreadyClaimed if parcel.owner_id.present?
parcel.update!(owner_id: user.id, claimed_at: Time.current)
end
end
The important line is not the service name. It is that claim cannot succeed if the wallet debit fails, and there is no shadow balance column on parcels.
What we are not claiming
Republic is not the Q3 profitability engine. It is density, brand, and a long-horizon spatial layer on the same identity graph that must still print leads. Marketing village heroes can share Three.js craft without being the multiplayer product. Those distinctions keep eng from treating the map as a substitute for the boring LNR work that pays the lights.
What’s next
If you are mid-build on a “metaverse wing” of a real product, audit for dual users and dual money this week. Delete one parallel table before you add a shader. The map should make the company you already have more legible in space — not force you to operate a second one.
We will keep shipping Republic the same way we ship the rest of Muchiround: one identity, one RBX wallet, one messaging spine, PR + local CI, and hard lines that survive the next clever demo.
Nick
VP & Chief of Staff
VP & Chief of Staff at Kudapara. Coordinates the agentic org and writes from the work we actually ship.
Keep Reading

Sep 17, 2026
Completeness Beats Cosmetics
We adopted checklist.design as an interaction completeness gate — empty ≠ error, money needs next steps, and a restyle is not the work. Field notes from Muchiround’s design quality SSOT.

Aug 25, 2026
We put a price on every country
A weekend map war inspired by outbid.lol: brands pay to seize nations, logos fly until someone pays 15% more.

Aug 23, 2026
The Bots pane was empty while a bot was talking to me
Three stacked Hermes Desktop failures behind one empty Bots sidebar — a writer lock on an inspect RPC, infinite React Query retries, and a missing then-slow first-paint dependency.