Internal Technical Reference

TouchToFix
Technical Documentation

Complete system reference covering architecture, API, database schema, mobile apps, remote desktop (TTFDesk), deployment, and security.

Version
v2.0 (vnext)
Date
March 2026
Status
Production
API Base
api.touchtofix.com
Platform
GCP us-central1-f
Table of Contents

01 System Architecture

TouchToFix v2 (vnext) is a microservice-oriented platform deployed on Google Cloud Platform. All components run in Docker containers orchestrated by Docker Compose behind an nginx reverse proxy with TLS termination.

📱
Customer App
Flutter · Dart
app.touchtofix.com
🧑‍💻
Expert App
Flutter · Dart
expert.touchtofix.com
📊
Admin Panel
React + Vite
admin.touchtofix.com
⚙️
Backend API
NestJS + Fastify
api.touchtofix.com · :3100
🗄️
PostgreSQL
postgres:16-alpine
:5432 (internal)
Redis
redis:7-alpine
:6379 (internal)
🌐
FixMe Portal
Vanilla JS · WebRTC
fixme.touchtofix.com
🎮
TTFDesk (hbbs)
RustDesk Server
:21115 :21116 :21118
🔄
TTFDesk (hbbr)
RustDesk Relay
:21117 :21119
ℹ️
All external traffic enters via nginx on ports 80/443. nginx terminates TLS using Let's Encrypt certificates and proxies to internal Docker services by container name. WebSocket upgrade is handled for /ws routes.

Request Flow

Browser / Mobile App
    ↓ HTTPS (443)
nginx (nginx:alpine)
    ↓ reverse proxy by Host header
    ├── api.touchtofix.com → api:3100 (NestJS)
    │       ↕ Prisma ORM
    │       ├── postgres:5432
    │       └── redis:6379 (WebSocket relay)
    ├── app.touchtofix.com → static Flutter web (customer)
    ├── expert.touchtofix.com → static Flutter web (expert)
    ├── admin.touchtofix.com → admin:80 (React SPA)
    ├── fixme.touchtofix.com → fixme:80 (FixMe Portal)
    └── touchtofix.com → website:80 (Marketing)

02 Technology Stack

LayerTechnologyVersionPurpose
API FrameworkNestJS + Fastify^10.xREST API + WebSocket server
ORMPrisma^5.xType-safe database access
DatabasePostgreSQL16Primary data store
Cache / WSRedis7WebSocket message relay, session cache
Mobile / WebFlutter + Dart3.xCustomer + Expert apps (iOS/Android/Web)
Admin PanelReact + Vite18 / 5.xOperations dashboard
Language (API)TypeScript5.xStrict type-safety across backend
Reverse ProxynginxalpineTLS termination, routing, WebSocket upgrade
Container RuntimeDocker + Compose26.xService orchestration
AI / MLOllama (LLaVA)latestMultimodal AI diagnosis engine
Remote ControlRustDesk ServerlatestTTFDesk self-hosted relay (hbbs + hbbr)
Screen ShareWebRTC (browser native)FixMe Portal peer-to-peer stream
TURN Servercoturn4.6NAT traversal for WebRTC
TLS CertsLet's Encrypt / CertbotAuto-renewing HTTPS certificates
CloudGoogle Cloud PlatformVM: e2-standard-2, us-central1-f

03 API Reference

Base URL: https://api.touchtofix.com · All authenticated routes require: Authorization: Bearer {jwt}

🔑
Authentication: Obtain a JWT by completing OTP flow (POST /auth/otp/requestPOST /auth/otp/verify). Tokens are long-lived JWTs stored securely on-device.

Auth Module

POST/auth/otp/request
Request OTP to phone. Body: {"phone": "60123456789", "phoneSuffix": "+60"}
POST/auth/otp/verify
Verify OTP and receive JWT. Body: {"phone": "...", "phoneSuffix": "...", "code": "123456"}. Returns {"token": "jwt...", "user": {...}}
POST/auth/ping
Validate current JWT and return user profile. Use on app startup to check session validity.
🔒 Requires Bearer token
POST/auth/desktop-transfer
Create a one-time desktop transfer token (5-min TTL) + 6-digit OTP. Returns {"token", "otpCode", "expiresAt", "url"} where url is the QR destination.
🔒 Requires Bearer token
POST/auth/desktop-transfer/verify
Verify desktop transfer. Body: {"token": "...", "otpCode": "123456"}. Returns {"token": "jwt...", "user": {...}} — auto-logs in the PC browser.

