Skip to content

Lattice — High-Level Design (HLD)

Document: 01-high-level-design.md Status: Draft v1 · Audience: engine integrators, infra/platform engineers, gameplay engineers, reviewers Scope: System-wide architecture, topologies, authority models, deployment, and end-to-end flows. Defers to: 02-netcode-lld.md for wire formats, channel internals, prediction/rollback math, and serialization.

Doc Purpose
README.md Suite intro, build, quickstart
00-overview.md Vision, glossary, requirements, non-goals
01-high-level-design.md This document — architecture & flows
02-netcode-lld.md Transport/replication/prediction low-level design
03-auth-service.md lattice-auth design, tokens, accounts
04-social-library.md lattice-social friends/presence/parties
05-engine-integration.md Unity / Unreal / Godot / Web bindings
06-implementation-roadmap.md Milestones, sequencing, risks

1. Purpose & scope

Lattice is a standalone, engine-agnostic networking suite that gives games Photon-Fusion-class netcode without binding them to one engine or one topology. The same gameplay simulation runs unchanged on a dedicated server, on a player acting as a peer-to-peer (P2P) host, or in a single-player/offline harness. Topology and role are injected at runtime; the compiled simulation logic is identical everywhere.

This HLD describes:

  • The split between the data plane (latency-critical game traffic, C++) and the control plane (account/matchmaking/social, managed services).
  • The three supported network topologies and when to use each.
  • The two first-class authority models and how they coexist.
  • Full system component and deployment architecture.
  • End-to-end sequence flows for cold start, P2P create/join, and host migration.
  • A high-level view of the replication & tick loop.

Anything below the wire is intentionally summarized and cross-referenced to 02-netcode-lld.md.


2. Architectural overview & design principles

2.1 Design principles

  1. Data plane vs control plane separation. Real-time game packets (lattice-core, lattice-gameserver, lattice-relay) never depend on a synchronous call into a control-plane service during gameplay. Control-plane services (lattice-auth, lattice-director, lattice-social) are stateless request/response or pub/sub services that gate entry into a session, not the per-tick loop.
  2. Deterministic shared simulation. Gameplay logic lives in a single shared simulation library compiled into both the dedicated server and the client. Given identical inputs and starting state, the simulation advances identically. This is the foundation of client prediction, server reconciliation/rollback, and "a client can be the host".
  3. Role injection, not role forking. There is no separate "server build" of the game logic. A runtime Role (Authority / Predicted / Replicated) and Topology (Dedicated / P2PHost / P2PClient / Offline) are supplied at startup. The code path is the same; only authority and I/O wiring differ.
  4. Stable C ABI core. lattice-core is C++20 but exposes a flat extern "C" boundary. Engine bindings (lattice-unity, lattice-unreal, lattice-godot, lattice-web) are thin idiomatic wrappers over that ABI. This keeps one battle-tested netcode implementation behind every engine.
  5. Transport independence. The custom UDP transport, our Relay, and an optional QUIC/WebTransport backend all implement the same internal transport interface. Higher layers (reliability channels, replication) are transport-agnostic.
  6. Authority is a property of objects, not the build. Server/host-authoritative and shared/distributed authority are both first-class and can be mixed within one session at object granularity.
  7. Stateless, horizontally scalable control plane. Auth/Director/Social nodes hold no per-request state; all durable state lives in PostgreSQL/Redis (optional NATS for events). Game state lives in the data-plane processes.

2.2 Layered view of lattice-core

