# Envelope Schema **Schema file:** `schemas/envelope.capnp` **File ID:** `@0xe4a7f2c8b1d63509` The Envelope is the legacy top-level wire message used in M1 for all quicproquo traffic. Every frame exchanged between peers was serialised as an Envelope, with the Delivery Service routing by `(groupId, msgType)` without inspecting the payload. > **Note:** The Envelope is the M1-era framing format. The current M3+ architecture uses Cap'n Proto RPC directly via the [NodeService](node-service-schema.md) interface. The Envelope schema remains in the codebase for backward compatibility and for use in integration tests. --- ## Full schema listing ```capnp # envelope.capnp -- top-level wire message for all quicproquo traffic. # # Every frame is serialised as an Envelope. # The Delivery Service routes by (groupId, msgType) without inspecting payload. # # Field sizing rationale: # groupId / senderId : 32 bytes -- SHA-256 digest # payload : opaque -- MLS blob or control data # timestampMs : UInt64 -- unix epoch milliseconds; sufficient until year 292M # # ID generated with: capnp id @0xe4a7f2c8b1d63509; struct Envelope { # Message type discriminant -- determines how payload is interpreted. msgType @0 :MsgType; # 32-byte SHA-256 digest of the group name. # The Delivery Service uses this as its routing key. # Zero-filled for point-to-point control messages (ping, keyPackageUpload, etc.). groupId @1 :Data; # 32-byte SHA-256 digest of the sender's Ed25519 identity public key. senderId @2 :Data; # Opaque payload. Interpretation is determined by msgType. payload @3 :Data; # Unix timestamp in milliseconds at the time of send. timestampMs @4 :UInt64; enum MsgType { ping @0; pong @1; keyPackageUpload @2; keyPackageFetch @3; keyPackageResponse @4; mlsWelcome @5; mlsCommit @6; mlsApplication @7; error @8; } } ``` --- ## Field-by-field analysis ### `msgType @0 :MsgType` A 16-bit enum discriminant (Cap'n Proto enums are encoded as UInt16). Determines how the `payload` field should be interpreted. The discriminant is the first field in the struct for efficient dispatch: a router can read the first two bytes of the struct section to decide how to handle the message without parsing any pointer fields. ### `groupId @1 :Data` A 32-byte `Data` field containing the SHA-256 digest of the group name. The Delivery Service uses this as its primary routing key when the Envelope-based protocol is active. **Sizing rationale:** SHA-256 produces a 32-byte (256-bit) digest. This is stored as a variable-length `Data` field rather than a fixed-size blob because Cap'n Proto does not have a fixed-size array type. Implementations must validate that the field contains exactly 32 bytes. **Special case:** For point-to-point control messages (`ping`, `pong`, `keyPackageUpload`, `keyPackageFetch`), the `groupId` is zero-filled (32 zero bytes) because these messages are not associated with any group. ### `senderId @2 :Data` A 32-byte `Data` field containing the SHA-256 digest of the sender's Ed25519 identity public key. This allows the receiver to identify the sender without inspecting the MLS-layer credentials. **Sizing rationale:** Same as `groupId` -- SHA-256 digest, 32 bytes. ### `payload @3 :Data` An opaque byte string whose interpretation depends on `msgType`. ### `timestampMs @4 :UInt64` Unix epoch timestamp in milliseconds, set by the sender at the time of send. Encoded as a `UInt64`, which provides sufficient range until approximately year 292,000,000 -- effectively unlimited for practical purposes. The timestamp is sender-asserted and **not** authenticated by the server. Receivers should treat it as advisory (for display ordering) rather than authoritative. --- ## MsgType enum The `MsgType` enum defines nine message types. Each variant determines how the `payload` field is interpreted: | Ordinal | Variant | Payload Contents | Direction | |---|---|---|---| | 0 | `ping` | Empty | Client -> Server or Peer -> Peer | | 1 | `pong` | Empty | Server -> Client or Peer -> Peer | | 2 | `keyPackageUpload` | openmls-serialised KeyPackage blob (TLS encoding) | Client -> Server | | 3 | `keyPackageFetch` | Target identity key (32 bytes, raw Ed25519 public key) | Client -> Server | | 4 | `keyPackageResponse` | openmls-serialised KeyPackage blob, or empty if none stored | Server -> Client | | 5 | `mlsWelcome` | `MLSMessage` blob (Welcome variant) | Peer -> Peer (via DS) | | 6 | `mlsCommit` | `MLSMessage` blob (PublicMessage / Commit variant) | Peer -> Group (via DS) | | 7 | `mlsApplication` | `MLSMessage` blob (PrivateMessage / Application variant) | Peer -> Group (via DS) | | 8 | `error` | UTF-8 error description string | Any direction | ### Control messages (0-1) `ping` and `pong` are keepalive probes with empty payloads. They serve as health checks over long-lived connections. ### Authentication messages (2-4) `keyPackageUpload`, `keyPackageFetch`, and `keyPackageResponse` implement the Authentication Service protocol over the Envelope format. In the current architecture, these operations are handled by the [NodeService RPC](node-service-schema.md) methods `uploadKeyPackage` and `fetchKeyPackage` instead. ### MLS messages (5-7) `mlsWelcome`, `mlsCommit`, and `mlsApplication` carry MLS protocol messages as opaque blobs. The Envelope does not inspect or validate the MLS content; it simply transports the bytes between peers via the Delivery Service. ### Error messages (8) `error` carries a UTF-8 string describing an error condition. Used for protocol-level error reporting (e.g., "no KeyPackage found for identity"). --- ## Relationship to NodeService The Envelope schema was the original M1 wire format. With the transition to QUIC + TLS 1.3 and Cap'n Proto RPC in M3, the Envelope's role has been superseded by the [NodeService interface](node-service-schema.md), which provides typed RPC methods for each operation. The key differences: | Aspect | Envelope (M1) | NodeService RPC (M3+) | |---|---|---| | Dispatch | Manual, based on `msgType` enum | Automatic, Cap'n Proto RPC method dispatch | | Type safety | Payload is opaque `Data` | Each method has typed parameters and return values | | Transport | QUIC + TLS 1.3 | QUIC + TLS 1.3 | | Auth | None | Explicit `Auth` struct per method call | --- ## Further reading - [Wire Format Overview](overview.md) -- serialisation pipeline context - [NodeService Schema](node-service-schema.md) -- the current RPC interface that replaced Envelope-based dispatch - [Auth Schema](auth-schema.md) -- standalone Authentication Service interface - [Delivery Schema](delivery-schema.md) -- standalone Delivery Service interface - [ADR-002: Cap'n Proto over MessagePack](../design-rationale/adr-002-capnproto.md) -- why Cap'n Proto was chosen for the wire format