Tickets Module

POST/tickets/request
Create a support ticket. Body: {"title", "description", "os", "priority": "URGENT|HIGH|MEDIUM|LOW"}
🔒 Customer
GET/tickets/me
List all tickets for the authenticated customer.
🔒 Customer
GET/tickets/queue
List open unassigned tickets available to accept. Expert-facing.
🔒 Expert
PUT/tickets/:id/accept
Expert accepts a ticket. Assigns it to the authenticated expert.
🔒 Expert
PUT/tickets/:id/status
Update ticket status. Body: {"status": "ACTIVE_SESSION|RESOLVED|CLOSED"}
🔒 Expert/Admin

Chat Module

POST/support/conversations
Create a new support conversation for a ticket. Returns conversation object with WebSocket channel.
🔒 Any
POST/chat/conversations/:conversationId/messages
Send a message. Body: {"content": "...", "type": "TEXT|IMAGE|FILE|rustdesk_request"}
🔒 Any
GET/chat/conversations/:conversationId/messages
Fetch message history. Returns paginated list ordered by createdAt ascending.
🔒 Any

Remote Sessions (VNC) Module

POST/vnc/sessions
Create a remote session for a ticket. Returns session ID used for all subsequent calls.
🔒 Any
POST/vnc/sessions/:id/rustdesk
Expert requests TTFDesk remote control. Backend auto-derives customer's TTFDesk ID from phone number + session count, generates OTP, sends rustdesk_request chat message. Returns {"sessionId", "ttfdeskId", "rustdeskOtp", "status"}
🔒 Expert
PUT/vnc/sessions/:id/recording
Toggle session recording. Body: {"enabled": true|false}
🔒 Expert
POST/vnc/sessions/:id/end
End the remote session. Records end time and triggers billing calculation.
🔒 Any
POST/vnc/sessions/:id/transfer
Create a FixMe Portal transfer token + OTP for screen-sharing session handoff. Returns {"token", "otpCode", "otpExpiresAt"}
🔒 Expert

AI Diagnosis Module

POST/ai/diagnose
Submit an AI diagnosis request. Body: {"description": "...", "imageBase64": "..."} (image optional). Async — returns request ID; poll for result.
🔒 Customer
GET/ai/requests/:id
Get AI diagnosis result. Returns {"status": "PENDING|COMPLETED|FAILED", "result": {...}}
🔒 Customer

Admin Module (admin key required)

All admin routes are protected by AdminAuthGuard. Pass admin API key in header: x-admin-key: {key}

GET/admin/users
List all users with pagination. Query: ?page=1&limit=50&role=CUSTOMER
PUT/admin/experts/:id/approval
Approve or reject an expert. Body: {"status": "APPROVED|REJECTED", "reason": "..."}
POST/admin/companies
Create a B2B company workspace. Body: {"name", "adminUserId", "slaPolicyId"}
PUT/admin/companies/:id/approval
Approve or reject a company. Body: {"status": "APPROVED|REJECTED"}
GET/admin/billing/summary
Get platform-wide billing summary. Returns total revenue, expert payouts, platform fees for a date range.
GET/admin/audit-logs
List all admin audit log entries. All admin actions are logged automatically.

WebSocket Events

Connect to: wss://api.touchtofix.com/ws with JWT in query: ?token={jwt}

EventDirectionPayload
message:newServer → ClientNew chat message object
ticket:updatedServer → ClientTicket status changed
session:startedServer → ClientRemote session created
session:endedServer → ClientSession terminated
notification:pushServer → ClientPush notification payload

04 Database Schema

PostgreSQL database managed by Prisma ORM. Migrations located at prisma/migrations/. Schema file: prisma/schema.prisma.

