Define authenticated and anonymous gRPC services for sending messages

This commit is contained in:
Jon Chambers 2025-04-02 08:47:34 -04:00 committed by GitHub
parent d4031893cc
commit 4a42ff562d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 494 additions and 0 deletions

View File

@ -0,0 +1,494 @@
/*
* 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 "org/signal/chat/common.proto";
import "org/signal/chat/require.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.
*
* This RPC may fail with a `NOT_FOUND` status if the destination account was
* not found. It may also fail with a `RESOURCE_EXHAUSTED` status if a rate
* limit for sending messages has been exceeded, in which case a `retry-after`
* header containing an ISO 8601 duration string may be present in the
* response trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
rpc SendMessage(SendAuthenticatedSenderMessageRequest) returns (SendMessageResponse) {}
/**
* Sends a "sync" message to all other devices linked to the authenticated
* sender's account. This RPC may fail with a `RESOURCE_EXHAUSTED` status if a
* rate limit for sending messages has been exceeded, in which case a
* `retry-after` header containing an ISO 8601 duration string may be present
* in the response trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
rpc SendSyncMessage(SendSyncMessageRequest) returns (SendMessageResponse) {}
}
/**
* 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.
*
* This RPC may fail with an `UNAUTHENTICATED` status if the given credentials
* were not accepted for any reason or if the destination account was not
* found while using an unidentified access key (UAK) for authorization. It
* may also fail with a `NOT_FOUND` status if the destination account was not
* found while using a group send token for authorization. It may also fail
* with a `RESOURCE_EXHAUSTED` status if a rate limit for sending messages has
* been exceeded, in which case a `retry-after` header containing an ISO 8601
* duration string may be present in the response trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
rpc SendSingleRecipientMessage(SendSealedSenderMessageRequest) returns (SendMessageResponse) {}
/**
* Sends a "sealed sender" message with a common payload to all devices linked
* to multiple destination accounts.
*
* This RPC may fail with a `NOT_FOUND` status if one or more destination
* accounts were not found. It may also fail with an `UNAUTHENTICATED` status
* if the given credentials were not accepted for any reason. It may also fail
* with a `RESOURCE_EXHAUSTED` status if a rate limit for sending messages has
* been exceeded, in which case a `retry-after` header containing an ISO 8601
* duration string may be present in the response trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
rpc SendMultiRecipientMessage(SendMultiRecipientMessageRequest) returns (SendMultiRecipientMessageResponse) {}
/**
* Sends a story message to devices linked to a single destination account.
*
* This RPC may fail with a `RESOURCE_EXHAUSTED` status if a rate limit for
* sending stories has been exceeded, in which case a `retry-after` header
* containing an ISO 8601 duration string may be present in the response
* trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
rpc SendStory(SendStoryMessageRequest) returns (SendMessageResponse) {}
/**
* Sends a story message with a common payload to devices linked to devices
* linked to multiple destination accounts.
*
* This RPC may fail with a `RESOURCE_EXHAUSTED` status if a rate limit for
* sending stories has been exceeded, in which case a `retry-after` header
* containing an ISO 8601 duration string may be present in the response
* trailers.
*
* Note that message delivery may not succeed even if this RPC returns an `OK`
* status; callers must check the response object to verify that the message
* was actually accepted and sent.
*/
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).max = 262144]; // 256 KiB
}
/**
* The time, in milliseconds since the epoch, at which this message was
* originally sent from the perspective of the sender.
*/
uint64 timestamp = 1;
/**
* 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 AuthenticatedSenderMessageType {
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;
}
message SendAuthenticatedSenderMessageRequest {
/**
* The service identifier of the account to which to deliver the message.
*/
common.ServiceIdentifier destination = 1;
/**
* The type identifier for this message.
*/
AuthenticatedSenderMessageType type = 2 [(require.specified) = true];
/**
* If true, this message will only be delivered to destination devices that
* have an active message delivery channel with a Signal server.
*/
bool ephemeral = 3;
/**
* 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 = 4;
/**
* The messages to send to the destination account.
*/
IndividualRecipientMessageBundle messages = 5;
}
message SendSyncMessageRequest {
/**
* The type identifier for this message.
*/
AuthenticatedSenderMessageType type = 1 [(require.specified) = true];
/**
* 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 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;
/**
* A group send endorsement token for the destination account.
*/
bytes group_send_token = 6;
}
}
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 {
/**
* An error preventing message delivery. If not set, then the message(s) in
* the original request were sent to all destination devices.
*/
oneof error {
/**
* 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 = 1;
/**
* A description of a challenge callers must complete before sending
* additional messages.
*/
ChallengeRequired challenge_required = 2;
}
}
message MultiRecipientMessage {
/**
* The time, in milliseconds since the epoch, at which this message was
* originally sent from the perspective of the sender.
*/
uint64 timestamp = 1;
/**
* 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;
}
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 SendMultiRecipientMessageResponse {
/**
* A list of destination service identifiers that could not be resolved to
* registered Signal accounts. If `mismatched_devices` is empty, then 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;
/**
* An error preventing message delivery. If not set, then the message was sent
* to some or all destination accounts/devices identified in the original
* request.
*/
oneof error {
/**
* 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;
/**
* A description of a challenge callers must complete before sending
* additional messages.
*/
ChallengeRequired challenge_required = 3;
}
}
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.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.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.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.
*/
bytes 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;
}