Skip to main content

TLS & WebSocket

Discord-PXE implements the secure transport layers required before any Gateway payload can flow.

Those layers are not wrappers around an operating-system TLS library. The runtime owns its own TLS state, record buffers, transcript data, key material, HTTP upgrade request, and WebSocket accept verification.

Primary sources:

Per-shard TLS workspace​

Each sharded TLS context allocates:

45 pages
45 × 4096 = 184,320 bytes

That workspace is intentionally private to one shard.

The layout includes storage for:

  • wire input
  • handshake accumulation
  • transcript
  • decrypted record data
  • plaintext
  • ciphertext
  • ClientHello
  • client/server randoms
  • ECDHE parameters
  • server signature
  • authentication hash
  • private/public/shared key material
  • master secret
  • key block
  • client/server traffic keys
  • IVs
  • verification buffers
  • certificate/RSA state
Why the workspace is large

TLS is stateful across multiple records and handshake messages. Reusing one global workspace across concurrent shards would produce non-deterministic corruption even if every shard had its own TCP connection.

Lazy allocation​

TLS pages are allocated only when a shard actually starts.

The shard descriptor stores the resulting pointer in SHARD_OFF_TLS_CTX.

If normal page allocation fails, the runtime can attempt the high-page allocator.

This keeps an unused 128-slot plan from immediately reserving tens of megabytes of TLS workspace.

Reset semantics​

A TLS reset zeros the entire private workspace, returns its state to the idle state, and increments a handshake generation counter.

That generation is useful because the same allocated pages can be reused across reconnects without pretending the new handshake is the same logical TLS session.

ClientHello construction​

The sharded path builds its own ClientHello from the shard's private workspace.

The process includes:

  • fresh 32-byte client random
  • TLS version fields
  • cipher suite selection
  • compression field
  • SNI extension
  • other required extensions
  • handshake/transcript accounting

The current sharded path is based on the proven singleton TLS engine rather than a brand-new protocol implementation.

Handshake transcript​

TLS Finished verification depends on the exact handshake transcript.

The sharded workspace keeps a bounded transcript buffer and explicitly appends handshake bytes.

Before appending, the implementation checks remaining capacity.

That is important because transcript overflow in C/assembly would otherwise become memory corruption, not a clean high-level exception.

Private sequence state​

TLS record protection uses read and write sequence numbers.

Each shard's workspace owns separate:

write sequence high/low
read sequence high/low
write-enabled state
read-enabled state
RX byte count
TX byte count

Those values cannot be shared between shard connections.

Failure propagation​

TLS failure marks the shard TLS state failed and sets a shard-level error if one has not already been recorded.

The shard lifecycle code then sees that failed state and transitions the entire shard into backoff/recovery.

This keeps TLS from trying to recover independently on top of stale TCP state.

TCP binding​

The sharded TLS engine binds to the receive callback of one shard's TCP context.

That gives the transport chain a direct identity:

shard descriptor
│
├─ TCP workspace
│ │
│ └─ RX callback
│ │
└────────────▼
TLS workspace

There is no need to inspect a global socket table to guess which TLS parser should receive the bytes.

WebSocket workspace​

After TLS authentication, the shard allocates a separate 4 KiB WebSocket workspace.

The workspace holds:

  • readiness state
  • request length
  • raw WebSocket nonce
  • base64 key
  • accept-input buffer
  • SHA-1 result
  • expected base64 accept value
  • HTTP upgrade request

Again, the point is ownership: each shard has its own nonce/request/accept state.

HTTP Upgrade​

The WebSocket layer builds an HTTP request using a fresh random key and sends it through the shard's authenticated TLS path.

The response must include a valid HTTP 101 upgrade and the expected Sec-WebSocket-Accept value.

Code example of WebSocket accept verification
legacy_shard_ws_validate_accept64:
push rbx
push r12
push r13
mov rbx, rcx
mov r12, rdx
mov r13d, r8d
test rbx, rbx
jz .no
mov rbx, [rbx + SHARD_OFF_WS_CTX]
test rbx, rbx
jz .no
cmp dword [rbx + SHARD_WS_READY], 1
jne .no
cmp r13d, 28
jb .no

xor r9d, r9d
.scan:
mov eax, r9d
add eax, 28
cmp eax, r13d
ja .no
xor ecx, ecx
.check:
cmp ecx, 28
jae .yes
mov al, [r12+r9]
cmp al, [rbx + SHARD_WS_ACCEPT_B64 + rcx]
jne .next
inc r9d
inc ecx
jmp .check
.next:
sub r9d, ecx
inc r9d
jmp .scan
.yes:
mov eax, 1
jmp .out
.no:
xor eax, eax
.out:
pop r13
pop r12
pop rbx
ret

A TLS connection becoming authenticated is therefore not yet enough to mark the Gateway path usable.

WebSocket framing​

Once the upgrade succeeds, the runtime can process Gateway WebSocket frames.

At that point the stack is:

TCP byte stream
↓
TLS records
↓
decrypted application bytes
↓
WebSocket framing
↓
Discord Gateway payloads