flowchart TB
    subgraph App["Game + Engine"]
        Bind["Engine binding<br/>(unity / unreal / godot / web)"]
        Sim["Shared simulation library<br/>(deterministic, role-injected)"]
    end

    subgraph Core["lattice-core (C++20, C ABI)"]
        ABI["Stable C ABI boundary (extern C)"]
        Rep["Replication + interest management"]
        Pred["Prediction / rollback / reconciliation"]
        Snap["Snapshot + interpolation"]
        Chan["Reliability + ordering channels"]
        Ser["Serialization (delta / quantization)"]
        Trans["Transport abstraction"]
        Crypto["Crypto + session keys"]
    end

    subgraph Backends["Transport backends"]
        UDP["Custom UDP"]
        Relay["Relay client (P2P fallback)"]
        QUIC["QUIC / WebTransport (web, optional)"]
    end

    Bind --> ABI
    Sim --> ABI
    ABI --> Rep
    ABI --> Pred
    Rep --> Snap
    Pred --> Snap
    Snap --> Chan
    Rep --> Ser
    Chan --> Ser
    Ser --> Trans
    Crypto --> Trans
    Trans --> UDP
    Trans --> Relay
    Trans --> QUIC

2.3 Data plane vs control plane

flowchart LR
    subgraph CP["Control plane (.NET 8 / C#) — stateless, scalable"]
        Auth["lattice-auth<br/>:8443"]
        Dir["lattice-director<br/>:8444"]
        Soc["lattice-social<br/>:9443"]
    end

    subgraph DP["Data plane (C++) — latency-critical"]
        GS["lattice-gameserver<br/>UDP 27015+"]
        RL["lattice-relay<br/>UDP 7777"]
        STUN["STUN<br/>:3478"]
    end

    subgraph Store["Shared stores"]
        PG[("PostgreSQL")]
        RD[("Redis")]
        NATS[("NATS (optional)")]
    end

    Client["Client (engine binding + lattice-core)"]

    Client -- "login / token (TLS)" --> Auth
    Client -- "matchmake / session (TLS)" --> Dir
    Client -- "friends / presence (TLS/WSS)" --> Soc
    Client -- "game traffic (UDP, encrypted)" --> GS
    Client -- "P2P / relayed traffic (UDP)" --> RL
    Client -- "NAT discovery (UDP)" --> STUN

    Auth --- PG
    Auth --- RD
    Dir --- PG
    Dir --- RD
    Soc --- PG
    Soc --- RD
    Dir -. "fleet events" .- NATS
    Soc -. "presence events" .- NATS
    Dir -- "allocate / health" --> GS

The dashed line is the principle in one picture: control-plane calls happen before and around a session (login, matchmake, presence), while the solid UDP arrows are the in-session hot path that must not block on any control-plane round trip.


3. Network topologies

Lattice supports three topologies behind the same API surface. A game selects topology at session creation; clients do not need separate code paths because the shared simulation and lattice-core adapt via role injection.

3.0 How the same sim runs as dedicated server or P2P host

The shared simulation library is compiled once and linked into:

  • lattice-gameserver (a headless C++ binary), and
  • the client application (via the engine binding).

At runtime the host of authority — whether a dedicated server or a peer — instantiates the simulation with Role = Authority. Everyone else instantiates the identical code with Role = Predicted (for owned objects) or Replicated (for remote objects). The only differences are:

Aspect Dedicated authority P2P host authority
Process lattice-gameserver Client app, same sim lib
Trust Trusted infra Semi-trusted peer (host)
Lifetime Fleet-managed Tied to host's connection
Failure handling Fleet reschedule Host migration (§7.3)
Wiring Director allocates endpoint Director registers peer endpoint
stateDiagram-v2
    [*] --> Boot
    Boot --> RoleResolve: read Topology + Role (injected)
    RoleResolve --> AuthoritySim: Role = Authority<br/>(dedicated OR p2p host)
    RoleResolve --> ClientSim: Role = Predicted/Replicated
    AuthoritySim --> Running: identical sim code
    ClientSim --> Running: identical sim code
    Running --> [*]

3.1 (a) Dedicated server / client (authoritative)

A fleet-managed lattice-gameserver owns authority. Clients predict locally and reconcile against server snapshots. Lag compensation runs on the server.

