You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Hebrew-first (RTL) family chores and rewards application. Kids complete tasks, earn virtual coins, and parents approve everything with photo proof. Multi-family, multi-user, real-time synced.
# 1. Install dependencies
npm install
# 2. Create .env from template
cp .env.example .env
# Fill in your Upstash Redis and Google OAuth credentials# 3. Start dev server
npm run dev
# Opens at http://localhost:5173
Environment Variables
Required (local .env and Vercel)
Variable
Description
KV_REST_API_URL
Upstash Redis REST URL
KV_REST_API_TOKEN
Upstash Redis REST token
GOOGLE_CLIENT_ID
Google OAuth 2.0 Client ID (from Google Cloud Console)
Required (Vercel only, production)
Variable
Description
APP_ORIGIN
Full origin URL, e.g. https://your-app.vercel.app (used for CORS)
ADMIN_EMAIL
Admin Google email, e.g. [email protected] (locks admin to Google-only login)
RP_ID
WebAuthn relying party ID — just the hostname, e.g. your-app.vercel.app (no https://)
Optional
Variable
Description
ADMIN_SETUP_KEY
One-time setup key for password-based admin bootstrap (not needed when ADMIN_EMAIL is set)
Family config (parents, kids, PINs, familyName, isSetup)
family:{id}:chores
JSON
Array of chore objects
family:{id}:log
LIST
Activity log entries
Invite keys
Key
Type
Description
app:invites:all
SET
All invite codes
app:invites:{code}
JSON
Invite details (familyName, familyId, type, used)
Security keys
Key
Type
Description
pin:attempts:{familyId}:{profileId}
INT
Failed PIN attempts counter
pin:locked:{familyId}:{profileId}
STRING
Lockout timestamp — 10min TTL
webauthn:challenge:{email}
STRING
WebAuthn challenge — 5min TTL
webauthn:creds:{email}
HASH
Stored WebAuthn credentials
rl:login:{id}
—
Rate limit: 5 per 15 min
rl:pin:{id}
—
Rate limit: 5 per 10 min
rl:api:{id}
—
Rate limit: 120 per min
Security Model
Authentication: Google OAuth + email/password + WebAuthn passkeys. Sessions stored in Redis with TTL.
Family isolation: Every data request passes through requireFamilyAccess() middleware that verifies the user's session includes the requested family ID.
Admin access: Locked to a single Google account via ADMIN_EMAIL env var. Password login is disabled when this is set.
Password hashing: Node.js crypto.scrypt (memory-hard). Legacy simpleHash values are transparently migrated on next login.
PIN validation: Server-side only. PINs are never sent to the client. 5 attempts before 10-minute lockout.
Rate limiting:@upstash/ratelimit sliding window on login, PIN, and general API endpoints.
CORS: Restricted to APP_ORIGIN when set.
Tokens: Generated with crypto.randomBytes (CSPRNG).
Admin Panel
Accessible at /admin. When ADMIN_EMAIL is set, only that Google account can log in.