# The network transport of your game (net.transport) The connection layer under every multiplayer kit: how a client reaches the server, how long a silent link is kept, how a dropped player comes back, how fast a peer may talk. For the choice of the sync kit and the server program, read the [multiplayer guide](guide:net). Readable offline: `gripforge docs net-transport`, or the MCP tool `gripforge_docs` (topic `net-transport`). ## What it is for `net.transport` owns the configuration of the connection and the status shown to the player. The sync kits (`net.state`, `net.claim`, `net.authoritative`, `net.rollback`, `net.async`) ask it for a session and never open a socket themselves. It checks your settings against the table of the network core, fills in the defaults, and hands them to that core: the same Rust core in the browser, in Godot, Unity, Unreal and in the dedicated server. It draws nothing. A game has exactly one transport kit; installing any sync kit brings it in. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "net.transport" } gripforge_gamekit_configure { project_id: "gkp_…", id: "net.transport", config: { "app": "my-game@1", "url": "wss://play.example.com/gfnet" } } ``` Write only the keys that differ from the defaults. `gripforge kit api net.transport` lists every key with its type and bounds. ## First example A wrong setting is named by its path, and networking stays off instead of running half configured: ```json { "heartbeatMs": 5, "nope": true, "rate": { "burst": 0.5 } } ``` The log gets `config.transport.heartbeatMs: expected >= 100`, `config.transport.nope: unknown property` and `config.transport.rate.burst: expected integer`. The service still answers its defaults, and `open` fails with the same message. ## Settings and defaults | Setting | Default | Meaning | |---|---|---| | `transport` | `auto` | `auto`, `websocket`, `webrtc`, `tcp_udp` or `enet`. `auto` is the WebSocket on the web (WebRTC when a `room` is set) | | `role` | `client` | `host` (web only): the page runs the server and accepts the WebRTC clients of `room` | | `url` | `""` | WebSocket address of the server; empty means the host of the page, path `/gfnet` | | `host`, `port`, `udpPort`, `path` | `127.0.0.1`, `27015`, `0`, `/gfnet` | address of the server for native clients; `udpPort` 0 is announced by the server | | `signalUrl`, `room`, `iceServers` | `""`, `""`, a public STUN server | WebRTC signalling and relay servers; a production game needs its own TURN server | | `app` | `gripforge-game@1` | application id and build: a client of another build is refused. Bump it when two builds can no longer play together | | `name` | unset | player name sent in the handshake | | `fallback` | `true` | native `auto`: fall back to the WebSocket bridge when direct IP fails | | `maxPeers`, `maxPeersPerIp` | `16`, `8` | server capacity, and sessions per remote address (0 = no cap) | | `heartbeatMs`, `timeoutMs`, `resumeMs` | `1000`, `5000`, `10000` | ping period, silence that loses a link, time a dropped peer keeps its id and slot | | `connectTimeoutMs`, `welcomeTimeoutMs`, `handshakeMs` | `3000`, `0` (auto), `5000` | connect, welcome and hello delays | | `maxMessageBytes`, `maxViolations` | `65536`, `20` | largest message; invalid messages tolerated before a peer is closed | | `rate` | `120`/s, burst `240`, `262144` bytes/s | per-peer limits (`messagesPerSecond`, `burst`, `bytesPerSecond`, `bytesBurst`) | | `reconnect` | on, `500` to `8000` ms, `10` tries, jitter `0.25` | client backoff (`enabled`, `minMs`, `maxMs`, `maxTries`, `jitter`); 0 tries is unlimited | | `relay` | | limits of the engine bridge (`maxPayload`, and its own `rate`) | | `eventQueueMax` | `4096` | events kept unread before the oldest are dropped | | `trustForwardedFor` | `false` | read the client address from `X-Forwarded-For`; only behind a proxy you run | Constraints: `heartbeatMs` must be below `timeoutMs`; `reconnect.minMs` at most `reconnect.maxMs`; `sendSoftBufferBytes` at most `sendHardBufferBytes`; `relay.maxPayload` at most `maxMessageBytes`. Keys marked hot in `gripforge kit api net.transport` can change on a running session; the others apply at the next connection. The remaining keys tune the sockets of a server (`listenHost`, `listenWebSocket`, `listenTcp`, `sniffMs`, the pending-connection caps, the send buffers) and the direct-IP client (`udpHelloMs`, `udpHelloTries`, `udpKeepaliveMs`). ## Services, events, variables - `net.transport` (service): `config()` the settings with their defaults, `app()` the application id of the handshake, `open(model, sync, extra?)` a session for a sync kit, `host(model, sync, physics?)` and `hosted()` the in-page server (`role: "host"`), `listen(callback)` and `unlisten(id)` its events. - `net.session` (service): the session the sync kits plug into. - `net.connected` (event): `{ id, resumed, transport }`, the welcome arrived. - `net.disconnected` (event): `{ reason, final }`; `final` when no reconnection follows. - `net.peer` (event): `{ id, on }`, another peer joined or left. - `net.refused` (event): `{ code, msg }`, the handshake was refused (version, `app`, full, `mods`…). - `net.overflow` (event): `{ dropped, total, cap }`, events were dropped past `eventQueueMax`. - `net.status`, `net.rtt`, `net.peers` (variables): connection state (`idle`, `connecting`, `connected`, `reconnecting`, `closed`), smoothed ping in ms, players online. Bind them to a HUD label. ## Hooks and modding The kit declares no hook and no setting is open to mods: two machines of a match must agree on addresses, limits and rates. With mods applied, the application id sent in the handshake is `app` followed by `~mods.`, so a peer running other mods is refused with `net.refused { code: "mods" }`. ## Engines The web uses WebSocket and WebRTC; Godot, Unity and Unreal use direct IP (`tcp_udp`; `enet` is treated as `tcp_udp`) with the WebSocket bridge as a fallback. `role: "host"` is available on the web only. ## When it does not do what you expect - Networking never starts and the log names `config.transport.`: that setting is unknown or out of bounds; fix it. - `open` or `host` fails with a message about `net.core`: the host does not provide the network core, so no session can open. - `net.refused` with code `app`: client and server have different `app` values; with `mods`: their mods differ. - `role: "host"` without `signalUrl` and `room` accepts only the local player (a warning is logged). - `host` says the page already hosts another model: one in-page server per page. - `per-connection overrides require role client`: `open` was given transport overrides while `role` is `host`. ## Limits No game rules, no matchmaking and no authentication: rooms, lobbies and logins belong to other kits. Rate limits are per peer, not per account. Direct IP needs an open port; the WebSocket bridge works through web proxies. ## Versions Kit 1.3.1, protocol `gfnet/1`. 1.3 added the mods fingerprint to the handshake; 1.3.1 lets the caller of `host` choose the WebRTC room.