model User
id
String (uuid)
Primary key, auto-generated UUID
phone
String
Phone number digits only (e.g. 60123456789)
phoneSuffix
String
Country code prefix (e.g. +60)
role
Enum
CUSTOMER | EXPERT | ADMIN | COMPANY_ADMIN
name
String?
Display name
desktopTransfers
Relation
One-to-many DesktopTransferToken
model Ticket
id
String (uuid)
Primary key
userId
String
Customer who created the ticket
expertId
String?
Assigned expert (null if unassigned)
status
Enum
OPEN | ASSIGNED | ACTIVE_SESSION | RESOLVED | CLOSED | SNOOZED
priority
Enum
URGENT (2h) | HIGH (4h) | MEDIUM (8h) | LOW (24h)
os
String?
Customer's operating system
slaBreachedAt
DateTime?
Set when SLA threshold is exceeded
model VncSession
id
String (uuid)
Primary key
userId
String
Customer user ID
expertId
String?
Expert who connected
ticketId
String?
Associated ticket
recordingEnabled
Boolean
Whether session is being recorded
rustdeskOtp
String?
One-time password for TTFDesk remote control
rustdeskId
String?
Customer's TTFDesk ID (derived from phone + session count)
startedAt
DateTime
Session start timestamp
endedAt
DateTime?
Session end timestamp (null if active)
model DesktopTransferToken
id
String (uuid)
Primary key
userId
String
Owner user ID
token
String (unique)
32-byte hex random token
otpCode
String
6-digit numeric OTP
role
String
CUSTOMER | EXPERT (determines post-auth redirect)
expiresAt
DateTime
Token expiry (5 minutes from creation)
usedAt
DateTime?
Timestamp when token was consumed (null = unused)
model Message (Chat)
id
String (uuid)
Primary key
conversationId
String
Parent conversation
senderId
String
User who sent the message
senderType
Enum
CUSTOMER | EXPERT | SYSTEM
content
String
Message text or JSON for structured messages
type
Enum
TEXT | IMAGE | FILE | rustdesk_request | system
attachments
JSON?
For rustdesk_request: {"ttfdeskId","rustdeskOtp","relayServer","downloadUrl"}

05 Mobile Apps

Both apps share the same Flutter codebase style, design system, and dependency set. They are compiled to iOS (IPA), Android (APK/AAB), and Web (static HTML).

Customer App — ttf_customer_vnext

🔐 Authentication
OTP-based phone login with country code selector. Persistent JWT session via flutter_secure_storage.
🏠 Home Screen
Browse available experts. View expert profiles, ratings, and hourly rate. Filter by specialisation.
🤖 AI Diagnosis
Take/upload screenshot. Describe issue. AI returns step-by-step fix. Free for all users.
🎟️ Tickets
Create support ticket with OS, priority, description. Real-time status tracking. History list.
💬 Chat
Real-time WebSocket chat. Image/video/file upload. Renders TTFDesk setup card when expert requests remote control.
🖥️ Remote Session
QR scan to link mobile → FixMe web portal. View session status and recording toggle.
📱→🖥️ Desktop Transfer
Generates QR code + URL with 5-min token. Scan on PC browser to auto-login without re-authenticating.
👤 Profile + Billing
Edit profile, view balance, payment history, fix history.

Expert App — ttf_expert_vnext

✅ Registration + Approval
Expert registers with qualifications. Pending screen while admin reviews. Email/push on approval.
🎟️ Ticket Queue
Live queue of open tickets. Accept/reject. View customer description, priority, OS.
💬 Chat
Full chat with customer. Image sharing. TTFDesk request button. Session controls.
🎮 Remote Control
Create/end remote sessions. Request TTFDesk — auto receives customer ID + OTP. Toggle recording.
📱→🖥️ Desktop Transfer
QR code for expert to transfer session to PC browser. Same 5-min token pattern as customer.
👤 Profile
Set availability, specialisations, hourly rate. Toggle online/offline status.

Build Commands

# Customer web app
cd version2/mobile/ttf_customer_vnext
flutter build web --release
# Output: build/web/

# Expert web app
cd version2/mobile/ttf_expert_vnext
flutter build web --release
# Output: build/web/

# Android APK
flutter build apk --release

# iOS IPA (requires macOS + Xcode)
flutter build ipa --release

App Configuration

Both apps read from lib/config/app_config.dart:

class AppConfig {
  static const String apiBaseUrl  = 'https://api.touchtofix.com';
  static const String wsBaseUrl   = 'wss://api.touchtofix.com';
  static const String fixmeBaseUrl = 'https://fixme.touchtofix.com';
  static const String appName     = 'TouchToFix';
}

06 Authentication & Security

OTP Auth Flow

1. Client → POST /auth/otp/request  { phone, phoneSuffix }
      Server: generates 6-digit OTP, stores with 5-min TTL, sends via SMS
2. Client → POST /auth/otp/verify   { phone, phoneSuffix, code }
      Server: validates OTP, returns { token: JWT, user: {...} }
3. All subsequent requests: Authorization: Bearer {JWT}

Desktop Transfer Token Flow

1. Mobile app → POST /auth/desktop-transfer
      Server: creates 32-byte hex token + 6-digit OTP, expires in 5 min
      Returns: { token, otpCode, expiresAt, url }
      url = https://app.touchtofix.com?dt={token}&otp={otpCode}

2. Mobile app: displays QR code of url, shows OTP with countdown

3. PC browser opens url (scans QR or manual):
   Flutter web app detects ?dt + ?otp params in Uri.base
   → POST /auth/desktop-transfer/verify  { token, otpCode }
   → Server validates, marks token used, returns { token: JWT, user }
   → SessionStore.saveSession(token, userId)
   → Navigates to /home as logged-in user

Role-Based Access Control

RoleCapabilities
CUSTOMERCreate tickets, chat, remote sessions, AI diagnosis, profile management
EXPERTAccept tickets, chat, TTFDesk remote control, toggle recording
COMPANY_ADMINManage company team, view company tickets, company billing
ADMINFull platform access, user management, config, billing, audit logs

Security Measures

07 Remote Desktop — TTFDesk

TTFDesk is our branded self-hosted remote control infrastructure built on RustDesk open-source (AGPL-3.0). It provides full keyboard and mouse control of the customer's PC from the expert's PC.

Infrastructure Components

ComponentServicePortsPurpose
ID Serverrustdesk-hbbs21115, 21116 (TCP+UDP), 21118ID registration, peer discovery, NAT traversal coordination
Relay Serverrustdesk-hbbr21117, 21119Traffic relay when direct P2P fails

TTFDesk ID Derivation

The customer's TTFDesk ID is automatically derived from their phone number, eliminating the need for manual ID lookup or sharing:

// In vnc.service.ts → requestRustdesk()
const customer = await prisma.user.findUnique({
  where: { id: session.userId },
  select: { phone: true, phoneSuffix: true }
});
const sessionCount = await prisma.vncSession.count({
  where: { userId: session.userId }
});
const counter = String(sessionCount).padStart(4, "0");
const phoneDigits = (customer.phoneSuffix ?? "").replace(/\D/g, "")
                  + (customer.phone ?? "").replace(/\D/g, "");
const ttfdeskId = phoneDigits
  ? `${phoneDigits}-${counter}`    // e.g. "60123456789-0001"
  : `ttf-${session.userId.slice(0,8)}-${counter}`;

Remote Control Flow

1. Expert taps "Request Remote Control (TTFDesk)" in expert app
2. Backend: derives ttfdeskId, generates OTP, stores in VncSession
3. Backend sends chat message { type: "rustdesk_request", ttfdeskId, rustdeskOtp, relayServer, downloadUrl }
4. Customer sees "TTFDesk Remote Control Request" card in chat:
   Step 1: Download TTFDesk from touchtofix.com/ttfdesk
   Step 2: In TTFDesk Settings → ID Server → set: api.touchtofix.com
   Step 3: In TTFDesk Settings → set Custom ID to: {ttfdeskId}
   Step 4: Set password to: {otp}
   Step 5: Confirm — expert will connect shortly
5. Expert sees: Customer TTFDesk ID + Password in styled card
6. Expert opens TTFDesk on PC, enters ID, connects with OTP

Client Configuration (TTFDesk App)

SettingValue
ID Serverapi.touchtofix.com
Relay Serverapi.touchtofix.com:21117
Public Key85klgfZt5P916CGhvBwiDdKljRSavIHZ2ERa3V7eriI=
Custom ID format{phoneSuffix digits}{phone}-{0001}
⚠️
TTFDesk (RustDesk) client must be downloaded and installed on the customer's PC. The expert's PC also needs TTFDesk to initiate the connection. iOS/Android TTFDesk apps exist but remote control is only supported from PC → PC.

