Overview
waproto contains the Protocol Buffers definitions for all WhatsApp message types. It’s auto-generated from whatsapp.proto using buffa and provides strongly-typed Rust structs for working with WhatsApp’s binary protocol.
Structure
whatsapp.rs and tags.rs to OUT_DIR. Do not commit these files. Consumers of the crate never need protoc installed — only editors of whatsapp.proto do, to regenerate the committed descriptor.
Usage
All protobuf types are under thewaproto::whatsapp module:
buffa::MessageField<T>, not Option<Box<T>> or Option<T>. Construct one with buffa::MessageField::some(..), and read it back with .as_option(), .is_set(), or .is_unset().
waproto re-exports buffa (waproto::buffa), and whatsapp-rust in turn re-exports it as whatsapp_rust::buffa (with MessageField also available directly from prelude). Naming buffa::MessageField no longer requires a direct buffa dependency of your own — see Installation. You can still add buffa directly if you want to pin your own version or use APIs beyond MessageField.String, Vec<u8>, bool, integers) are still Option<T>, same as before. Enum fields are a typed Option<EnumType> rather than a raw Option<i32>, and enum variants are SCREAMING_SNAKE_CASE (e.g. wa::message::protocol_message::Type::MESSAGE_EDIT).
Key message types
Core message types
Message
The main message container used for all WhatsApp messages.MessageKey
Identifies a specific message in a conversation.Media Messages
ImageMessage
VideoMessage
AudioMessage
DocumentMessage
StickerMessage
Rich content messages
ExtendedTextMessage
Text with formatting, links, and quoted messages.InteractiveMessage
Buttons, lists, and other interactive elements.ButtonsMessage
ListMessage
Encryption Messages
SenderKeyDistributionMessage
Used for group message encryption.PreKeySignalMessage
Used for establishing 1:1 encryption.System & protocol messages
ProtocolMessage
For protocol-level operations.REVOKE- Revoke sent messageMESSAGE_EDIT- Edit sent messageEPHEMERAL_SETTING- Ephemeral message setting
ReactionMessage
EditMessage
AlbumMessage
Parent message for grouped media albums. Declares expected image/video counts so WhatsApp clients know how many items to group together.associated_child_message (a FutureProofMessage) and linked to the parent via a MessageAssociation:
whatsapp_rust::proto_helpers::wrap_as_album_child to construct album children. See Sending Messages - Album messages for usage examples.
AI & bot messages
AIRichResponseMessage
BotFeedbackMessage
Metadata & Context
MessageContextInfo
ContextInfo
Quoted messages, mentions, and forwarding info.Device & identity types
ADV Messages
Account Device Verification messages.These ADV types are capitalized
ADV... (not Adv...) to match buffa’s acronym-preserving type naming.Signal protocol structures
MessageField change above.
Handshake & connection types
HandshakeMessage
Used during initial connection handshake.ClientPayload
Device and client information during pairing.History sync types
HistorySyncNotification
HistorySync
Media reference types
ExternalBlobReference
References to uploaded media files.App state types
SyncActionValue
App state synchronization actions.Enums
waproto generates real Rust enums (not rawi32 constants). Variant names are SCREAMING_SNAKE_CASE:
buffa generates a closed enum by default: an unrecognized wire value decodes to
None rather than being preserved, unlike the old prost-generated Option<i32> fields which round-tripped any integer. In practice WhatsApp only ever sends in-schema values, so this only matters for forward compatibility with brand-new server-side enum variants.SyncdMutation.operation (SyncdOperation) is the one deliberate exception: build.rs opts it into buffa’s open enum mode, so the field type is Option<buffa::EnumValue<SyncdOperation>> instead of Option<SyncdOperation>. An unrecognized wire value decodes to EnumValue::Unknown(n) rather than None. wacore_appstate’s process_patch rejects an unknown operation with a typed AppStateError::UnsupportedSyncdOperation before mutating any state, instead of silently treating it as SET and corrupting the app-state LTHash — which is what the previous closed-enum decode did.Feature flags
The
generate feature was removed in #836. Code generation is now always-on — buffa-build, buffa-descriptor, heck, and sha2 are unconditional build dependencies. Remove --features generate from any build scripts.Serde support
All generated types deriveserde::Serialize by default. Deserialization and snake_case renaming are behind optional feature flags (serde-deserialize and serde-snake-case above).
Enable them in your Cargo.toml:
Default behavior (no feature flags)
All types deriveSerialize only:
With serde-deserialize
All types also derive Deserialize with #[serde(default)], matching protobuf semantics where missing fields use default values:
With serde-snake-case
All types additionally accept snake_case during deserialization. This primarily affects enum and oneof variants (buffa generates SCREAMING_SNAKE_CASE names), while struct fields are already snake_case. Serialization output remains unchanged.
The
serde-snake-case feature is primarily useful for WASM bridge scenarios where JavaScript sends snake_case JSON to the Rust backend. For most Rust-only use cases, you only need the default Serialize support.waproto::tags
The build generates thewaproto::tags module from the compiled protobuf descriptor. You get one pub mod per proto message with one pub const FIELD_NAME: u32 = N; per field. Nested messages produce nested modules.
assert! blocks pin them in the hand-written mirror structs. If whatsapp.proto renumbers a field the consts update automatically on next build; if a field referenced by the walkers is renamed or removed, compilation fails rather than the decoder silently reading the wrong wire data.
Code generation
The build generates protobuf code —build.rs always runs. It reads the committed binary descriptor (src/whatsapp.desc), verifies its SHA-256 against src/whatsapp.desc.sha256, and hands it to buffa-build, writing two files into OUT_DIR:
whatsapp.rs— full buffa-generated structs and enums, including zero-copy view typestags.rs—waproto::tagsfield-number constants (see waproto::tags above)
whatsapp.proto stays in its upstream camelCase form; buffa-build’s idiomatic_field_names option converts field and oneof identifiers to snake_case Rust idents at codegen time (word boundaries match heck/prost), so the generated API keeps the prost-style names build.rs used to produce by hand. buffa-descriptor and heck remain build dependencies — they back the waproto::tags generation described above, which reads the original (camelCase) descriptor directly.
Neither generated file is committed. buffa-build, buffa-descriptor, heck, and sha2 are unconditional build dependencies. protoc is only needed to regenerate the descriptor after editing whatsapp.proto — normal builds of waproto (or anything depending on it) never invoke protoc.
To update after modifying whatsapp.proto:
The
generate feature flag was removed. Code generation is now always-on and does not require any feature flags. Remove --features generate from any existing build scripts.Usage Examples
Constructing Messages
Pattern Matching
MessageField doesn’t pattern-match like Option directly — match on .as_option() instead:
Media Downloads
See Media Handling for complete examples.WhatsApp Version
The protobuf definitions are based on:Relationship with wacore
wacore provides utilities for working with waproto messages:proto_helpers- Conversion between protobuf and internal typesdownload-Downloadabletrait for media messagesupload- Media encryption for uploadmessages- Message encryption/decryptionsend- Message building and sending
Next Steps
wacore
Platform-agnostic protocol implementation
Sending Messages
Sending and receiving messages
Media Handling
Working with media uploads and downloads
Signal Protocol
End-to-end encryption details