Architecture¶
This page describes the internal architecture of QS-Bridge: how its components are arranged, how data flows between them, and how the system scales to support multiple game servers from a single deployment.
Prerequisite reading: Platform Overview for a high-level introduction to QS-Bridge and its design philosophy.
Multi-Server Topology¶
QS-Bridge follows a hub-and-spoke topology where a single SpacetimeDB instance acts as the central data store for N game servers, the API gateway, and the admin panel.
graph TD
AdminPanel["Admin Panel<br/>React 19 + Vite 6<br/>TypeScript 5.6, Tailwind CSS v4"] -->|HTTPS| Gateway["API Gateway<br/>Hono TypeScript<br/>Steam OpenID 2.0"]
Gateway -->|ws://| STDB
subgraph GameServers["Game Servers"]
GS1["Game Server #1<br/>libqsbridge.so"]
GS2["Game Server #2<br/>libqsbridge.so"]
GS3["Game Server #3<br/>libqsbridge.so"]
GSN["Game Server #N<br/>libqsbridge.so"]
end
subgraph STDB["SpacetimeDB v2"]
Platform["Platform Module<br/>12 tables, 24 reducers"]
ModRegistry["Mod Registry Module<br/>10 tables, 30+ reducers"]
GameModule["Game Module e.g. HumanitZ<br/>49 tables, 117 reducers"]
end
GS1 -->|ws://| STDB
GS2 -->|ws://| STDB
GS3 -->|ws://| STDB
GSN -->|ws://| STDB
Key Properties¶
- Single STDB instance — all game servers, the API gateway, and the admin panel connect to the same SpacetimeDB deployment. There is no per-server database.
- Row-level partitioning — every table row that is server-specific includes a
server_idcolumn. Subscriptions filter byserver_id, so each game server only receives its own data. - Shared platform state — tables like
Account,ServerConfig, andAuditLogspan all servers, enabling cross-server administration from a single panel. - Stateless frontends — both the API gateway and the admin panel are stateless processes. They can be restarted or replaced without affecting game server operation.
C++ Framework Layers¶
The libqsbridge.so shared library is structured as a layered framework. Each layer depends only on the layer below it, enforcing clean separation of concerns.
graph TD
A["Game Module<br/><i>e.g. HumanitZ: 49 tables, 117 reducers</i>"] --> B
B["Engine Adapter<br/><i>UE4 4.27 hooks, actor replication intercept</i>"] --> C
C["Bridge Core<br/><i>Lifecycle, table registry, sync scheduler</i>"] --> D
D["STDB Client<br/><i>WebSocket transport, subscriptions, reducer dispatch</i>"] --> E
E["BSATN Codec<br/><i>18 header-only files, binary serialisation</i>"]
style A fill:#d4915c,color:#fff,stroke:#c07840
style B fill:#b87a4a,color:#fff,stroke:#a06838
style C fill:#9c6438,color:#fff,stroke:#845628
style D fill:#7a4e2a,color:#fff,stroke:#6a421e
style E fill:#5c3a1e,color:#e8d8c8,stroke:#4a2e14
Layer 1: BSATN Codec¶
The Binary SpacetimeDB Algebraic Type Notation codec is the lowest layer. It provides:
- Zero-copy serialisation and deserialisation of all STDB primitive types.
- Algebraic sum and product type encoding, matching the Rust BSATN specification.
- 18 header-only C++ files — no link-time dependencies, no runtime overhead.
- Compile-time type reflection via C++17
constexprand template metaprogramming.
The codec is header-only by design: game modules include it directly, avoiding ABI compatibility issues across compiler versions.
Layer 2: STDB Client¶
The client layer manages the WebSocket connection to SpacetimeDB:
| Responsibility | Detail |
|---|---|
| Connection lifecycle | Connect, reconnect with exponential backoff, graceful disconnect |
| Authentication | Identity token exchange, credential persistence |
| Subscription management | Register SQL-style subscriptions, receive incremental updates |
| Reducer dispatch | Invoke server-side reducers with BSATN-encoded arguments |
| Event loop integration | Non-blocking I/O compatible with game server tick loops |
Layer 3: Bridge Core¶
Bridge Core is the orchestration layer. It manages:
- Table Registry — a compile-time registry of all tables declared by the game module, with schema validation against the STDB module.
- Sync Scheduler — configurable background flush interval; batches pending writes into single STDB transactions.
- Identity Resolution — maps the server's
server_id(human-readable display name) to itsstdb_identity(cryptographic backend identity). See Server Identity Strategy. - Hook Dispatch — routes engine events (player join, actor spawn, world save) to registered game module callbacks.
- Health Reporting — exposes connection state, queue depth, and sync latency to systemd watchdog and the admin panel.
Layer 4: Engine Adapter¶
The engine adapter provides engine-specific glue code. For Unreal Engine 4.27:
- Hooks into
UWorld::Tickto drive the sync scheduler. - Intercepts
AActorreplication relevancy checks to offload state to STDB. - Provides
FString↔std::string↔ BSATN string conversion utilities. - Maps UE4
FVector,FRotator,FTransformto STDB-compatible column types.
Layer 5: Game Module¶
Game modules are the user-authored layer. Each module targets a specific game and declares:
- Tables — the STDB schema for that game's managed state.
- Reducers — server-side functions that mutate table rows.
- Hooks — callbacks invoked by Bridge Core when engine events occur.
The first shipped game module is HumanitZ (Unreal Engine 4.27), comprising 49 tables and 117 reducers.
Component Reference¶
| Component | Technology | Artefact | Description |
|---|---|---|---|
libqsbridge.so |
C++17, CMake | Shared library (~8.6 MB) | Injected into game server via LD_PRELOAD. Contains all 5 framework layers. |
| Platform STDB Module | Rust | WASM module | 12 tables, 24 reducers. Manages accounts, servers, permissions, audit logs. |
| Mod Registry Module | Rust | WASM module | 10 tables, 30+ reducers, 8 ModKind variants. Catalogues and configures server-side mods. |
| Admin Panel | React 19, Vite 6, TypeScript 5.6, Tailwind CSS v4 | Static SPA | Server management dashboard. Stateless; all data from API gateway. |
| API Gateway | Hono (TypeScript), Node.js 20+ | Node.js service | Steam OpenID 2.0 authentication, RBAC, REST-to-reducer translation. |
| Codegen | TypeScript | CLI tool | Generates C++ table/reducer stubs from STDB module schemas. |
Server Identity Strategy¶
Every game server in QS-Bridge has two identities:
graph LR
A["server_id<br/><code>String</code><br/><i>Human-readable display name</i>"] --- B["stdb_identity<br/><code>Identity</code><br/><i>Cryptographic backend identity</i>"]
style A fill:#d4915c,color:#fff,stroke:#c07840
style B fill:#b87a4a,color:#fff,stroke:#a06838
server_id — Display Name¶
- Type:
String - Example:
"us-east-1-pvp","au-sydney-pve","eu-west-creative" - Set via the
QSB_SERVER_IDenvironment variable. - Used in the admin panel UI, API routes, and log messages.
- Must be unique within a deployment.
- Mutable — can be renamed without affecting data integrity.
stdb_identity — Backend Identity¶
- Type:
Identity(SpacetimeDB's built-in cryptographic identity) - Generated automatically by SpacetimeDB on first connection.
- Used as the primary key for row-level partitioning in all server-scoped tables.
- Immutable — changing it would orphan all associated table rows.
- Stored in a local credential file and reused across restarts.
Why Two Identities?¶
Separating the display name from the backend identity provides:
- Rename safety — server owners can rename a server in the panel without migrating data.
- Cryptographic authenticity — STDB reducers can verify the caller's identity without trusting a user-supplied string.
- Collision avoidance —
Identityvalues are globally unique by construction;server_iduniqueness is enforced at the application layer.
Data Flow¶
Write Path (Game Server → SpacetimeDB)¶
sequenceDiagram
participant GS as Game Server (UE4)
participant EA as Engine Adapter
participant BC as Bridge Core
participant SC as STDB Client
participant DB as SpacetimeDB
GS->>EA: Actor state change
EA->>BC: Enqueue row update
BC->>BC: Batch in write queue
Note over BC: Flush on tick interval
BC->>SC: Submit transaction (BSATN)
SC->>DB: WebSocket send
DB->>DB: Execute reducer
DB-->>SC: Transaction result
SC-->>BC: Confirm / retry
Read Path (SpacetimeDB → Game Server)¶
sequenceDiagram
participant DB as SpacetimeDB
participant SC as STDB Client
participant BC as Bridge Core
participant EA as Engine Adapter
participant GS as Game Server (UE4)
DB->>SC: Subscription update (BSATN)
SC->>BC: Decoded row delta
BC->>BC: Update local cache
BC->>EA: Notify changed rows
EA->>GS: Apply to engine state
Authentication Flow (Admin Panel → API Gateway)¶
sequenceDiagram
participant U as User (Browser)
participant P as Admin Panel
participant G as API Gateway
participant S as Steam OpenID
participant DB as SpacetimeDB
U->>P: Click "Sign in with Steam"
P->>G: GET /auth/steam
G->>S: OpenID 2.0 redirect
S-->>U: Steam login page
U->>S: Authenticate
S-->>G: OpenID assertion
G->>G: Validate assertion
G->>DB: Lookup/create Account
DB-->>G: Account + roles
G-->>P: JWT session token
P-->>U: Dashboard
Build System¶
The C++ shared library is built with CMake 3.20+ using a 5-layer build structure that mirrors the framework layers:
graph TD
Root["CMakeLists.txt root"] --> L1["src/bsatn/<br/>Layer 1: BSATN Codec<br/>header-only, interface library"]
Root --> L2["src/client/<br/>Layer 2: STDB Client"]
Root --> L3["src/core/<br/>Layer 3: Bridge Core"]
Root --> L4["src/engine/ue4/<br/>Layer 4: Engine Adapter UE4"]
Root --> L5["src/games/humanitz/<br/>Layer 5: Game Module HumanitZ"]
Each layer is a separate CMake target with explicit dependency declarations. This ensures:
- Incremental builds only recompile affected layers.
- Layer boundary violations produce compile-time errors.
- New engine adapters or game modules can be added without modifying existing layers.
Testing¶
QS-Bridge maintains 860+ test assertions across 7 test suites:
| Suite | Scope | Key Assertions |
|---|---|---|
bsatn_tests |
BSATN codec correctness | Round-trip encoding for all primitive and algebraic types |
client_tests |
STDB client transport | Connection lifecycle, reconnection, subscription management |
core_tests |
Bridge Core logic | Table registry, sync scheduler, identity resolution |
adapter_tests |
Engine adapter | UE4 type conversions, hook dispatch |
platform_tests |
Platform module | Account CRUD, RBAC, server lifecycle |
registry_tests |
Mod registry | Mod CRUD, per-server enablement, ModKind validation |
e2e_tests |
End-to-end integration | Full write/read path, auth flow, multi-server scenarios |
All suites run in CI on every commit and pull request.
Related Pages¶
- Requirements & Prerequisites — hardware, software, and environment setup.
- Quick Start Guide — deploy your first QS-Bridge server.