Hello
Sent by a joiner to the host immediately after transport connection.
displayName — human-readable display name. sessionId — joiner-minted session identifier (UUID-style). deviceId — optional hardware-stable identifier for reconnect dedup. targetRoom — the host us.tractat.kuilt.core.Pattern.roomKey this joiner intends to enter, from us.tractat.kuilt.core.Tag.roomKey. null (the default) means the transport already bound the room, so the host admits without comparing; a non-null value that mismatches the host's own room key is rejected. Nullable + defaulted so it is wire-safe: an older build that never sends it decodes as null (see the shared cbor codec's ignoreUnknownKeys), and an old encoded frame with no targetRoom key still decodes to null. protocolVersion — the admit-handshake version this joiner speaks (ProtocolVersion.CURRENT). The host compares it against its supported range and rejects a mismatch at admit time with RejectCode.ProtocolMismatch (#1569). Additive, exactly like targetRoom: nullable + defaulted, so a peer that predates the field decodes as null and is admitted as legacy (kotlinx omits the defaulted null, so a version-less Hello is byte-identical to the pre-#1569 wire form).