๐1. Introduction
Sudoku Battle ("the Application") is developed by TheCatsizerLab and available on Google Play Store. We respect your privacy and are committed to processing your personal data transparently and securely in accordance with GDPR and applicable data protection laws worldwide.
This privacy policy explains how we collect, use, share, and protect your personal data when you use our Application.
๐ค2. Data Controller
TheCatsizerLab
๐ Zigliara, South Corsica, France
๐ง Email: contact.thecatsizerlab@gmail.com
For any data-related inquiries, please contact us at the address above.
2.2 App Package Information
- App Package Name: com.catsizerlab.sudoku
- Google Play Store Link: View on Google Play
- Minimum Android Version: Android 7.0 (API 24)
- Target Android Version: Android 14/15 (API 34/35)
๐3. Data We Collect
3.1 Data Collected Directly from You
๐ฎGoogle Play Games Services (GPGS)
- Unique player identifier (Player ID)
- Display name
- Profile picture (optional)
- Game progression data synchronized via Google Cloud
Legal Basis: Consent (explicit upon connection) | Purpose: Authentication, cloud sync, leaderboards, achievements
๐ฏGame Data
- Game scores (solo and multiplayer)
- Match history (duration, error count, game mode)
- Progression through levels
- Selected difficulty
- Elapsed time in games
- Number of correct placements
- Combo statistics
Legal Basis: Service contract | Purpose: Save progression, populate leaderboards, improve gameplay
๐ฅMultiplayer Data
- Opponent's display name
- Real-time game progression
- Match statistics (correct moves, errors, combo)
- Match results
- Energy and power-ups used
- Friends list and friend codes
- Pending friend invitations
Legal Basis: Service contract | Purpose: Real-time multiplayer sync, leaderboards, game balancing
๐พMatch Details Backup (Local)
- Last 5 matches stored in device SharedPreferences
- Includes: opponent name, scores, victory/defeat, reason, timestamp
Purpose: Display recent match history offline
Retention: Until manual app data clear
Storage: SharedPreferences (JSON format)
โฑ๏ธDeferred Game Results (Backup System)
Purpose: Prevent lost match results due to disconnects during game_over event
Storage:
- PostgreSQL database (Neon) - table: finished_games
- Columns stored:
- room_id (UNIQUE) - Match identifier
- winner_id, winner_name, winner_score
- loser_id, loser_name, loser_score
- reason (completed, abandoned, timeout, too_many_errors)
- winner_delivered (BOOLEAN) - Has winner retrieved result?
- loser_delivered (BOOLEAN) - Has loser retrieved result?
- created_at (TIMESTAMP)
Dual-Flag Delivery System:
- When a player is offline at match end, result is stored with their delivery flag = FALSE
- When player reconnects and retrieves result via GET /api/game_over/check/{playerId}, their flag becomes TRUE
- Row is ONLY deleted when BOTH winner_delivered AND loser_delivered are TRUE
- This ensures both players receive their match result exactly once
API Endpoints:
- POST /api/game_over/batch - Store results for multiple disconnected players (atomic batch insert)
- GET /api/game_over/check/{playerId} - Atomic retrieval sequence:
- SELECT FOR UPDATE SKIP LOCKED (prevents race conditions)
- UPDATE delivery flag for requesting player (winner_delivered or loser_delivered)
- DELETE row if both flags are TRUE
- Returns match result in single transaction
Retention:
- Until both players retrieve result (automatic deletion after dual-confirmation)
- Auto-cleanup: Results older than 1 hour are deleted (even if not retrieved)
- Cleanup runs every hour via scheduled job
Privacy:
- Stored in encrypted PostgreSQL (Neon) with SSL/TLS connections
- No persistent retention after both players confirm delivery
- Maximum retention: 1 hour (hard limit via cleanup job)
Security:
- Atomic database operations prevent duplicate delivery
- SKIP LOCKED prevents concurrent retrieval conflicts
- Transaction rollback on any error ensures data consistency
โ๏ธPreference Data
- Selected visual theme
- Language preference
- Audio settings (sound enabled/disabled)
- Vibration preferences
- Premium theme purchase history
Storage: Device local storage (SharedPreferences)
Legal Basis: Service contract | Purpose: Personalize user experience
๐Audio Preferences
- Sound enabled/disabled state
- Music volume level (0.0-1.0)
- Sound effects volume level (0.0-1.0)
- Last played music track
Storage: Device local storage (SharedPreferences)
Legal Basis: Service contract
Purpose: Remember your audio settings across sessions
โ๏ธCloud Game Save (GPGS)
- Current grid state (9x9 array of numbers)
- Initial puzzle grid (unmodified)
- Solution grid (complete puzzle)
- Difficulty level (easy/medium/hard/expert)
- Elapsed time (in seconds)
- Error count
- Continues used (if game restarted after loss)
- Current score
- Cell notes (9x9 array of note arrays)
- Hints remaining
Storage: Google Play Games Saved Games
Legal Basis: Service contract (cloud sync)
Purpose: Resume game across devices
Retention: As long as GPGS account exists
Sync: Automatic when GPGS is connected
๐ Achievement Progress Tracking (Local)
- Win counters (per difficulty + total)
- Flawless games count (0 errors)
- Hints usage statistics
- Notes usage frequency
- Correct placement accuracy
- Win streak counters (current + best)
- Error-free grids completed
- Daily challenge participation streak
- Multiplayer victories
Storage: Device local storage (SharedPreferences)
Legal Basis: Service contract
Purpose: Track achievement unlock progress
Sync: Synced with Google Play Games when achievements are unlocked
Retention: As long as app is installed
๐Player Profile Statistics
- Total games played (solo + multiplayer)
- Games won/lost (per game mode)
- Win rate percentage (automatically calculated)
- Last game timestamp
- Profile creation date
Legal Basis: Service contract
Purpose: Display player performance statistics
Visibility: Private (only visible to you)
Retention: As long as needed to provide the service and maintain integrity of features, subject to security and legal requirements.
๐พProfile Statistics Storage (PostgreSQL)
Storage Structure:
- Solo Statistics Table (player_profile_solo):
- games_played (INTEGER)
- games_won (INTEGER)
- games_lost (INTEGER)
- last_game_at (TIMESTAMP - last solo match completed)
- created_at / updated_at (TIMESTAMP)
- Multiplayer Statistics Table (player_profile_multi):
- games_played (INTEGER)
- games_won (INTEGER)
- games_lost (INTEGER)
- last_game_at (TIMESTAMP - last multiplayer match completed)
- created_at / updated_at (TIMESTAMP)
Calculation:
- Win rate: (games_won / games_played) ร 100
- Calculated server-side on each API request
API Endpoints:
- GET /api/profile/stats?gameMode=solo - Retrieve solo statistics
- GET /api/profile/stats?gameMode=multi - Retrieve multiplayer statistics
- POST /api/profile/update - Updates statistics after each match completion
Storage Location: PostgreSQL (Neon) - encrypted at rest (AES-256)
Retention: As long as needed to provide the service and maintain integrity of features, subject to security and legal requirements.
Privacy: Statistics are private and only visible to the account owner
๐Profile Statistics Cache (In-Memory)
- Solo/Multi stats cached in RAM (not persistent storage)
- Auto-refresh from server on app resume
- Cached data: gamesPlayed, gamesWon, gamesLost, winrate, lastGameAt, updatedAt
Purpose: Reduce API calls, improve performance
Storage: Volatile memory (lost on app close)
Retention: Session only (cleared when app is closed)
3.2 Data Collected Automatically
๐ฅ๏ธTechnical Data
- Device type and model
- Android OS version
- Unique identifiers (Android Advertising ID)
- IP address
- Region and timezone
- Storage capacity and remaining storage
- Device memory information
Legal Basis: Legitimate interest (technical maintenance, security, analytics) | Purpose: Performance optimization, fraud prevention, debugging
๐ฑConnection Data
- Session timestamps
- Session duration
- Game events (matches started, completed, abandoned)
- Ad interaction data (impressions, clicks, rewarded ad views)
- Connection type (WiFi vs cellular)
- App launch frequency
Legal Basis: Legitimate interest (service improvement and performance monitoring) | Purpose: Service improvement, usage analysis, performance monitoring
โ ๏ธCrash and Error Data (Firebase Crashlytics)
Automatic Collection: Firebase Crashlytics automatically captures and sends crash reports when the app crashes.
- Crash reports: Automatic fatal error reports with stack traces
- Non-fatal errors: Logged exceptions (manually recorded by developers)
- Stack traces: Full error trace for debugging
- Application state: Memory usage, device orientation, free disk space at crash time
- Device information: Model, OS version, manufacturer
- Session data: App version, crash timestamp, time since app launch
- Custom logs: Developer-added breadcrumbs for debugging context
Data NOT collected by Crashlytics:
- โ User identifiers (Player ID, GPGS name) - NOT automatically linked
- โ IP addresses
- โ Personal data from game state (grid content, scores)
Storage & Retention:
- Storage: Firebase services operated by Google.
- Retention: Crash reports are retained according to Google's Firebase retention policies and settings. We do not implement custom server-side deletion for Crashlytics data.
- Security: Data is encrypted in transit (TLS) and handled under Google's security controls.
Legal Basis: Legitimate interest (GDPR Article 6.1.f - technical maintenance, security, app stability)
Purpose: Detect and fix crashes, improve app stability, prevent future errors
User Controls:
- In-app controls: The Application does not provide an in-app switch to disable essential diagnostics or basic usage measurement.
- Google/Android controls: Some advertising-related controls (such as ad personalization preferences and Advertising ID controls) may be available through your Google account and Android settings.
- Stop collection: Uninstalling the Application stops data collection from the app.
๐Android Permissions
The Application requests the following Android permissions:
| Permission | Purpose | Required |
|---|---|---|
INTERNET |
Connect to leaderboard servers, daily challenges, multiplayer | โ Yes |
ACCESS_NETWORK_STATE |
Check network availability before server requests | โ Yes |
VIBRATE |
Haptic feedback on correct/incorrect moves (can be disabled in Settings) | โ Yes (declared in manifest) |
BILLING |
In-App Purchases (Google Play Billing) | โ Yes |
Note: All permissions are requested at runtime and can be revoked in Android Settings > Apps > Sudoku Battle > Permissions.
3.3 Payment Data
๐ณIn-App Purchase Transactions
- Products purchased (premium lifetime, theme bundle, individual themes)
- Purchase tokens (Google Play verification data)
- Purchase amount and currency
- Transaction date and time
- Transaction status (completed, pending, failed, restored)
- Product IDs (premium_lifetime, theme_spring_premium, theme_tropical_premium, theme_midnight_premium, theme_rainy_premium, theme_desert_premium, theme_golden_premium, theme_cherry_premium, theme_sky_premium, theme_rice_premium, theme_bundle_premium)
Legal Basis: Service contract | Purpose: Purchase management, product attribution, fraud prevention
๐Purchase Restoration
- Purchase restoration requests (timestamp)
- Server-side purchase validation (Google Play receipt verification)
- Backup purchase records (in case of Google Play sync failure)
- Restoration attempts (frequency, device changes)
Purpose: Ensure purchases are not lost on device change/reinstall
Security: Dual validation (Google Play + server-side)
3.4 Third-Party Data
๐ขGoogle AdMob (Advertising)
Advertisements displayed in the Application are managed by Google through AdMob.
Data collected by Google:
- Android Advertising ID
- Inferred interest categories
- Ad view history
- Ad clicks
- App usage patterns
Regional Notice: In certain regions (such as the EEA, UK, and Switzerland), Google may require a consent message for personalized ads. When required, consent is handled according to Google's requirements and supported consent solutions.
Legal Basis: Consent (compliant with Google policy) | Purpose: Personalized ad delivery, app monetization
Manage Ad Preferences: Go to Settings > Google > Manage your Google Account > Data & Privacy > Ad Settings
๐ฅFirebase Services (Google)
Firebase Analytics
- Automatic screen view tracking
- Session duration and frequency
- Device type, OS version, app version
- Custom events logged by developers (game_start, game_end, purchase, etc.)
Provider: Google LLC
Privacy Policy: firebase.google.com/support/privacy
Firebase Crashlytics
- Crash reports (fatal errors only)
- Stack traces for debugging
- Device state at crash time (memory, storage, battery)
- NO personal data (Player ID, game scores) unless manually logged
Provider: Google LLC
Privacy Policy: firebase.google.com/support/privacy/crashlytics
Legal Basis: Legitimate interest (GDPR Article 6.1.f)
Retention: Retention periods are controlled by Google's Firebase policies and configuration. We do not implement custom deletion routines for Firebase Analytics or Crashlytics data.
โกPower-Up Cooldown & Energy Management
Cooldown System:
- 5 seconds minimum between power-up activations
- Last activation timestamp stored in RAM (player.lastPowerUpTime)
- Throttle protection: 100ms minimum between trigger_power events (spam prevention)
Energy Deduction:
- Energy cost: 1 point per power-up activation
- Deducted BEFORE power-up validation (prevents race conditions)
- Automatic refund if power-up fails execution
Refund Scenarios (Energy returned to player):
- Opponent disconnected at activation time
- No valid cells to erase (cell_eraser power-up on empty grid)
- Self-target on invalid grid state
- Invalid game state (match already finished)
- Power-up activation during cooldown period
Power-Up Targeting:
- 80% chance: Targets opponent
- 20% chance: Targets yourself (random server-side selection)
- Randomization performed server-side for fairness
Storage: RAM only (session-based, not persistent)
Purpose: Prevent power-up spam, ensure fair energy consumption, maintain game balance
Retention: Until match ends or player disconnects (volatile memory)
๐ฌRewarded Video Ads
- Ad impression timestamps
- Ad completion status (watched fully vs skipped)
- Rewards earned (continue game, bonus hints)
- Ad unit IDs shown
- Frequency capping data
Legal Basis: Consent (Google ToS)
Purpose: Provide optional rewards in exchange for ad views
Provider: Google AdMob
Control: You can refuse to watch ads (feature unavailable without viewing)
โฑ๏ธRewarded Ad Cooldown
- Last ad view timestamp stored locally
- Minimum 3 minutes cooldown between rewarded ads
Purpose: Prevent ad spam exploitation
Control: Premium subscription removes ads entirely
โฑ๏ธInterstitial Ad Frequency Control
- Last ad display timestamp (stored locally)
- Games played since last ad (counter)
- Cooldown timer (3 minutes minimum between ads)
- Ad display frequency (1 ad per 3 games completed)
Storage: Device local storage (SharedPreferences)
Purpose: Limit ad annoyance, respect user experience
Control: Premium subscription removes all interstitial ads
๐Google Play Games Services (Leaderboards & Achievements)
- Public scores on leaderboards
- Unlocked achievement badges
- Public profile information
Legal Basis: Service contract | Purpose: Leaderboards, social features
๐Private Statistics Leaderboards (GPGS)
In addition to public leaderboards, we maintain private leaderboards for internal tracking:
- Total playtime (cumulative seconds)
- Games played (all modes combined)
- Total wins (all difficulties)
Visibility: Private (only visible to you via GPGS)
Status: These leaderboards are coded in the app but currently disabled in the Google Play Console (for internal use only)
Google Play IDs:
- CgkIzuudz8UbEAIQAg (playtime)
- CgkIzuudz8UbEAIQAw (games played)
- CgkIzuudz8UbEAIQBA (total wins)
3.5 Daily Challenge Data
๐ Daily Puzzle Progress
- Daily puzzle completion status (completed date stored locally)
- Best daily scores per challenge date
- Completion times (in seconds)
- Number of hints used per challenge (0-2)
- Submission timestamps
Legal Basis: Service contract | Purpose: Daily challenge leaderboards, player engagement tracking
Storage: PostgreSQL database (Neon) via Fly.io servers (USA)
Retention: Indefinite (for leaderboard integrity and historical rankings)
๐Daily Leaderboards
- Public rankings (today + all-time)
- Player names associated with scores
- Total days participated
- Average completion times
Visibility: All daily challenge scores are publicly visible in leaderboards
๐Duplicate Submission Prevention
Server-Side Validation:
- Before accepting a score, server checks if player already completed today's challenge
- SQL query:
SELECT COUNT(*) WHERE player_id = $1 AND challenge_date = $2 - HTTP 409 Conflict error returned if already completed
- Prevents score manipulation and duplicate submissions
Client-Side Check:
- GET /api/daily/check-completed - Returns boolean completion status
- Called on app launch to disable "Play" button if already completed
- Local cache updated after successful submission
Purpose: Ensure fair leaderboards, prevent score manipulation, maintain competitive integrity
Legal Basis: Legitimate interest (fraud prevention - GDPR Article 6.1.f)
๐Anti-Cheat Validation
- Maximum score limit: 9500 points (server-side enforcement)
- Time validation: Must be positive integer (in seconds)
- Hints validation: 0-2 hints only (expert difficulty allows max 2 hints)
- Scores exceeding limits are rejected with HTTP 400 Bad Request
- Suspicious submissions flagged for review (see Section 6.1)
3.6 Friends System Data
๐ฅFriend Connections
- Unique friend code (8-digit numeric format: XXXX-XXXX, e.g., 1234-5678)
- Friend code validation (must match format
^\d{4}-\d{4}$) - Friend list (player IDs + display names)
- Friend invitations sent/received (status: pending, accepted, declined)
- Friendship timestamps (creation date)
- Last synchronization time
Legal Basis: Consent + Service contract | Purpose: Social features, friend-to-friend matching
๐Friend Code Generation
Your friend code is generated using a deterministic, non-reversible hash algorithm based on your Player ID. This means:
- The code cannot be traced back to your Player ID
- It remains the same across all your sessions
- No personally identifiable information is embedded in the code
โกFriend Invitations
- Sender information (Player ID, name, friend code)
- Recipient friend code (8-digit code)
- Invitation status (pending, accepted, declined)
- Game mode and difficulty requested (if challenge)
Retention: 60 seconds (auto-expire if not responded), then permanently deleted
Storage: PostgreSQL database (Neon) via Fly.io servers (USA)
3.7 Challenge System Data
โ๏ธPlayer-to-Player Challenges
- Challenge sender/receiver information (Player ID, name)
- Game mode requested (classic, powerup, timeAttack)
- Difficulty level requested (easy, medium, hard, expert)
- Challenge acceptance/decline status
- Challenge timestamps (sent, responded)
Legal Basis: Service contract | Purpose: Facilitate direct player-to-player matches
Retention: 60 seconds maximum (auto-expire), then permanently deleted
Storage: Temporary in-memory storage (server RAM), no persistent database storage
3.8 Authentication Tokens
๐JWT Authentication
- JWT tokens (JSON Web Tokens) containing:
- Player ID (encrypted)
- Player Name (encrypted)
- Expiration timestamp (30 days)
- Token generation timestamp
- Token stored locally (SharedPreferences - encrypted)
Legal Basis: Service contract + Security (legitimate interest)
Purpose: Secure API authentication for leaderboards, daily challenges, friends system
Expiration: 30 days (automatic renewal on reconnection)
Storage Location: Device local storage (encrypted) + server-side validation
Security: Tokens are signed using industry-standard HS256 algorithm with a 32+ character secret key
3.9 Real-Time Multiplayer Data (WebSocket/Socket.IO)
๐WebSocket Connection
During multiplayer matches, your device establishes a real-time WebSocket connection to our game server.
| Data Type | Details | Retention |
|---|---|---|
| IP Address | Used for WebSocket routing and connection management | Session only |
| Socket ID | Temporary unique identifier for your WebSocket connection | Until disconnect |
| Room ID | Temporary match identifier (shared with opponent) | Until match ends |
| Game Moves | Row, column, value placed in real-time | Match duration |
| Player Stats | Progress, combo, errors, energy (synced live) | Match duration |
| Heartbeat Signals | Connection alive pings (throttled to max 1 per 3 seconds) | Not stored |
| Power-Up Events | Type, duration, target player | Match duration |
| Match Start Timestamp | Unix timestamp (ms) when room was created (startTime) | Session only |
| Server Timestamp | Current server time (ms) for client synchronization (serverTime) | Not stored |
| Time Consumed (Time Attack) | Elapsed time in seconds (calculated from personalEndTime - currentTime) | Session only |
Timestamp Synchronization:
- startTime: Match creation timestamp (UTC) - used to calculate elapsed time
- serverTime: Current server timestamp (UTC) - sent on matchFound to sync client clock
- Usage: Calculate elapsedSeconds = (currentTime - startTime) / 1000
- Time Attack: Sync personalEndTime countdown timers across devices
- Privacy Note: Timestamps are in UTC (no timezone data sent), but difference between server and client time may indirectly reveal timezone
Time Attack - Time Consumed:
- Calculated when match ends or timer expires:
timeConsumed = (timeLimit - timeRemaining) / 1000 - Stored temporarily in player object (RAM) during match
- Sent in game_over event to both players
- Purpose: Display actual time taken to complete puzzle (performance tracking)
- Storage: Not persisted to database (session-only data)
Server: sudokupuzzle-server.fly.dev (Fly.io infrastructure, USA)
Transport Security: WebSocket Secure (WSS) over TLS 1.3
Legal Basis: Service contract (multiplayer functionality)
Purpose: Enable real-time 1v1 synchronization
๐Reconnection Dialog
When you reconnect after temporary disconnect (within 60 seconds), server sends:
- Current grid state (your puzzle with filled cells)
- Initial puzzle (frozen state at match start)
- Solution grid (complete puzzle reference)
- Opponent progress (percentage completion 0-81)
- Your stats:
- correctMoves (validated placements)
- errors (incorrect placements)
- combo (current streak)
- energy (power-up charges available)
- energyCombo (progress toward next energy point)
- currentScore (recalculated at reconnection)
- drainRate (Classic/PowerUp only)
- Elapsed time since match start (in seconds):
elapsedSeconds = Math.floor((Date.now() - room.startTime) / 1000) - personalEndTime (Time Attack only - your timer deadline in Unix timestamp ms)
- startTime (match creation timestamp - used to recalculate elapsed time client-side)
Score Recalculation on Reconnection:
- Classic/PowerUp:
score = initialPool - (elapsedSeconds ร drainRate) + (correctMoves ร basePoints) - (errors ร errorPenalty) - Time Attack:
score = currentScore(accumulated points, no recalculation needed) - Ensures fair scoring even after disconnect/reconnect
Data Source: All data retrieved from server RAM (rooms object), no database queries
Retention: Sent once on reconnection, not stored client-side beyond session
3.10 Backend Server Infrastructure
๐Leaderboard Backend Server
- Server URL: sudokupuzzle-leaderboards.fly.dev
- Location: Fly.io infrastructure (USA)
- Purpose: Store and rank player scores globally
- Data stored:
- Player ID (GPGS)
- Player Name (GPGS)
- Total scores per category (solo/multi/daily)
- Games played count
- Last submission timestamp
- Security: JWT authentication (Bearer tokens)
- Retention: 90 days inactivity-based cleanup
๐พPostgreSQL Database (Neon)
- Purpose: Persistent storage for daily challenges, friends, leaderboards
- Location: Neon infrastructure (USA)
- Data stored:
- Daily challenge scores and completion times
- Friend codes and friendships
- Friend invitations (60s expiry)
- Player statistics
- Security: Encrypted at rest (AES-256)
- Access: API requests only (no direct database access)
๐ฏ4. How We Use Your Data
4.1 Primary Purposes
| Purpose | Data Used | Legal Basis |
|---|---|---|
| Provide game service | Game data, player profile | Contract |
| Cloud synchronization | Progression data | Contract |
| Leaderboards & Achievements | Scores, name, stats | Contract |
| Performance optimization | Technical data, crash logs | Legitimate Interest |
| Security & fraud prevention | IP, device ID, transactions | Legitimate Interest |
| Personalized ads | Advertising ID, interests | Consent (Google) |
| Technical maintenance | All relevant logs | Legitimate Interest |
| Legal compliance | Relevant data | Legal Obligation |
๐Cloud Sync Conflict Resolution
When syncing data between device and cloud (GPGS), we use a MAX merge strategy:
- For win counters: Highest value kept (prevents data loss)
- For playtime: Longest time kept
- For settings: Most recent timestamp wins
Example: If you have 10 wins locally and 15 wins in cloud, we keep 15.
Rationale: This prevents accidental data loss from reinstalls or device changes.
4.2 Data Sharing
Your data is NEVER sold to third parties.
| Third Party | Data Shared | Reason | Data Processing Agreement |
|---|---|---|---|
| Google Play Games | Player ID, name, scores | Authentication & leaderboards | โ Yes (Google ToS) |
| Google AdMob | Advertising ID | Ad personalization | โ Yes (Google ToS) |
| Game Server (Fly.io) | Multiplayer stats | Real-time sync | โ Yes (encrypted) |
| Google Play Billing | Transaction data | Payment processing | โ Yes (PCI DSS) |
| PostgreSQL (Neon) | Scores, daily data, friends, challenges | Persistent database storage | โ Yes (encrypted at rest) |
| JWT Tokens (Local) | Authentication tokens (Player ID + Name) | API authentication | โ Yes (30-day expiry, HS256 signed) |
| Legal Authorities | Relevant data | Legal obligation upon request | N/A |
โณ5. Data Retention
We retain data only for as long as necessary to provide the Application, maintain competitive integrity (such as leaderboards), prevent abuse, and comply with legal obligations.
5.1 Local Device Data
Some preferences and gameplay-related data may be stored locally on your device (e.g., settings, cached data, recent matches). This data remains on your device until you clear the app data or uninstall the Application.
5.2 Server Data (Game Features)
Where server-side features are provided (such as multiplayer, friends, and leaderboards), related records may be retained as long as needed for the operation and integrity of these features and for security/fraud prevention.
5.3 Third-Party Services (Google)
Some data is processed by third-party services such as Google Play Games Services, Google AdMob, and Firebase (Analytics/Crash reporting). Retention for those services is governed by their policies and configuration. We do not implement custom deletion routines for data stored and retained by these third-party services.
5.4 Legal and Accounting
Where required by law (for example, for accounting, taxation, or fraud prevention), certain transaction-related records may be retained for the legally required period.
๐6. Data Security
6.1 Technical Measures
- Encryption in Transit: HTTPS/TLS 1.3 for all transfers
- Encryption at Rest: Cloud data encrypted via GPGS, server data encrypted via PostgreSQL (AES-256)
- Server Isolation: Fly.io infrastructure with network isolation
- No Payment Storage: Payment data remains with Google (never stored locally)
- Input Validation: Protection against SQL injection, XSS attacks
- JWT Authentication: Secure tokens with 30-day expiration, HS256 signature algorithm
- PostgreSQL Encryption: Data encrypted at rest via Neon infrastructure (AES-256)
- Friend Code Hashing: Non-reversible deterministic hashing (cannot be traced back to Player ID)
- Challenge Data Volatility: Challenges stored in RAM only (60s max), never written to disk
- Purchase Token Validation: Tokens sent to server over HTTPS/TLS 1.3, validated once, then discarded
- Request Timeouts: 15 seconds maximum per API request (prevents hanging)
- Cold Start Handling: Extended timeout (12s) on server wake-up (Fly.io sleep mode)
๐WebSocket Connection Retry Logic
- Connection Timeout: 30 seconds maximum (60 attempts ร 500ms intervals)
- Retry Mechanism: Automatic retry every 500ms until connected or timeout
- Reconnection Window: 60 seconds grace period after disconnect before match abandonment
Purpose: Handle temporary network interruptions, prevent match loss due to brief disconnects
๐ก๏ธRate Limiting
Protection Levels:
- General API: 100 requests/minute per IP address
- Score Submission: 50 requests/minute per IP address
- Friend System: 30 requests/minute per IP address
- Auth Endpoints: 5 requests/minute per IP address (failed attempts counted)
Data Collected for Rate Limiting:
- IP address (temporary tracking for rate limit enforcement only)
- Request counters per IP (rolling window)
- Timestamp of first request in current window
Implementation:
- Express-rate-limit middleware (server-side)
- Rolling window algorithm (resets every 60 seconds)
- HTTP 429 "Too Many Requests" response when limit exceeded
Retention: 60 seconds (rolling window), then automatically cleared
Purpose: Prevent API abuse, DDoS attacks, score manipulation via spam
Legal Basis: Legitimate interest (security - GDPR Article 6.1.f)
๐จAnti-Cheat - Suspicious Score Flagging
Detection Criteria:
- Scores exceeding maximum thresholds:
- Solo Easy: > 5000 points
- Solo Medium: > 8000 points
- Solo Hard: > 10000 points
- Solo Expert: > 15000 points
- Multi Classic: > 7000 points
- Multi PowerUp: > 12000 points
- Time Attack Classic: > 6000 points
- Time Attack PowerUp: > 10000 points
- Negative scores (impossible under normal gameplay)
- Invalid difficulty/mode combinations
- Submission rate anomalies (via rate limiting)
Flagging Process:
- Suspicious scores are logged to PostgreSQL table:
suspicious_scores - Data stored: player_id, score, game_type, difficulty, reason (validation failure message), flagged_at (timestamp)
- Flagged scores are rejected (HTTP 400 Bad Request) - NOT saved to leaderboards
- Console log generated:
๐จ Score suspect: {playerId} - {score} pts ({reason})
Storage & Retention:
- Database: PostgreSQL (Neon) - table: suspicious_scores
- Retention: Indefinite (for fraud investigation and pattern analysis)
- Access: Internal review only (not shared with third parties)
Your Rights:
- Request deletion of flagged records via email: contact.thecatsizerlab@gmail.com
- Appeal false positives with evidence (screenshots, gameplay videos)
- GDPR Right to Erasure applies (Article 17) - unless fraud investigation ongoing
Purpose: Detect and prevent score manipulation, maintain leaderboard integrity, protect fair play
Legal Basis: Legitimate interest (fraud prevention - GDPR Article 6.1.f)
6.2 Organizational Measures
- Access to data limited to authorized personnel only
- Confidentiality agreements with all service providers
- Regular security audits
- Automated dependency updates and security patches
โ7. Your Privacy Rights
7.1 Right of Access (GDPR Article 15)
You can request access to your personal data.
How to Request:
- Email: contact.thecatsizerlab@gmail.com
- Response Time: 30 days
Data Accessible Directly:
- GPGS Profile: Google Play Games > Settings
- Purchase History: Google Play Store > Account > Payments and Subscriptions
- Google Privacy Settings: myaccount.google.com
7.2 Right of Correction (GDPR Article 16)
You can correct inaccurate data about yourself.
- Edit your GPGS profile directly in Google Play Games
- Update preferences within the Application settings
7.3 Right to Erasure (GDPR Article 17)
You can request deletion of your personal data. However, certain data must be retained for the app to function properly.
๐๏ธWhat Can Be Deleted
| Data Type | Deletion Method | Effect |
|---|---|---|
| Google Play Games Profile | Delete GPGS account via Google Settings | Complete account removal from GPGS (managed by Google) |
| Local Device Data | Uninstall app or Clear Data in Android Settings | All local progress deleted |
| Specific Friendships | Settings > Friends > Remove Friend | Individual friend removed from your list |
| JWT Tokens | Automatic after 30 days OR manual logout | Re-authentication required |
| Temporary Game Backups | Automatic after 1 hour | No manual action needed |
๐What CANNOT Be Deleted (Service Integrity)
The following data is essential for the app to function and cannot be deleted while you maintain an active account:
| Data Type | Reason for Retention | Legal Basis (GDPR) |
|---|---|---|
| Leaderboard Scores | Required for competitive ranking integrity. Deleting scores would artificially boost other players' ranks. | Article 17.3.b (Public Interest) + Article 6.1.b (Contract) |
| Player Profile Statistics (games played, win rate, etc.) |
Core functionality of your account. Without stats, profile features are non-functional. | Article 6.1.b (Contract - necessary for service provision) |
| Friend Connections | Social feature foundation. Your friends list is part of your account data. | Article 6.1.b (Contract) + Article 6.1.a (Consent - given when adding friends) |
| Friend Code | Unique identifier for friend system. Required for other players to find you. | Article 6.1.b (Contract) |
| Daily Challenge History | Historical leaderboard integrity. Past rankings cannot be retroactively altered. | Article 17.3.b (Public Interest) |
| Match History | Used for matchmaking balancing and anti-cheat systems. | Article 6.1.f (Legitimate Interest - fraud prevention) |
| Premium Purchases | Legal requirement for tax records and fraud prevention. | Article 17.3.b (Legal Obligation - 7 years retention, France/EU law) |
| Player Name (GPGS) | Managed by Google Play Games Services. We cannot delete GPGS data. | Third-party service (see Google's Privacy Policy) |
- Data is necessary for contract performance (Article 6.1.b) โ
- Data serves a public interest (leaderboard integrity - Article 17.3.b) โ
- Data is required by law (tax records - Article 17.3.b) โ
Note: Deleting data linked to Google Play Games is managed by Google through Google Play Games settings. For our own server-side records, we can delete or anonymize where feasible, subject to integrity and legal obligations.
๐How to Completely Remove Your Data
If you want to completely erase your presence from the app, you must:
- Delete Your Google Play Games Account
- Open Google Play Games app
- Tap Profile > Settings
- Select "Delete Play Games account & data"
- Effect: Google manages deletion timelines according to its own policies and processes.
- Uninstall Sudoku Battle
- Long-press app icon > Uninstall
- Effect: All local device data deleted immediately
- Request Server Data Cleanup (Optional)
- Email contact.thecatsizerlab@gmail.com
- Provide proof of GPGS account deletion
- We will delete: Orphaned server records (scores, friendships, etc.) within 90 days
- We will retain: Purchase records (7 years - legal requirement)
- Your former Player ID (if known)
- Your former display name
- Screenshot of GPGS account deletion confirmation
Processing time: 30 days maximum
โ๏ธGDPR Compliance Justification
We comply with GDPR while maintaining service integrity through these legal bases:
- Article 6.1.b (Contract Necessity): Your game data, stats, and friends are necessary to provide the service you signed up for. Deleting them would render your account non-functional.
- Article 17.3.b (Public Interest): Leaderboard integrity is a public interest for all competitive players. Retroactive score deletion would unfairly manipulate rankings.
- Article 17.3.b (Legal Obligation): Purchase records must be retained 7 years for tax compliance (French/EU law).
- Article 6.1.f (Legitimate Interest): Match history used for anti-cheat and matchmaking balance.
You can stop app data collection by uninstalling the Application. You may also request deletion or anonymization of our server-side records where feasible, subject to leaderboard integrity and legal obligations. Data handled by Google services (Play Games / Firebase / AdMob) is managed under Google's controls.