flowchart TB
    subgraph DC["Game data center / region"]
        GS["lattice-gameserver<br/>Role = Authority<br/>UDP 27015+"]
    end
    C1["Client A (Predicted)"] -- "inputs ↑ / snapshots ↓ (UDP)" --> GS
    C2["Client B (Predicted)"] -- "inputs ↑ / snapshots ↓ (UDP)" --> GS
    C3["Client C (Predicted)"] -- "inputs ↑ / snapshots ↓ (UDP)" --> GS
    Dir["lattice-director"] -- "allocate / monitor" --> GS

When to use: competitive/ranked play, cheat-sensitive titles, persistent worlds, larger player counts, anything needing consistent low-latency authority and lag compensation. Highest infra cost; strongest integrity.

3.2 (b) P2P host-client (a peer hosts; authoritative host)

One peer runs the shared sim with Role = Authority; others connect to it as predicted clients. No dedicated infra needed for gameplay; Director is still used for discovery and session registration.

flowchart TB
    Host["Peer HOST<br/>(client app + shared sim, Role = Authority)"]
    P1["Peer (Predicted)"] -- "UDP (direct)" --> Host
    P2["Peer (Predicted)"] -- "UDP (direct)" --> Host
    Dir["lattice-director"] -. "register / discover session" .-> Host
    Dir -. "session endpoint" .-> P1
    Dir -. "session endpoint" .-> P2

When to use: co-op, casual/social sessions, small lobbies, cost-sensitive titles, LAN/console party play. Low infra cost; integrity bounded by host trust; vulnerable to host disconnect (mitigated by migration).

3.3 (c) P2P with relay fallback (NAT punch fails → relay)

Peers first try a direct connection via STUN-assisted hole punching. If that fails (symmetric NAT, restrictive firewalls), traffic falls back to lattice-relay, which forwards encrypted packets between peers. The relay is a dumb forwarder — it never sees plaintext and never runs sim logic.

flowchart TB
    Host["Peer HOST (Role = Authority)"]
    Peer["Joining peer (Predicted)"]
    STUN["STUN :3478"]
    Relay["lattice-relay<br/>UDP 7777"]

    Peer -- "1 discover public mapping" --> STUN
    Host -- "1 discover public mapping" --> STUN
    Peer -- "2 attempt direct hole punch (UDP)" --- Host
    Peer -- "3a SUCCESS: direct path" --> Host
    Peer -- "3b FAIL → relay path (encrypted)" --> Relay
    Relay -- "forward (encrypted)" --> Host

When to use: automatically, as the resilience layer under any P2P session. Direct is preferred for latency; relay guarantees connectivity at the cost of an extra hop and relay bandwidth. Selection is automatic per peer-pair (some peers direct, some relayed in the same session).


4. Authority models

Lattice treats authority as a per-object property, so both models are first-class and can coexist within one session.

4.1 Server/host-authoritative (prediction + rollback)

The authority (dedicated server or P2P host) is the single source of truth. Clients:

  1. Apply local input immediately (client-side prediction) for responsiveness.
  2. Receive authoritative snapshots and reconcile — replay un-acked inputs from the corrected state (rollback).
  3. Interpolate remote objects from a buffered snapshot history (~100 ms) for smoothness.

