Gessa Docs
Product · Reference

Reference

Reference: Netcode Contract

The emitted RuntimeNetcodeContract fields, input modes, movement specs, replication lanes, policies with their constants, capacity numbers, and the proof surfaces that pin them.
engine v1.0.234since v1Copy for LLM
engine v1.0.232

Last verified 2026-09-02 against engine v1.0.232.

The netcode contract is the backend-owned interface a room hands each client at session start. Unlike the component, action, or script references, it has no single generated file; its source of truth is the runtime code plus a fixture test that hashes the emitted JSON. This page enumerates the emitted fields, the input modes and movement specs, the replication lanes, the policies with the constants that carry them, the capacity numbers (with the code's own provisional qualifier where it applies), and the proof surfaces. For the model behind these values (the clock equations, authority, admission, load), read the Netcode Model; this page states the values, that page states the why.

Source of truth

The contract is resolved by resolveRuntimeNetcodeContract in server/src/modules/runtime/runtimeNetcodeContract.ts. It takes a room, the authored project graph, and a runtime budget, validates the result against the protocol schema RuntimeNetcodeContractSchema, and hashes the canonical JSON with hashRuntimeNetcodeContract. The version tag is the constant RUNTIME_NETCODE_CONTRACT_VERSION and the wire protocol tag is RUNTIME_PROTOCOL_VERSION, both in packages/engine-version.

The contract is cached per content by runtimeNetcodeContractRevisionKey, which fingerprints exactly the room and content coordinates (roomId, gameId, deploymentId, contentVersionId, environment, tickRateHz, snapshotRateHz, blueprintId, roomTemplateKey, runtimeBudgetKey, runtimeBudget) and excludes volatile bookkeeping such as player counts, the lease, and tick progress, so identical content always resolves an identical contract.

Emitted fields

resolveRuntimeNetcodeContract emits these top-level fields.

FieldWhat it carries
version, hashThe contract version tag and the sha256 of the canonical JSON.
tickPolicyClock domain, tick and snapshot rates, time precision.
transportPolicyProtocol version, supported encodings, per-stream state-stream limits.
inputPolicyThe input cadence policy (packet rate, redundancy window, liveness refresh).
inputModesThe static input-mode contracts (RUNTIME_NETCODE_INPUT_MODES).
actionContracts, actionsPer-authored-action resolved mode, prediction policy, and controller.
movementControllerSpecsThe kinematic and physics movement controller specs.
predictionPoliciesprediction.none and prediction.replay.shared.
interpolationPoliciesThe remote interpolation profiles.
presentationPoliciesThe presentation smoothing policy.
replicationManifestLanes, component policies, and bandwidth.
scriptPredictionContractsDeterministic predicted-script contracts derived from the graph.
lagCompensationPoliciesThe live and rewind_to_client_sample policies.
collisionSnapshotThe authored static-collision snapshot for the shared controller.
diagnosticsResolver diagnostics for unsupported or demoted actions.

Tick and time policy

tickPolicy declares clock: "server_tick", timePrecision: "nanoseconds", and integerMillisecondStep: false; the tick and snapshot rates are taken from the room. The default room shape used by the load harness is RUNTIME_LOAD_TEST_POLICY.defaultTickRateHz of 20 and defaultSnapshotRateHz of 10.

Clock values are branded time domains defined in packages/runtime-shared/src/time/brandedTime.ts: MonotonicMs (process-relative, cadence only) and WallMs (epoch, the only domain written to a durable or wire record). The brands are nominal, so a cross-domain assignment is a compile error, and the single conversion boundary is wallFromMonotonic against an explicit ClockAnchor. The server module server/src/modules/runtime/kernel/time.ts re-exports that hoisted module, and scripts/check-clock-domains.mjs bans raw Date construction from a number in the cadence and netcode paths.

Input modes

RUNTIME_NETCODE_INPUT_MODES declares the static input-mode contracts the client admission gate enforces.

ModeSends to serverMutates replicated stateAllowed effect systemsNotes
server_authoritativeyesyesmovement, action, scriptThe fail-closed default.
client_predictedyesyesmovement, action, scriptPredicted and replayed; falls back to disabling prediction.
client_onlynonolookPresentation-only; local visual state.
anticipatedyesnoaction, lookSent for storage but visual-only; gameplay stays server-authoritative.

Scripted prediction is admitted through admitPredictedScript only for a script the graph declares deterministic, whose host functions are on the declared allowlist, and whose ops fall in the narrow predicted-op allowlist; otherwise the script's ops stay server-authoritative and reconcile.

Movement controller specs

The contract emits two movement controller specs. The kinematic spec movement.transform.shared_kcc (shared_kinematic_character) supports prediction and is selected when the transform owner is transform. The physics spec movement.physics.authority (server_authoritative_physics) disables prediction and is selected when the transform owner is physics; a client_predicted movement action on a physics body is demoted to server-authoritative because dynamic-rigidbody prediction would require a rollback physics contract the engine does not ship.

Prediction and interpolation

predictionPolicies emits prediction.none (rollback and replay off) and prediction.replay.shared (rollback and replay on), both bounded by DEFAULT_MAX_CATCHUP_STEPS.

interpolationPolicies emits three remote profiles, selected per entity via componentNetworkProfileComponent NetworkProfileComponent:

  • interpolation.remote.default: the standard delay buffer; extrapolation enabled to the cap.
  • interpolation.remote.responsive: extrapolation disabled (maxMs: 0), so it hugs the newest sample and never bridges a gap.
  • interpolation.remote.smooth: the delay buffer doubled for fluid slow movers, extrapolation enabled to the cap.

The extrapolation cap is DEFAULT_INTERPOLATION_GAP_BRIDGE_MAX_MS of 200 ms: a stalled lane bridges at most that long before halting. Presentation smoothing uses DEFAULT_PRESENTATION_TAU of 90 ms.

Lag compensation

The lag-compensation numbers are a default, a clamp band, and a fairness ceiling, and they are easy to conflate. The values below are pinned by server/tests/runtimeNetcodeContractFixtureHash.test.ts.

ValueConstantMeaning
150 msRUNTIME_LAG_COMPENSATION_POLICY.defaultMaxRewindMsThe rewind window a room advertises by default.
100 msRUNTIME_LAG_COMPENSATION_POLICY.minRewindMsThe clamp floor for a per-blueprint override.
200 msRUNTIME_LAG_COMPENSATION_POLICY.maxRewindMsThe clamp ceiling; not the shipped value.
220 msDEFAULT_FAIRNESS_CEILINGThe fairness ceiling emitted on both policies.

The emitted lagCompensationPolicies.live.maxRewindMs and lagCompensationPolicies.rewind_to_client_sample.maxRewindMs are both 150 ms by default, because clampLagCompensationMaxRewindMs computes min(200, max(100, requested)) over defaultMaxRewindMs. The 200 ms figure is only reached when a per-blueprint lagCompensationMaxRewindMs override is set above it. Read the emitted value, not the ceiling.

Replication

State replicates on two unreliable lanes emitted in replicationManifest.lanes.

LaneCadenceAudienceReliable
pose (RUNTIME_REPLICATION_LANE_POSE)tick ratepublicfalse
world (RUNTIME_REPLICATION_LANE_WORLD)snapshot rateinterestedfalse

Interest and bandwidth come from RUNTIME_REPLICATION_POLICY: bytesPerSecond of 256 KiB, defaultInterestRadius of 80, interestCellSize of 40, and the priority tiers priorityHigh 100, priorityMedium 50, priorityLow 10.

The world delta is fitted to a byte budget by selectRuntimeWorldDeltaForBudget, which returns undefined (refuses) when the delta carries a sceneFrameUpdate, and, when trimming to fit, pulls each required parent upsert in before its children via sortEntityUpsertsParentBeforeChild. Which entities a client receives is composed by composeRuntimeClientSnapshotEntities, which unions the interested and always-relevant sets and subtracts owner-private entities last.

State streams add their own budgets and anti-cheat guards in RUNTIME_STATE_STREAM_POLICY and runtimeStateStreamPolicyConfig: perStreamMaxRateHz 60 and perStreamMaxBytesPerSec 32768, perPlayerMaxAggregateBytesPerSec 128000, perRoomMaxAggregateBytesPerSec 1048576, sample windows sampleTimeWindowBehindMs 500 and sampleTimeWindowAheadMs 100, antiCheatMaxLinearVelocityMps 50 and antiCheatMaxAngularVelocityRadps 25, plus maxStreamsPerPlayer 16, maxSamplesPerStream 8, and teleportDistanceMeters 25.

The three authority fan-out producers (prepareAuthorityFrame, prepareNotice, preparePlayerPrivateFrame) may be referenced only from the authorized corridor files, enforced by scripts/check-netcode-replication-fanout-boundary.mjs.

Load ladders

Overload runs down two ordered ladders in server/src/modules/runtime/backpressure.ts.

The per-room ladder RUNTIME_LOAD_DEGRADATION_ORDER sheds in order: downsample_or_drop_state_streams, drop_low_priority_replication, reject_room_commands, evict_slow_consumers, mark_room_unhealthy.

The per-shard ladder RUNTIME_SHARD_LOAD_DEGRADATION_ORDER composes above it and sheds in order: thin_snapshots, reduce_snapshot_rate, pause_non_essential_lanes, refuse_joins, migrate_or_close. evaluateShardDegradation returns the single highest rung whose trigger the current signals meet, and the ladder never relaxes the event-loop-lag kill threshold to make room.

Backpressure is driven by the ack-lag thresholds ACK_LAG_WATCH 8, ACK_LAG_THROTTLING 32, and ACK_LAG_SHEDDING 128, and by the socket-byte thresholds in RUNTIME_SLOW_CONSUMER_POLICY: watchBytes 1 MiB, throttleBytes 4 MiB, evictBytes 16 MiB.

Durability and capacity

A room checkpoints on RUNTIME_DURABLE_CHECKPOINT_INTERVAL_MS of 30000 ms. The owner lease term is RUNTIME_ROOM_OWNER_LEASE_TTL_SECONDS of 30 s. A rejoining player presents a MAC-signed resume token bounded by RUNTIME_RESUME_TOKEN_TTL_SECONDS of 120 s. Environment state replicates on ENV_HEARTBEAT_INTERVAL_SECONDS of 5 s. Admission overflow is queued with a ticket TTL of DEFAULT_TICKET_TTL_MS 30000 ms in CapacityQueue. These durability values are read from source and are provisional in the dossier's sense: no test asserts them by value (the suites covering the checkpoint coordinator, lease supervisor, resume token, environment clock, and capacity queue pass explicit intervals or consume the constant by name), unlike the lag-compensation and policy constants pinned by the fixture-hash test.

The protocol epoch is a one-way ratchet RUNTIME_PROTOCOL_EPOCH_LADDER of legacy.v1, canonical.v1, kernel.v1, enforced by the database guard runtime_guard_protocol_activation(); kernel.v1 is the durability-inversion epoch (journaled and projected behind the tick) that the capacity numbers below were measured on.

The durability-honest per-loop capacity knee is RUNTIME_LOAD_TEST_POLICY.maxLocalAuthoritativeRooms of 128 and maxLocalAuthoritativePlayers of 1024.

Canonical ordering

Runtime identity ordering uses compareCanonicalRuntimeStrings in server/src/modules/runtime/canonicalOrder.ts, which compares by UTF-16 code unit so it cannot drift with host locale. It is deliberately not the platform warehouse collation (Unicode code-point order, equal to UTF-8 bytes and Postgres COLLATE "C"); the two orders differ only for supplementary-plane characters, and every current input is an engine-generated ASCII identifier for which they are identical. Do not use it for warehouse record ordering, and do not feed it non-ASCII identity strings.

Proof surfaces

The contract's guarantees are held by static gates and tests, not by prose.

  • Contract-closure gate: scripts/check-runtime-netcode-contract.mjs asserts that the accepted handshake publishes the contract, that authority frames carry its hash, that the client prediction gate routes through the shared helper, and that script prediction contracts come from the graph. One caveat below.
  • Five prediction hard gates: scripts/check-runtime-netcode-prediction-hardgates.mjs enforces Gate 6 (no dynamic-physics import in the prediction path), Gate 9 (deterministic, host-allowlisted, narrow script prediction), Gate 10 (no client script op applicator), Gate 11 (both resolvers source the immutable authored graph), and Gate 12 (the collision lane reads both movement dialects).
  • Fan-out boundary: scripts/check-netcode-replication-fanout-boundary.mjs restricts the authority producers to the authorized corridor files.
  • Timing-literal ratchet: scripts/check-netcode-timing-literals.mjs keeps a shrink-only baseline so no new local millisecond or hertz literal enters the netcode timing paths; the detector carries a self-test.
  • Fixture-hash test: server/tests/runtimeNetcodeContractFixtureHash.test.ts resolves the contract for a canonical room and graph fixture, pins the emitted contract hash, and asserts by value the lag, fairness, extrapolation, tick, input, and lane fields plus the projected policy constants (backpressure, slow-consumer, shard caps with provisional, and the capacity knee).
Was this helpful?Report an issueContact support

On this page