Purpose. This is a standalone wire reference for the current StarCraft II
Battle.net path. It begins with the generic protobuf control connection, follows
the handoff into SC2's native service connection, and documents the bit-packed
records used there. Live observations and build constants target macOS Base97563.
Native type names and logical schemas are taken from a build-97364 metadata
snapshot; layouts marked live verified have also been reconstructed against
Base97563 traffic. Private account data, credentials, session secrets, endpoints,
and message contents are intentionally omitted.
1. Names and protocol layers
StarCraft II's online path is a stack, not one protocol. This document uses the
following names consistently:
Name used here
Meaning
Battle.net platform
The complete account, routing, session, and game-service system sometimes called Battle.net 2.0.
BGS
Battle.net Game Services, the generic protobuf RPC control plane carried here in WebSocket binary messages inside TLS.
Front
SC2's name for its regional BGS connection. It authenticates the account and obtains the game-native handoff.
Web challenge
The HTTPS account-login step requested by BGS. It returns a short-lived credential to the client.
GameUtilities handoff
The BGS RPC that returns the native endpoint, session material, identity data, and native logon response.
Sunken
SC2's name for the game-native service connection reached after Front.
BSN
This reference's short name for SC2's native bit-packed service protocol, generated by the bsnc toolchain.
BSN metadata
The build-time type library embedded in SC2. Numeric type IDs resolve to structural records and optional human-readable names; the library itself is not sent on the wire.
Naming note. “BGS,” “Front,” and “Sunken” come from
Blizzard service and client terminology. “BSN” is only a compact label used by
this reference; no expanded form is implied.
With these names established, the next chapter follows one session from the
first Front connection through the Sunken service stream.
SC2 signs in on Battle.net's shared BGS service, then moves the authorized
session to SC2's native service. The first connection establishes who you are
and which game account may log in; the second proves that it received that
authorization and carries the live SC2 services. The protocol calls that
continuation Resume.
01
Open the Battle.net control connection
The client connects to the regional Front endpoint. TLS protects the
socket, WebSocket supplies message boundaries, and BGS protobuf RPC carries
connection and account operations.
TCP/1119 → TLS → WebSocket → BGS
02
Authenticate the Battle.net account
BGS accepts a cached credential or asks the client to complete an HTTPS
account challenge. Successful logon returns the available SC2 game accounts
and a BGS session key.
BGS Logon → optional web challenge → LogonResult
03
Authorize SC2 and obtain the native handoff
The client selects an SC2 game account and calls
GameUtilities.ProcessClientRequest. Its response supplies the
private material needed to continue on SC2's native service plane.
Native endpointwhere Sunken accepts the second connection
64-byte session seedinput to the Resume proof and transport keys
LogonResponse3identity, region, and native bootstrap values
04
Open a new connection and resume the authorized context
The client opens a separate TCP connection to Sunken and sends
Auth/1 ResumeRequest. The Auth proof exchange demonstrates
possession of the handoff seed and binds the new socket to the account and
game account authorized on Front.
The TCP connection changes here.
Authorization continues; the Front socket itself does not.
new TCP/1119 → plaintext BSN Auth → Resume proof
05
Enable stream protection and run SC2 services
After the server proof succeeds, Conn/5 EnableEncryption
switches both directions to independent RC4 states. The same BSN stream then
carries connection bootstrap, identity, presence, friends, channels, chat,
parties, and game services.
RC4-protected BSN → service registry → routed records
The next chapter begins with the outer BGS control plane: its framing,
dispatch rules, account logon, and the GameUtilities request that creates the
handoff.
BGS is the generic Battle.net Game Service control plane used before SC2 enters
its native service stream. SC2 embeds a standard protobuf descriptor set for connection,
authentication, challenge, session, GameUtilities, account, channel, presence,
and related services.
3.1 WebSocket transport and RPC frame
The Base97563 client first establishes TLS to the regional Front endpoint, then
performs an RFC 6455 upgrade with subprotocol
v1.rpc.battle.net. Each binary WebSocket message contains exactly one
BGS RPC frame. WebSocket therefore supplies the outer message and body boundary;
the two-byte BGS prefix below measures only the protobuf header.
2 bytes header length uint16_be
header_length bytes bgs.protocol.Header
remaining message bytes protobuf body; may be empty
The length prefix covers only the protobuf header.
When Header.size is present it must agree with the remaining
WebSocket-message length; Base97563 requests can omit it.
Header.size is a body-length assertion when present, not the outer
message delimiter on this path. The two-byte prefix measures only the encoded
protobuf header, not the complete RPC frame or WebSocket message.
Client-to-server WebSocket messages set the RFC 6455 mask bit. The current
macOS client uses an all-zero four-byte mask, so the payload bytes remain visibly
unchanged even though the frame is formally masked. Server messages are
unmasked.
Header field
Protobuf wire type
Meaning
1 service_id
varint
Numeric dispatch value. The first Connect uses 0; responses use 0xfe.
2 method_id
varint
Method number within the selected service.
3 token
varint
Request/callback correlation token.
4 object_id
varint
Optional addressed service object.
5 size
varint
Body byte count.
6 status
varint
RPC result/status code.
7 error
length-delimited, repeated
Structured error details.
8 timeout
varint
Optional request timeout.
9 is_response
varint boolean
Explicit response marker when present.
11 service_hash
fixed32
FNV-1a hash of the full protobuf service name. Protobuf fixed32 is little-endian on the wire.
13 client_id
length-delimited UTF-8
Optional client identifier.
3.2 Dispatch and callbacks
A BGS header answers two separate questions: which operation should receive
this body? and which earlier call does this reply belong to? Service and
method fields perform dispatch; the token performs correlation.
Connection bootstrapThe first ConnectionService.Connect is the exception:
service_id=0, method_id=1, token=0.
Client call
Request headerservice_hash method_id token = t
→
Server dispatchhash selects service method selects operation
→
Response headerservice_id=0xfe token = t status
Server callback
Callback headerlistener service_hash method_id token = c
→
Client dispatchdecode listener body run callback
→
Declared resultreply with token c or send nothing for NO_RESPONSE
Tokens correlate calls; they do not serialize traffic.
Callbacks and connection-control messages may arrive while an unrelated request
remains pending, so dispatch must examine every complete BGS header.
After the special first Connect, client requests identify a service with the
32-bit FNV-1a hash of its full protobuf name:
h = 0x811c9dc5for eachUTF-8 byte b in full_service_name:
h = h XOR b
h = (h * 0x01000193) AND0xffffffff
Relevant names include:
bnet.protocol.connection.ConnectionService
bnet.protocol.authentication.AuthenticationServer
bnet.protocol.authentication.AuthenticationClient
bnet.protocol.challenge.ChallengeNotify
bnet.protocol.game_utilities.GameUtilities
bnet.protocol.account.AccountService
bnet.protocol.session.SessionService and SessionListener
Server-to-client calls use listener services. If the descriptor declares an
ordinary response, answer with a header whose service_id is
0xfe, whose token is the callback token, and whose status is zero.
If the declared return type is NO_RESPONSE, send nothing. An empty
response is not equivalent to no response.
Connection-control calls and listener callbacks can arrive while another token
remains pending. Decode and dispatch each message by its own header, acknowledge
it when its declared result requires one, and continue waiting for unrelated
tokens.
3.3 Logon sequence
Step
Direction
Service / method
Notes
1
C→S
ConnectionService / 1
Special service_id=0, token=0; advertises bindless RPC.
2
C→S
AuthenticationServer / 1
Logon: program S2, platform Mc64, locale enUS, embedded BGS SDK identity, application_version=0, and optionally cached web credentials.
3
S→C
ChallengeNotify / 3
Conditional external challenge with payload type web_auth_url. Declared NO_RESPONSE; omitted when cached credentials are accepted.
4
local HTTPS
Battle.net account flow
When challenged, cookies, MFA, CAPTCHA, and account state stay in the web view.
5
C→S
AuthenticationServer / 7
When challenged, submits the short-lived credential returned by the web flow.
6
S→C
AuthenticationClient / 5
LogonResult: account, game accounts, region, and 64-byte session key.
7
C→S
AuthenticationServer / 8
After successful authentication, generates a successor cached credential for a later Logon; this also rotates a credential that was just accepted.
8
C→S
GameUtilities / 1
SC2 Front bootstrap. On this path it follows logon directly, without an intervening SessionService call.
For Base97563, LogonRequest.version is the embedded SDK identity
Battle.net Game Service SDK v1.48.2 "cf68e241e0"/104, while protobuf
field 6 application_version is zero. These fields must not be inferred
from the installation build number.
Authentication listener traffic can be interleaved with the ordinary RPC
responses: method 10 reports a logon update, method 14 reports game-account
selection, and methods 11–13 are declared NO_RESPONSE. Dispatch them
as callbacks rather than mistaking them for whichever request token is pending.
The browser or embedded web view handles only the account challenge. The BGS
connection remains the protocol client: after the web flow returns its short-lived
credential, the client submits that value with AuthenticationServer method 7.
The cached credential is distinct from the one-time credential returned by the
web challenge. It is opaque, can suppress the next browser challenge, and should
be replaced after a successful use. Rejection simply returns the flow to the
ordinary web challenge.
3.4 GameUtilities Front request
Front sends GameUtilities.ProcessClientRequest
method 1 with a deliberately sparse ClientRequest. Top-level field 1
repeats typed attributes; top-level field 4 is the selected SC2 game-account
entity. Fields for host process, Battle.net account, program, and client info are
absent on this path.
Attribute name
Variant type
Current value shape
LogonTokenRequest
string
"0.0.1"
environment
string
Regional environment string; "US" for this target.
session_key
blob
64 private bytes from LogonResult.
locale
string
"enUS"
An Attribute is protobuf field 1 name plus field 2
Variant. Known Variant arms are field 2 bool, 3 signed integer, 4
double, 5 string, 6 blob, 7 embedded message, 8 FourCC string, 9 unsigned
integer, and 10 entity ID.
A successful response is another attribute bag. The handoff requires:
address: native endpoint string;
session_key: exactly 64 private bytes;
account_region: unsigned integer in 0..255;
game_account_name and account_mail: private identity strings;
logon_response: a bit-packed native LogonResponse3.
These response attributes are the boundary between the generic BGS control
plane and SC2's native protocol. The next chapter explains how they are used to
open the Sunken connection.
The SC2 client names the generic BGS side “Front” and the native endpoint
“Sunken.” Front authenticates the Battle.net account, selects an SC2 game account,
and returns the values needed to open and authenticate the second connection.
The logon_response attribute is not an opaque ticket. It decodes
as Battlenet::Client::Authentication::LogonResponse3, including
response status, final authentication-module requests, ping timeout, regulator
rules, account and game-account region, game-account name, failure count, and
optional RAF state. The enclosing protobuf blob carries no native bit count;
unused bits in the last byte must be zero.
The native TCP connection may be established before Front finishes closing, but
the first native Auth/1 ResumeRequest is
written after the Front shutdown. The sockets remain independent even when their
lifetimes briefly overlap.
An ordered Front shutdown uses
ConnectionService.RequestDisconnect (method 7, declared
NO_RESPONSE). The server answers with the one-way
ConnectionService.ForceDisconnect notification (method 4) and then
closes TLS. This notification is a protocol event, not the response frame that
NO_RESPONSE explicitly forbids.
From this point onward, the transport and payload format are native BSN. The
next chapter introduces its bit cursor, record header, and schema-delimited
boundaries before any authentication payloads are decoded.
“BSN” is used here for the native bit-packed service protocol generated by
Blizzard's embedded bsnc toolchain. Unlike the preceding BGS
connection, native records are schema-delimited bitstreams rather than protobuf
messages inside WebSocket boundaries.
5.1 The bitstream from first principles
A BSN record is not a list of byte-sized packet fields. It is a sequence of
fields packed back-to-back into an array of bytes. Each field declares exactly
how many bits it consumes, and the next field begins wherever the previous one
stopped—even if that position is halfway through a byte.
One byte has eight numbered positions.
A hex viewer conventionally draws the most-significant position first,
so its columns run from bit 7 down to bit 0.
hex-display order
76543210
but the BSN cursor visits
cursor order
01234567
start → → → → → → end
A decoder owns one position: (byte index, bit index). Reading a
field consumes its declared number of bits and advances that position. Reaching
bit 7 does not end the field; the cursor wraps to bit 0 of the next byte and
keeps going.
The first sixteen cursor positions of a BSN record
This view is deliberately arranged in
cursor order, so read it left to right. Hover or focus a field to follow
what it consumes.
byte 0first 8 cursor positions
bit 01234567
wrap→
byte 1next 8 cursor positions
bit 01234567
11 header bits6 command + 1 flag + 4 slotpayload bitsbegin at absolute cursor position 11
The byte boundary is only a storage boundary.
It does not align fields. Here the four-bit slot begins in the final position
of byte 0, continues through the first three positions of byte 1, and leaves
the cursor at byte 1, bit 3 for the payload.
Command consumes positions 0–5.
It fits inside byte 0, so the cursor simply advances six positions and stops
at byte 0, bit 6.
The flag consumes position 6.
After this one-bit field, only byte 0 bit 7 remains available.
Slot is one field, drawn in two pieces.
Its declared width is four bits. One bit fits before the boundary and three
bits continue in byte 1. Both green regions belong to the same value.
Payload begins at absolute position 11.
That is byte 1, bit 3—not the start of a new byte. Payload fields continue
from this exact cursor position until the selected schema is complete.
How a number is split when it crosses a byte
Cursor movement explains where a field is stored. Integer chunking is a
second rule that explains which part of the value goes into each region.
If an integer does not fit before the next byte boundary, its most-significant
chunk goes into the remaining space and its lower bits continue in the next
byte. The boundary splits the value; it does not reverse it.
fieldservice slotvalue14 = 1110₂starting positionbyte 0, bit 7
Bytes below use the conventional display order
bit 7 → bit 0. The BSN cursor moves in the opposite visual direction:
it begins at bit 0, advances toward bit 7, then wraps to bit 0 of the next byte.
byte 011000000₂ = c0₁₆
cursor wraps to the next byte ↓
byte 1ppppp110₂
1command
2has-service flag
3slot 1 + 110
4payload
Hover or focus a colored field. The step
numbers show cursor order. The diagram itself keeps the conventional byte
display order so the resulting bytes remain recognizable in a hex dump.
Command is read first.
Six bits fit entirely in byte 0, occupying its low positions 0 through 5.
This example uses command 0 only to keep those bits visually quiet.
The one-bit flag follows.
It occupies byte 0 bit 6 and leaves exactly one bit available in that byte.
The four-bit slot must split.
Its binary value is 1110₂. The one available position receives
the value's high bit, 1. The remaining low value,
110₂, continues in byte 1 bits 2 through 0. Reading the two
chunks as 1 || 110 reconstructs 1110₂ = 14.
The next field starts immediately.
After the slot consumes byte 1 bits 0 through 2, the payload begins at bit 3.
The letters p are payload bits, not zero padding.
The decoder below expresses the same rule. The important operation is
value = (value << take) | chunk: each earlier wire chunk becomes
the more-significant part of the reconstructed integer.
read(width):
value = 0while width remains:
take = min(width, bits_left_in_current_byte)
chunk = (current_byte >> current_bit_offset) & ((1 << take) - 1)
value = (value << take) | chunk
advance(take)
Consequently, a field cannot be cut out of the byte stream and decoded as an
ordinary standalone integer. Its interpretation depends on the bit position at
which the field began. Keep one cursor for the complete record, including its
routing header.
5.2 Routing header
command_id6 bits 0..63service_present1 bit 0 = no service slot follows
service_slot4 bits present only when service_present = 1total: 7 bits without a slot, 11 bits with a slot
6 bitscommand
1 bithas service
4 bitsservice slot if present
variable bitspayload selected by route schema
0..7 bitszero pad
Hover or focus a field to isolate it. There is
no outer record length: the route selects the payload schema, and that schema
determines where the record ends. Section 5.3
follows that process.
Command, 6 bits.
A number from 0 through 63. Its meaning is local to the selected service and
may also depend on direction; command 11 in Chat is unrelated to command 11
in Connection.
Has-service flag, 1 bit.
If set, a four-bit service slot follows. If clear, the header ends here and
the payload begins at bit 7 of the first byte.
Service slot, 4 bits when present.
This session-local number resolves through the runtime registry to a service
such as Auth, Conn, or Chat. It is not a
chat-room index.
Payload, schema-sized.
The tuple of direction, service, and command selects its decoder. Because
there is no generic length prefix, the decoder must consume exactly the
right number of bits.
Final padding, 0 through 7 zero bits.
Padding ends the record on a byte boundary so the next routing header starts
at the beginning of the next byte.
With a service slot, the payload begins three bits into the second byte; the
upper five bits of that byte already belong to the payload. Without a service
slot, the payload begins at bit 7 of the first byte.
for a route with a service slotbyte0 = (command & 0x3f) | 0x40 | (((slot >> 3) & 1) << 7)
byte1 bits 0..2 = slot & 0x07worked arithmetic only: command 11, slot 5byte0 = 0x4bbyte1 bits 0..2 = 0x05
The worked slot is arbitrary. Service slots are assigned for a connection;
only the service FourCC and command meaning are stable.
5.3 How record length is determined
BSN records are schema-delimited. There is no universal field saying
“this record is N bytes long.” Instead, the route identifies a payload decoder,
and that decoder advances the shared bit cursor until its top-level value is
complete. The cursor position at that moment is the logical end of the record.
Concrete example: a server-to-client
Chat/11 MessageRecv record. The values are omitted; only the
decoder’s consumed-bit counts are shown.
→→→→
11 header+336 payload+5 pad=352 bits=44 bytes
The length is a result, not an input. The
decoder learns the route, executes that route’s payload schema, rounds its
final bit position up to a byte boundary, and only then knows how many bytes
belong to the record.
The route is the only universal prefix.
It consumes 7 or 11 bits and identifies the command plus an optional runtime
service slot. It contains no payload-size or record-size field.
The route chooses the decoder.
Direction, resolved service FourCC, and command identify the payload type and
generated field order. Looking up the decoder consumes no additional wire bits.
The payload decoder advances the shared cursor.
Fixed-width members, local counts, option flags, choice selectors, and nested
members collectively determine when the top-level value is complete.
Final padding is deterministic.
Once the top-level payload ends at bit 347, five zero bits reach bit 352, the
next byte boundary. Non-zero final padding indicates a wrong schema or a
corrupted stream.
The buffer can now release 44 bytes.
The next byte begins another routing header. TCP may have delivered less than,
exactly, or more than those 44 bytes in any particular read.
What makes a payload’s extent deterministic?
Fixed-width valueThe schema supplies the exact width: for example, one bit for a Boolean or 32 bits for a FourCC.
Array or byte stringA bounded local count is decoded first. It determines the number of elements or raw bytes that follow.
Optional valueOne presence bit decides whether the nested value follows.
ChoiceA bounded selector chooses one arm; only that arm is decoded.
StructureMembers are decoded recursively in generated wire order until the structure is complete.
Alignment pointStrings and blobs may advance to the next byte before their raw bytes; final record padding does the same after the top-level value.
Local counts are not record lengths. An array count describes one array; a
string length describes one string; an optional flag and choice selector control
only their nested values. The enclosing schema combines all of those decisions
recursively to determine the payload’s total extent.
Counts use the same bounded-integer machinery as other ranged values. For a
declared range [minimum..maximum], the metadata supplies the encoded
bit width and the wire stores value - minimum. Thus an array declared
[1..64] can store count - 1 in six bits. A fixed range,
where minimum equals maximum, consumes no count bits at all.
An incremental socket decoder therefore does not wait for a declared record
size. It tries to execute the selected schema against the bytes currently
buffered. If a field is incomplete, it reads more TCP data and resumes. Once the
schema finishes, it validates the final zero padding, removes exactly that many
bytes, and starts the next routing header at the next byte.
Parser procedure
1. Read the 7- or 11-bit routing header.
2. Resolve (direction, service slot, command) to a payload type and generated order.
3. Decode fields until that schema is complete.
4. Consume zero padding through the next byte boundary.
5. The next byte begins the next routing header.
Consequence for unknown routes. Without the selected
payload schema, a decoder generally cannot calculate the record boundary or skip
safely to the next record. A wrong field order has the same effect: it changes
the cursor position and causes later records to be interpreted at the wrong
byte. TCP packet boundaries cannot be used to recover alignment.
Internal alignment is also relative to the complete record. Strings, byte
strings, and blobs can align the cursor before their raw bytes, so encoding a
payload at bit zero and prepending a separately encoded route header produces a
different layout. The next chapter resolves the numeric
service slot into a stable FourCC; Section 7 then explains
how the selected payload type is described.
The routing header has room for a four-bit service slot, so one connection can
address at most sixteen native services. The slot is an index into that
connection's registry; the registry entry supplies a stable FourCC and its
command-handler table.
Record headerdirection S→C command 11 slot s
→
Connection registryregistry[s] = Chat
→
Stable route identity(S→C, Chat, 11)
→
EndpointMessageRecv payload decoder
The example uses symbolic slot s deliberately.
Store the connection's slot-to-FourCC mapping, then identify records by direction,
FourCC, and command. A hard-coded numeric slot is not a portable route name.
The complete route identity is
(direction, service FourCC, command). The numeric slot is session
state, not protocol identity. A decoder should resolve the slot first and use the
resulting FourCC for every later lookup.
6.1 Assignments used by the documented build
Base97563 traffic uses the assignments below. They make a captured route such
as slot 5, command 11 readable as Chat/11. Keep the FourCC in decoded
output: a slot number is only the compact address used by this build's native
connection and should not be treated as a permanent service ID.
Slot
Service
Routes confirmed in this reference
0
Auth
0, 1, 2, 17, 18
1
Conn
1, 3, 5, 10, 11, 13, 14
3
Frnd
30, 31, 33
4
Pres
0, 1
5
Chat
0, 1, 2, 11, 21, 22, 25, 26, 27, 32, 33
10
S2Ms
27
11
Cach
9
13
S2Mp
57
14
Prfl
4
15
Toon
0, 5, 6, 10, 14
Connection core
Auth authenticates the native
session. Conn controls the transport, reports failures, and carries
generic routed envelopes.
Identity and social
Toon, Pres,
Frnd, Chat, and Prfl cover playable identity,
presence, friends, channels, messages, and profile settings.
Game and shared data
S2Ms, S2Mp,
Cach, Ladd, Achv, Prty, and
Supp cover SC2 master data, multiplayer, caches, ladder,
achievements, parties, and support operations.
Resolving the service still does not reveal the payload fields. The next
chapter explains the embedded type library that associates a routed endpoint
with a recursive BSN schema.
The previous chapter established that a BSN
route selects the schema which determines where its payload ends. That raises the
next question: where does that schema come from?
SC2 ships a compact, version-7 type library produced by Blizzard's
bsnc toolchain. It is embedded in the client executable alongside the
generated encoders and decoders. It is not transmitted in each record, and
it is separate from the protobuf descriptors used by BGS.
7.1 From a route to a schema
The problem: a BSN record says
Auth/2, but those route bits do not describe the payload fields.
The route and the embedded type library have to be connected.
→→→→
Structural recordkind, bounds, flags, and child type IDs
+
Naming recordtype name, member names, enum and choice labels
Route numbers are not type IDs. Dispatch
connects a direction/service/command tuple to generated code and its top-level
payload type. The metadata then explains that type recursively.
The record supplies only an address.
Direction, resolved service, and command identify which operation this is.
They contain neither field definitions nor a metadata type number.
Generated service dispatch is the bridge.
It maps the route to the handler and payload decoder produced for that build.
That binding identifies the top-level payload type associated with the route.
A type ID is an array index.
ID T selects entry T from both metadata offset tables.
The number is build-specific and has no meaning outside that metadata block.
The two records answer different questions.
The structural record says how to decode the value; the optional naming record
supplies the readable type, member, enum, and choice names.
Resolution is recursive. A struct
points to member type IDs, an array points to its element type, and an optional
points to its contained type. Expansion stops at primitives such as Boolean,
integer, FourCC, or blob.
Each metadata type has a build-specific numeric ID. Looking up that ID yields
a structural record and, when reflection names were retained, a naming record.
Structural records refer to other type IDs, so resolving one top-level payload
produces a graph rather than a flat field list.
The payload structures in this document are a human-readable rendering of that
decoded type graph. They are documentation notation, not a language carried on the
wire. For example:
illustrative rendering of decoded metadataAuthentication::ProofRequest {
requestModuleInput[0..4]
}
ModuleInput {
id blob[40]
data blob[0..1023]
}
Here the array bound, blob bounds, nested type relationship, type name, and
member names all come from metadata. The braces and formatting are ours.
7.2 Native type vocabulary
The low six bits of a structural record identify one of sixteen kinds. The
kind determines what other information the record carries and what the value
consumes from the BSN bitstream.
Scalar values
5 Boolean
One bit.
7 enum
A bounded integer, optionally restricted to listed values.
8 FourCC
Exactly 32 bits.
9 integer
A bounded, fixed-width integer.
10 void
No bits.
12 float32
32 raw bits.
13 float64
64 raw bits.
Containers
1 array
Bounded count, then repeated elements of another type.
2 byte string
Bounded count, byte alignment, then raw bytes.
3 bit array
Bounded bit count, then packed bits.
4 blob
Bounded count, byte alignment, then raw bytes.
11 optional
One presence bit, then the contained value when present.
15 UTF-8 string
Bounded byte count, alignment, then UTF-8 bytes.
Composition
6 choice
A bounded selector followed by the selected arm.
14 struct
A sequence of named or unnamed member types.
16 alias
A transparent reference to another type.
Most bounded values encode value - minimum using the recorded bit
width. Fixed-size forms can omit the count entirely. For indexed choices, enums,
and structs, bit 7 can mean that indices are the full contiguous range rather than
an explicit list.
7.3 How the type library is stored
This binary layout matters when extracting the type library from an SC2 build.
A packet decoder which has already loaded the parsed type graph does not encounter
this header or these tables on the network.
Header5 × big-endian u32
→
Shape offsetsone u32 per type ID
→
Shape recordsdump_size bytes
→
Name offsetsone u32 per type ID
→
Name recordsstring_size bytes
Type ID n indexes element n in both offset tables. Its
shape offset points to the structural record. Its name offset points to a small
record of 32-bit offsets into the NUL-terminated UTF-8 string pool, or is zero when
no names were retained. The header's type_count,
dump_size, and string_size delimit the five regions.
MetadataHeader# five unsigned big-endian u32 valuesversion# currently 7flagstype_countdump_sizestring_sizefollowed bydump_offset[type_count]
dump_data[dump_size]
string_record_offset[type_count]
string_data[string_size]
The first byte of each structural record begins with:
bits 0..5 kind tag (1..16)
bit 6obfuscated flag
bit 7 kind-specific compact-form flag
Bit 7 is interpreted by the selected kind; for example, it can indicate an
implicit contiguous index range instead of an explicit list.
Logical schema is not always exact wire order. For types marked
obfuscated, the generated encoder can reorder reflected members and insert
reserved bit runs that the logical metadata does not show. The metadata still
explains the type relationships and names, but an exact serializer or decoder
must recover the generated order from client code.
The registry and type library now provide everything needed to parse native
records. The next chapter applies them to the first Sunken exchange: Resume
authentication and the transition to protected service traffic.
The Resume path starts immediately after the Front handoff. The Sunken endpoint
does not greet first; it waits for client Auth/1.
8.1 Where the name “Resume” comes from
Resume is Blizzard’s type name. SC2’s embedded BSN metadata contains the
fully qualified types
Battlenet::Client::Authentication::ResumeRequest and
Battlenet::Client::Authentication::ResumeResponse. The runtime Auth
route maps client-to-server Auth/1 to the request and
server-to-client Auth/1 to the response. This chapter uses those
embedded names throughout.
What is resumed is the authenticated session context. Front has already
authenticated the Battle.net account, selected an SC2 game account, and obtained
the native endpoint plus session material from GameUtilities. The new Sunken
connection uses ResumeRequest and its proof exchange to demonstrate
possession of that handed-off state and continue it on the native service plane.
In other words, authorization crosses from one protocol connection to another.
Native authentication entry points named in the embedded metadata
Route
Embedded request type
Starting context
Auth/0
LogonRequest3
Cold native account logon; asks the native service to choose account-logon modules.
Auth/1
ResumeRequest
Continues the account and game-account session authorized by the Front handoff. This is the path documented here.
Auth/17
SingleSignOnRequest3
Begins from a native SSO identifier supplied by another trusted context.
Resume does not mean reconnecting the TCP socket.
Sunken is a new TCP connection. The term also does not promise restoration of a
previous chat roster, game, or disconnected native session. It names the
authentication entry point that continues Front-authorized identity and key
material on that new connection.
8.2 Auth/1 ResumeRequest
Logical payload:
RequestCommon:
m_program = FourCC("S2")
m_platform = FourCC("Mc64")
m_locale = FourCC("enUS")
m_versions = five Version records (table below)m_account = private account-login bytes
m_gameAccountRegion = u8 region from Front LogonResponse3m_gameAccountName = private game-account bytes
Program
Component/platform
Version
S2
NGD1
0x5bc8dcc1
S2
NGD2
0xfade3a32
S2
NGD3
0x0c129365
S2
NGD4
0x86b7c0ed
Bnet
Mc64
0x000a16a7 (661159)
These are Base97563-specific values. The final native compatibility number is
not the installation build number, and the outer BGS LogonRequest independently
sends application_version=0. Omitting the NGD records, substituting
97563, or labeling the final record S2/Mc64 produces a different and
invalid version set.
8.3 Configuration and proof
C -> SAuth/1ResumeRequestS -> CAuth/18ConfigurationS -> CAuth/2ProofRequest[two modules]C -> SAuth/2ProofResponse[one session-proof output]S -> CAuth/1ResumeResponse[phase-two server proof]
The Resume ProofRequest contains two 40-byte module IDs (eight-byte usage
prefix plus a 32-byte module identity):
Thumbprint module. The server supplies a 4096-bit RSA PKCS#1 v1.5
SHA-512 signature. Hash the 16-byte connected peer address followed by the
ASCII domain Thumbprint.IPv6. IPv4 peers use IPv4-mapped IPv6 form.
Verify with exponent 65537 and the public modulus embedded in SC2. This is a
one-way server-authentication check and contributes no
ModuleOutput element.
Session-proof module. Server data is
00 || server_nonce[16]. Treat the 64-byte GameUtilities value as
seed B, generate client_nonce[16], derive the
per-challenge key C below, and return
01 || client_nonce || HMAC-SHA256(C,
00 || client_nonce || server_nonce). The successful ResumeResponse
returns 02 || HMAC-SHA256(C,
01 || server_nonce || client_nonce), which must be verified.
For GameUtilities seed B, client nonce Nc, and
server nonce Ns, the Resume ratchet and proofs are:
Resume first ratchets seed B into C; Auth/2 proofs use
C, not B directly. The domain-two KDF belongs to the
alternate full-logon handshake and is not part of Resume.
An optional plaintext Conn/11 RegulatorUpdate can appear before
Auth/1 ResumeResponse.
8.4 Conn/5 and stream protection
After validating the server proof, the client sends an empty
Conn/5 EnableEncryption BSN record in plaintext. Both directions then
immediately use ordinary stateful RC4, with no initial keystream discard:
The two RC4 states are independent and persist across TCP reads, writes, and
BSN record boundaries. TCP segmentation is irrelevant. If one receive call contains
plaintext ResumeResponse followed by protected bytes, decrypt only the buffered
suffix after enabling the inbound state, exactly once.
Once protection is active, the connection enters the service bootstrap covered
by the endpoint reference in the next chapter.
Native BSN exposes service command endpoints, not BGS protobuf RPC methods.
An endpoint is identified by (direction, service FourCC, command).
Each entry below separates the stable route from the session-assigned numeric
service slot and describes the payload in actual generated order where known.
How to read the payload blocks
The blocks use compact pseudocode created for this reference. It is not
Blizzard source, protobuf syntax, or a wire-level DSL.
field Typeone named value of the stated type
[0..50]inclusive element-count or value range
?an optional value controlled by a presence bit
uN / sNunsigned or signed N-bit integer
repeat countdecode the indented structure exactly count times
alignToByteadvance the shared cursor to the next byte
reserved bits[N]consume N bits with no assigned field meaning
choice { … }decode only the arm selected by its discriminator
A range states what values are allowed; it does not by itself define the
count encoding. Fields are in actual wire order only when an entry says
generated order. Logical order follows metadata member order.
Leading m_ prefixes are omitted for readability.
The entries follow the Resume and post-login path. “Logical structure” means
metadata member order; “generated order” means the serializer's actual bit order,
including any reserved runs inserted for an obfuscated type. Entries with an
incomplete structure state the missing portion directly.
9.1 Auth — authentication
These plaintext endpoints bind the Sunken connection to the account and
session authorized by Front. The cryptographic sequence is described in
Native Resume authentication.
The Base97563 macOS version records and compatibility
number are listed in section 8. Account fields are
private session material.
Auth/18 Authentication::Configuration
Direction
server → client
Phase
plaintext
Structure
logical order
Supplies a small authentication configuration record before proof.
useS3Depotbool
Auth/2 Authentication::ProofRequest
Direction
server → client
Phase
plaintext
Structure
logical order
Requests execution of up to four authentication modules. The array count
is explicit: the payload begins with a three-bit count, and the decoder then
consumes exactly that many ModuleInput values. Resume encodes
count 2 for the thumbprint and session-proof modules.
requestCountu3valid values 0..4; 5..7 are invalidrepeat requestCount:
alignToByteidblob[40] fixed size; no length prefixdataByteCountu100..1023alignToBytedatabytes[dataByteCount]
The 40-byte module identifier selects the authentication-module
implementation that must interpret data. It is not a request
token or an identifier for the Auth/2 route. The 2 in
Auth/2 is the separate six-bit command ID in the BSN routing
header.
Resume supplies these two modules in the order shown.
The count cannot be inferred from a containing record length,
because native BSN has no general record-length field.
Auth/2 Authentication::ProofResponse
Direction
client → server
Phase
plaintext
Structure
logical order
Returns outputs requested by the authentication modules. This is not a
one-element-for-every-input array: the thumbprint module is verified locally
and emits no output. The Resume response therefore contains one output,
belonging to the session-proof module.
Terminates the native session with a numeric protocol error.
erroru16Error::Code
Conn/3 Connection::ServerVersion
Direction
server → client
Phase
protected bootstrap
Structure
32-bit shape
The 32-bit payload has the embedded ServerVersion shape. The
command-to-handler name remains provisional, but the payload boundary is
unambiguous.
versionu32
Conn/5 Connection::EnableEncryption
Direction
client → server
Phase
last plaintext record
Structure
empty payload
Commits the connection to independent inbound and outbound RC4 streams.
Protection begins immediately after this empty record.
EnableEncryption { } zero payload bits
Conn/10 Connection::Ping
Direction
client → server
Phase
protected keepalive
Structure
generated order
The client sends this record roughly once per minute. Its optional
signed value is the current Unix epoch timestamp in microseconds. Omitting the
ping eventually makes the server close an otherwise healthy chat session.
timeDataoptionals64Unix epoch microseconds
Conn/11 Connection::RegulatorUpdate
Direction
server → client
Phase
plaintext or protected
Structure
logical order
Updates the connection rate regulator. It may appear immediately before
ResumeResponse.
Carries a generic routed application envelope. A route header inside the
frame can name another service by stable FourCC and command, independently of
the outer runtime service slot.
Reflection lists frame type and headers before the
payload. Using that order corrupts the following record.
Retail also emits two independent maintenance
requests to the TrnC route once per minute: command 3 carries
four zero bytes and command 2 carries the stable 11-byte payload
030080057e4002020a0301. Both are CS_ROUTED frames
with route, correlation, content, and stream headers. Their correlation
headers set reply = false, so these are periodic client requests,
not replies to a server challenge. A chat-only client must emit them in
addition to Conn/10 Ping to match retail idle-session behavior.
Conn/14 Connection::GameSiteInfo
Direction
server → client
Phase
first protected bootstrap
Structure
generated order
Announces the client's public IPv4 endpoint as seen by the service, plus
the available SC2 game sites.
externalIp4AddrIP4::AddressPort { address, port }
siteDataSiteDataForClient[0..64] {
namebytes[0..32]
addressPortoptionalIP4::AddressPort
}
9.3 Toon — identity and bootstrap
Toon endpoints provide the account's playable identities and global SC2
bootstrap configuration.
Marks completion of the initial Toon notification set.
InitialNotifiesComplete { } zero payload bits
9.4 Pres — presence
Presence begins by defining a dynamic field table. Later updates carry
field handles plus a length-framed data blob whose individual values depend on
that table.
Defines the numeric handles and storage rules used by subsequent presence
updates.
definitionCountu70..100 accepted by the current clientrepeat definitionCount:
clientOnlyboolwritableboolephemeralboolsizeKindu10 = fixed, 1 = variablefixedSize?u16serverOnlyboolidentifieru8handleu32
Pres/0 Presence::UpdateNotify
Direction
server → client
Phase
protected
Structure
generated order
Updates online state and one or more dynamically announced fields. The
field data is sliced in handle order using each Pres/1 fixed size or the
next variable size. Cleared handles remove retained values. Local and
master presence IDs are aliases for the same retained record.
FIELD_AVATAR (65555) is
a four-byte ImageTableEntry. Public channel membership usually
supplies FIELD_TOON_PROFILE (65556) instead; its
12-byte profile address is followed through Prfl/0 path
04 to obtain the selected avatar.
9.5 Frnd — friends and blocks
These paged notifications carry the initial friends and block lists and can
later carry incremental changes.
Nested account and character records contain
private names, notes, profile addresses, and presence values. Those private
leaf values are omitted here; the enclosing wire structure is unchanged.
Requests items from a named cache stream. The chat bootstrap uses channel
BNET and item name CONF.
tokenu32generatedChecku23rolling check seeded with 7maxItemsMinus1u6request range 1..50streamKindu11 = descriptionchannelFourCCitemNameFourCClocaleFourCCreferenceTimes32directionu10 = before, 1 = after
Supplies club-name and tag validation expressions plus cache size and expiry
policy.
namePatternUTF-8u13 byte count; up to 4096 bytestagPatternUTF-8u13 byte count; up to 4096 bytesinformationCacheSizeu32memberCacheSizeu32informationExpirys32onlineStatusExpirys32eventExpirys32
9.9 Prfl — profile data
The FourCC is Prfl; embedded type names use the full
Profile namespace.
Reads a field path from a profile record. Roster avatar resolution uses
the profile address advertised by presence and path 04, the
zero-based position of ToonProfile.m_avatar.
Both directions differ from reflection member
order. A returned avatar is an ImageTableEntry containing the
retail ICON atlas ID and row-major cell offset.
Prfl/4 Profile::SettingsAvailable
Direction
server → client
Phase
protected bootstrap
Structure
generated order
Announces a profile settings record that can be addressed by type, field
path, and record address.
Reflection lists address before path. The generated
client reads type, path, then address.
9.10 Chat — channels, roster, and messages
The four-bit service slot in the outer route selects the Chat service. The
separate three-bit channelIndex inside Chat payloads selects one of
the rooms already joined by this client; it is returned by a successful join and
must be preserved for later message, roster, and leave records.
Entering the protected BSN stream does not immediately make the session
chat-ready. Base97563 establishes the playable identity and loads the regional
channel directory before it attempts a join:
When Toon/0 supplies the account's
playable identities, choose one with Toon/5.
Wait for both the cache response and Toon/6.
Send Chat/25 and
Chat/21. Collect
Chat/26 pages until isLast is true, and wait for
Chat/22.
Send Chat/0 for the desired
public or private channel. A successful Chat/27
assigns the channelIndex and local member handle.
Apply Chat/1 membership batches
until endOfInitial is true. The roster is then complete and the
assigned channel index can be used by Chat/11 and Chat/2.
These are state dependencies, not TCP adjacency rules. Other protected
records may appear between them, so a client must continue dispatching every
complete record while it waits for each milestone.
Chat/21 Chat::ChannelListRequest
Direction
client → server
Phase
protected discovery
Structure
empty payload
Requests the account's current channel-list descriptors.
ChannelListRequest { } zero payload bits
Chat/22 Chat::ChannelListResponse
Direction
server → client
Phase
protected discovery
Structure
generated order
Returns auto-join and recently joined channel descriptors.
Choice value 1 has no defined arm. Generated code
writes the locator before the request token. The private arm uses a
seven-bit UTF-8 byte count, byte alignment, and then the name bytes. Both
public ID 1033 and a named private room have been acknowledged live.
Chat/27 Chat::JoinNotify2
Direction
server → client
Phase
protected
Structure
generated order
Acknowledges or rejects a join and assigns the local channel index used by
membership, message, and leave records.
The generated decoder is result-first/token-last and
includes reserved runs absent from reflection metadata.
Chat/1 Chat notify family
Direction
server → client
Phase
protected
Structure
generated order
Command 1 carries MembershipChangeNotify, which creates the
initial roster and then applies incremental membership changes.
endOfInitialboolchannelIndexu30..6changesMembershipChange[1..64]
MembershipChange = choice {
leavingjoiningmember handle, presence ID, status valuesupdateStatusmember handle, one status value
}
endOfInitial marks completion of the
initial roster snapshot. Generated ToonName status values provide display
names directly. After the initial snapshot, the same structure carries joins,
leaves, and status changes.
The complete modern account and game-service system. It includes BGS, web
authentication, GameUtilities, and game-native services.
BGS (Battle.net Game Services)
The protobuf RPC layer carried here as one RPC per WebSocket binary message
inside TLS. BGS handles generic connection, authentication, challenge, account,
and GameUtilities calls.
BGS WebSocket
The RFC 6455 transport selected with subprotocol
v1.rpc.battle.net. Its binary-message boundary delimits the complete
BGS RPC body when the protobuf header omits size.
Cached web credential
An opaque value returned by AuthenticationServer method 8 after successful
authentication and offered in a later LogonRequest. It is distinct from the
short-lived credential returned by the interactive web challenge.
BSN
This document's short name for SC2's native, bit-packed service protocol.
It is schema-delimited and routed by service slot and command.
Front
The regional SC2 BGS connection used before the native handoff.
Sunken
The SC2-native endpoint that accepts Resume authentication and then carries
protected BSN service traffic.
GameUtilities
A generic BGS service. Its ProcessClientRequest method performs
the SC2-specific Front-to-Sunken handoff on this path.
Protobuf descriptor
Schema metadata for a BGS message or service. BGS service names and methods
are recoverable from standard protobuf descriptors embedded in the client.
BSN metadata
SC2's separate version-7 native type library, embedded in the executable
rather than sent in BSN records. A numeric type ID selects a structural record
and an optional naming record; recursively resolving child IDs produces the
logical payload schema. It is not a protobuf descriptor set.
Frame
A BGS header/body unit, or the explicitly named native
Connection/13 MessageFrame. The term is not used as a synonym for
every native BSN record.
Packet
A convenient but ambiguous informal term. This document prefers BGS
frame, BSN record, and TCP segment because each has a different
boundary.
Record
One native BSN route header plus its schema-delimited payload and final
byte-boundary padding.
Schema-delimited
A record boundary determined by executing the route-selected payload schema,
rather than by reading an outer record-length field. Local counts, selectors,
and presence bits determine nested extents; completion of the top-level value
determines the logical record end.
Route
The native tuple (direction, service FourCC, command). Direction
matters because the same command number may name different request and notify
types.
Service hash
A 32-bit FNV-1a hash of a full protobuf service name, used for BGS dispatch
after the initial Connect.
BGS token
A protobuf-header correlation value joining an RPC response or callback
acknowledgement to the corresponding call. It is unrelated to a Chat request's
application-level m_token.
Authentication module ID
The 40-byte identifier in an Auth/2 ModuleInput: an eight-byte
usage prefix followed by a 32-byte opaque module identity. It selects how that
module's data is processed. A module may produce no response element: in Resume,
the locally checked thumbprint produces none and the session-proof module
produces the sole output. See the
exact layout and known IDs.
Service slot
A session-local four-bit number selecting a native BSN service. It is not a
chat channel.
Client channel index
A three-bit Chat payload field selecting one of up to seven locally tracked
chat channels. It is independent of the native service slot.
LocatorKey
A native Chat choice identifying a private, public, or club conference.
Public channels use a locale FourCC and a channel-name string ID.
Generated order
The actual field order emitted by SC2's generated native serializer. For an
obfuscated type it may differ from reflection-member order and may include
otherwise unnamed reserved bits.
Resume
Blizzard’s name for the native Auth exchange that continues Front-authorized
account, game-account, and key context on a new Sunken connection. The exact
names Authentication::ResumeRequest and
Authentication::ResumeResponse occur in SC2’s embedded BSN
metadata. It does not mean resuming the previous TCP socket or restoring an
earlier chat or game session. See the nomenclature
explanation.
NO_RESPONSE
A protobuf RPC declaration stating that a BGS callback has no reply frame.
It is semantically different from a response whose protobuf body is empty.
FourCC
A four-byte symbolic identifier such as Chat, Auth,
Mc64, or enUS.
The 6+1+4 bit header and unusual bit order were publicly described during the
original Battle.net 2 era. The sources below also provide protobuf, replay-format,
and GameUtilities context useful when interpreting the live SC2 protocols.