Each layer has separate state and error handling.

Why TLS and WebSocket are separate workspaces​

Combining them into one monolithic block would be simpler in the short term, but would blur reset boundaries.

A reconnect may need to:

  • rebuild TCP
  • completely restart TLS
  • generate a new WebSocket nonce
  • preserve a valid Discord resume session

Those are not the same lifecycle.

Acceptance behavior​

For the two-shard hardware milestone, both shards must independently reach:

TCP connected
TLS authenticated
HTTP 101 verified
Gateway Hello
Identify / Resume
READY
heartbeat ACK

and the acceptance check confirms that the TLS and WebSocket workspace pointers differ between shard 0 and shard 1.

TLS workspace allocation and initialization​

Each active shard owns 45 pages of TLS state.

Context before the block
Symbol / registerMeaning
RBXOwning shard descriptor.
SHARD_OFF_TLS_CTXPointer field inside the shard descriptor.
SHARD_TLS_PAGES45 pages.
SHARD_TLS_BYTES45 × 4096 = 184,320 bytes.
RAXAllocation result / returned TLS workspace pointer.
legacy_shard_tls_alloc64:
push rbx ; Preserve RBX because it will hold the shard context.
mov rbx, rcx ; RBX = owning shard descriptor.
test rbx, rbx ; Reject a null shard pointer.
jz .bad ; Null owner cannot receive TLS state.

mov rax, [rbx + SHARD_OFF_TLS_CTX] ; RAX = existing TLS workspace pointer.
test rax, rax ; Has this shard already allocated TLS state?
jnz .done ; Yes: return the existing workspace.

mov ecx, SHARD_TLS_PAGES ; ECX = 45 pages requested.
call alloc_pages64 ; Try the normal page allocator.
test rax, rax ; Did normal allocation succeed?
jnz .got ; Yes: continue with this allocation.

mov ecx, SHARD_TLS_PAGES ; Request the same number of pages again.
call alloc_high_pages64 ; Fall back to high-memory allocator.

.got:
test rax, rax ; Did either allocator succeed?
jz .bad ; No: return failure.

mov [rbx + SHARD_OFF_TLS_CTX], rax ; Publish workspace ownership in shard descriptor.

push rax ; Preserve workspace pointer across REP STOSQ setup.
mov rdi, rax ; RDI = first byte of TLS workspace.
xor eax, eax ; RAX = zero fill value.
mov ecx, SHARD_TLS_BYTES / 8 ; ECX = qword count covering all 184,320 bytes.
rep stosq ; Zero all private TLS state.
pop rax ; Restore workspace pointer for return/state write.

mov dword [rax + STLS_STATE], LEGACY_TLS_IDLE ; Initialize TLS state machine to IDLE.

.done:
pop rbx ; Restore caller's RBX.
ret ; Return workspace pointer in RAX.

.bad:
xor eax, eax ; RAX = 0 signals allocation failure.
pop rbx ; Restore caller's RBX.
ret ; Return failure.
Memory cost

One active TLS shard consumes roughly 180 KiB before counting TCP, WebSocket, Discord, RX/TX, session, or reliable-retransmission storage. The 128-slot descriptor pool is therefore not equivalent to 128 fully materialized TLS clients.

Success condition

SHARD_OFF_TLS_CTX points to a zeroed 45-page workspace with STLS_STATE = LEGACY_TLS_IDLE.

TLS transcript bounds enforcement​

TLS Finished verification depends on the exact handshake transcript, so append operations have an explicit capacity check.

Register context
RegisterMeaning
RBXOwning shard descriptor.
R12Source pointer containing handshake bytes to append.
R13DNumber of bytes requested.
RAXTLS workspace pointer.
EDXExisting transcript length.
ECXRemaining capacity / copy count.
mov edx, [rax + STLS_TRANSCRIPT_LEN] ; EDX = bytes already stored in transcript.
mov ecx, LEGACY_TLS_TRANSCRIPT_CAP ; ECX = absolute transcript buffer capacity.
sub ecx, edx ; ECX = remaining free bytes.
cmp r13d, ecx ; Does requested append fit in remaining space?
ja .bad ; No: fail before touching adjacent TLS state.

lea rdi, [rax + STLS_TRANSCRIPT] ; RDI = transcript buffer base.
add rdi, rdx ; Advance RDI to current transcript end.
mov rsi, r12 ; RSI = caller's handshake-byte source.
mov ecx, r13d ; ECX = exact number of bytes to copy.
rep movsb ; Append bytes sequentially to transcript.

mov rax, [rbx + SHARD_OFF_TLS_CTX] ; Reload TLS workspace pointer after string copy.
add [rax + STLS_TRANSCRIPT_LEN], r13d ; Publish the new transcript length.
Memory-safety boundary

There is no managed runtime here. If the capacity test were removed or wrong, an oversized handshake transcript would overwrite adjacent cryptographic state in the same TLS workspace.

Success condition

The transcript contains the new handshake bytes contiguously and STLS_TRANSCRIPT_LEN advances by exactly the appended byte count.

Source​