BILLIARD SaaS + IoT AUTOMATION PLATFORM
MASTER TECHNICAL SPECIFICATION FOR CLAUDE CODE
You are the lead software architect, senior full-stack engineer, DevOps engineer, database engineer, security engineer and IoT engineer for this project.
Your task is to BUILD A REAL PRODUCTION-READY SYSTEM, not a demo, prototype or toy project.
The system will be commercially used by multiple independent billiard hall owners. It must be designed to operate continuously for years, support multiple clients, isolate every client’s data, control billiard-table lights through ESP32 devices, run on Android tablets, store session photos on the owner’s hosting, and send reports through Telegram.
Do not skip architecture, security, error handling, offline behavior, logging, backups, migrations or testing.
⸻
The platform belongs to the platform owner.
The platform owner rents access to the software to billiard-hall owners through a subscription/license model.
The platform owner does NOT manage the client’s billiard business.
The platform owner only manages:
The client manages everything inside their own account:
The client may have multiple branches.
Example:
CLIENT A
The number of allowed branches is controlled by the platform owner through the client’s license.
⸻
This is a MULTI-TENANT SaaS system.
Every client is a completely isolated tenant.
CLIENT A must NEVER be able to access:
Do not rely only on frontend filtering.
Tenant isolation MUST be enforced on the backend/database authorization layer.
Every tenant-owned entity must have tenant ownership.
Recommended conceptual structure:
tenant ├── users ├── branches ├── tables ├── devices ├── sessions ├── session_photos ├── payments/session_records ├── reports ├── telegram_config └── settings
Never trust tenantId supplied by the client.
Resolve tenant identity from the authenticated user/device/session on the server.
⸻
Create a separate Super Admin area.
The Super Admin can:
The platform owner does NOT configure the client’s tables or daily business operations unless explicitly required for support.
⸻
Clients pay the platform owner manually.
No online payment gateway is required in the first version.
Payment can be:
The Super Admin records the payment manually.
Example:
Client: Ali Billiard
Amount: 500,000 UZS
Payment method: CASH
Subscription: 30 days
Branch limit: 2
After activation:
status = ACTIVE
When expiration is approaching:
5 days before expiration: send/display warning.
Example:
“Your system subscription expires in 5 days.”
When expiration is reached:
status = EXPIRED
The client must not be able to use the management system normally.
IMPORTANT:
Subscription expiration MUST NOT delete client data.
All data remains stored.
After the client pays again, Super Admin can extend the subscription.
Example:
current expiration: 2026-10-31
add 30 days:
new expiration: 2026-11-30
Support both:
Create a complete subscription history.
⸻
The client can create branches themselves.
However, they cannot exceed the number of branches allowed by their license.
Example:
license branchLimit = 2
Client currently has:
Branch 1 Branch 2
If they try to create Branch 3:
show:
“Your branch limit has been reached.”
The Super Admin can increase the limit.
Example:
2 -> 5
The client immediately becomes able to create additional branches.
Do NOT make the platform owner manually create each branch.
⸻
The client admin panel must contain:
Dashboard
Show:
Branches
Client can:
Tables
For every branch:
Example:
TABLE 01
Price: 20,000 UZS / hour
Device: ESP32-001
Status: ONLINE
Pricing
Support configurable pricing.
At minimum:
price per hour
Design the database so future pricing models can be added without rewriting the system.
Working Hours
Client can configure:
Users
Allow client owner to create additional staff users.
Example roles:
OWNER MANAGER OPERATOR
Do not force every employee to share the owner’s password.
Devices
Client can see:
Sessions
Client can see:
Reports
Daily/monthly reports:
⸻
Each billiard branch has an Android tablet.
The tablet is a dedicated kiosk device.
The tablet must remain inside the billiard application.
Users must not be able to normally:
Use Android kiosk/lock-task architecture where possible.
The tablet should automatically launch the billiard application after reboot.
The app should recover automatically after crashes/reboots.
⸻
The exact customer flow is:
STEP 1
Customer arrives.
Tablet displays all tables.
Example:
TABLE 1 - AVAILABLE TABLE 2 - BUSY TABLE 3 - AVAILABLE TABLE 4 - AVAILABLE
STEP 2
Customer selects an available table.
STEP 3
Customer selects duration.
Example:
30 minutes 1 hour 2 hours etc.
STEP 4
System calculates the session amount.
STEP 5
Tablet activates its FRONT CAMERA.
Important:
This camera is ONLY the tablet camera.
The billiard hall’s separate CCTV/cash camera is NOT part of this system.
STEP 6
Tablet displays:
“Please look at the camera.”
Use face detection to determine whether a face is visible and sufficiently positioned.
Do NOT implement unnecessary facial recognition/identity matching.
The requirement is to capture a customer photo when a face is properly visible.
STEP 7
Capture ONE customer photo.
Do not continuously record video.
STEP 8
Upload the photo securely to the platform server/storage.
STEP 9
Create session.
Example:
sessionId tenantId branchId tableId deviceId startAt endAt duration price photoId status
STEP 10
Send START command to the corresponding ESP32.
STEP 11
ESP32 turns the table light ON.
STEP 12
Tablet displays countdown timer.
⸻
There are TWO DIFFERENT CAMERA SYSTEMS.
CAMERA A — TABLET CAMERA
This belongs to this platform.
Purpose:
The photo is stored on the platform’s hosting/storage.
CAMERA B — CASH/CCTV CAMERA
This belongs to the billiard hall.
Purpose:
THIS CAMERA IS NOT PART OF OUR SYSTEM.
Do NOT:
The billiard hall manages its own CCTV server/NVR independently.
Our system knows NOTHING about the cash camera.
This separation is mandatory.
⸻
The system does NOT automatically verify the physical cash payment.
The customer selects:
table + duration.
The system calculates the amount.
The customer pays cash separately to the hall’s cashier/cash register.
The cash CCTV independently records the payment process.
The platform only records the session information and customer photo.
Do NOT implement fake “cash detected by camera” functionality.
Do NOT claim that the platform verified payment through CCTV.
If a payment status field is required, make it manually configurable by authorized client staff.
⸻
When a session starts:
Example:
Start: 14:00
End: 15:00
The system must prevent normal customers from changing the active session.
While active:
At 5 minutes remaining:
trigger warning.
⸻
At:
remaining time <= 5 minutes
the system should trigger:
Example audio:
“1-stol, sizda 5 daqiqa vaqtingiz qoldi.”
The exact language must be configurable in the future.
Important:
The audio system can be implemented separately from the ESP32 if required.
Design an event system so the warning can trigger reliably.
⸻
At session expiration:
Do not rely only on the browser timer.
The backend must store authoritative start/end timestamps.
The ESP32 must also have local fail-safe timing.
⸻
This is critical.
The system must continue safely if the internet temporarily fails.
When ESP32 receives:
START
it should receive enough information to operate safely.
Example:
sessionId startTimestamp endTimestamp tableId
ESP32 stores the active session state locally.
If internet connection disappears:
When connection returns:
ESP32 synchronizes state with server.
Implement heartbeat.
Example:
ESP32 -> server:
deviceId timestamp firmwareVersion currentState currentSessionId
The server should show:
ONLINE or OFFLINE
based on heartbeat timeout.
⸻
Use ESP32 as the table controller.
Recommended conceptual architecture:
Internet/Network | Wi-Fi Router | ESP32 | Relay/Driver/Contactor | Table Light
Each table should preferably have its own ESP32.
Example:
ESP32-001 -> Table 1 ESP32-002 -> Table 2 ESP32-003 -> Table 3
etc.
Every ESP32 must have a unique device identity.
Example:
deviceId: ESP32-001-A8F4
Do not identify devices only by IP address.
⸻
Primary communication:
Wi-Fi
Recommended protocol:
MQTT or another reliable device messaging protocol.
Design the system so commands are authenticated.
Example:
platform -> MQTT broker -> ESP32
Commands:
START_SESSION STOP_SESSION WARNING SYNC PING CONFIG_UPDATE
Do not expose unauthenticated device commands to the local network.
Every device must authenticate.
⸻
New ESP32 should not automatically belong to a client.
Use secure pairing.
Example:
ESP32 displays/creates pairing code:
482719
Client Admin:
Devices -> Add Device -> enter pairing code -> choose branch -> choose table -> confirm
Then:
deviceId is permanently associated with:
tenantId branchId tableId
until explicitly unpaired.
A device belonging to Client A must never be usable by Client B without a secure re-pairing process.
⸻
Never trust:
tenantId branchId tableId
sent by an arbitrary device.
The server must resolve ownership from the authenticated device identity.
Implement:
⸻
Do not connect ESP32 GPIO directly to 220V.
Recommended conceptual chain:
ESP32 -> driver/optocoupler -> relay or contactor -> 220V lighting circuit
Use appropriate:
The exact relay/contactor rating must be calculated from the actual lamp load.
Electrical mains wiring must be performed according to local electrical safety requirements by a qualified person.
⸻
Primary communication architecture should be:
Tablet -> Backend -> MQTT/device service -> ESP32
Do NOT make the system depend exclusively on:
Tablet -> ESP32 direct communication.
The tablet can communicate through the backend.
USB may be supported for service/debugging if useful, but it should NOT be the primary production communication method.
⸻
Use a production-grade relational database such as PostgreSQL if supported by the hosting architecture.
Design normalized tables/entities.
At minimum:
users tenants subscriptions subscription_payments branches tables devices device_pairings sessions session_photos pricing working_hours telegram_integrations audit_logs notifications system_settings
Recommended additional entities:
refresh_tokens / sessions device_heartbeats subscription_events branches_settings table_settings failed_jobs notification_logs
Every tenant-owned entity must have tenant ownership.
Use foreign keys and database constraints.
Do not depend solely on application code for relational integrity.
⸻
Do NOT store customer photos directly inside database blobs unless there is a strong reason.
Store files in hosting/object storage.
Database stores:
photoId sessionId tenantId storagePath mimeType size createdAt deletedAt
Directory/object structure should isolate tenants.
Example:
/uploads/tenants/{tenantId}/sessions/{sessionId}/customer-photo.jpg
Prevent path traversal and unauthorized direct access.
Prefer private storage with authenticated access rather than public image URLs.
⸻
The system must support deletion.
Client admin may delete the session photo after the business need has ended, subject to configured permissions.
Record deletion in audit log.
Do not silently delete without trace.
Because customer photos are personal data, implement:
Do not implement facial recognition unless explicitly required later.
⸻
Each client can connect/configure their own Telegram bot.
The bot must only expose that client’s data.
Example daily report:
TODAY’S REPORT
Branch: Central
Sessions: 37
Total playing time: 29h 20m
Session amount: 740,000 UZS
Table 1: 6h 120,000 UZS
Table 2: 4h 80,000 UZS
etc.
The client may optionally receive session photo information.
Do not use Telegram as the primary storage system.
Photos remain in platform storage.
Telegram is a notification/reporting channel.
⸻
The intended production environment is Hostmaster hosting.
Before implementing deployment:
INSPECT THE AVAILABLE HOSTMASTER ENVIRONMENT.
Determine whether it supports:
Do NOT assume unsupported services are available.
If shared hosting cannot safely run MQTT/background services, design the architecture so the supported components remain on Hostmaster and only a technically necessary external/VPS service is proposed.
However, do not introduce external infrastructure unnecessarily.
The user’s requirement is that the main application, database/files and Telegram integration should operate through their hosting where technically supported.
⸻
Build a clean REST API or equivalent backend.
Recommended modules:
/auth /super-admin /clients /subscriptions /branches /tables /devices /sessions /photos /reports /telegram /notifications /audit /health
Use strict authorization middleware.
Example:
authenticate() authorizeRole() resolveTenant() checkSubscription() validateResourceOwnership()
Do not duplicate authorization logic randomly throughout controllers.
Centralize it.
⸻
Implement secure authentication.
Super Admin: separate role and permissions.
Client Owner: tenant-scoped.
Client Manager: tenant-scoped.
Client Operator: tenant-scoped.
Device: device authentication, not normal user authentication.
Tablet: device/session authentication.
Use:
Never store plaintext passwords.
⸻
Roles:
SUPER_ADMIN
CLIENT_OWNER
CLIENT_MANAGER
CLIENT_OPERATOR
DEVICE
TABLET
Define permissions explicitly.
Example:
CLIENT_OPERATOR:
CLIENT_MANAGER:
CLIENT_OWNER:
SUPER_ADMIN:
Never use frontend role checks as the only security layer.
⸻
Every client request that requires an active subscription must pass through subscription validation.
Possible states:
ACTIVE EXPIRING_SOON EXPIRED SUSPENDED
When expired:
allow only necessary restricted actions such as:
Do not allow normal business operation.
Do not delete data.
⸻
Record important actions.
Examples:
SUPER_ADMIN:
CLIENT:
DEVICE:
Store:
actor actorType tenantId action entityType entityId timestamp metadata
Avoid storing unnecessary sensitive information.
⸻
Implement:
Do not allow duplicate session creation because of double-clicks or network retries.
Use idempotency keys or equivalent where appropriate.
⸻
This is critical.
Two people must not be able to start two simultaneous sessions on the same table.
Use backend/database-level transaction and locking strategy.
Example:
TABLE 1: AVAILABLE
Request A -> starts session
Request B -> arrives at same time
Only ONE request may succeed.
The other must receive:
“Table is no longer available.”
Do not solve this only in JavaScript frontend.
⸻
Use explicit states.
Example:
AVAILABLE RESERVED STARTING ACTIVE WARNING COMPLETING COMPLETED CANCELLED FAILED
Do not use random booleans like:
isPlaying isFinished isStarted isStopped
as the only state mechanism.
Use a well-defined state machine.
⸻
Every command should have:
commandId deviceId sessionId createdAt expiresAt status
Possible status:
PENDING SENT ACKNOWLEDGED FAILED EXPIRED
ESP32 should acknowledge important commands.
Example:
SERVER: START_SESSION command #ABC123
ESP32: ACK #ABC123
Server: command status = ACKNOWLEDGED
If ACK does not arrive: retry according to policy.
Do not blindly send commands repeatedly.
⸻
If server says:
END at 15:30
ESP32 must locally know that end time.
If MQTT disconnects:
ESP32 continues safely.
At 15:30:
light OFF.
If the device reboots:
it should recover its local state and query the server for authoritative state.
Avoid a situation where a reboot causes the light to remain permanently ON.
⸻
Create responsive web admin interface.
Super Admin UI: desktop/tablet friendly.
Client Admin: desktop/tablet/mobile friendly.
Tablet Customer UI: large touch targets minimal UI fullscreen kiosk design high readability.
Do not overload the tablet UI with administrative features.
⸻
Tablet must have a unique device identity.
Example:
TABLET-CLIENTA-BRANCH1-001
Use secure pairing.
Client admin:
Devices -> Add Tablet -> Pairing Code -> Branch selection -> Save
Tablet cannot simply enter another tenantId manually.
⸻
The tablet should cache enough information to display:
However, starting a new session should be carefully controlled because the backend is authoritative.
If network is unavailable:
display:
“Connection unavailable. Please wait.”
Do not create unverified duplicate sessions.
For active sessions, the tablet should continue displaying countdown using the stored session end timestamp.
⸻
Do not rely on the user’s device clock for business-critical session calculations.
Use server timestamps.
Use UTC internally.
Convert to client/branch timezone for display.
The branch timezone must be configurable.
⸻
Implement notification service.
Examples:
Subscription expires in 5 days.
Subscription expires in 3 days.
Subscription expires tomorrow.
Subscription expired.
Device offline.
Device back online.
Session warning.
Use database-backed notification logs.
Do not send duplicate notifications unnecessarily.
⸻
Show:
Total clients Active clients Expiring clients Expired clients Suspended clients
Revenue recorded from manual subscription payments
Device health summary
System health
Recent audit events
Do NOT expose unnecessary customer personal data.
⸻
Show only the client’s own tenant data.
Example:
Today: 37 sessions
Playing: 5
Available: 3
Today’s session amount: 740,000 UZS
Unpaid: 2
Device status: 7 online 1 offline
⸻
Before declaring the project finished, test:
⸻
Implement a backup strategy.
At minimum:
Do not consider “we have hosting” as a backup strategy.
⸻
Never hardcode:
Use environment variables / secure configuration.
Provide:
.env.example
without real secrets.
⸻
Use a clean modular architecture.
Do not create one enormous file containing the entire application.
Separate:
frontend backend database device firmware tablet app shared types documentation
Conceptually:
/apps /web-admin /tablet /api
/devices /esp32
/packages /shared-types /validation /protocol
/infrastructure /database /deployment /mqtt
/docs architecture.md api.md device-protocol.md deployment.md security.md testing.md
Adapt the exact structure to the selected technology stack.
⸻
Before writing large amounts of code:
Preferred architecture:
Frontend: React / Next.js or another stable production framework.
Backend: Node.js/NestJS/Express OR another robust backend supported by hosting.
Database: PostgreSQL preferred.
IoT: ESP32 firmware in C++/PlatformIO or ESP-IDF.
Protocol: MQTT if infrastructure supports it; otherwise design a secure WebSocket/HTTPS fallback.
Android tablet: A dedicated Android application or reliable kiosk-capable web/PWA architecture depending on hardware requirements.
Do not blindly use technologies just because they are popular.
Choose what is actually deployable on the target infrastructure.
⸻
localStorage may be used only for:
Do NOT store the actual business database in localStorage.
The authoritative data must be on the server/database.
⸻
Every resource must have an explicit ownership chain.
Example:
Session -> Table -> Branch -> Tenant
Photo -> Session -> Table -> Branch -> Tenant
Device -> Table -> Branch -> Tenant
The backend must verify this ownership chain.
⸻
Never show raw backend errors to customers.
Create user-friendly messages.
Log technical details securely on the server.
Example:
User:
“Something went wrong. Please try again.”
Server log:
SESSION_START_FAILED reason: DEVICE_OFFLINE
Do not expose stack traces.
⸻
Create OpenAPI/Swagger documentation if practical.
Document:
authentication clients subscriptions branches tables sessions devices photos reports telegram health
Document request/response schemas.
⸻
Create a dedicated document:
docs/device-protocol.md
Document:
device registration pairing authentication heartbeat START_SESSION STOP_SESSION WARNING SYNC ACK reconnect offline behavior firmware update strategy
⸻
The firmware must include:
Implement a watchdog so the device can recover from unexpected software hangs.
On boot:
⸻
The software must assume the relay/contactor is controlling potentially dangerous voltage.
Never expose 220V directly to ESP32 GPIO.
Provide hardware fail-safe behavior.
Use proper electrical isolation.
The final electrical design must be reviewed by a qualified electrician.
⸻
Design for future OTA firmware updates.
Each device reports:
firmwareVersion
Later the platform owner can publish:
firmware version 1.1.0
Devices can be updated securely.
Do not implement insecure arbitrary firmware download.
⸻
Provide:
GET /health
and if practical:
GET /health/db GET /health/storage GET /health/messaging
Super Admin should be able to see basic system health.
⸻
Protect:
from abuse.
⸻
Validate:
Do not trust filename.
Generate server-side filenames.
Prevent executable files from being uploaded as images.
⸻
The customer photo is personal data.
Design the system so:
The client should be responsible for informing customers about the photo capture according to applicable local law.
Do not implement facial recognition unless separately required.
⸻
Create automated tests where practical.
At minimum:
Unit tests:
Integration tests:
End-to-end tests:
IoT tests:
⸻
Test this exact scenario:
CLIENT A has 2 branches.
CLIENT B has 5 branches.
A must never see B.
CLIENT A creates Branch 1 and Branch 2.
Attempt Branch 3.
Expected: DENIED.
Super Admin changes limit from 2 to 3.
Attempt Branch 3 again.
Expected: SUCCESS.
Then create Table 1.
Pair ESP32.
Start a 10-minute test session.
Verify:
photo captured session created ESP32 receives command light turns on timer runs 5-minute warning triggers session ends light turns off table becomes available
Disconnect internet during active session.
Expected: ESP32 continues safely and turns light off at end.
Reconnect.
Expected: state synchronizes.
⸻
Never create fake implementations such as:
If a real external dependency is unavailable, clearly mark the integration point and implement a proper adapter/interface.
Do not pretend a feature works when it doesn’t.
⸻
DO NOT generate the entire project blindly in one pass.
Follow this process:
PHASE 1: Inspect project and hosting environment.
PHASE 2: Create architecture documentation.
PHASE 3: Create database schema and migrations.
PHASE 4: Implement authentication and multi-tenancy.
PHASE 5: Implement Super Admin.
PHASE 6: Implement Client Admin.
PHASE 7: Implement branch/table/session system.
PHASE 8: Implement tablet application.
PHASE 9: Implement photo capture/upload.
PHASE 10: Implement ESP32 communication.
PHASE 11: Implement firmware.
PHASE 12: Implement Telegram.
PHASE 13: Implement subscription expiration/notifications.
PHASE 14: Implement monitoring/logging/backups.
PHASE 15: Security testing.
PHASE 16: Production deployment.
After each phase:
⸻
Use Git properly.
Create meaningful commits.
Do not commit:
.env secrets API keys passwords Telegram bot tokens MQTT credentials
Create:
.env.example
⸻
The finished project must include:
⸻
Do not declare the project complete until these work:
SUPER ADMIN:
CLIENT:
TABLET:
ESP32:
SERVER:
⸻
The platform owner sells ACCESS TO THE SOFTWARE.
The platform owner does NOT own or operate the client’s billiard halls.
The client controls their own billiard business.
The only platform-level restrictions are the license/subscription restrictions configured by the Super Admin.
The client can use their licensed system across the number of branches allowed by their subscription.
⸻
Remember this permanently during implementation:
TABLET CAMERA: YES — part of this system. Purpose: capture one customer face/photo before session.
CASH CCTV CAMERA: NO — NOT part of this system. Purpose: client’s independent cash/CCTV recording. Storage: client’s own DVR/NVR/server. Our backend: NO ACCESS.
Never accidentally implement CCTV integration.
⸻
PLATFORM SUBSCRIPTION PAYMENT:
Client pays the platform owner manually by cash/bank/card. Super Admin records it manually. No payment gateway required in first version.
BILLIARD SESSION PAYMENT:
Customer pays the billiard hall separately. The platform does NOT verify the cash through CCTV. The cash CCTV belongs to the client and remains independent.
⸻
Before writing implementation code:
docs/ARCHITECTURE.md docs/DATABASE.md docs/SECURITY.md docs/DEVICE_PROTOCOL.md docs/DEPLOYMENT.md
If an architectural decision is required, choose the simplest production-grade solution that satisfies the requirements.
Do not reduce this project to a demo.
The final objective is a real commercial SaaS + IoT system that can be sold to many independent billiard hall owners and operated reliably for years.