🧠 Why Puter — The Honest Analysis
You asked for best, most usable, most reliable, most implementable. Here's the full comparison so you understand exactly what you're choosing and why.
✦ Verdict: Puter for auth + cloud + player data. Supabase for game-critical server data. Crossmint for wallets.
Puter handles what players interact with directly — their account, their cloud files, their preferences. Supabase handles what you need to own and control — GBux ledgers, characters, transactions. Crossmint handles the blockchain. These three together are the most implementable, most reliable, lowest-cost architecture for Grudge Studio.
| Feature | Puter.js | Supabase Only | Firebase | Custom Backend |
|---|---|---|---|---|
| Setup time | 1 script tag | Few hours | Few hours | Days/weeks |
| Auth (email+social) | Built-in, free | Built-in | Built-in | Must build |
| Player cloud storage | Auto per user | You provision | You provision | Must build |
| Dev infra cost | $0 always | Free tier, then $ | Free tier, then $ | Server costs |
| Scales to 1M users | User-pays model | Costs scale with you | Costs scale with you | You handle it |
| Atomic transactions | KV only (no SQL) | Full Postgres | Firestore limited | Full control |
| GBux ledger integrity | Not suitable | Perfect fit | Risky | Full control |
| NFT / Web3 built-in | None | None | None | DIY + Crossmint |
Player Layer
Account identity, cloud storage (screenshots, saves, character art), AI access, session management. Zero cost to you, players pay their own usage.
Game Layer
GBux ledger (atomic transactions), characters, islands, inventory, game state, everything that needs SQL integrity and server-side validation.
Web3 Layer
Custodial wallets (auto-provisioned on signup), cNFT minting for characters/islands, NFT transfers, on-chain ownership. Abstracted from the player.
🏗️ Full System Architecture
How every piece connects — from a player clicking Sign Up to having a full cloud account, wallet, and Grudge identity in seconds.
✦ Puter account (UUID identity)
✦ Personal cloud drive (unlimited storage)
✦ App-scoped KV store (preferences, settings)
✦ AI API access (Claude, GPT, DALL-E)
✦ Serverless compute access
✦ File hosting (for character art, screenshots)
✦ Player DB record with UUID
✦ GBux balance (starts at 0)
✦ Character slots
✦ Island ownership
✦ Transaction ledger history
✦ Achievement/quest state
✦ Custodial Solana wallet (player owns it, you manage it)
✦ Wallet address stored in player record
✦ Can receive cNFTs immediately
✦ Player can export to self-custody later
✦ No crypto knowledge required by player
Puter is the identity layer — who you are.
Supabase is the game state layer — what you own.
Crossmint is the ownership layer — provably yours on-chain.
🚀 The Signup Flow
One click → full Grudge account. Here's exactly what happens technically.
// ============================================================ // STEP 1: Frontend — Grudge sign-in button click // ============================================================ import puter from '@heyputer/puter.js' async function grudgeSignIn() { // 1. Trigger Puter auth (user's own Puter account) const user = await puter.auth.signIn() // user.uuid — their permanent Puter ID // user.username — their Puter username // user.email — their email (if shared) // 2. Call YOUR server to register/sync them in Supabase const grudgeAccount = await fetch('/api/auth/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ puterId: user.uuid, username: user.username, email: user.email }) }).then(r => r.json()) // 3. Store Grudge session locally await puter.kv.set('grudge:session', JSON.stringify({ playerId: grudgeAccount.playerId, walletAddress: grudgeAccount.walletAddress, gbuxBalance: grudgeAccount.gbuxBalance })) // Player is now fully initialized → redirect to game window.location.href = '/game/hub' }
// ============================================================ // STEP 2: Server — /api/auth/sync (Next.js API Route) // Creates or retrieves the full Grudge account // ============================================================ import { supabase } from '@/lib/supabase' import { provisionWallet } from '@/lib/crossmint' import { z } from 'zod' const Schema = z.object({ puterId: z.string().uuid(), username: z.string().min(1).max(50), email: z.string().email().optional() }) export async function POST(req) { const { puterId, username, email } = Schema.parse(await req.json()) // Check if player already exists let { data: player } = await supabase .from('players') .select('*') .eq('puter_id', puterId) .single() if (!player) { // NEW PLAYER — create everything in parallel const [newPlayer, walletAddress] = await Promise.all([ // Create Supabase player record supabase.from('players').insert({ puter_id: puterId, username, email, gbux_balance: 100, // welcome bonus! }).select().single(), // Provision Crossmint custodial wallet provisionWallet(puterId, email) ]) // Store wallet address on player record await supabase.from('players').update({ wallet_address: walletAddress }).eq('puter_id', puterId) // Log the welcome GBux award await supabase.from('gbux_transactions').insert({ player_id: newPlayer.data.id, amount: 100, reason: 'welcome_bonus' }) player = { ...newPlayer.data, wallet_address: walletAddress } } return Response.json({ playerId: player.id, walletAddress: player.wallet_address, gbuxBalance: player.gbux_balance, isNewPlayer: !player.created_at }) }
// ============================================================ // STEP 3: Puter Cloud — Set up player's Grudge folder // Run this after sign-in to give them their cloud space // ============================================================ async function initPlayerCloud(username) { // Create folder structure in player's Puter cloud drive await Promise.all([ puter.fs.mkdir(`grudge-studio/characters`, { createMissingParents: true }), puter.fs.mkdir(`grudge-studio/screenshots`, { createMissingParents: true }), puter.fs.mkdir(`grudge-studio/saves`, { createMissingParents: true }), // Write their initial profile card to their cloud puter.fs.write('grudge-studio/profile.json', JSON.stringify({ username, joinedAt: new Date().toISOString(), studio: 'Grudge Studio' })) ]) }
⚙️ Step-by-Step Implementation
In exact order. Do these one at a time. Each step works before moving to the next.
Install Puter.js in your Next.js project
Add to your app layout. No API keys, no config. That's it.
npm install @heyputer/puter.js // In app/layout.tsx import Script from 'next/script' export default function Layout({ children }) { return ( <html> <body> <Script src="https://js.puter.com/v2/" strategy="beforeInteractive" /> {children} </body> </html> ) }
Set up Supabase project + schema
Create a new Supabase project. Run the schema migrations. Enable Row Level Security.
-- Run in Supabase SQL editor CREATE TABLE players ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), puter_id TEXT UNIQUE NOT NULL, -- Puter UUID username TEXT UNIQUE NOT NULL, email TEXT, wallet_address TEXT, -- Crossmint wallet gbux_balance BIGINT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), last_seen TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE gbux_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), player_id UUID REFERENCES players(id) ON DELETE CASCADE, amount BIGINT NOT NULL, reason TEXT NOT NULL, ref_id UUID, created_at TIMESTAMPTZ DEFAULT NOW() ); -- RLS: players can only read their own data ALTER TABLE players ENABLE ROW LEVEL SECURITY; CREATE POLICY "players_self" ON players FOR SELECT USING (auth.uid()::text = puter_id);
Build the /api/auth/sync endpoint
This is the bridge between Puter identity and your game. See the full code in the Signup Flow tab. Add Crossmint wallet provisioning here.
Set up Crossmint staging account
Create a Crossmint developer account. Get your Server API key. Create your first collection (Characters) on devnet. Test wallet provisioning end-to-end.
// lib/crossmint.ts export async function provisionWallet(userId, email) { const res = await fetch('https://staging.crossmint.com/api/2022-06-09/wallets', { method: 'POST', headers: { 'X-API-KEY': process.env.CROSSMINT_SERVER_KEY!, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'solana-custodial-wallet', linkedUser: `email:${email}` }) }) const data = await res.json() return data.publicKey }
💎 GBux + Wallet System
How GBux and crypto wallets coexist. GBux is your game economy. Wallet is for on-chain NFT ownership. They're separate — but connected.
Virtual Currency
GBux lives entirely in your Supabase database. You control it. Atomic transactions. Ledger accounting. Can be earned in-game, purchased via Stripe, or spent on upgrades and minting.
NFT Ownership
The Crossmint custodial wallet holds cNFTs (characters, islands). Players own the NFTs but you manage the wallet. They can optionally self-custody later by exporting their private key.
// Spending GBux to mint a character as cNFT // This is the key flow connecting GBux economy to Web3 export async function mintCharacter(playerId, characterId) { const MINT_COST = 500 // GBux // Step 1: Debit GBux atomically (Supabase transaction) const { data: player } = await supabase .from('players') .select('gbux_balance, wallet_address') .eq('id', playerId) .single() if (player.gbux_balance < MINT_COST) { throw new Error('Insufficient GBux') } // Atomic: debit balance + log transaction await supabase.rpc('spend_gbux', { p_player_id: playerId, p_amount: MINT_COST, p_reason: 'character_mint', p_ref_id: characterId }) // Step 2: Queue the mint job (don't block the request) await inngest.send({ name: 'character/mint.requested', data: { playerId, characterId, walletAddress: player.wallet_address } }) return { queued: true, message: 'Minting in progress...' } }
puter.kv to cache the player's GBux balance locally for instant UI display. Always verify the real balance from Supabase before any spend operation. The KV cache is a read-through cache only — never write the authoritative balance there.
🗺️ Exact Next Steps — In Order
Prioritized by what unlocks everything else. Do these in sequence and you'll have a fully operational account + game system.
This week — Auth + Account Foundation
Puter auth in your Next.js app → /api/auth/sync endpoint → Supabase players table → Crossmint staging wallet provision. End state: user clicks Sign In, gets a full account with a wallet in <3 seconds.
This week — GBux Engine
gbux_transactions table + spend_gbux Postgres function (atomic) + awardGbux server utility. Welcome bonus on signup. Balance visible in UI. End state: GBux is tracked and unhackable.
Next — Player Profile & Cloud UI
Account page showing: avatar, username, GBux, wallet, cloud storage usage. Player can upload avatar directly to their Puter cloud. Pull stats from Supabase via your API.
Next — Characters
Character creation → stored in Supabase + art in player's Puter cloud. Dynamic metadata endpoint. No minting yet — just gameplay. Crossmint minting added in Phase 2.
Next — Islands
Island creation, ownership, and GBux earning. Same pattern as characters — off-chain first, NFT later. Island maps/data stored in Puter cloud (large files), ownership in Supabase.