TMA CloudTMA Cloud
Concepts

Architecture Overview

System architecture and design patterns for TMA Cloud.

System architecture and design patterns for TMA Cloud.

System Architecture

TMA Cloud uses Single-Origin Architecture:

  • Production: The Express backend serves both the built frontend and all APIs from the same origin.
  • Development: The browser loads the app from the Vite dev server at http://localhost:5173, which proxies /api/* and /s/* to the backend on http://localhost:3000.

Production Architecture

┌─────────┐
 Browser
└────┬────┘

 HTTP Requests

┌────▼─────────────────────────────────────────────┐
         Express Backend (:3000)                  │
  ┌──────────────────────────────────────────┐
 / Static Frontend Files
 /api/* API Endpoints
 /s/* Share Links
 /health Health Check
 /metrics Prometheus Metrics
  └──────────────────────────────────────────┘
└────┬───────────────────┬─────────────────────────┘


┌────▼─────┐      ┌──────▼──────┐
│PostgreSQL│    Redis
 Database   (Cache)   │
└──────────┘      └─────────────┘

Development Architecture

┌─────────┐
 Browser
└────┬────┘

 HTTP Requests

┌────▼──────────────────────────────────────┐
     Vite Dev Server (:5173)               │
  ┌────────────────────────────────────┐
 Frontend Development Server
 Proxies /api/* Backend (:3000)   │   │
 Proxies /s/* Backend (:3000)   │   │
  └────────────────────────────────────┘
└────┬──────────────────────────────────────┘

 Proxy Requests

┌────▼─────────────────────────────────────┐
      Express Backend (:3000)             │
  ┌────────────────────────────────────┐
 API Endpoints
 Share Links
  └────────────────────────────────────┘
└────┬───────────────────┬─────────────────┘


┌────▼─────┐      ┌──────▼──────┐
│PostgreSQL│    Redis
 Database   (Cache)   │
└──────────┘      └─────────────┘

Benefits: No CORS issues, simplified deployment, cookie-based auth, no frontend env vars needed.

Custom Share Domain (Optional)

Configure custom share base URL in Settings → Share Base URL (admin only). When configured, share links use the custom domain. Share domain middleware blocks all routes except /s/*, /health, and /metrics.

Backend Structure

backend/
├── config/          # Configuration (database, redis, logger)
├── controllers/     # Request handlers
├── middleware/      # Express middleware (auth, error handling)
├── migrations/      # Database migrations
├── models/          # Data models
├── routes/          # API routes
├── services/        # Background services
└── utils/           # Utilities

Key Components:

  • Controllers: Handle HTTP requests and business logic
  • Middleware: Auth, error handling, rate limiting, share domain blocking
  • Models: Database abstraction layer
  • Services: pg-boss producers, worker tasks, access-time buffering, metrics, and storage operations
  • Caching: Redis-based caching with short-lived prefix registries for invalidation; request handlers never scan the Redis keyspace
  • Real-Time Events: Redis pub/sub + SSE for file event broadcasting; a burst for one user is published as one batch envelope and causes one list refresh

Frontend Structure

frontend/src/
├── components/   # React components
├── contexts/     # State management
├── hooks/        # Custom hooks
└── utils/        # Utilities

Key Features:

  • Responsive Design: Separate mobile (≤1024px) and desktop (>1024px) UIs
  • Contexts: AuthContext, AppContext, ThemeContext
  • Viewers: Image and document viewers with device-specific implementations

Electron Desktop App

Optional Windows client. Loads the same frontend from the TMA Cloud server URL; no separate frontend build.

Electron Architecture

┌─────────────────────────────────────────────────────────────────┐
  Main Process (Node)                                            │
  src/main: createWindow, getServerUrl(), IPC
  Server URL: embedded at build, or userData/config.json,
  or electron/src/config/build-config.json (dev)                 │
└──────────────────────────────┬──────────────────────────────────┘
 loadURL(serverUrl)
 IPC
┌──────────────────────────────▼──────────────────────────────────┐
  Renderer (BrowserWindow)                                       │
  Same React frontend as web; origin = server URL
  Preload (src/preload) exposes window.electronAPI to renderer   │
└──────────────────────────────┬──────────────────────────────────┘
 fetch /api/* (same origin, cookies)

┌─────────────────────────────────────────────────────────────────┐
  TMA Cloud Backend
└─────────────────────────────────────────────────────────────────┘

Electron Folder Structure

electron/
├── src/
   ├── main/         # Main process: window, server URL, IPC
   ├── preload/       # contextBridge, exposes electronAPI
   ├── config/       # build-config.json (serverUrl when run from source)
   └── build/        # electron-builder configs + icon
├── scripts/          # prepare-client-build.js
└── dist-electron/    # Staging for packaging (generated)

Main process: Creates a single BrowserWindow; loads a loading page, then the server URL. Resolves server URL from embedded constant (at build time) or src/config/build-config.json (when run from source).

Preload: contextIsolation: true, nodeIntegration: false. Exposes a small API (e.g. electronAPI.clipboard) via contextBridge and ipcRenderer.invoke.

Renderer: Same React app as web. Detects Electron via window.electronAPI and uses it where needed; all API calls go to the same backend via fetch.

Some deployments enable an optional mode where the desktop app is only allowed. In that mode, normal browsers are blocked from the main app while share links continue to work.

Cloud Drive (WinFsp)

The desktop app can mount TMA Cloud as a Windows drive so files can be opened and saved from any application's file dialogs. It has two parts:

  • desktop-fs — a small .NET host (TmaCloudFs.exe) built on WinFsp. It presents the drive to Windows and translates filesystem operations into requests, but holds no credentials.
  • electron/src/main/clouddrive.cjs — the trusted half. The host forwards each operation over a local named pipe (newline-delimited JSON); the main process runs the matching authenticated call to /api/* using the app's session cookies, so the drive reuses the same auth, permissions, and audit as the rest of the app.
Any app's Save As → Z:\ TMA Cloud (WinFsp)
        │  named pipe (JSON)
   TmaCloudFs.exe (desktop-fs, no credentials)

   Electron main (clouddrive.cjs, session cookies) → /api/* → backend

The main process mounts the drive when the auth cookie appears (sign-in) and unmounts on sign-out or app close. Directory listings are cached briefly and refreshed by the backend event stream. See Desktop App — Cloud Drive.

Bridge authentication. The pipe name is random per session and every request on it carries a per-session token; the main process rejects anything else, so another local process that reaches the pipe cannot issue operations against the signed-in account. The main process passes the token to the host on stdin (--token-stdin), not as a command-line argument, because a command line is readable by any process running as the same user for as long as the drive is mounted.

Drive permissions. Files and folders on the drive report a security descriptor granting the signed-in Windows user, SYSTEM and the local Administrators group but not Everyone so other accounts on a shared machine cannot read the mount.

Local staging. File bytes never travel on the pipe. Reads and writes stage through temp files under %TEMP%\tma-cloud-fs\ whose paths are exchanged over the pipe. Staging files are deleted when their handle closes, and any survivors are removed when the drive unmounts.

Data Flow

Authentication Flow

┌─────────┐     POST /api/login       ┌──────────┐
 Browser ───────────────────────> Backend
└─────────┘                           └────┬─────┘

 Validate credentials
 Create session
 Generate JWT token

<────────────────────────────────────┘
 Set httpOnly cookie

┌────▼─────────┐
 AuthContext
   Updated
└──────────────┘

File Upload Flow

┌──────────┐     POST /api/files/upload     ┌──────────┐
 Browser ────────────────────────────> Backend
│(FormData)│                                └────┬─────┘
└──────────┘
 Enforce size while streaming
 Encrypt and write to bucket
 Validate request metadata
 Create database record
 Update cache

┌─────────┐     Response with file info
 Browser<─────────────────────────────────────┘
└─────────┘

Storage: S3-compatible object storage is required. File contents stream through encryption to the bucket. Large files use multipart upload with a part size calculated to stay within the S3 and R2 10,000-part limit. If later validation rejects the request, the uploaded object is queued for deletion.

┌─────────┐     POST /api/files/share      ┌──────────┐
 Browser ────────────────────────────> Backend
└─────────┘                                └────┬─────┘

 Create share link
 Generate token
 Link files

┌─────────┐     Response with share URL
 Browser<────────────────────────────────────┘
└─────────┘

Database Schema

Key tables: users, files, share_links, share_link_files, sessions, mfa_backup_codes, client_heartbeats, storage_reservations, file_operation_results, app_settings, audit_log

See Database Schema for details.

Security

  • JWT tokens in httpOnly cookies
  • Input validation and sanitization
  • SQL injection protection (parameterized queries)
  • XSS protection (HTML escaping)
  • Path traversal protection
  • Rate limiting
  • Security headers (CSP, X-Frame-Options, etc.)

Logging & Audit

  • Structured Logging: Pino with automatic secret masking
  • Background Work: One pg-boss worker handles batched audit writes, account-ordered file operations, object cleanup, durable maintenance schedules, admin-requested orphan work, and OnlyOffice force-save commands
  • Request Logging: All requests logged with context

See Logging and Audit Logs for details.

Technology Stack

Backend: Express.js, PostgreSQL, Redis, JWT, Pino, pg-boss

Frontend: React 19, TypeScript, Vite, Tailwind CSS

Integration: OnlyOffice, Google OAuth

On this page