mirror of
https://github.com/mautrix/signal.git
synced 2026-09-17 08:12:05 -04:00
437 lines
17 KiB
Protocol Buffer
437 lines
17 KiB
Protocol Buffer
/*
|
|
* Copyright 2025 Signal Messenger, LLC
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
syntax = "proto3";
|
|
|
|
option java_multiple_files = true;
|
|
|
|
package org.signal.chat.messages;
|
|
|
|
import "google/protobuf/empty.proto";
|
|
|
|
import "org/signal/chat/common.proto";
|
|
import "org/signal/chat/require.proto";
|
|
import "org/signal/chat/errors.proto";
|
|
import "org/signal/chat/tag.proto";
|
|
|
|
import "signalpb/SignalService.proto";
|
|
|
|
// Provides methods for sending "unsealed sender" messages.
|
|
service Messages {
|
|
|
|
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
|
|
|
|
// Sends an "unsealed sender" message to all devices linked to a single
|
|
// destination account.
|
|
//
|
|
// The destination account must not be the same as the authenticated caller.
|
|
// Callers should use `SendSyncMessage` to send messages to themselves.
|
|
rpc SendMessage(SendAuthenticatedSenderMessageRequest) returns (SendMessageAuthenticatedSenderResponse) {}
|
|
|
|
// Sends a "sync" message to all other devices linked to the authenticated
|
|
// sender's account.
|
|
rpc SendSyncMessage(SendSyncMessageRequest) returns (SendMessageAuthenticatedSenderResponse) {}
|
|
|
|
// Retrieve messages for the authenticated device. When the caller receives
|
|
// and successfully processes a message returned in a GetMessagesResponse they
|
|
// must send a corresponding GetMessagesRequest indicating that the message
|
|
// has been processed (an ack). Acks should only be sent for messages
|
|
// delivered via the currently open RPC.
|
|
//
|
|
// Only the first GetMessagesRequest may contain request parameters that
|
|
// configure the stream. The first request must not contain an ack.
|
|
//
|
|
// The server will keep the stream open until the client disconnects. Only
|
|
// one GetMessages stream may be open per device. If a second GetMessages
|
|
// stream is opened, the server may terminate any of the streams with
|
|
// a STREAM_CLOSED error reason. A GetMessagesStreamClosed message will be
|
|
// present in the error details.
|
|
rpc GetMessages(stream GetMessagesRequest) returns (stream GetMessagesResponse) {}
|
|
}
|
|
|
|
message GetMessagesRequest {
|
|
message GetMessageOptions {
|
|
// If present and true, the server will not deliver any messages with the
|
|
// story flag set. This flag may only be set on the first GetMessagesRequest
|
|
// sent from the client to the server in an RPC stream.
|
|
bool drop_stories = 1;
|
|
}
|
|
oneof request {
|
|
// Configuration options for the message stream. Required for the first
|
|
// request of the stream, forbidden on subsequent reqeusts.
|
|
GetMessageOptions options = 1;
|
|
|
|
// The server_guid of an envelope previously returned in a
|
|
// GetMessagesResponse that has been successfully processed by the caller.
|
|
// Forbidden on the first request of the stream, required on subsequent
|
|
// requests.
|
|
bytes server_guid_ack = 2;
|
|
}
|
|
}
|
|
|
|
// The reason why a GetMessages RPC is being closed by the server.
|
|
message GetMessagesStreamClosed {
|
|
oneof reason {
|
|
// Another caller has opened a GetMessages stream for the same device.
|
|
google.protobuf.Empty conflicting_stream = 1;
|
|
}
|
|
}
|
|
|
|
message GetMessagesResponse {
|
|
oneof response {
|
|
// A message. On successful receipt of an envelope the caller should ack
|
|
// the envelope by sending a GetMessagesRequest with the envelope's server
|
|
// guid. Acks should only be sent for envelopes received on the currently
|
|
// open RPC.
|
|
signalservice.Envelope envelope = 1;
|
|
|
|
// An indicator that all outstanding messages for the device have been
|
|
// drained and acked by the client. The stream will remain open and continue
|
|
// to deliver newly arrived messages.
|
|
google.protobuf.Empty queue_empty = 2;
|
|
}
|
|
}
|
|
|
|
// Provides methods for sending "sealed sender" messages.
|
|
service MessagesAnonymous {
|
|
|
|
option (require.auth) = AUTH_ONLY_ANONYMOUS;
|
|
|
|
// Sends a "sealed sender" message to all devices linked to a single
|
|
// destination account.
|
|
//
|
|
// If this RPC is authorized with an unidentified access key, it will fail
|
|
// with an authorization failure if the credential is invalid OR if the
|
|
// destination account was not found. If it is authorized using a group send
|
|
// token, it will fail with an authorization failure if the credential is
|
|
// invalid and with an destination not found error if the account does not
|
|
// exist
|
|
rpc SendSingleRecipientMessage(SendSealedSenderMessageRequest) returns (SendMessageResponse) {}
|
|
|
|
// Sends a "sealed sender" message with a common payload to all devices linked
|
|
// to multiple destination accounts.
|
|
rpc SendMultiRecipientMessage(SendMultiRecipientMessageRequest) returns (SendMultiRecipientMessageResponse) {}
|
|
|
|
// Sends a story message to devices linked to a single destination account.
|
|
rpc SendStory(SendStoryMessageRequest) returns (SendMessageResponse) {}
|
|
|
|
// Sends a story message with a common payload to devices linked to devices
|
|
// linked to multiple destination accounts.
|
|
rpc SendMultiRecipientStory(SendMultiRecipientStoryRequest) returns (SendMultiRecipientMessageResponse) {}
|
|
}
|
|
|
|
message IndividualRecipientMessageBundle {
|
|
|
|
// A message for an individual device linked to a destination account.
|
|
message Message {
|
|
|
|
// The registration ID for the destination device.
|
|
uint32 registration_id = 1 [(require.range).max = 0x3fff];
|
|
|
|
// The content of the message to deliver to the destination device.
|
|
bytes payload = 2 [(require.size) = {min: 1, max: 262144}]; // 256 KiB
|
|
|
|
// The message type of the message. If this message is part of an
|
|
// unidentified send, this must be UNIDENTIFIED_SENDER
|
|
SendMessageType type = 3;
|
|
}
|
|
|
|
// The time, in milliseconds since the epoch, at which this message was
|
|
// originally sent from the perspective of the sender. Note that the maximum
|
|
// allowable timestamp for JavaScript clients is less than Long.MAX_VALUE; see
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
|
|
// for additional details and discussion.
|
|
uint64 timestamp = 1 [(require.range).min = 1, (require.range).max = 8640000000000000];
|
|
|
|
// A map of device IDs to individual messages. Generally, callers must include
|
|
// one message for each device linked to the destination account. In cases of
|
|
// "sync messages" where a sender is distributing information to other devices
|
|
// linked to the sender's account, senders may omit a message for the sending
|
|
// device.
|
|
map<uint32, Message> messages = 2 [(require.nonEmpty) = true];
|
|
}
|
|
|
|
enum SendMessageType {
|
|
UNSPECIFIED = 0;
|
|
|
|
// A double-ratchet message represents a "normal," "unsealed-sender" message
|
|
// encrypted using the Double Ratchet within an established Signal session.
|
|
DOUBLE_RATCHET = 1;
|
|
|
|
// A prekey message begins a new Signal session. The `content` of a prekey
|
|
// message is a superset of a double-ratchet message's `content` and
|
|
// contains the sender's identity public key and information identifying the
|
|
// pre-keys used in the message's ciphertext.
|
|
PREKEY_MESSAGE = 2;
|
|
|
|
// A plaintext message is used solely to convey encryption error receipts
|
|
// and never contains encrypted message content. Encryption error receipts
|
|
// must be delivered in plaintext because encryption/decryption of a prior
|
|
// message failed and there is no reason to believe that
|
|
// encryption/decryption of subsequent messages with the same key material
|
|
// would succeed.
|
|
//
|
|
// Critically, plaintext messages never have "real" message content
|
|
// generated by users. Plaintext messages include sender information.
|
|
PLAINTEXT_CONTENT = 3;
|
|
|
|
// An unidentified sender message is an encrypted message. No other
|
|
// information about the type of the encrypted message is known to the server.
|
|
//
|
|
// Unidenitfied sender messages require an unidentified access token or a
|
|
// group send endorsement token to prove the unidentified sender is authorized
|
|
// to send messages to the destination.
|
|
UNIDENTIFIED_SENDER = 4;
|
|
}
|
|
|
|
message SendAuthenticatedSenderMessageRequest {
|
|
|
|
// The service identifier of the account to which to deliver the message.
|
|
common.ServiceIdentifier destination = 1;
|
|
|
|
// If true, this message will only be delivered to destination devices that
|
|
// have an active message delivery channel with a Signal server.
|
|
bool ephemeral = 2;
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 3;
|
|
|
|
// The messages to send to the destination account.
|
|
IndividualRecipientMessageBundle messages = 4;
|
|
}
|
|
|
|
message SendMessageAuthenticatedSenderResponse {
|
|
|
|
// The outcome of the message delivery
|
|
oneof response {
|
|
|
|
// The message was successfully delivered to all destination devices
|
|
google.protobuf.Empty success = 1;
|
|
|
|
// A list of discrepancies between the destination devices identified in a
|
|
// request to send a message and the devices that are actually linked to an
|
|
// account.
|
|
MismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
|
|
|
|
// A description of a challenge callers must complete before sending
|
|
// additional messages.
|
|
ChallengeRequired challenge_required = 3 [(tag.reason) = "challenge_required"];
|
|
|
|
// The destination account did not exist
|
|
errors.NotFound destination_not_found = 4 [(tag.reason) = "destination_not_found"];
|
|
|
|
}
|
|
}
|
|
|
|
|
|
message SendSyncMessageRequest {
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 1;
|
|
|
|
// The messages to send to the destination account.
|
|
IndividualRecipientMessageBundle messages = 2;
|
|
}
|
|
|
|
message SendSealedSenderMessageRequest {
|
|
|
|
// The service identifier of the account to which to deliver the message.
|
|
common.ServiceIdentifier destination = 1;
|
|
|
|
// If true, this message will only be delivered to destination devices that
|
|
// have an active message delivery channel with a Signal server.
|
|
bool ephemeral = 2;
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 3;
|
|
|
|
// The messages to send to the destination account.
|
|
IndividualRecipientMessageBundle messages = 4;
|
|
|
|
// A means to authorize the request.
|
|
oneof authorization {
|
|
|
|
// The unidentified access key (UAK) for the destination account.
|
|
bytes unidentified_access_key = 5 [(require.exactlySize) = 16];
|
|
|
|
// A group send endorsement token for the destination account.
|
|
bytes group_send_token = 6;
|
|
|
|
// The destination account allows unrestricted unidentified access
|
|
google.protobuf.Empty unrestricted_access = 7;
|
|
}
|
|
}
|
|
|
|
message SendStoryMessageRequest {
|
|
|
|
// The service identifier of the account to which to deliver the message.
|
|
common.ServiceIdentifier destination = 1;
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 2;
|
|
|
|
// The messages to send to the destination account.
|
|
IndividualRecipientMessageBundle messages = 3;
|
|
}
|
|
|
|
message SendMessageResponse {
|
|
|
|
// The outcome of the message delivery
|
|
oneof response {
|
|
|
|
// The message was successfully delivered to all destination devices
|
|
google.protobuf.Empty success = 1;
|
|
|
|
// A list of discrepancies between the destination devices identified in a
|
|
// request to send a message and the devices that are actually linked to an
|
|
// account.
|
|
MismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
|
|
|
|
// The provided unidentified authorization credential was invalid
|
|
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
|
|
|
|
// The destination account did not exist
|
|
errors.NotFound destination_not_found = 4 [(tag.reason) = "destination_not_found"];
|
|
|
|
}
|
|
}
|
|
|
|
message MultiRecipientMessage {
|
|
|
|
// The time, in milliseconds since the epoch, at which this message was
|
|
// originally sent from the perspective of the sender. Note that the maximum
|
|
// allowable timestamp for JavaScript clients is less than Long.MAX_VALUE; see
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
|
|
// for additional details and discussion.
|
|
uint64 timestamp = 1 [(require.range).min = 1, (require.range).max = 8640000000000000];
|
|
|
|
// The serialized multi-recipient message payload.
|
|
bytes payload = 2 [(require.size).max = 762144]; // 256 KiB payload + (5000 * 100) of overhead
|
|
}
|
|
|
|
message SendMultiRecipientMessageRequest {
|
|
|
|
// If true, this message will only be delivered to destination devices that
|
|
// have an active message delivery channel with a Signal server.
|
|
bool ephemeral = 1;
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 2;
|
|
|
|
// The multi-recipient message to send to all destination accounts and
|
|
// devices.
|
|
MultiRecipientMessage message = 3;
|
|
|
|
// A group send endorsement token for the destination account.
|
|
bytes group_send_token = 4 [(require.nonEmpty) = true];
|
|
}
|
|
|
|
message SendMultiRecipientStoryRequest {
|
|
|
|
// Indicates whether this message is urgent and should trigger a high-priority
|
|
// notification if the destination device does not have an active message
|
|
// delivery channel with a Signal server
|
|
bool urgent = 1;
|
|
|
|
// The multi-recipient story message to send to all destination accounts and
|
|
// devices.
|
|
MultiRecipientMessage message = 2;
|
|
}
|
|
|
|
message MultiRecipientSuccess {
|
|
// A list of destination service identifiers that could not be resolved to
|
|
// registered Signal accounts. The message in the original request was sent
|
|
// to all service identifiers/devices in the original request except for the
|
|
// destination devices associated with the service identifiers in this list.
|
|
repeated common.ServiceIdentifier unresolved_recipients = 1;
|
|
}
|
|
|
|
message SendMultiRecipientMessageResponse {
|
|
|
|
// The outcome of the message delivery
|
|
oneof response {
|
|
// The message was sent to at least some of the destination accounts/devices
|
|
// identified in the original request.
|
|
MultiRecipientSuccess success = 1;
|
|
|
|
// A list of sets of discrepancies between the destination devices
|
|
// identified in a request to send a message and the devices that are
|
|
// actually linked to a destination account.
|
|
MultiRecipientMismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
|
|
|
|
// The provided unidentified authorization credential was invalid
|
|
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
|
|
}
|
|
}
|
|
|
|
message MismatchedDevices {
|
|
|
|
// The service identifier to which the devices named in this object are
|
|
// linked.
|
|
common.ServiceIdentifier service_identifier = 1;
|
|
|
|
// A list of device IDs that are linked to the destination account, but were
|
|
// not included in the collection of messages bound for the destination
|
|
// account.
|
|
repeated uint32 missing_devices = 2 [(require.each) = {range: {max: 0x7f}}];
|
|
|
|
// A list of device IDs that were included in the collection of messages bound
|
|
// for the destination account, but are not currently linked to the
|
|
// destination account.
|
|
repeated uint32 extra_devices = 3 [(require.each) = {range: {max: 0x7f}}];
|
|
|
|
// A list of device IDs that present in the collection of messages bound for
|
|
// the destination account and are linked to the destination account, but have
|
|
// a different registration ID than the registration ID presented by the
|
|
// sender (indicating that the destination device has likely been replaced by
|
|
// another device).
|
|
repeated uint32 stale_devices = 4 [(require.each) = {range: {max: 0x7f}}];
|
|
}
|
|
|
|
message MultiRecipientMismatchedDevices {
|
|
|
|
// A list of sets of discrepancies between the destination devices identified
|
|
// in a request to send a message and the devices that are actually linked to
|
|
// a destination account.
|
|
repeated MismatchedDevices mismatched_devices = 1;
|
|
}
|
|
|
|
message ChallengeRequired {
|
|
|
|
enum ChallengeType {
|
|
UNSPECIFIED = 0;
|
|
|
|
// A challenge that callers can fulfill by completing a captcha.
|
|
CAPTCHA = 1;
|
|
|
|
// A challenge that callers can fulfill by supplying a token delivered via
|
|
// push notification.
|
|
PUSH_CHALLENGE = 2;
|
|
};
|
|
|
|
// An opaque token identifying this challenge request. Clients must generally
|
|
// submit this token when submitting a challenge response.
|
|
string token = 1;
|
|
|
|
// A list of challenge types callers may choose to complete to resolve the
|
|
// challenge requirement. May be empty, in which case callers cannot resolve
|
|
// the challenge by any means other than waiting.
|
|
repeated ChallengeType challenge_options = 2;
|
|
|
|
// A duration (in seconds) after which the challenge requirement may be
|
|
// resolved by simply waiting. May not be set if the challenge cannot be
|
|
// resolved by waiting.
|
|
optional uint64 retry_after_seconds = 3;
|
|
}
|