FixMe Portal — Screen Share (WebRTC)

For lighter interactions, the FixMe Portal provides browser-based screen sharing without software installation:

08 Billing Engine

Session Billing Model

Billing Calculation

durationMinutes = ceil((endedAt - startedAt) / 60_000)
slabs = ceil(durationMinutes / 15)
grossAmount = slabs * (expertRate / 4)  // rate is per hour ÷ 4
platformFee = grossAmount * 0.20
expertPayout = grossAmount * 0.80

// B2B discount applied before calculation:
grossAmount *= (1 - company.discountPercentage / 100)

Admin Billing Controls

09 Deployment Guide

Server Access

gcloud compute ssh ttfnewv02 --zone=us-central1-f
# Working directory: /opt/ttf
# Compose file: docker-compose.prod.yml

Deploy Backend Update

# 1. Build locally
cd version2/backend/ttf_backend_vnext
npm run build

# 2. Package dist
tar -czf /tmp/backend_dist.tar.gz dist/ package.json package-lock.json

# 3. Upload to VM
gcloud compute scp /tmp/backend_dist.tar.gz ttfnewv02:/tmp/ --zone=us-central1-f

# 4. On VM: extract + rebuild container
cd /opt/ttf/backend/ttf_backend_vnext
tar -xzf /tmp/backend_dist.tar.gz
cd /opt/ttf
docker compose -f docker-compose.prod.yml up -d --build api

# 5. Restart nginx
docker compose -f docker-compose.prod.yml restart nginx

Deploy Flutter Web Apps

# Build
flutter build web --release

# Package + upload
tar -czf /tmp/customer_web.tar.gz -C ttf_customer_vnext/build web
gcloud compute scp /tmp/customer_web.tar.gz ttfnewv02:/tmp/ --zone=us-central1-f

# On VM: deploy
sudo rm -rf /opt/ttf/web/customer/*
mkdir -p /tmp/customer_web_tmp
tar -xzf /tmp/customer_web.tar.gz -C /tmp/customer_web_tmp
sudo cp -r /tmp/customer_web_tmp/web/* /opt/ttf/web/customer/

Database Migrations

# Development (interactive)
npx prisma migrate dev --name your_migration_name

# Production (non-interactive)
npx prisma migrate deploy

# Seed database
node prisma/seed.js

Check Service Health

cd /opt/ttf
docker compose -f docker-compose.prod.yml ps
curl https://api.touchtofix.com/health
docker logs ttf-api-1 --tail=50

10 Ports & Infrastructure

PortProtocolServiceExposure
80TCPnginx (HTTP → HTTPS redirect)Public
443TCPnginx (HTTPS, all web traffic)Public
3100TCPNestJS APIInternal (proxied)
5432TCPPostgreSQLInternal only
6379TCPRedisInternal only
3478TCP+UDPcoturn TURN/STUNPublic (WebRTC)
5349TCPcoturn TURNS (TLS)Public (WebRTC)
21115TCPTTFDesk hbbs (ID server)Public (TTFDesk)
21116TCP+UDPTTFDesk hbbs (relay signal)Public (TTFDesk)
21117TCPTTFDesk hbbr (relay data)Public (TTFDesk)
21118TCPTTFDesk hbbs web clientPublic (TTFDesk)
21119TCPTTFDesk hbbr web clientPublic (TTFDesk)

Domain Routing

DomainBackend ServicePurpose
touchtofix.comwebsite:80Marketing website
api.touchtofix.comapi:3100REST API + WebSocket
app.touchtofix.comstatic filesCustomer Flutter web app
expert.touchtofix.comstatic filesExpert Flutter web app
admin.touchtofix.comadmin:80Admin React SPA
fixme.touchtofix.comfixme:80FixMe screen share portal
All services verified healthy as of March 2026. GET https://api.touchtofix.com/health returns {"status":"ok"}. All TTFDesk ports confirmed open in GCP firewall rule ttf-rustdesk.

TouchToFix Technologies · Technical Documentation v2.0 · March 2026 · Confidential