Files
minecraft_protocol/versions/1.19.md
T
claude-timemachine d73c1c9537 minecraft_protocol: foundation + per-version protocol docs 1.7.10->26.2
8 topical docs (overview, data types, lifecycle, handshake, status/ping,
login+encryption, configuration, version-differences) + proxy-forwarding set
+ 16 per-version release-line docs, sourced from minecraft.wiki, ViaVersion
(source + commits), minecraft-data, node-minecraft-protocol, Velocity, BungeeCord.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:15:32 +02:00

279 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Java Edition 1.19.x — Protocol Deep-Dive
| Release | Protocol # | Release date | minecraft-data dir | ViaVersion package (into this version) |
|---|---|---|---|---|
| 1.19 | **759** | 2022-06-07 | `data/pc/1.19` | `v1_18_2to1_19` |
| 1.19.1 | **760** | 2022-07-27 | *(no separate dir; = 1.19.2)* | `v1_19to1_19_1` |
| 1.19.2 | **760** | 2022-08-05 | `data/pc/1.19.2` | `v1_19to1_19_1` |
| 1.19.3 | **761** | 2022-12-07 | `data/pc/1.19.3` | `v1_19_1to1_19_3` |
| 1.19.4 | **762** | 2023-03-14 | `data/pc/1.19.4` | `v1_19_3to1_19_4` |
Protocol numbers cross-checked against minecraft-data `protocolVersions.json` (`1.19`→759 dataVersion 3105, `1.19.1`→760 dv 3117, `1.19.2`→760 dv 3120, `1.19.3`→761 dv 3218, `1.19.4`→762 dv 3337) and each dir's `version.json`. **1.19.1 and 1.19.2 share protocol 760** — the wire format is identical; 1.19.2 was a same-week bug-fix that did not bump the protocol. Release dates from minecraft.wiki release articles (fetched 2026-06-19): [1.19](https://minecraft.wiki/w/Java_Edition_1.19), [1.19.1](https://minecraft.wiki/w/Java_Edition_1.19.1), [1.19.3](https://minecraft.wiki/w/Java_Edition_1.19.3), [1.19.4](https://minecraft.wiki/w/Java_Edition_1.19.4). ViaVersion source at `/tmp/mcproto-refs/ViaVersion/`.
---
## Headline — The Wild Update + Secure Chat
1.19 ("**The Wild Update**", 2022-06-07) shipped the Deep Dark biome + Warden, Ancient Cities, the Mangrove Swamp, and the Allay/Frog mobs ([minecraft.wiki/w/Java_Edition_1.19](https://minecraft.wiki/w/Java_Edition_1.19), fetched 2026-06-19). But on the wire, **the dominant story of the whole 1.19 line is chat signing / Secure Chat** — not gameplay. The wiki states plainly that as of 1.19, "Chat messages between players, as well as chat from the `/say`, `/msg`, `/teammsg`, and `/me` commands, are now cryptographically signed", and players receive a Mojang-provided key pair on startup ([1.19 article](https://minecraft.wiki/w/Java_Edition_1.19), fetched 2026-06-19).
The reason this release line carries **four protocol numbers** (759 → 760 → 761 → 762) is that the signing design **churned three times**:
- **759 (1.19):** v1 — client signs each chat message with a per-message signature, key delivered in Login Start; signed/unsigned content split; chat preview.
- **760 (1.19.1/1.19.2):** v2 — the **player-reporting** rework. A signature *chain* is introduced (each message signs over the preceding signature + a set of last-seen message acknowledgements), so message order is cryptographically verifiable for reports.
- **761 (1.19.3):** **partial revert + re-architecture.** Chat preview removed entirely; per-message "header" packets gone; the chain is rebuilt around a per-session ID + monotonic message index (`MessageLink`); system messages are no longer in the signed path; signing becomes more conditional on `enforce-secure-profile`. The disguised-chat packet is introduced for server-authored messages that need chat-type decoration without a signature.
- **762 (1.19.4):** refinements only — no change to the signing payload; clients reset their secure-chat session state on the Login (Play) packet.
This is the most complicated signing story in the protocol's history; the per-patch sub-sections below document each model precisely from ViaVersion source.
---
## Login-phase: the session profile public key (759760 only)
Secure Chat needs the client to prove ownership of a Mojang-signed key pair. In **1.191.19.2** that key is delivered **during the Login state**, appended to **Login Start (Serverbound `HELLO`)**, and it is what later chat-message signatures are verified against. This is the protocol's first use of a player-supplied public key in login. Full field layout and the Encryption-Response salt/signature variant are in [../05-login-encryption.md](../05-login-encryption.md) §2 and §5; summarised here for the signing context:
- **Login Start gains an optional `Signature` (profile key) container** (1.191.19.2): `Timestamp` (i64 key-expiry), `Public Key` (DER `SubjectPublicKeyInfo`), and a Mojang `Signature` over them. ViaVersion's type is `Types.OPTIONAL_PROFILE_KEY`. In 1.19.1+ Login Start also carries an optional `Player UUID`.
- `v1_18_2to1_19/Protocol1_18_2To1_19.java:278-284` — serverbound `HELLO` maps the name and **reads-and-discards** `OPTIONAL_PROFILE_KEY` (ViaVersion's own clients downstream don't sign).
- `v1_19to1_19_1/Protocol1_19To1_19_1.java:231-248` — serverbound `HELLO` reads the incoming profile key but **substitutes ViaVersion's own `ChatSession1_19_0.getProfileKey()`** when a chat session is configured, then reads the optional UUID (`Types.OPTIONAL_UUID`).
- **Encryption Response salt/signature variant** (1.191.19.2): when the client holds a profile key it replies to Encryption Request with `salt + signature` instead of the encrypted verify-token nonce; the server verifies it against the profile key. ViaVersion swaps this back to a plain nonce when its downstream client has no key:
- `v1_18_2to1_19/Protocol1_18_2To1_19.java:286-307` (the salt branch, `// 🧂`).
- `v1_19to1_19_1/Protocol1_19To1_19_1.java:264-283`.
- **761 (1.19.3) moves the key out of Login Start.** The profile key is no longer sent at login; instead the *active* key is sent in-Play via the new `CHAT_SESSION_UPDATE` packet (see §761). At login 1.19.3 only carries name + optional UUID:
- `v1_19_1to1_19_3/Protocol1_19_1To1_19_3.java:284-294` — serverbound `HELLO` writes `OPTIONAL_PROFILE_KEY` from the chat session (or null) but the *client* no longer supplies one; `:295-322` handles the Encryption-Response salt/signature using the in-Play `ChatSession1_19_1` key.
> Cross-reference: [../05-login-encryption.md](../05-login-encryption.md) lines 3553 (Login Start `Signature` container, present 1.191.19.2, removed 1.19.3) and 100123 (Encryption Response profile-key form).
---
## 759 — 1.19 (2022-06-07): chat signing v1
**Model.** Each player chat message is individually signed by the client. There is **no chain** yet: the signature covers a fixed-width metadata block plus the (canonicalised) decorated message JSON. The server delivers player chat to recipients in a dedicated, signed `PLAYER_CHAT` packet distinct from `SYSTEM_CHAT`.
**Signature payload (1.19, `ChatSession1_19_0`).** `v1_18_2to1_19` is a *down*-translation (1.18.2-side ↔ 1.19-side), and ViaVersion's own clients don't sign, so the canonical signing payload lives in `ChatSession1_19_0.signChatMessage`:
```java
// api/.../signature/storage/ChatSession1_19_0.java:42-51
byte[] data = new byte[32]; // big-endian
buffer.putLong(metadata.salt()); // 8B salt
buffer.putLong(sender.MSB).putLong(sender.LSB); // 16B sender UUID
buffer.putLong(metadata.timestamp().getEpochSecond); // 8B timestamp (seconds)
signer.accept(data);
signer.accept(GsonUtil.sort(content.decorated()) // canonical-sorted JSON of
.toString().getBytes(UTF_8)); // the decorated component
```
So the v1 signed bytes are `salt(8) ‖ senderUUID(16) ‖ timestampSeconds(8) ‖ sortedDecoratedJSON`, signed `SHA256withRSA` with the player's private key.
**Serverbound chat packets (1.19, `ServerboundPackets1_19`):**
| Packet | ID | Fields (1.19) |
|---|---|---|
| `CHAT_COMMAND` | 0x03 | command String, timestamp i64, salt i64, VarInt array of `{argumentName, signature[]}`, `signedPreview` bool |
| `CHAT` | 0x04 | message String, timestamp i64, salt i64, signature byte[], `signedPreview` bool |
| `CHAT_PREVIEW` | 0x05 | preview request (queryId + text) |
Source: `v1_18_2to1_19/packet/ServerboundPackets1_19.java:27-29`. ViaVersion (translating *up* from a 1.18 server that has no signing) simply **reads and drops** all the signing fields and **cancels `CHAT_PREVIEW`**: `Protocol1_18_2To1_19.java:225-254`.
**Clientbound chat packets (1.19, `ClientboundPackets1_19`):**
| Packet | ID | Notes |
|---|---|---|
| `CHAT_PREVIEW` | 0x0C | server-side preview response |
| `PLAYER_CHAT` | 0x30 | signed player message: `signedContent` (Component), `unsignedContent` (opt Component), chat-type VarInt, sender UUID, sender name, team name, **timestamp i64, salt i64, signature byte[]** |
| `SERVER_DATA` | 0x3F | MOTD/icon/`previewsChat` |
| `SET_DISPLAY_CHAT_PREVIEW` | 0x4B | toggles preview UI |
| `SYSTEM_CHAT` | 0x5F | unsigned system/overlay message (Component + type VarInt) |
Source: `v1_18_2to1_19/packet/ClientboundPackets1_19.java:36,72,87,99,119`. Note the **signed/unsigned content split** in `PLAYER_CHAT` — a 1.19 novelty: the server can show an unsigned, server-decorated rendering while still carrying the original signed text for reporting.
ViaVersion from a 1.18 server: there is no signed player chat to translate *up*, so 1.18's `CHAT` (0x0F) is rewritten into `SYSTEM_CHAT` (0x5F) — *every* incoming message becomes a system message ("we don't want to analyze and remove player names"), which sidesteps signing entirely: `Protocol1_18_2To1_19.java:212-223`.
**ViaVersion package/commits.** Package `common/.../protocols/v1_18_2to1_19/` (`Protocol1_18_2To1_19.java` + `packet/{Clientbound,Serverbound}Packets1_19.java` + `provider/AckSequenceProvider.java` + `storage/{NonceStorage1_19,SequenceStorage,DimensionRegistryStorage}.java`). `git log --oneline -- common/.../v1_18_2to1_19` (top relevant): `ab3927dff` "Implement our own hash writing", `32e51b52a` "Cleanup LOGIN/STATUS packet handlers (#4113)", `c5756fe45` "Rename Position to BlockPosition", `501f65e21` "Packet and entity type renames", `e965e9713` "Package/class renames and moves". <!-- VERIFY: no surviving commit subject names the original 759 chat-signing implementation; it predates the renames captured in this shallow-unshallowed log -->
---
## 760 — 1.19.1 / 1.19.2 (2022-07-27 / 2022-08-05): player reporting + signature chain v2
**Headline.** 1.19.1 added the **player-reporting** system: players can report abusive chat, multiple messages per report, with categories (harassment, hate speech, etc.); reported players can be banned from online play/Realms after review. Messages that are unsigned or server-tampered are now flagged "Not Secure"/"Modified". `enforce-secure-profile` now **defaults to true** on dedicated servers. Crucially: "The order of chat messages are now cryptographically verified" — the chain. ([minecraft.wiki/w/Java_Edition_1.19.1](https://minecraft.wiki/w/Java_Edition_1.19.1), fetched 2026-06-19.) **1.19.2 is wire-identical (still protocol 760)** — a bug-fix that did not change the format.
**Model — the v2 signature chain (`ChatSession1_19_1`).** Each message now signs over the **preceding message's signature** (the "header") plus the body (which itself folds in **last-seen message acknowledgements**). This is what makes message *order* tamper-evident for reporting.
```java
// api/.../signature/storage/ChatSession1_19_1.java:43-52
MessageHeader header = new MessageHeader(this.precedingSignature, sender);
MessageBody body = new MessageBody(content, timestamp, salt, lastSeenMessages);
header.update(signer); // preceding-sig ‖ senderUUID
body.update(signer); // SHA-256 hash of body, see below
this.precedingSignature = signature; // chain advances
```
Header bytes (`chain/v1_19_1/MessageHeader.java:38-44`): `precedingSignature (if present) ‖ senderUUID`.
Body — the body is **SHA-256-hashed first**, then the digest is fed to the signer (`chain/v1_19_1/MessageBody.java:53-75`). The pre-hash buffer is:
```
salt(i64) ‖ timestampSeconds(i64) ‖ plainContent(UTF-8) ‖ 0x46
‖ [if decorated] sortedDecoratedJSON(UTF-8)
‖ for each lastSeenMessage: 0x46 ‖ uuidMSB(i64) ‖ uuidLSB(i64) ‖ signatureBytes
```
`0x46` (= byte 70, `HASH_SEPARATOR_BYTE`) delimits sections. So v2 differs from v1 in three ways: (a) a `SHA-256` pre-hash of the body, (b) inclusion of the **plain** content plus a separator before the optional decorated JSON, and (c) the **last-seen acknowledgement set** is part of the signed body.
**Serverbound chat (1.19.1, `ServerboundPackets1_19_1`):**
| Packet | ID | Change vs 1.19 |
|---|---|---|
| `CHAT_ACK` | 0x03 | **new** — acknowledge last-seen messages out of band |
| `CHAT_COMMAND` | 0x04 | +`lastSeenMessages` array + optional `lastReceivedMessage` appended |
| `CHAT` | 0x05 | +`lastSeenMessages` array + optional `lastReceivedMessage` |
| `CHAT_PREVIEW` | 0x06 | unchanged role |
Source: `v1_19to1_19_1/packet/ServerboundPackets1_19_1.java:27-30`. The IDs all shift by one to make room for `CHAT_ACK` at 0x03. ViaVersion's signing handler on the serverbound `CHAT`/`CHAT_COMMAND` calls `chatSession.signChatMessage(metadata, decoratableMessage)` and writes the resulting signature + `signedPreview = decoratableMessage.isDecorated()`, then reads (and drops, for the upstream 1.19-side) the `PLAYER_MESSAGE_SIGNATURE_ARRAY` last-seen and `OPTIONAL_PLAYER_MESSAGE_SIGNATURE`: `Protocol1_19To1_19_1.java:112-193`. `CHAT_ACK` is cancelled outbound: `:194`.
**Clientbound chat (1.19.1, `ClientboundPackets1_19_1`):**
| Packet | ID | Change vs 1.19 |
|---|---|---|
| `CUSTOM_CHAT_COMPLETIONS` | 0x15 | **new** |
| `DELETE_CHAT` | 0x18 | **new** — server retracts a message by signature |
| `PLAYER_CHAT_HEADER` | 0x32 | **new** — standalone signed header (preceding-sig + body hash) for messages whose body the client already has |
| `PLAYER_CHAT` | 0x33 | reworked: carries the last-seen context + filter mask |
| `SERVER_DATA` | 0x42 | +`enforcesSecureChat` bool appended |
| `SET_DISPLAY_CHAT_PREVIEW` | 0x4E | unchanged role |
| `SYSTEM_CHAT` | 0x62 | type field becomes an `overlay` bool downstream |
Source: `v1_19to1_19_1/packet/ClientboundPackets1_19_1.java:45,48,74,75,90,122`. ViaVersion, lacking a way to faithfully reproduce the 760 signed chat onto a 759 client, **collapses `PLAYER_CHAT` (760) back into `SYSTEM_CHAT` (759)** — re-decorating the message via the chat-type registry rather than forwarding signatures: `Protocol1_19To1_19_1.java:85-111` + the `decorateChatMessage`/`translatabaleComponentFromTag` helpers (`:321-417`). It also injects the `enforcesSecureChat` flag into `SERVER_DATA` from its own config: `:221-229` (`create(Types.BOOLEAN, Via.getConfig().enforceSecureChat())`).
**Velocity-forwarding wrinkle.** Because the profile key now travels in Velocity modern forwarding, a 760 key would reach a server expecting a 759 key; ViaVersion rewrites the `velocity:player_info` forwarding-version byte down to 1 in the login `CUSTOM_QUERY`: `Protocol1_19To1_19_1.java:284-308`.
**ViaVersion package/commits.** Package `common/.../protocols/v1_19to1_19_1/` (`Protocol1_19To1_19_1.java` + `data/{ChatDecorationResult,ChatRegistry1_19_1}.java` + `storage/{ChatTypeStorage,NonceStorage1_19_1}.java`). `git log --oneline -- …/v1_19to1_19_1`: `fe9ca4992` "Update mcstructs", `29f299d88` "Update MCStructs to 3.0.0 (#4422)", `32e51b52a` "Cleanup LOGIN/STATUS packet handlers (#4113)", `b1f64fd08` "Use enhanced switches in more places (#4043)". <!-- VERIFY: the original 760 chain/reporting implementation commit subject is not in the captured log (predates the renames) -->
---
## 761 — 1.19.3 (2022-12-07): partial revert + chain v3
**Headline.** 1.19.3 **removed chat preview entirely** ("Removed chat preview"); deleted messages now show a "deleted by the server" placeholder for ≥3s; the "Modified" tag stops appearing for style-only server edits. ([minecraft.wiki/w/Java_Edition_1.19.3](https://minecraft.wiki/w/Java_Edition_1.19.3), fetched 2026-06-19.) The wiki release article does not narrate the wire-level chat-security rework — that detail comes from ViaVersion source below.
**What was reverted / re-architected at the wire level:**
1. **Chat preview gone.** All preview packets are dropped: ViaVersion cancels `CHAT_PREVIEW`, `SET_DISPLAY_CHAT_PREVIEW`, `PLAYER_CHAT_HEADER`, and `DELETE_CHAT` when translating a 1.19.1 server down: `v1_19_1to1_19_3/Protocol1_19_1To1_19_3.java:325-328`. The standalone `PLAYER_CHAT_HEADER` packet (0x32 in 760) is **gone** from the 761 clientbound set.
2. **`signedPreview` bool removed** from serverbound `CHAT`/`CHAT_COMMAND`; the message format is acknowledgement-based instead.
3. **Last-seen moves to an offset + bitset** (`ACKNOWLEDGED_BIT_SET`) rather than a full signature array on every message — far more compact: serverbound `CHAT` (0x05) ends with `offset VarInt` + `ACKNOWLEDGED_BIT_SET`: `Protocol1_19_1To1_19_3.java:230-270`; `CHAT_COMMAND` (0x04) likewise `:176-229`.
4. **New `CHAT_SESSION_UPDATE` (serverbound 0x20)** — the player's profile key + a per-session ID are announced *in-Play* via this packet, no longer in Login Start. ViaVersion cancels it (its downstream clients have no key): `Protocol1_19_1To1_19_3.java:324`.
5. **`DISGUISED_CHAT` (clientbound 0x18) introduced** for server-authored messages that need chat-type decoration but **no signature** (e.g. `/say`, command output) — separating "decorated but unsigned" from genuinely signed `PLAYER_CHAT`. ViaVersion rewrites a 1.19.1 `PLAYER_CHAT` *down* into `DISGUISED_CHAT`: `Protocol1_19_1To1_19_3.java:128-174`.
6. **System messages out of the signed path.** Combined with `DISGUISED_CHAT`, system/`/say`-style output no longer rides the signature chain; only genuine player chat is signed, and signing is optional unless `enforce-secure-profile` forces it.
**Model — the v3 chain (`ChatSession1_19_3`).** The chain is rebuilt around a **per-session random `sessionId` + a monotonic message index** (`MessageLink`), replacing the v2 "preceding-signature header":
```java
// api/.../signature/storage/ChatSession1_19_3.java:37-54
private final UUID sessionId = UUID.randomUUID();
private MessageLink link = new MessageLink(uuid, sessionId); // index 0
...
signer.accept(Ints.toByteArray(1)); // a constant version/prefix int = 1
messageLink.update(signer); // senderUUID ‖ sessionId ‖ index(i32)
messageBody.update(signer); // see below
```
`MessageLink.update` (`chain/v1_19_3/MessageLink.java:45-49`): `senderUUID ‖ sessionId(UUID) ‖ index(i32)`, with `index` incrementing per message (`next()`, capped at `Integer.MAX_VALUE`).
`MessageBody.update` (`chain/v1_19_3/MessageBody.java:46-57`) is now fed to the signer **raw (no intermediate SHA-256 of the whole body)**:
```
salt(i64) ‖ timestampSeconds(i64) ‖ contentLength(i32) ‖ content(UTF-8)
‖ lastSeenCount(i32) ‖ for each lastSeen: signatureBytes
```
Differences from v2: (a) the `MessageHeader{precedingSig, sender}` is replaced by `MessageLink{sender, sessionId, index}` — order is now proven by a session-scoped counter rather than a literal back-link to the previous signature; (b) a constant `int 1` is prefixed; (c) the body uses a **length-prefixed** plain content and last-seen list (only the **signatureBytes**, not the UUID, per acknowledged message) and **drops the decorated JSON** from the signed payload entirely; (d) the body is no longer pre-hashed by the chain code itself.
**Serverbound chat (1.19.3, `ServerboundPackets1_19_3`):**
| Packet | ID | Change vs 1.19.1 |
|---|---|---|
| `CHAT_ACK` | 0x03 | now carries an offset/count, not a full array <!-- VERIFY: exact CHAT_ACK 761 field layout not read from enum --> |
| `CHAT_COMMAND` | 0x04 | argument sigs use `SIGNATURE_BYTES`; ends with `offset` + `ACKNOWLEDGED_BIT_SET` |
| `CHAT` | 0x05 | optional `SIGNATURE_BYTES`, ends with `offset` + `ACKNOWLEDGED_BIT_SET` |
| `CHAT_SESSION_UPDATE` | 0x20 | **new** — profile key + session id |
Source: `v1_19_1to1_19_3/packet/ServerboundPackets1_19_3.java:27,28,29,56`. ViaVersion's serverbound `CHAT` handler signs with `chatSession.signChatMessage(metadata, decoratableMessage, messagesStorage.lastSignatures())` when a session exists, else writes empty signature: `Protocol1_19_1To1_19_3.java:230-270`.
**Clientbound chat (1.19.3, `ClientboundPackets1_19_3`):**
| Packet | ID | Change vs 1.19.1 |
|---|---|---|
| `CUSTOM_CHAT_COMPLETIONS` | 0x14 | renumbered |
| `DELETE_CHAT` | 0x16 | retained (signature-based delete), preview-deletes gone |
| `DISGUISED_CHAT` | 0x18 | **new** — decorated-but-unsigned |
| `PLAYER_CHAT` | 0x31 | reworked: `previousSignature` opt + `PLAYER_MESSAGE_SIGNATURE` + last-seen + filter mask |
| `SERVER_DATA` | 0x41 | MOTD becomes mandatory Component; icon becomes byte[] (1.19.4 change) |
| `SYSTEM_CHAT` | 0x60 | renumbered; system messages now strictly unsigned |
Source: `v1_19_1to1_19_3/packet/ClientboundPackets1_19_3.java:44,46,48,73,89,120`. `PLAYER_CHAT_HEADER` and the two preview packets are **absent** from this enum.
**Acknowledgement bookkeeping.** ViaVersion maintains a `ReceivedMessagesStorage`: every incoming signed `PLAYER_CHAT` is recorded, and after 64 unacknowledged it auto-sends a `CHAT_ACK`: `Protocol1_19_1To1_19_3.java:135-148`.
**ViaVersion package/commits.** Package `common/.../protocols/v1_19_1to1_19_3/` (`Protocol1_19_1To1_19_3.java` + `storage/{NonceStorage1_19_3,ReceivedMessagesStorage}.java` + rewriters). `git log --oneline -- …/v1_19_1to1_19_3` (relevant): `3eec520eb` "Send enable features packet after the play login packet in 1.19.1->1.19.3 (#4205)", `3caaed00d` "Write enabled features as string array", `815ec24af` "Remove removed registries from command arguments", `ab3927dff` "Implement our own hash writing", `32e51b52a` "Cleanup LOGIN/STATUS packet handlers (#4113)". <!-- VERIFY: the original 761 chat-rework commit subject is not in the captured log -->
---
## 762 — 1.19.4 (2023-03-14): refinements (no signing-payload change)
**Headline.** 1.19.4 added Display entities, Interaction entities, armour trims, archaeology (brush + suspicious sand), the Cherry Grove biome, the Sniffer, and `/ride` + `/damage`. ([minecraft.wiki/w/Java_Edition_1.19.4](https://minecraft.wiki/w/Java_Edition_1.19.4), fetched 2026-06-19.)
**Chat / signing:** **the signed-message payload did not change from 761.** The only chat-security change is procedural: "Clients now reset their secure chat session state when receiving the login packet" ([1.19.4 article](https://minecraft.wiki/w/Java_Edition_1.19.4), fetched 2026-06-19) — i.e. the `MessageLink` index/session resets on a (re)Login, so a dimension change / server-switch starts a fresh chain. ViaVersion's `v1_19_3to1_19_4` package contains **no chat-signing handler at all**; its `registerPackets` only touches `COMMANDS`, `UPDATE_MOB_EFFECT` (infinite-duration 1 sentinel), and `SERVER_DATA` (MOTD → mandatory Component, icon String → `OPTIONAL_BYTE_ARRAY_PRIMITIVE`): `v1_19_3to1_19_4/Protocol1_19_3To1_19_4.java:60-105`. The `CHAT`/`PLAYER_CHAT`/`DISGUISED_CHAT` packets pass through structurally unchanged at 762 (no rewriter registered for them).
**ViaVersion package/commits.** Package `common/.../protocols/v1_19_3to1_19_4/`. `git log --oneline -- …/v1_19_3to1_19_4`: `b4d8fe6ba` "Display infinite potion effect duration for 1.19.4+…(#4953)", `97aff00ce` "Fill registries used by delayed item loading", `c13b40a37` "Add ParticleRewriter base (#4203)", `501f65e21` "Packet and entity type renames". No chat-signing commits — consistent with "no payload change at 762".
---
## Signature-chain evolution (759 → 761)
```mermaid
flowchart TD
subgraph v1["759 (1.19) — ChatSession1_19_0"]
A1["per-message signature<br>NO chain"]
A2["payload: salt ‖ senderUUID ‖ timestamp ‖ sortedDecoratedJSON"]
A1 --> A2
end
subgraph v2["760 (1.19.1/1.19.2) — ChatSession1_19_1"]
B1["chain via PRECEDING signature<br>(MessageHeader)"]
B2["header: precedingSig ‖ senderUUID"]
B3["body (SHA-256 hashed): salt ‖ ts ‖ plain ‖ 0x46 [‖ decoratedJSON]<br>‖ for each lastSeen: 0x46 ‖ uuid ‖ sig"]
B1 --> B2 --> B3
end
subgraph v3["761 (1.19.3) — ChatSession1_19_3"]
C1["chain via sessionId + index<br>(MessageLink)"]
C2["prefix int 1 ‖ link: senderUUID ‖ sessionId ‖ index"]
C3["body: salt ‖ ts ‖ len ‖ content ‖ lastSeenCount ‖ sigs<br>(no decoratedJSON, no decorated path)"]
C1 --> C2 --> C3
end
v1 -->|"+ player reporting,<br>last-seen acks"| v2
v2 -->|"preview removed,<br>header→sessionId+index,<br>DISGUISED_CHAT split"| v3
```
Sources: `ChatSession1_19_0.java:42-51`, `ChatSession1_19_1.java:43-52` + `chain/v1_19_1/{MessageHeader,MessageBody}.java`, `ChatSession1_19_3.java:37-54` + `chain/v1_19_3/{MessageLink,MessageBody}.java`.
---
## Proxy / translation impact
A proxy or version-translation layer sitting across the 1.19 line must understand **four mutually-incompatible chat shapes**, because each protocol bump moved the signature fields and the chain rules:
1. **All four signing models coexist in the wild.** Vanilla 1.19, 1.19.1/.2, 1.19.3, and 1.19.4 clients each speak a different chat-packet shape. A proxy that translates between them (ViaVersion) cannot just renumber packets — it must **re-sign or strip-and-downgrade** the message:
- Down-translating signed chat to an older format usually means **discarding the signature and re-rendering as a system/disguised message** (ViaVersion does exactly this: 760→759 collapses `PLAYER_CHAT``SYSTEM_CHAT` (`Protocol1_19To1_19_1.java:85-111`); 761→760 turns `PLAYER_CHAT``DISGUISED_CHAT` (`Protocol1_19_1To1_19_3.java:128-174`)).
- When the proxy itself holds the player's key (a configured `ChatSession`), it **forges valid signatures** for the target format (`signChatMessage(...)` in each protocol's serverbound `CHAT`/`CHAT_COMMAND` handler).
2. **Risk: relaying stripped signatures to an `enforce-secure-profile` server.** If `enforce-secure-profile=true` (default since 1.19.1), a server **rejects** unsigned or invalidly-signed player chat. A proxy that strips signatures while down/upgrading — without re-signing — will have its players' chat dropped or the connection kicked. ViaVersion mitigates by (a) substituting its own `ChatSession` key in Login Start (759/760) / `CHAT_SESSION_UPDATE` (761), and (b) re-signing per target format. A proxy that does **not** hold the key can only forward to non-enforcing servers, or must route player chat as system/disguised (unsigned) messages — which the target may flag "Not Secure" or reject.
3. **`enforcesSecureChat` flag must be synthesised.** `SERVER_DATA` gained the `enforcesSecureChat` bool at 760; a proxy bridging from an older server has to fabricate it (ViaVersion sources it from `Via.getConfig().enforceSecureChat()`: `Protocol1_19To1_19_1.java:227`).
4. **Last-seen / acknowledgement bookkeeping is mandatory at 760+.** Because the chain folds in last-seen signatures, a translating proxy must track received `PLAYER_CHAT` signatures and emit `CHAT_ACK`/offset+bitset acknowledgements, or the server's chain validation desyncs (ViaVersion's `ReceivedMessagesStorage`, auto-`CHAT_ACK` at 64 unacked: `Protocol1_19_1To1_19_3.java:135-148`).
5. **Login-phase profile key handling differs 759/760 vs 761.** A proxy must read/strip/substitute the Login-Start profile key and the Encryption-Response salt/signature variant for 759/760, but switch to the in-Play `CHAT_SESSION_UPDATE` path for 761+. Velocity modern-forwarding carries the key too, so the forwarding-version byte may need clamping (`Protocol1_19To1_19_1.java:284-308`). See [../05-login-encryption.md](../05-login-encryption.md) §2/§5.
6. **762 is cheap.** No signing change; a proxy only resets per-session chat state on Login and handles the `SERVER_DATA` MOTD/icon type change.
---
## Sources
- minecraft.wiki release articles (fetched 2026-06-19): [1.19](https://minecraft.wiki/w/Java_Edition_1.19), [1.19.1](https://minecraft.wiki/w/Java_Edition_1.19.1), [1.19.3](https://minecraft.wiki/w/Java_Edition_1.19.3), [1.19.4](https://minecraft.wiki/w/Java_Edition_1.19.4).
- ViaVersion `/tmp/mcproto-refs/ViaVersion/``common/.../protocols/{v1_18_2to1_19, v1_19to1_19_1, v1_19_1to1_19_3, v1_19_3to1_19_4}/` (Protocol classes + packet enums) and `api/.../minecraft/signature/` (`ChatSession1_19_0/1_19_1/1_19_3` + `chain/v1_19_1/{MessageHeader,MessageBody}` + `chain/v1_19_3/{MessageLink,MessageBody}`). Git logs per package as cited.
- minecraft-data `/tmp/mcproto-refs/minecraft-data/data/pc/``1.19`,`1.19.2`,`1.19.3`,`1.19.4` `version.json` + `common/protocolVersions.json` (protocol numbers + dataVersions).
- Cross-link: [../05-login-encryption.md](../05-login-encryption.md) (profile keys in Login Start; Encryption Response salt/signature form).
```