Cajas is packed with features for users, administrators, and developers. This guide covers everything from case opening mechanics to provably fair verification.
-- User seeds (current)create table public.user_seeds ( user_id uuid references auth.users not null primary key, server_seed text not null, client_seed text not null, nonce bigint not null default 0, created_at timestamp with time zone default timezone('utc'::text, now()) not null, updated_at timestamp with time zone default timezone('utc'::text, now()) not null);-- Game rolls (audit log)create table public.game_rolls ( id uuid default gen_random_uuid() primary key, user_id uuid references auth.users not null, case_id uuid references public.cases not null, server_seed text not null, client_seed text not null, nonce bigint not null, roll_result bigint not null, item_won_id uuid not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null);
The server seed must remain secret until the user requests seed rotation. Never expose unhashed server seeds in API responses during active gameplay.
create table public.transactions ( id uuid default uuid_generate_v4() primary key, user_id uuid references public.users not null, amount numeric not null, type text not null, -- deposit, withdraw, case_open, item_sell reference_id uuid, created_at timestamp with time zone default timezone('utc'::text, now()) not null);alter table public.transactions enable row level security;create policy "Users can view own transactions." on public.transactions for select using ( auth.uid() = user_id );
Integrate real payment processors by creating API routes that handle payment webhooks and update user balances accordingly.
Powerful admin interface for managing cases and items.Admin Features:
Create and edit cases
Add items to cases with probabilities
Set pricing and rarity
Upload images
View admin logs
User management
Role-Based Access:
supabase/migrations/0000_create_cases_system.sql
-- Add role to profilesALTER TABLE profiles ADD COLUMN IF NOT EXISTS role text DEFAULT 'user' CHECK (role IN ('user', 'admin'));-- Admin-only policiesCREATE POLICY "Admins insert cases" ON cases FOR INSERT WITH CHECK ( EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin'));CREATE POLICY "Admins update cases" ON cases FOR UPDATE USING ( EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin'));CREATE POLICY "Admins delete cases" ON cases FOR DELETE USING ( EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin'));
Admin Logging:
CREATE TABLE IF NOT EXISTS admin_logs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), admin_id uuid REFERENCES auth.users(id), action text NOT NULL, details jsonb, created_at timestamptz DEFAULT now());
Always check user roles on the server side, never trust client-side role checks. Use RLS policies to enforce permissions.
# Public (safe for client-side)NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.coNEXT_PUBLIC_SUPABASE_ANON_KEY=eyJxxx...# Private (server-side only)SUPABASE_SERVICE_ROLE_KEY=eyJxxx... # DO NOT EXPOSE
Never expose the service_role key in client-side code. It bypasses all RLS policies and grants full database access.
create table public.users ( id uuid references auth.users not null primary key, username text unique, avatar_url text, balance numeric default 0, client_seed text, nonce integer default 0, created_at timestamp with time zone default timezone('utc'::text, now()) not null);
create table public.cases ( id uuid default uuid_generate_v4() primary key, name text not null, slug text unique not null, price numeric not null, image_url text, description text, created_at timestamp with time zone default timezone('utc'::text, now()) not null);
create table public.case_items ( id uuid default uuid_generate_v4() primary key, case_id uuid references public.cases not null, name text not null, value numeric not null, image_url text not null, probability numeric not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null);
create table public.game_rolls ( id uuid default gen_random_uuid() primary key, user_id uuid references auth.users not null, case_id uuid references public.cases not null, server_seed text not null, client_seed text not null, nonce bigint not null, roll_result bigint not null, item_won_id uuid not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null);