Network Protocols¶
Vanguard's network protocol is the client/server compatibility contract for remote hierarchy, discovery, call shapes, Player identity, guard timing, error transport, and service proxy construction.
Package version and protocol version are intentionally separate. A package can add local utilities without changing protocol. A package must change protocol when old clients and new servers can no longer safely interpret the same replicated remote layout.
Current Protocol¶
Vanguard 0.1.15 uses network protocol 2.
| Release | Protocol | Compatibility note |
|---|---|---|
0.1.15 |
2 | Adds remote-kind metadata and the Replicator remote kind |
0.1.14 |
1 | Last protocol 1 release |
0.1.13 |
1 | Protocol-compatible with 0.1.14 |
Mixed protocol installs fail during client service proxy construction with
VG-NET-001. Install the same package on both runtimes.
Handshake Attributes¶
The server publishes these attributes on _VanguardRemotes:
| Attribute | Protocol 2 value | Purpose |
|---|---|---|
VanguardProtocol |
2 |
Hard compatibility gate for remote layout and semantics |
VanguardVersion |
Server package version | Diagnostic signal for mixed package installations |
The client requires an exact protocol match before it builds a service proxy. Package versions may differ only when the protocol matches; Vanguard warns because local behavior, fixes, or utilities may still differ.
| Client protocol | Server protocol | Result |
|---|---|---|
| 2 | 2 | Proxy construction continues |
| 2 | Missing | VG-NET-001; proxy construction stops |
| 2 | 1 | VG-NET-001; upgrade the older runtime |
| 1 | 2 | VG-NET-001; upgrade the older runtime |
Protocol 2 Remote Hierarchy¶
Protocol 2 publishes remotes beneath the Vanguard package ModuleScript:
Vanguard [ModuleScript]
└── _VanguardRemotes [Folder]
├── VanguardProtocol = 2 [Attribute]
├── VanguardVersion = "0.1.15" [Attribute]
└── <ServiceName> [Folder]
├── <FunctionName> [RemoteFunction]
├── <SignalName> [RemoteEvent or UnreliableRemoteEvent]
├── <PropertyName> [Folder]
│ ├── VanguardRemoteKind = "Property" [Attribute]
│ ├── _Get [RemoteFunction]
│ └── _Changed [RemoteEvent]
└── <ReplicatorName> [Folder]
├── VanguardRemoteKind = "Replicator" [Attribute]
├── _Get [RemoteFunction]
└── _Changed [RemoteEvent]
Only services with at least one supported Client member receive a service
folder. A purely server-side service is intentionally absent from the client
protocol surface.
Service and remote names come directly from registration names and Client
table keys. Renaming one is a game API change even when Vanguard's protocol
number stays the same.
Remote Kind Metadata¶
Protocol 2 adds VanguardRemoteKind to folder-backed remotes:
| Kind | Folder shape | Client proxy |
|---|---|---|
Property |
_Get, _Changed |
RemoteProperty.Client |
Replicator |
_Get, _Changed |
Replicator.Client |
Protocol 1 inferred any folder with _Get and _Changed as a property.
Protocol 2 needs explicit metadata because properties and replicators share a
similar folder shape but have different change payloads.
Publication Timeline¶
On the server, startup publishes remotes in this order:
- module definitions are registered;
- plugin
BeforeStarthooks run; - plugin init hooks run;
- network options and service rules validate;
- service folders, guards, functions, signals, properties, and replicators are compiled;
- service
VanguardInitpriority groups complete; - plugin start hooks run;
- service
VanguardStarthooks are scheduled; _VanguardRemotesis parented beneath the Vanguard package;- components start and readiness completes.
The server builds the remote tree before service init, but keeps the root folder unparented while required initialization runs. A failed init therefore cannot expose a partially-ready remote API to clients.
Client Discovery Handshake¶
The first client GetService(name) performs this sequence:
- return the cached proxy when one already exists;
- wait for
_VanguardRemotesfor at mostRemoteTimeoutseconds; - read
VanguardProtocolandVanguardVersion; - reject a protocol other than
2withVG-NET-001; - warn when the server package version differs;
- wait for the named service folder for at most
RemoteTimeoutseconds; - map each recognized child to a client proxy member;
- cache the completed service proxy.
RemoteTimeout defaults to 15 seconds. A timeout uses VG-NET-002. Increasing
it can accommodate intentionally long server init, but it does not repair a
failed server bootstrap or a service with no client remotes.
Remote Function Contract¶
Generated client methods support dot and colon calls:
For colon calls, the generated proxy removes its own service table before
invoking the RemoteFunction. Only explicit application arguments cross the
Roblox boundary.
Roblox supplies the sending Player; clients cannot choose that argument.
Vanguard invokes service client methods as:
The temporary callContext provides:
self.Server: the owning server service;self.<RemoteName>: another entry from the service'sClienttable;- writes to normal keys: updates the
Clienttable; - writes to
self.Server: rejected as read-only.
Application errors thrown by the service method propagate through
RemoteFunction. With ServicePromises = true, the generated client method
catches that transport error and rejects a Vanguard Promise.
Signal Contract¶
CreateSignal() maps to RemoteEvent. CreateUnreliableSignal() attempts to
use UnreliableRemoteEvent and falls back to RemoteEvent when unavailable.
Client-to-server signal payloads pass through the inbound guard once before any listener runs. Server listeners receive:
A rejected event is dropped; one-way signal transport has no client rejection response.
Server-to-client signal delivery supports Fire, FireFor, FireAll,
FireExcept, and FireWhere. Client listeners receive only the application
payload.
Remote Property Contract¶
A property is a folder marked VanguardRemoteKind = "Property" with _Get
and _Changed.
ClientProperty:Get() invokes _Get. The request passes through rate
limiting, validation, authentication, and verification with remote type
Property.
Property update operations:
Set(value)updates the global value and fires_Changedto all clients;SetFor(player, value)stores an override and fires only to that Player;ClearFor(player)removes an override and sends the current global value.
Property values are internally packed so nil remains a valid stored value.
Replicator Contract¶
A replicator is a folder marked VanguardRemoteKind = "Replicator" with _Get
and _Changed.
Client reads invoke _Get and pass through the guard pipeline with remote type
Replicator. The server returns the Player override when one exists, otherwise
the global state.
Server update operations:
Set(value)replaces the global state;SetPath(path, value)updates one path in a table state;Patch(patch)recursively merges a patch table;SetFor,SetPathFor, andPatchForapply only to one Player;ClearFor(player)removes a Player override and sends the global state.
Replicator change events carry:
where operation is Set, SetPath, or Patch. Clients apply the change to
their hydrated local copy and fire local observers with the resulting state and
change descriptor.
Guard Pipeline On The Wire¶
Every inbound function, client-fired signal, property read, and replicator read
uses one compiled guard. Protocol 2 enforces this order:
- Rate limit consumes one unit for the Roblox-supplied Player.
- Validate checks client payload only.
- Global authenticate checks server-wide identity/session policy.
- Remote authenticate checks merged service and named-remote policy.
- Global verify checks server-wide contextual policy.
- Remote verify authorizes the specific action and payload.
Malformed requests consume rate-limit capacity because rate limiting happens
first. Every callback must explicitly return true. Returning false, nil,
or throwing rejects the request.
The callback context contains:
type NetworkContext = {
Player: Player,
Service: any,
ServiceName: string,
RemoteName: string,
RemoteType: "Function" | "Signal" | "Property" | "Replicator",
Arguments: { any } & { n: number },
}
Arguments.n preserves trailing nil payload values.
Rejection And Error Transport¶
Protocol 2 keeps the stable rejection names introduced in protocol 1:
| Rejection | Catalog code | Client-visible behavior |
|---|---|---|
RATE_LIMITED |
VG-NET-101 |
Function/property/replicator error; signal dropped |
INVALID_PAYLOAD |
VG-NET-102 |
Function/property/replicator error; signal dropped |
UNAUTHENTICATED |
VG-NET-103 |
Function/property/replicator error; signal dropped |
UNVERIFIED |
VG-NET-104 |
Function/property/replicator error; signal dropped |
GUARD_ERROR |
VG-NET-105 |
Generic function/property/replicator error; signal dropped |
Function, property, and replicator read errors retain the rejection name and
include a direct error-catalog URL. Internal guard exceptions remain
server-only in rejection detail, logs, and OnRejected.
Legacy Protocol 1 Deep Reference¶
Vanguard 0.1.13 and 0.1.14 use protocol 1.
Protocol 1 publishes this hierarchy:
Vanguard [ModuleScript]
└── _VanguardRemotes [Folder]
├── VanguardProtocol = 1 [Attribute]
├── VanguardVersion = "0.1.14" [Attribute]
└── <ServiceName> [Folder]
├── <FunctionName> [RemoteFunction]
├── <SignalName> [RemoteEvent or UnreliableRemoteEvent]
└── <PropertyName> [Folder]
├── _Get [RemoteFunction]
└── _Changed [RemoteEvent]
Protocol 1 defines:
_VanguardRemotesas the only replicated remote root;- service folders named after registered server services;
RemoteFunctionchildren for client-callable functions;RemoteEventorUnreliableRemoteEventchildren for signals;- property folders identified by
_Getand_Changedchildren; - Roblox-supplied Player identity as the first server payload argument;
- the six-stage guard order;
- rejection names used for metrics and client errors;
- client discovery through
WaitForChildwithRemoteTimeout; - version warnings when package versions differ but protocols match.
Protocol 1 does not include:
VanguardRemoteKind;- replicator remotes;
- a way to distinguish one folder-backed remote kind from another.
That limitation is why 0.1.15 moves to protocol 2.
Serialization And Delivery Boundaries¶
Vanguard relies on Roblox remote serialization. It does not add a custom serializer, schema negotiation, compression, retry layer, ordering layer, or transaction system.
Consequences:
- only Roblox-serializable payloads can cross the boundary;
- validators run after Roblox has delivered a decoded payload to the server;
- unreliable delivery guarantees come from
UnreliableRemoteEvent; - a successful guard does not lock game state before mutation;
- critical mutation code should re-check state that can change concurrently;
- server-to-client data is visible to the receiving client and is not secret.
What Requires A Protocol Bump¶
A future Vanguard release should change the protocol number when clients and servers can no longer safely interpret the same replicated contract. Examples:
- renaming
_VanguardRemotes,_Get, or_Changed; - changing service, property, or replicator folder structure;
- changing function argument placement or Player identity semantics;
- changing marker-to-remote mappings;
- requiring new handshake attributes;
- changing rejection transport in a way old clients cannot interpret.
These changes do not require a protocol bump by themselves:
- local-only utilities;
- class registry or member-access features;
- plugin hooks that do not alter replicated remotes;
- richer logs, error codes, and documentation links;
- type-export improvements;
- bug fixes that preserve hierarchy and call contracts.
Diagnostics¶
print(Vanguard.Version)
print(Vanguard.NetworkProtocol)
local info = Vanguard.GetNetworkInfo()
print(info.Protocol, info.ServerVersion)
On the server, GetNetworkInfo() is available immediately. On the client,
fields populate during first remote-container discovery.
When Studio reports a protocol mismatch:
- inspect the Wally
_Indexversion in both server and client stacks; - confirm both bootstraps require the same package alias;
- run
wally installin the game project; - remove stale or duplicate package copies;
- restart Rojo sync and the play session.
Continue with Networking for API usage, Replicators for state trees, Network Security for policy, and Errors for diagnostics.