The authority runs lag compensation (rewinding hit targets to the shooter's view) for fairness. This is the default for competitive and cheat-sensitive gameplay.

sequenceDiagram
    participant C as Client (Predicted)
    participant A as Authority (server/host)
    C->>C: apply input locally (predict)
    C->>A: input(tick N) [reliable-seq]
    A->>A: simulate authoritative tick
    A-->>C: snapshot(tick N, ack input)
    C->>C: reconcile: rewind to snapshot, replay un-acked inputs
    Note over C,A: Remote objects interpolated from snapshot buffer (~100 ms)

4.2 Shared/distributed authority (per-object ownership)

Each replicated object has an owner that holds authority for it; ownership can be transferred at runtime (e.g., the player who grabbed a physics prop becomes its owner). Non-owners apply dead reckoning (extrapolation) between updates, and the system converges to eventual consistency. Conflicts are resolved by ownership + tie-break rules (e.g., authority tick / owner priority), detailed in 02-netcode-lld.md.

stateDiagram-v2
    [*] --> OwnedByA
    OwnedByA --> TransferPending: request transfer (B grabs object)
    TransferPending --> OwnedByB: authority confirms
    TransferPending --> OwnedByA: rejected / timeout
    OwnedByB --> TransferPending: another transfer
    OwnedByB --> [*]
    OwnedByA --> [*]

When to use: large dynamic-physics sandboxes, many interactive objects, builder/social worlds, and cases where round-tripping every object through one authority would be too costly. Trades strict consistency for scalability and local responsiveness.

4.3 How a project chooses & whether they coexist

flowchart TD
    Q1{"Cheat-sensitive /<br/>competitive?"}
    Q1 -- Yes --> SA["Server/host-authoritative<br/>+ prediction & rollback"]
    Q1 -- No --> Q2{"Many dynamic<br/>physics objects?"}
    Q2 -- Yes --> DA["Distributed authority<br/>+ per-object ownership"]
    Q2 -- No --> Q3{"Need low infra cost /<br/>small lobby?"}
    Q3 -- Yes --> P2P["P2P host-authoritative"]
    Q3 -- No --> SA
    SA -.-> Mix["Coexist: critical objects authoritative,<br/>props distributed"]
    DA -.-> Mix
    P2P -.-> Mix

Coexistence is explicit and common. A typical shooter might keep players, projectiles, and scoring under host/server authority while letting destructible debris and grabbed props use distributed ownership. Authority is configured per object/prefab archetype; the runtime tags each replicated object with its model. The two models share the same transport, snapshot, and interest-management machinery.


5. System component architecture

flowchart TB
    subgraph Clients["Clients (per engine)"]
        U["lattice-unity"]
        E["lattice-unreal"]
        G["lattice-godot"]
        W["lattice-web (stretch)"]
    end

    subgraph CoreLib["lattice-core (C ABI) + shared sim"]
        CoreA["Transport / channels / crypto"]
        CoreB["Replication / prediction / interest mgmt"]
    end

    subgraph Edge["Edge / LB"]
        LB["Load balancer (TLS termination)"]
    end

    subgraph Control["Control plane (.NET 8)"]
        Auth1["lattice-auth #1 :8443"]
        Auth2["lattice-auth #2 :8443"]
        Dir["lattice-director :8444<br/>matchmaking + directory + fleet orch"]
        Soc["lattice-social :9443"]
    end

    subgraph DataPlane["Data plane (C++)"]
        STUN["STUN :3478"]
        Relay["lattice-relay :7777"]
        subgraph Fleet["Game fleet"]
            GS1["lattice-gameserver :27015"]
            GS2["lattice-gameserver :27016"]
            GSn["lattice-gameserver :2701n"]
        end
    end

    subgraph Storage["Storage"]
        PG[("PostgreSQL")]
        RD[("Redis")]
        NATS[("NATS (optional)")]
    end

    U --> CoreA
    E --> CoreA
    G --> CoreA
    W --> CoreA
    CoreA --- CoreB

    CoreA -- "TLS" --> LB
    LB --> Auth1
    LB --> Auth2
    LB --> Dir
    LB --> Soc

    CoreA -- "UDP game" --> GS1
    CoreA -- "UDP game" --> GS2
    CoreA -- "UDP relay" --> Relay
    CoreA -- "UDP STUN" --> STUN

    Dir -- "allocate / drain / health" --> Fleet
    Dir -- "session token (Ed25519)" --> Auth1
    GS1 -- "validate token" --> Auth1

    Auth1 --- PG
    Auth2 --- PG
    Auth1 --- RD
    Dir --- PG
    Dir --- RD
    Soc --- PG
    Soc --- RD
    Dir -. events .- NATS
    Soc -. events .- NATS
    GS1 -. session results .-> Dir

Component responsibilities

Component Plane Language Responsibility
lattice-core Data C++20 (C ABI) Transport, reliability channels, serialization, replication, prediction/rollback, interest management, crypto
Engine bindings Data C#/C++/GDScript/JS Idiomatic wrappers over the C ABI
Shared sim lib Data C++20 Deterministic gameplay; linked into server and client
lattice-gameserver Data C++20 Dedicated authoritative host; links core + sim
lattice-relay Data C++20 Encrypted P2P packet forwarder (no plaintext, no sim)
STUN Data NAT mapping discovery for hole punching
lattice-auth Control .NET 8 Accounts, login, Ed25519-signed tokens, session-token issuance
lattice-director Control .NET 8 Matchmaking, session directory, fleet orchestration (allocate/drain)
lattice-social Control .NET 8 Friends, presence, parties, invites
PostgreSQL Store Durable accounts, sessions, social graph
Redis Store Hot session/directory/presence cache, rate limits, ticket queues
NATS (optional) Store Fleet/presence/event fan-out

6. Deployment & infrastructure

Control-plane nodes are stateless and replicated behind regional load balancers; game fleets are autoscaled per region; stores are shared (with read replicas/regional caches). Relay and STUN are deployed near players for latency.

flowchart TB
    GLB["Global anycast / DNS<br/>(geo-routing)"]

    subgraph R1["Region: us-east"]
        LB1["Regional LB (TLS)"]
        subgraph CP1["Stateless control nodes"]
            A1["auth x N :8443"]
            D1["director x N :8444"]
            S1["social x N :9443"]
        end
        subgraph DP1["Data plane"]
            ST1["STUN :3478"]
            RL1["relay :7777"]
            F1["game fleet :27015+ (autoscaled)"]
        end
        RDc1[("Redis (regional)")]
    end

    subgraph R2["Region: eu-west"]
        LB2["Regional LB (TLS)"]
        CP2["auth / director / social x N"]
        DP2["STUN / relay / game fleet"]
        RDc2[("Redis (regional)")]
    end

    subgraph Shared["Global shared stores"]
        PGp[("PostgreSQL primary")]
        PGr[("PostgreSQL read replicas")]
        NATSc[("NATS cluster (optional)")]
    end

    GLB --> LB1
    GLB --> LB2
    LB1 --> A1
    LB1 --> D1
    LB1 --> S1
    A1 --- RDc1
    D1 --- RDc1
    S1 --- RDc1
    D1 --> F1
    A1 --- PGp
    D1 --- PGp
    S1 --- PGp
    A1 --- PGr
    CP2 --- RDc2
    CP2 --- PGp
    PGp --> PGr
    D1 -. events .- NATSc
    CP2 -. events .- NATSc

Deployment notes

  • Geo-routing: clients hit the nearest regional LB; Director may steer matchmaking to the lowest-latency region with capacity.
  • Statelessness: any auth/director/social node can serve any request; horizontal scale is add-a-pod. All session state is in Redis/PostgreSQL.
  • Fleet management: Director allocates/drains lattice-gameserver instances; cordoned servers finish active matches before shutdown.
  • Stores: PostgreSQL primary + read replicas for durable data; Redis regional caches for hot/low-latency lookups; optional NATS cluster for cross-node events.
  • Edge data plane: STUN and Relay run per region near players; game fleet capacity autoscales on ticket demand.

7. End-to-end sequence flows

7.1 Cold start — login → matchmake → connect → spawn (dedicated)

sequenceDiagram
    autonumber
    participant Cl as Client (binding + core)
    participant LB as Regional LB
    participant Au as lattice-auth :8443
    participant Di as lattice-director :8444
    participant GS as lattice-gameserver :27015+
    participant RD as Redis / PostgreSQL

    Cl->>LB: login(credentials) [TLS]
    LB->>Au: forward
    Au->>RD: verify account
    Au-->>Cl: auth token (Ed25519-signed)

    Cl->>Di: matchmake / request session [TLS, auth token]
    Di->>Au: validate auth token
    Di->>RD: enqueue ticket / read fleet capacity
    Di->>GS: allocate slot (region, mode)
    Di->>Di: mint session token (Ed25519) bound to server + player
    Di-->>Cl: assignment {server endpoint :2701x, session token}

    Cl->>GS: UDP connect + handshake (X25519, session token)
    GS->>Au: validate session token (Ed25519)
    GS->>GS: spawn player into session (Role=Authority for sim)
    GS-->>Cl: accepted + initial snapshot (baseline state)
    Note over Cl,GS: In-session UDP loop begins (inputs ↑ / snapshots ↓)

7.2 P2P host create + peer join (hole punch success and relay fallback)

sequenceDiagram
    autonumber
    participant H as Host peer (Role=Authority)
    participant Di as lattice-director
    participant ST as STUN :3478
    participant P as Joining peer (Predicted)
    participant RL as lattice-relay :7777

    H->>ST: discover public mapping
    H->>Di: register session {host public/private endpoints}
    P->>Di: discover / join session [auth token]
    Di-->>P: session info {host candidates, relay endpoint, session token}
    P->>ST: discover own public mapping

    par Direct path attempt
        P->>H: UDP hole punch (to host candidates)
        H->>P: UDP hole punch (to peer candidates)
    end

    alt Hole punch SUCCESS
        P->>H: direct UDP handshake (X25519, session token)
        H-->>P: accepted + baseline snapshot
        Note over P,H: Direct low-latency UDP session
    else Hole punch FAIL (symmetric NAT)
        P->>RL: connect via relay (encrypted)
        RL->>H: forward (encrypted, opaque payload)
        H-->>RL: accepted + baseline snapshot (encrypted)
        RL-->>P: forward
        Note over P,H: Relayed UDP session via RL (extra hop, end-to-end encrypted)
    end

7.3 Host migration when a P2P host drops

sequenceDiagram
    autonumber
    participant P1 as Peer 1 (candidate)
    participant P2 as Peer 2
    participant H as Old host (dropping)
    participant Di as lattice-director

    Note over H: Host connection lost (crash / disconnect)
    P1->>P1: detect host timeout (no snapshots)
    P2->>P2: detect host timeout
    P1->>Di: report host loss + last-known state digest
    P2->>Di: report host loss + last-known state digest
    Di->>Di: select new host (best connectivity / latency / last good tick)
    Di-->>P1: you are new HOST (Role -> Authority)
    Di-->>P2: new host = P1 {endpoint, refreshed session token}

    P1->>P1: promote local replicated state to authoritative<br/>(reuse latest snapshot as baseline)
    P2->>P1: reconnect (hole punch / relay fallback)
    P1-->>P2: accepted + baseline snapshot (resync)
    Di->>Di: update session directory (new host)
    Note over P1,P2: Session resumes — same shared sim, new authority

State handoff relies on every peer keeping a recent snapshot buffer (the same buffer used for interpolation), so the promoted host already holds a near-current world state to use as the authoritative baseline. Reconnection reuses the §7.2 direct/relay logic.


8. High-level replication & tick flow

The in-session loop is the same shape for dedicated and P2P-host authority — only who holds Role=Authority changes.

flowchart LR
    subgraph ClientIn["Client (predicted)"]
        IN["Sample input @60 Hz"]
        PRED["Predict locally (apply input)"]
        SND["Send input (reliable-seq)"]
    end

    subgraph Authority["Authority (server / host) @60 Hz"]
        RCV["Receive + buffer inputs"]
        SIM["Authoritative sim tick"]
        IM["Interest management (per-client view)"]
        SNAP["Build delta snapshot @20-30 Hz"]
    end

    subgraph ClientOut["Client (recv)"]
        RECON["Reconcile owned objects (rollback + replay)"]
        INTERP["Interpolate remote objects (~100 ms buffer)"]
        EXTRA["Extrapolate / dead reckoning (distributed-authority objects)"]
        RENDER["Render"]
    end

    IN --> PRED --> SND --> RCV
    RCV --> SIM --> IM --> SNAP
    SNAP --> RECON
    SNAP --> INTERP
    SNAP --> EXTRA
    RECON --> RENDER
    INTERP --> RENDER
    EXTRA --> RENDER

In prose:

  1. Input: clients sample input at the sim tick rate (60 Hz) and send it on a reliable-sequenced channel; locally they predict immediately.
  2. Authority sim: the authority buffers inputs and advances the deterministic sim at 60 Hz, running lag compensation for authoritative hit resolution.
  3. Interest management: per-client relevant sets are computed so each client only receives what it can perceive (bandwidth + anti-cheat).
  4. Snapshot: delta-compressed snapshots are emitted at 20–30 Hz (against ~1200 B MTU payload, fragmented as needed).
  5. Client reconcile/interp: owned objects reconcile via rollback + replay of un-acked inputs; remote objects interpolate from a ~100 ms buffer; distributed-authority objects extrapolate via dead reckoning toward their estimated destination.

Channel semantics (Unreliable / Unreliable-Sequenced / Reliable-Unordered / Reliable-Ordered), delta/quantization, congestion control, fragmentation, and the rollback algorithm are specified in 02-netcode-lld.md.


9. Request lifecycle summary

Each user action maps to a defined set of services. Data-plane actions (anything per-tick) never touch the control plane.

User action Plane Services involved Transport Notes
Sign in Control lattice-auth → PostgreSQL/Redis TLS :8443 Returns Ed25519 auth token
View friends / presence Control lattice-social → PostgreSQL/Redis TLS/WSS :9443 See 04-social-library.md
Create / accept party Control lattice-social (→ NATS) TLS/WSS :9443 Party state cached in Redis
Quick match Control lattice-director (validates via lattice-auth) → Redis TLS :8444 Enqueues ticket, allocates fleet slot
Create dedicated session Control lattice-director → fleet TLS :8444 Allocates lattice-gameserver
Create P2P host session Control + Data lattice-director (register), STUN TLS + UDP :3478 Host = Role=Authority
Join P2P session Control + Data lattice-director (discover), STUN, maybe lattice-relay TLS + UDP :3478/:7777 Direct or relay fallback
Connect to game server Data lattice-gameserver (token validated via lattice-auth) UDP :27015+ X25519 handshake
Send input / receive snapshot Data lattice-gameserver or P2P host UDP Hot path; no control-plane calls
Authority/ownership transfer Data Authority + peers UDP Distributed-authority objects
Host migration Control + Data lattice-director, peers, STUN/relay TLS + UDP New host promoted (§7.3)
Match end / report Control lattice-gameserverlattice-director → PostgreSQL TLS/event Results, stats, fleet drain
Sign out Control lattice-auth, lattice-social TLS Invalidate session, set offline

10. Assumptions & invariants for sibling docs

These are HLD-level commitments other documents should honor:

  1. Parameters are fixed defaults across the suite: sim tick 60 Hz, snapshot 20–30 Hz, interp buffer ~100 ms, MTU payload ~1200 B, channels = Unreliable / Unreliable-Sequenced / Reliable-Unordered / Reliable-Ordered.
  2. Crypto: session encryption X25519 + ChaCha20-Poly1305; tokens Ed25519. Auth issues auth tokens; Director mints per-session tokens; game servers/hosts validate them.
  3. Ports: auth 8443, social 9443, director 8444, relay UDP 7777, game UDP 27015+, STUN 3478.
  4. Role/Topology is runtime-injected; there is exactly one shared simulation build. 02 and 05 must not introduce a separate "server-only" sim build.
  5. Authority is per-object; both authority models share one transport/snapshot/interest stack. 02 owns the conflict-resolution and rollback specifics.
  6. Control plane is stateless; all durable state in PostgreSQL/Redis (optional NATS). 03/04 should not introduce node-local session state.
  7. Relay is a blind forwarder — never plaintext, never sim. Anti-cheat reasoning in other docs must assume relayed traffic is opaque to the relay.

End of 01-high-level-design.md. Continue to 02-netcode-lld.md.