Port Brave Sync server to Rust

Use SQLite for persistence and generate Rust protobuf bindings at build time.

Add Docker image support for containerized deployment.

Co-Authored-By: codex <[email protected]>
This commit is contained in:
janisslsmandcodex committed 2026-06-04 23:57:44 +03:00
commit 28826af88d
93 files changed
+11666

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
target
.git
.serena
*.sqlite3
*.sqlite3-shm
*.sqlite3-wal
+5
View File
@@ -0,0 +1,5 @@
/target
.serena/
*.sqlite3
*.sqlite3-shm
*.sqlite3-wal
Generated
+1608
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
[package]
name = "brave-sync-rs"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1"
async-trait = "0.1"
axum = { version = "0.8", features = ["macros"] }
base64 = "0.22"
bytes = "1"
ed25519-dalek = "2"
flate2 = "1"
hex = "0.4"
prost = "0.14"
regex = "1"
rusqlite = { version = "0.37", features = ["bundled"] }
thiserror = "2"
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "signal"] }
tower-http = { version = "0.6", features = ["limit", "trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4"] }
[build-dependencies]
prost-build = "0.14"
protoc-bin-vendored = "3"
+33
View File
@@ -0,0 +1,33 @@
FROM rust:1.96-bookworm AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock build.rs ./
COPY schema ./schema
COPY src ./src
RUN cargo build --release --locked
FROM debian:bookworm-slim AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends gosu \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --uid 10001 --home-dir /var/lib/brave-sync brave-sync \
&& mkdir -p /var/lib/brave-sync \
&& chown -R brave-sync:brave-sync /var/lib/brave-sync
COPY --from=builder /app/target/release/brave-sync-rs /usr/local/bin/brave-sync-rs
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
WORKDIR /var/lib/brave-sync
ENV LISTEN_ADDR=0.0.0.0:8295
ENV SQLITE_PATH=/var/lib/brave-sync/brave-sync.sqlite3
EXPOSE 8295
VOLUME ["/var/lib/brave-sync"]
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/usr/local/bin/brave-sync-rs"]
+43
View File
@@ -0,0 +1,43 @@
# Brave Sync Server v2 Rust Port
Rust port of the Brave Sync v2 command server from `/home/user/go-sync`.
The server exposes:
- `GET /`
- `GET /health-check`
- `POST /v2/command/`
- `GET /metrics`
## Storage
This port uses SQLite instead of DynamoDB. On startup it creates the required
tables automatically.
Environment variables:
- `SQLITE_PATH`: SQLite database path. Defaults to `brave-sync.sqlite3`.
- `LISTEN_ADDR`: HTTP listen address. Defaults to `0.0.0.0:8295`.
- `BLOCKED_CLIENT_IDS`: comma-separated lowercase or uppercase client public key hex values.
- `HIGH_DEVICE_LIMIT_CLIENT_IDS`: comma-separated client IDs allowed up to 100 active devices.
## Build and Test
```sh
cargo test
cargo run
```
## Docker
```sh
docker build -t brave-sync-rs .
docker run --rm -p 8295:8295 -v brave-sync-data:/var/lib/brave-sync brave-sync-rs
```
For bind mounts, the container starts as root only long enough to create/chown
the SQLite directory, then runs the server as the `brave-sync` user.
The protobuf Rust bindings are generated at build time from
`schema/protobuf/sync_pb`. A vendored `protoc` binary is used, so a system
`protoc` install is not required.
+21
View File
@@ -0,0 +1,21 @@
use std::{env, path::PathBuf};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let proto_dir = PathBuf::from("schema/protobuf/sync_pb");
let mut protos = Vec::new();
for entry in std::fs::read_dir(&proto_dir)? {
let path = entry?.path();
if path.extension().is_some_and(|ext| ext == "proto") {
println!("cargo:rerun-if-changed={}", path.display());
protos.push(path);
}
}
let protoc = protoc_bin_vendored::protoc_bin_path()?;
unsafe {
env::set_var("PROTOC", protoc);
}
prost_build::Config::new().compile_protos(&protos, &[proto_dir])?;
Ok(())
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
set -eu
data_dir="${SQLITE_PATH%/*}"
if [ "$data_dir" = "$SQLITE_PATH" ]; then
data_dir="/var/lib/brave-sync"
fi
mkdir -p "$data_dir"
chown -R brave-sync:brave-sync "$data_dir"
exec gosu brave-sync "$@"
@@ -0,0 +1,110 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for the app list (aka app launcher).
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of app list objects.
message AppListSpecifics {
// Unique identifier for the item:
// * TYPE_FOLDER: Folder id (generated)
// * TYPE_APP: App Id
optional string item_id = 1;
// What type of item this is.
enum AppListItemType {
// An app, whether a web app, Android app, etc.
//
// For bookmark apps (URL shortcuts), additional information such as their
// URLs are kept in the AppSpecifics.bookmark_app_foobar fields.
TYPE_APP = 1;
// A request to remove any matching default installed apps.
TYPE_REMOVE_DEFAULT_APP = 2;
// A folder containing entries whose |parent_id| matches |item_id|.
TYPE_FOLDER = 3;
// Obsolete type, intended for URL shortcuts, that was never implemented.
TYPE_OBSOLETE_URL = 4;
// A "page break" item (Indicate creation of a new page in app list).
TYPE_PAGE_BREAK = 5;
}
optional AppListItemType item_type = 2;
// Item name (FOLDER).
optional string item_name = 3;
// Id of the parent (folder) item.
optional string parent_id = 4;
// Marked OBSOLETE because this is unused for the app list.
// Which page this item will appear on in the app list.
optional string OBSOLETE_page_ordinal = 5 [deprecated = true];
// Where on a page this item will appear.
optional string item_ordinal = 6;
// Where on a shelf this item will appear.
// Note that not all apps are pinned to the shelf by the user -- some might
// originate from the PinnedLauncherApps policy. See `is_user_pinned` for more
// details.
optional string item_pin_ordinal = 7;
// The color groups used for grouping together icons by colors.
enum ColorGroup {
COLOR_EMPTY = 0;
COLOR_WHITE = 1;
COLOR_RED = 2;
COLOR_ORANGE = 3;
COLOR_YELLOW = 4;
COLOR_GREEN = 5;
COLOR_BLUE = 6;
COLOR_MAGENTA = 7;
COLOR_BLACK = 8;
}
message IconColor {
// The item icon's background color.
optional ColorGroup background_color = 1;
// The item icon's hue.
optional int32 hue = 2;
}
optional IconColor item_color = 8;
// Whether the app was pinned to shelf by the user or not (apps can also be
// pinned by other means such as enterprise policies).
// The eventual consistency (a sufficient amount of time after the rollout)
// promise here is for this field to be synced with the pin ordinal:
// `item_pin_ordinal.IsValid() <=> is_user_pinned.has_value()`.
// However, it's okay for this contract to be violated in the meantine.
//
// * missing value indicates that either `item_pin_ordinal` is invalid or
// this field is new and hasn't yet been processed by sync.
// * `true` means that the app was pinned by the user.
// This definition is used in a relaxed way -- for instance, default
// OS apps that are shown in the shelf (like Chrome itself) also have this
// value set to true.
// * `false` means that the app was pinned by PinnedLauncherApps policy.
// Note that user pin has priority: if an app was first pinned by the user
// and then additionally specified in PinnedLauncherApps, this value will
// be set to true.
optional bool is_user_pinned = 9;
// Identifier for the app package of the item. Package Ids are stable and
// globally unique across app platforms, whereas App Id stored in item_id is
// not.
optional string promise_package_id = 10;
}
@@ -0,0 +1,28 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for an app setting.
// This is the same as for an extension setting, but uses a separate datatype
// in order to control syncability separately.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "extension_setting_specifics.proto";
// Properties of app setting sync objects; just an extension setting.
message AppSettingSpecifics {
optional ExtensionSettingSpecifics extension_setting = 1;
}
+103
View File
@@ -0,0 +1,103 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for apps.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "extension_specifics.proto";
// Settings related to push notifications for apps.
message AppNotificationSettings {
// DEPRECATED: Use oauth_client_id below.
// Whether or not the user has setup notifications at least once.
// The value for this field will start out false and will be set
// to true when the user accepts receiving notifications for the
// first time and then it will always remain true.
optional bool initial_setup_done = 1 [deprecated = true];
// Whether or not the user has disabled notifications.
optional bool disabled = 2;
// OAuth2 client id to which the user granted the notification permission.
// This field will start out empty.
// It will be set when the user accepts receiving notifications.
// This field is used when the user revokes the notifications permission.
// Note that it is never cleared after it was set once. Hence, the presence
// of this field can be used to determine if the user has setup notifications
// at least once for the given app.
optional string oauth_client_id = 3;
}
// Information about a linked app icon.
message LinkedAppIconInfo {
// The URL of the app icon.
optional string url = 1;
// The size of the app icon in DIPs.
optional uint32 size = 2;
}
// Properties of app sync objects.
//
// For now, an app is just an extension. We keep the two data types
// separate for future-proofing purposes.
message AppSpecifics {
// Extension data.
optional ExtensionSpecifics extension = 1;
// Notification settings.
optional AppNotificationSettings notification_settings = 2;
// This controls where on a page this application icon will appear.
optional string app_launch_ordinal = 3;
// This specifics which page the application icon will appear on in the NTP.
// This values only provide the order within the application pages, not within
// all of the panels in the NTP.
optional string page_ordinal = 4;
// The possible launch types for an app.
// This enum should be kept in sync with extensions::LaunchType.
enum LaunchType {
PINNED = 0;
REGULAR = 1;
FULLSCREEN = 2;
WINDOW = 3;
}
// This describes how the extension should be launched.
optional LaunchType launch_type = 5;
// DEPRECATED. See https://crbug.com/1233303.
optional string bookmark_app_url = 6 [deprecated = true];
// DEPRECATED. See https://crbug.com/1233303.
optional string bookmark_app_description = 7 [deprecated = true];
// DEPRECATED. See https://crbug.com/1233303.
optional string bookmark_app_icon_color = 8 [deprecated = true];
// This is information about linked icons (that is, icons that are downloaded
// from outside the app's bundle of files.
repeated LinkedAppIconInfo linked_app_icons = 9;
// DEPRECATED. See https://crbug.com/1233303.
optional string bookmark_app_scope = 10 [deprecated = true];
// DEPRECATED. See https://crbug.com/1233303.
optional uint32 bookmark_app_theme_color = 11 [deprecated = true];
}
@@ -0,0 +1,32 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for ARC pakcages.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of arc package objects.
message ArcPackageSpecifics {
// Unique identifier for the item:
// Android package name.
optional string package_name = 1;
// Android package version.
optional int32 package_version = 2;
// Last Android id generated when Android side backups data.
optional int64 last_backup_android_id = 3;
// Last time internal value that Android side backups data.
optional int64 last_backup_time = 4;
}
@@ -0,0 +1,110 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of Autofill wallet offer related objects.
// Next tag: 11
message AutofillOfferSpecifics {
// The id for this offer data. Will be used as the client tag.
optional int64 id = 1;
// The link leading to the offer details page on Gpay app. Will be populated
// on Android only.
optional string offer_details_url = 2;
// Merchant domain and merchant app package name refers to the merchant this
// offer is applied to.
repeated string merchant_domain = 3;
repeated string merchant_app_package = 4;
// The expiry of this offer. Will be represented in the form of unix epoch
// time in seconds. Once the offer is expired it will not be shown in the
// client.
optional int64 offer_expiry_date = 5;
// Proto containing data specific to a card-linked offer.
message CardLinkedOfferData {
reserved 1, 2;
// The server id of the card to which the offer is linked.
repeated int64 instrument_id = 3;
}
// Proto containing data specific to a promo code offer.
message PromoCodeOfferData {
// The actual promo code which can be applied at checkout.
optional string promo_code = 1;
}
// The unique offer data for different offer types.
oneof type_specific_offer_data {
CardLinkedOfferData card_linked_offer_data = 6;
PromoCodeOfferData promo_code_offer_data = 9;
}
// Strings to be shown in client UI, based on the offer type and details.
message DisplayStrings {
// A message translated in the user's GPay app locale, explaining the value
// of the offer. For example, a promo code offer might display
// "$5 off on shoes, up to $50."
optional string value_prop_text = 1;
// A message translated in the user's GPay app locale and shown on mobile as
// a link, prompting the user to click it to learn more about the offer.
// Generally, "See details".
optional string see_details_text_mobile = 2;
// A message translated in the user's GPay app locale and shown on desktop
// (not as a link), informing the user that exclusions and restrictions may
// apply to the value prop text. Generally, "Terms apply."
optional string see_details_text_desktop = 3;
// A message translated in the user's GPay app locale and shown on mobile,
// instructing them on how to redeem the offer. For example, a promo code
// offer might display "Tap the promo code field at checkout to autofill
// it."
optional string usage_instructions_text_mobile = 4;
// A message translated in the user's GPay app locale and shown on desktop,
// instructing them on how to redeem the offer. For example, a promo code
// offer might display "Click the promo code field at checkout to autofill
// it."
optional string usage_instructions_text_desktop = 5;
}
optional DisplayStrings display_strings = 10;
// This value will be shown in the offer text template as "XXX% cashback".
// Percentage has a range of (0, 100].
message PercentageReward {
// The string contains a number and a percent sign.
optional string percentage = 1;
}
// This value will be shown in the offer text template as "XXX$ off".
message FixedAmountReward {
// The string contains a number and a currency sign.
optional string amount = 1;
}
// The reward type of the offer. Will be used to generate the display text in
// the UI. Each type has its own client side text template.
oneof reward_type {
PercentageReward percentage_reward = 7;
FixedAmountReward fixed_amount_reward = 8;
}
}
@@ -0,0 +1,665 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for autofill.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of autofill sync objects.
// An AutofillProfile.
message AutofillProfileSpecifics {
// Represents the validation status of value stored in the AutofillProfile.
enum VerificationStatus {
// No verification status assigned.
VERIFICATION_STATUS_UNSPECIFIED = 0;
// The value token was parsed from a parent token.
// For example, the first name was derived by splitting a full name into
// its components.
PARSED = 1;
// Value was built from its subcomponents.
// For example, the full name was built from the first, middle and last
// name.
FORMATTED = 2;
// The value was observed in a form transmission.
// For example, the user filled a form that contained at NAME_FULL field.
// The value of NAME_FULL will be stored as OBSERVED.
OBSERVED = 3;
// The user used the autofill settings to verify and store this token.
// This is currently only applicable to the full name, since users cannot
// edit individual components of their name.
USER_VERIFIED = 4;
// The token was parsed remotely.
SERVER_PARSED = 5;
}
optional string guid = 15;
// Starting from M115, Autofill ignores the origin and doesn't distinguish
// between verified (meaning `deprecated_origin == kSettingsOrigin`) and
// unverified profiles anymore. However, clients prior to this milestone only
// accept updates to verified profiles through Sync if the remote profile is
// verified as well. For this reason, M115 clients and above always set the
// origin to kSettingsOrigin.
// TODO(crbug.com/1441905): Remove in a few milestones.
optional string deprecated_origin = 16;
optional int64 use_count = 22;
// The time_t value of the last time this profile was used. This
// value makes sense wrt base::Time::To/FromTimeT, which measures
// from the Windows epoch.
optional int64 use_date = 23;
// A user chosen profile label that is used to identify the profile.
// The value of the label can be freely chosen by the user. Two possible label
// values are '$HOME$' or '$WORK$' which have a special meaning.
optional string profile_label = 62;
// Contact info name fields.
repeated string name_first = 2;
repeated string name_middle = 3;
repeated string name_last = 4;
// Sometimes the last name is composed of two names as it is common for
// Hispanic/Latinx names. In the unstructured representation of the last name,
// there may be even a conjunction between the first and the second last
// name. For example, the more-complete version of Pablo Picasso's surname is
// "Ruiz y Picasso" containing a first last name, a conjunction (the y) and a
// second last name.
repeated string name_last_first = 27;
repeated string name_last_conjunction = 28;
repeated string name_last_second = 29;
repeated string name_full = 21;
reserved 60;
reserved "name_full_with_honorific";
// Validation status records for name fields.
reserved 30;
reserved "name_honorific_status";
repeated VerificationStatus name_first_status = 31;
repeated VerificationStatus name_middle_status = 32;
repeated VerificationStatus name_last_status = 33;
repeated VerificationStatus name_last_first_status = 34;
repeated VerificationStatus name_last_conjunction_status = 35;
repeated VerificationStatus name_last_second_status = 36;
repeated VerificationStatus name_full_status = 37;
reserved 61;
reserved "name_full_with_honorific_status";
// Contact info additional fields.
repeated string email_address = 5;
optional string company_name = 6;
// Address field.
// The address_home_line1/2 fields are deprecated and
// address_home_street_address should be used instead by
// joining address_home_line1/2 with a newline ("\n").
// Full deprecation can not be achieved before all sync profiles have been
// updated with a M86+ client.
// TODO(crbug/1111740): Remove usages of address_home_line1/2 and mark field
// as deprecated.
optional string address_home_line1 = 7;
optional string address_home_line2 = 8;
optional string address_home_city = 9;
optional string address_home_state = 10;
optional string address_home_zip = 11;
optional string address_home_country = 12;
optional string address_home_admin_level_2 = 71;
// Additional address fields for i18n.
optional string address_home_street_address = 17;
optional string address_home_sorting_code = 18;
optional string address_home_dependent_locality = 19;
optional string address_home_language_code = 20;
optional string address_home_thoroughfare_name = 38;
optional string address_home_thoroughfare_number = 39;
optional string address_home_street_location = 73;
optional string address_home_subpremise_name = 42;
// All the information related to the apartment. Normally a combination of the
// apartment type (address_home_apt_type) and number (address_home_apt_num).
// E.g. "Apt 5".
optional string address_home_apt = 85;
// The raw number (or identifier) of an apartment (e.g. "5") but without a
// prefix. The value "apt 5" would correspond to the address_home_apt.
optional string address_home_apt_num = 56;
// Information describing the type of apartment (e.g. Apt, Apartamento, Sala,
// Departamento).
optional string address_home_apt_type = 87;
optional string address_home_floor = 57;
optional string address_home_landmark = 67;
// The type indicates that the address is at the intersection between two
// streets. This is a common way of writing addresses in Mexico.
optional string address_home_between_streets = 69;
// The meaning of the field is similar to address_home_between_streets. The
// type should be used for "Entre calle 1" in MX addresses which also
// contain the "Entre calle 2" field.
optional string address_home_between_streets_1 = 77;
// The meaning of the field is similar to address_home_between_streets. The
// type should be used for "Entre calle 2" in MX addresses which also
// contain the "Entre calle 1" field.
optional string address_home_between_streets_2 = 79;
optional string address_home_overflow = 75;
// Combination of types address_home_between_streets or address_home_landmark.
// Note that some address forms require the combination of both in a single
// field as opposed to each one separately.
optional string address_home_between_streets_or_landmark = 81;
// Combination of types address_home_overflow and address_home_landmark.
// Note that some address forms require the combination of both in a single
// field as opposed to each one separately.
optional string address_home_overflow_and_landmark = 83;
// Combination of types address_home_street_location and
// address_home_dependent_locality. Note that some address forms require the
// combination of both in a single field as opposed to each one separately.
optional string address_home_street_location_and_locality = 89;
// Validation status records for address fields.
optional VerificationStatus address_home_city_status = 43;
optional VerificationStatus address_home_state_status = 44;
optional VerificationStatus address_home_zip_status = 45;
optional VerificationStatus address_home_country_status = 46;
optional VerificationStatus address_home_landmark_status = 68;
optional VerificationStatus address_home_between_streets_status = 70;
optional VerificationStatus address_home_between_streets_1_status = 78;
optional VerificationStatus address_home_between_streets_2_status = 80;
optional VerificationStatus address_home_admin_level_2_status = 72;
optional VerificationStatus address_home_overflow_status = 76;
optional VerificationStatus address_home_between_streets_or_landmark_status =
82;
optional VerificationStatus address_home_overflow_and_landmark_status = 84;
optional VerificationStatus address_home_street_location_and_locality_status =
90;
optional VerificationStatus address_home_street_address_status = 47;
optional VerificationStatus address_home_sorting_code_status = 48;
optional VerificationStatus address_home_dependent_locality_status = 49;
optional VerificationStatus address_home_language_code_status = 50;
optional VerificationStatus address_home_thoroughfare_name_status = 51;
optional VerificationStatus address_home_thoroughfare_number_status = 52;
optional VerificationStatus address_home_street_location_status = 74;
optional VerificationStatus address_home_subpremise_name_status = 55;
optional VerificationStatus address_home_apt_status = 86;
optional VerificationStatus address_home_apt_num_status = 58;
optional VerificationStatus address_home_apt_type_status = 88;
optional VerificationStatus address_home_floor_status = 59;
// Phone.
repeated string phone_home_whole_number = 13;
reserved 63;
reserved "disallow_settings_visible_updates";
// Removed in M123.
reserved 64;
reserved "birthdate_day";
reserved 65;
reserved "birthdate_month";
reserved 66;
reserved "birthdate_year";
// Deprecated.
optional string deprecated_label = 1 [deprecated = true];
optional string phone_fax_whole_number = 14 [deprecated = true];
// The following validity-related fields have only been used in combination
// with an experimental features that are disabled for all clients and have
// been removed in M100.
optional int64 validity_state_bitfield = 24 [deprecated = true];
optional bool is_client_validity_states_updated = 25 [deprecated = true];
// Removed in M118.
reserved 40;
reserved "address_home_dependent_thoroughfare_name";
reserved 41;
reserved "address_home_premise_name";
reserved 53;
reserved "address_home_dependent_thoroughfare_name_status";
reserved 54;
reserved "address_home_premise_name_status";
// Removed in M122.
reserved 26;
reserved "name_honorific";
}
message AutofillSpecifics {
// If any of these 3 fields are present, then all 3 should be, and it implies
// that this entity represents a classic autofill object. In this case,
// none of the autofill++ objects below should be present.
optional string name = 1;
optional string value = 2;
repeated int64 usage_timestamp = 3;
// An autofill++ profile object. If present, indicates this entity
// represents an AutofillProfile exclusively, and no other fields (such as
// name/value or credit_card) should be present.
optional AutofillProfileSpecifics profile = 4;
reserved 5;
reserved "encrypted_credit_card";
reserved 6;
reserved "credit_card";
}
// TODO(crbug.com/1020740): Deprecated, remove this once dependent change is
// done. Use WalletCreditCardCloudTokenData instead.
message CloudTokenData {
// Last 4-5 digits of the Cloud Primary Account Number (CPAN).
optional string suffix = 1;
// CPAN Month number 1-12.
optional int32 exp_month = 2;
// CPAN Four-digit year (e.g. 2017).
optional int32 exp_year = 3;
// URL of the card art to be displayed for CPAN.
optional string art_fife_url = 4;
// Opaque identifier for the cloud token associated with the payment
// instrument.
optional string instrument_token = 5;
}
// Next id: 8
message CardBenefit {
// The unique identifier of the benefit. Generated originally in Chrome Sync
// server.
optional string benefit_id = 7;
// The benefit description (Engligh only currently) to be shown in the UI.
optional string benefit_description = 1;
// When the benefit should be displayed, in UTC time.
optional int64 start_time_unix_epoch_milliseconds = 2;
optional int64 end_time_unix_epoch_milliseconds = 3;
// Proto containing data specific to a flat rate benefit. Empty as flat rate
// benefits don't have unique fields. The actual benefit (e.g. "2% cash back")
// is already included in the `benefit_description`.
message FlatRateBenefit {}
// For category benefits, represents the category of purchases that the
// benefit can be applied to.
enum CategoryBenefitType {
CATEGORY_BENEFIT_TYPE_UNKNOWN = 0;
SUBSCRIPTION = 1;
FLIGHTS = 2;
DINING = 3;
ENTERTAINMENT = 4;
STREAMING = 5;
GROCERY_STORES = 6;
}
// Proto containing data specific to a category benefit.
message CategoryBenefit {
// The type of category purchases the benefit is eligible on.
optional CategoryBenefitType category_benefit_type = 1;
}
// Proto containing data specific to a merchant benefit.
message MerchantBenefit {
// The merchant domain that the benefit is eligible on.
// Example: https://www.acme.com
repeated string merchant_domain = 1;
}
// Proto data representing the actual data of the credit card benefit,
// depending on the benefit type.
oneof DomainSpecificBenefit {
FlatRateBenefit flat_rate_benefit = 4;
CategoryBenefit category_benefit = 5;
MerchantBenefit merchant_benefit = 6;
}
}
message CardIssuer {
enum Issuer {
ISSUER_UNKNOWN = 0;
// Card where the issuer is Google.
GOOGLE = 1;
// Card where the issuer is external.
EXTERNAL_ISSUER = 2;
}
optional Issuer issuer = 1;
// Uniquely identifies the issuer of the card. This field is required when
// Issuer is EXTERNAL_ISSUER.
// Example: capitalone, amex.
optional string issuer_id = 2;
}
message WalletMaskedCreditCard {
reserved 9, 14;
reserved "card_class";
enum WalletCardStatus {
VALID = 0;
EXPIRED = 1;
}
enum WalletCardType {
UNKNOWN = 0;
AMEX = 1;
DISCOVER = 2;
JCB = 3;
MAESTRO = 4;
MASTER_CARD = 5;
SOLO = 6;
SWITCH = 7;
VISA = 8;
UNIONPAY = 9;
ELO = 10;
}
enum VirtualCardEnrollmentState {
UNSPECIFIED = 0;
UNENROLLED = 1 [deprecated = true];
ENROLLED = 2;
UNENROLLED_AND_NOT_ELIGIBLE = 3;
UNENROLLED_AND_ELIGIBLE = 4;
}
enum VirtualCardEnrollmentType {
TYPE_UNSPECIFIED = 0;
ISSUER = 1;
NETWORK = 2;
}
// Server-generated unique ID string. This is opaque to the client.
// This is the legacy version of |instrument_id|.
optional string id = 1;
// What the server thinks of this card.
optional WalletCardStatus status = 2;
optional string name_on_card = 3;
optional WalletCardType type = 4;
// Last 4 digits of the credit card number.
optional string last_four = 5;
// Month number 1-12.
optional int32 exp_month = 6;
// Four-digit year (e.g. 2017).
optional int32 exp_year = 7;
// The WalletPostalAddress.id of the billing address.
optional string billing_address_id = 8;
// Issuing Bank name which is internationalized (e.g. "Chase", "工商银行")
optional string bank_name = 10;
// TODO(crbug.com/1020740): Deprecated, remove this once dependent change is
// done.
// Use WalletCreditCardCloudTokenData instead. This field will be set if
// a cloud token is available for the instrument.
optional CloudTokenData cloud_token_data = 11;
// The card's nickname, if it exists.
optional string nickname = 12;
// Issuer of the card.
optional CardIssuer card_issuer = 13;
// Server-generated unique ID. This is opaque to the client.
// |id| is the legacy version of this.
optional int64 instrument_id = 15;
// The state of virtual card enrollment.
optional VirtualCardEnrollmentState virtual_card_enrollment_state = 16;
// The URL for the client to fetch the card art image.
optional string card_art_url = 17;
// The product description for the card. Used to be shown in the UI.
optional string product_description = 18;
// The type of virtual card enrollment. kTypeUnspecified indicates that the
// card is unenrolled.
optional VirtualCardEnrollmentType virtual_card_enrollment_type = 19;
// The benefits (e.g. points or discounts) associated with purchases made
// with the card.
repeated CardBenefit card_benefit = 20;
// Separate link from the card issuer which provides additional context or
// terms and conditions regarding the credit card product and its benefits.
optional string product_terms_url = 21;
}
// Unused by the client since M121.
// TODO(crbug.com/1457187): Some server-side code still relies on this message.
// To keep the protos in sync, it is kept until that code is cleaned up.
// Different than an AutofillProfile because this represents some known address
// on the server that is pulled down rather than synced between Chromes.
message WalletPostalAddress {
optional string id = 1;
optional string recipient_name = 12;
optional string company_name = 2;
// This is the street address, of which there may be multiple lines. This
// corresponds to "address_home_line[1|2] in the AutofillProfileSpecifics
// message above. In some locales there may be more than two lines.
repeated string street_address = 3;
// Also known as "administrative area". This is normally the state or
// province in most countries.
optional string address_1 = 4;
// Also known as "locality". In the US this is the city.
optional string address_2 = 5;
// A sub-classification beneath the city, e.g. an inner-city district or
// suburb. Also known as "dependent_locality"
optional string address_3 = 6;
// Used in certain countries. Also known as "sub_dependent_locality".
optional string address_4 = 7;
optional string postal_code = 8;
// Similar to the zipcode column, but used for businesses or organizations
// that might not be geographically contiguous. The canonical example is
// CEDEX in France.
optional string sorting_code = 9;
optional string country_code = 10;
optional string language_code = 11;
// Phone number. The format is unspecified and will be explicitly ignored.
optional string phone_number = 13;
}
// Contains information about a Payments Customer.
message PaymentsCustomerData {
// The billable customer ID associated with the account.
optional string id = 1;
}
// Contains information about the cloud token data of server credit cards.
message WalletCreditCardCloudTokenData {
// Server-generated ID string for the card this cloud token data is related
// to. This should match the id in WalletMaskedCreditCard for to fetch the
// corresponding actual card data. Note this is not unique across all cloud
// token data, which means one card can have multiple sets of cloud token data
// with the same masked_card_id.
optional string masked_card_id = 1;
// Last 4-5 digits of the Cloud Primary Account Number (CPAN).
optional string suffix = 2;
// CPAN Month number 1-12.
optional int32 exp_month = 3;
// CPAN Four-digit year (e.g. 2017).
optional int32 exp_year = 4;
// URL of the card art to be displayed for CPAN.
optional string art_fife_url = 5;
// Opaque identifier for the cloud token.
optional string instrument_token = 6;
}
// Contains information of payment instruments that can be used by the user.
message PaymentInstrument {
// Opaque identifier for the account stored in Payments Platform.
// This will be used to initiate the payment.
optional int64 instrument_id = 1;
// Supported payment infrastructures (rail) that allow users to send money
// between the payer and the payee. https://en.wikipedia.org/wiki/Payment_rail
enum SupportedRail {
SUPPORTED_RAIL_UNKNOWN = 0;
// Payment rails for Brazil Open Banking.
PIX = 1;
// International Bank Account Numbers.
IBAN = 2;
}
// The supported rails for which a payment can be made for the given account.
// For example, Pix is the open-banking rail in Brazil, UPI is the
// open-banking rail in India.
repeated SupportedRail supported_rails = 2;
// The URL for the image to be displayed when showing the account to the
// user.
optional string display_icon_url = 3;
// Nickname for the instrument.
optional string nickname = 4;
// The backing source for the applicable rails.
oneof instrument_details {
BankAccountDetails bank_account = 5;
WalletMaskedIban iban = 6;
}
}
// Contains information of server IBAN (International Bank Account Number).
message WalletMaskedIban {
// Opaque identifier for the account stored in Payments Platform.
optional string instrument_id = 1;
// Prefix of the full IBAN value, if available.
optional string prefix = 2;
// Suffix of the full IBAN value, if available.
optional string suffix = 3;
// Length of the full IBAN value, if available.
optional int32 length = 4;
// Nickname for the IBAN, if available.
optional string nickname = 5;
}
// Details of a bank account required to show it in the payment instrument
// selector.
message BankAccountDetails {
// The name of the bank where the user has an account.
optional string bank_name = 1;
// The last four digits of the account number to help the user identify their
// account.
optional string account_number_suffix = 2;
enum AccountType {
ACCOUNT_TYPE_UNSPECIFIED = 0;
CHECKING = 1;
SAVINGS = 2;
CURRENT = 3;
SALARY = 4;
TRANSACTING_ACCOUNT = 5;
}
// The type of bank account.
optional AccountType account_type = 3;
}
message AutofillWalletSpecifics {
enum WalletInfoType {
UNKNOWN = 0;
MASKED_CREDIT_CARD = 1;
// POSTAL_ADDRESS is unused by the client since M121.
POSTAL_ADDRESS = 2;
CUSTOMER_DATA = 3;
CREDIT_CARD_CLOUD_TOKEN_DATA = 4;
PAYMENT_INSTRUMENT = 5;
MASKED_IBAN = 6 [deprecated = true];
}
optional WalletInfoType type = 1;
// This field exists if and only if the "type" field equals to
// MASKED_CREDIT_CARD.
optional WalletMaskedCreditCard masked_card = 2;
// Unused by the client since M121.
// This field exists if and only if the `type` equals POSTAL_ADDRESS.
// TODO(crbug.com/1457187): Some server-side code still relies on this field.
// To keep the protos in sync, it is kept until that code is cleaned up.
optional WalletPostalAddress address = 3;
// This field exists if and only if the "type" field equals to CUSTOMER_DATA.
optional PaymentsCustomerData customer_data = 4;
// This field exists if and only if the "type" field equals to
// CREDIT_CARD_CLOUD_TOKEN_DATA.
optional WalletCreditCardCloudTokenData cloud_token_data = 5;
// This field exists if and only if the "type" field equals to
// PAYMENT_INSTRUMENT.
optional PaymentInstrument payment_instrument = 6;
// This field exists if and only if the `type` field equals to
// MASKED_IBAN.
optional WalletMaskedIban masked_iban = 7 [deprecated = true];
}
// Wallet card usage information that can be synced.
message WalletMetadataSpecifics {
enum Type {
UNKNOWN = 0;
CARD = 1;
// Unused by the client since M121.
// TODO(crbug.com/1457187): Some server-side code still relies on this.
// To keep the protos in sync, it is kept until that code is cleaned up.
ADDRESS = 2;
IBAN = 3;
}
// The type of the Wallet metadata.
optional Type type = 1;
// Base64 encoding of the unique ID string of the corresponding
// AutofillMetadata.
// For Wallet cards, this value is WalletMaskedCreditCard::id.
// For Wallet IBANs, this value is WalletMaskedIban::instrument_id.
optional string id = 2;
// The number of times that this Wallet card or address was used.
optional int64 use_count = 3;
// The last use date of this Wallet card or address. Measured in microseconds
// since the Windows epoch (1601).
optional int64 use_date = 4;
// The id of the profile/address that represents the billing address of this
// Wallet card.
optional string card_billing_address_id = 5;
// Unused by the client since M121.
// TODO(crbug.com/1457187): Some server-side code still relies on this field.
// To keep the protos in sync, it is kept until that code is cleaned up.
optional bool address_has_converted = 6;
}
@@ -0,0 +1,29 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Autofill wallet data which stores the relevant card credential data.
message AutofillWalletCredentialSpecifics {
// A server-generated unique ID for the instrument/card.
optional string instrument_id = 1;
// A 3 or 4 digit card verification code.
optional string cvc = 2;
// The timestamp of the last update. Used to determine data freshness.
optional int64 last_updated_time_unix_epoch_millis = 3;
}
@@ -0,0 +1,45 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Autofill usage data related objects which contains usage data of a payment
// method related to a specific merchant.
message AutofillWalletUsageSpecifics {
// A unique ID identifying this item. Used as the client tag.
optional string guid = 1;
// The type of usage data this entity is associated with.
oneof usage_data_type {
VirtualCardUsageData virtual_card_usage_data = 2;
}
message VirtualCardUsageData {
// The instrument id of the actual card that the virtual card is related to.
optional int64 instrument_id = 1;
// The last four of the virtual card number. This is tied to the usage
// data because the virtual card number may vary depending on merchants.
optional string virtual_card_last_four = 2;
// The url of the merchant where the virtual card is used.
optional string merchant_url = 3;
// The merchant the usage data is related to on the Android platform.
optional string merchant_app_package = 4;
}
}
@@ -0,0 +1,81 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "model_type_state.proto";
import "entity_metadata.proto";
// Corresponds to a single bookmark id/metadata pair.
message BookmarkMetadata {
// Bookmark local id.
optional int64 id = 1;
// Bookmarks sync metadata.
optional EntityMetadata metadata = 2;
}
// Sync proto to carry the sync metadata for the bookmarks model. It is used for
// persisting and loading sync metadata from disk.
message BookmarkModelMetadata {
// Bookmark global metadata.
optional ModelTypeState model_type_state = 1;
// A set of all bookmarks metadata.
repeated BookmarkMetadata bookmarks_metadata = 2;
reserved 3;
reserved 4;
reserved 5;
// Indicates whether the reupload of bookmarks has been triggered such that
// they include fields like |type|, |unique_position| and |parent_guid|, which
// means that their sequence number has been increased (independently of
// whether the commit has succeeded or even started).
// TODO(crbug.com/1232951): remove this code when most of bookmarks are
// reuploaded.
optional bool bookmarks_hierarchy_fields_reuploaded = 6;
// Number of remote updates that were ignored because the parent folder could
// not be determined since sync (bookmark sync) started (since initial sync,
// including initial sync itself). Note that this excludes updates
// representing changes (moves) to existing/tracked bookmarks (i.e. the parent
// folder used to be known, but the new one specified in the update isn't).
//
// Only a minority of users should have a non-zero value, due to a) corrupt
// data, reasons unknown; or b) permanent folders that were unsupported at the
// time the update was received (either for the permanent folder itself or for
// descendants). The absence of this field should be interpreted as 'unknown',
// which roughly means the counter wasn't supported by the browser -M77 or
// below- at the time sync was enabled (it also means, it will remain
// 'unknown' until initial sync is exercised, e.g. sync disabled and
// reenabled).
optional int64 num_ignored_updates_due_to_missing_parent = 7;
// See field above. Among such ignored updates, this field tracks which was
// the highest server version. This may be useful -in the future- to get a
// sense of "time" (or, since versions are opaque to the client, at least a
// notion of ordering with respect to other updates). The absence of this
// field should be interpreted as 'unknown' and does not distinguish the
// case between a) no updates ignored; and b) field wasn't supported at the
// time the last update was ignored. To distinguish between these two, the
// field above can be used.
optional int64 max_version_among_ignored_updates_due_to_missing_parent = 8;
// Indicates whether the count of remote updates during the latest initial
// merge exceeded the bookmark limit. This field is mutually exclusive to all
// the other fields and is managed completely by the processor.
optional bool last_initial_merge_remote_updates_exceeded_limit = 9;
}
@@ -0,0 +1,97 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for bookmarks.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "unique_position.proto";
// Corresponds to a single meta info key/value pair for a bookmark node.
message MetaInfo {
optional string key = 1;
optional string value = 2;
}
// Properties of bookmark sync objects.
message BookmarkSpecifics {
// URL of the bookmarked page (unset for folders).
optional string url = 1;
// PNG-encoded content of the favicon image (in practice 16x16, as determined
// by |kFaviconSize|). Empty if the bookmark has no favicon (which includes
// bookmark folders).
optional bytes favicon = 2;
// Contains legacy title which is truncated and may contain escaped symbols.
optional string legacy_canonicalized_title = 3;
// Corresponds to BookmarkNode::date_added() represented as microseconds since
// the Windows epoch.
optional int64 creation_time_us = 4;
// The URL of the favicon image encoded in field |favicon|. Note that there
// are various cases where this URL may be missing (field unset or empty) even
// if the |favicon| field (image content) is populated:
// 1. WebUI pages such as "chrome://bookmarks/" are missing a favicon URL but
// they have a favicon.
// 2. Data generated by ancient clients (prior to M25) may not contain the
// favicon URL.
// 3. If the favicon URL is too large (determined by |kMaxFaviconUrlSize|) it
// may be omitted by clients to avoid running into the max-entity-size
// limit. Most notably, this includes URLs prefixed with the data: scheme
// that may encode the image content itself in the URL.
optional string icon_url = 5;
repeated MetaInfo meta_info = 6;
reserved 7;
reserved 8;
reserved 9;
// Introduced in M81, it represents a globally unique and immutable ID.
//
// If present, it must be the same as originator_client_item_id in lowercase,
// unless originator client item ID is not a valid GUID. In such cases (which
// is the case for bookmarks created before 2015), this GUID must match the
// value inferred from the combination of originator cache GUID and
// originator client item ID, see InferGuidForLegacyBookmark().
//
// If not present, the value can be safely inferred using the very same
// methods listed above.
optional string guid = 10;
// Contains full title as is. |legacy_canonicalized_title| is a prefix of
// |full_title| with escaped symbols.
optional string full_title = 11;
reserved 12;
reserved 13;
// Introduced in M94, represents the GUID (field |guid|) of the parent.
optional string parent_guid = 14;
// Introduced in M94, determines whether this entity represents a bookmark
// folder. This field is redundant to the similar field in SyncEntity. If this
// field in specifics is set, it takes precedence over the one in SyncEntity.
enum Type {
// |UNSPECIFIED| is relevant only for the case where the field is not set.
// M94 and above should not use this value.
UNSPECIFIED = 0;
URL = 1;
FOLDER = 2;
}
optional Type type = 15;
// Introduced in M94, determines ordering among siblings. This field is
// redundant to the similar field in SyncEntity. If this field in specifics is
// set, it takes precedence over the one in SyncEntity.
optional UniquePosition unique_position = 16;
// Introduced in M106, corresponds to BookmarkNode::date_last_used()
// represented as microseconds since the Windows epoch.
optional int64 last_used_time_us = 17;
}
@@ -0,0 +1,19 @@
// Copyright (c) 2020 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message BraveSpecificFields {
optional bool is_self_delete_supported = 1;
}
@@ -0,0 +1,57 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol for communication between sync client and server.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message CustomNudgeDelay {
optional int32 datatype_id = 1; // Datatype id.
optional int32 delay_ms = 2; // Delay in milliseconds.
}
message ClientCommand {
// Time to wait before sending any requests to the server.
optional int32 set_sync_poll_interval = 1; // in seconds
// This has been deprecated since M75.
optional int32 set_sync_long_poll_interval = 2 [deprecated = true];
optional int32 max_commit_batch_size = 3;
// Number of seconds to delay before the throttled client should retry.
optional int32 throttle_delay_seconds = 5;
// This has been deprecated since M109.
optional int32 client_invalidation_hint_buffer_size = 6 [deprecated = true];
// Time to wait before issuing a retry GU.
optional int32 gu_retry_delay_seconds = 7;
// A dictionary of custom nudge delays.
repeated CustomNudgeDelay custom_nudge_delays = 8;
// Configuration for commit quota of extension-related data types.
// The maximum count of tokens (=initial at browser startup) and the
// interval for refilling one more token if not at maximum.
optional int32 extension_types_max_tokens = 9;
optional int32 extension_types_refill_interval_seconds = 10;
// Extended nudge delay for data types with depleted quota.
optional int32 extension_types_depleted_quota_nudge_delay_seconds = 11;
reserved 4;
reserved "sessions_commit_delay_seconds";
}
@@ -0,0 +1,94 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol for debug info clients can send to the sync server.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "get_updates_caller_info.proto";
import "sync_enums.proto";
// Per-type hint information.
message TypeHint {
// The data type this hint applied to.
optional int32 data_type_id = 1;
// Whether or not a valid hint is provided.
optional bool has_valid_hint = 2;
}
// The additional info here is from the StatusController. They get sent when
// the event SYNC_CYCLE_COMPLETED is sent.
message SyncCycleCompletedEventInfo {
reserved 1;
reserved "syncer_stuck";
reserved 2;
reserved "num_blocking_conflicts";
reserved 3;
reserved "num_non_blocking_conflicts";
// These new conflict counters replace the ones above.
// TODO(crbug.com/1315573): Deprecated in M103.
optional int32 num_encryption_conflicts = 4 [deprecated = true];
optional int32 num_hierarchy_conflicts = 5 [deprecated = true];
optional int32 num_simple_conflicts = 6; // No longer sent since M24.
optional int32 num_server_conflicts = 7;
// Counts to track the effective usefulness of our GetUpdate requests.
optional int32 num_updates_downloaded = 8;
// TODO(crbug.com/1315573): Deprecated in M103.
optional int32 num_reflected_updates_downloaded = 9 [deprecated = true];
// |caller_info| was mostly replaced by |get_updates_origin|; now it only
// contains the |notifications_enabled| flag.
optional GetUpdatesCallerInfo caller_info = 10;
// Deprecated in M67.
reserved 11;
reserved "source_info";
optional SyncEnums.GetUpdatesOrigin get_updates_origin = 12;
}
message DebugEventInfo {
// Each of the following fields correspond to different kinds of events. as
// a result, only one is set during any single DebugEventInfo.
// A singleton event. See enum definition.
optional SyncEnums.SingletonDebugEventType singleton_event = 1;
// A sync cycle completed.
optional SyncCycleCompletedEventInfo sync_cycle_completed_event_info = 2;
// A datatype triggered a nudge.
optional int32 nudging_datatype = 3;
// A notification triggered a nudge.
repeated int32 datatypes_notified_from_server = 4;
reserved "datatype_association_stats";
reserved 5;
}
message DebugInfo {
repeated DebugEventInfo events = 1;
// Whether cryptographer is ready to encrypt and decrypt data.
optional bool cryptographer_ready = 2;
// Cryptographer has pending keys which indicates the correct passphrase
// has not been provided yet.
optional bool cryptographer_has_pending_keys = 3;
// Indicates client has dropped some events to save bandwidth.
optional bool events_dropped = 4;
}
@@ -0,0 +1,29 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Special sync datatype to notify client about People Group changes.
message CollaborationGroupSpecifics {
// Unique identifier for a collaboration group, also known as `group_id` or
// `group_name`.
optional string collaboration_id = 1;
// Indicates staleness of the collaboration group, used to determine whether
// client should refresh cached group. Server-side derived from group
// `consistency_token`.
optional int64 last_updated_timestamp_millis_since_unix_epoch = 2;
}
@@ -0,0 +1,42 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Sync protocol datatype extension for compare.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// A Compare sync entity, containing sufficient data to enable all data
// acquisition for rendering the Compare experience.
message CompareSpecifics {
// The primary key for local storage.
optional string uuid = 1;
// Tracks when this entity was created.
optional int64 creation_time_unix_epoch_micros = 2;
// Tracks when this entity was last modified.
optional int64 update_time_unix_epoch_micros = 3;
// The name of the comparison group.
optional string name = 4;
// The list of product URLs that constitute this comparison group.
repeated ComparisonData data = 5;
}
message ComparisonData {
// The URL of the item in the comparison.
optional string url = 1;
}
@@ -0,0 +1,217 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message ContactInfoSpecifics {
// Represents the validation status of a stored value.
enum VerificationStatus {
// No verification status assigned.
VERIFICATION_STATUS_UNSPECIFIED = 0;
// The value token was parsed from a parent token.
// For example, the first name was derived by splitting a full name into
// its components.
PARSED = 1;
// Value was built from its subcomponents.
// For example, the full name was built from the first, middle and last
// name.
FORMATTED = 2;
// The value was observed in a form transmission.
// For example, the user filled a form that contained at NAME_FULL field.
// The value of NAME_FULL will be stored as OBSERVED.
OBSERVED = 3;
// The user used the settings UI to verify and store this token.
// This is currently only applicable to the full name, since users cannot
// edit individual components of their name.
USER_VERIFIED = 4;
// The token was parsed remotely.
SERVER_PARSED = 5;
}
message Observation {
// An `autofill::ProfileTokenQuality::ObservationType`. Represented as a
// uint32 to retain future enum values (since this is proto2).
// The `ObservationType`'s underlying type is uint8_t, but this is not
// supported by protos. Since the uint32 Proto type uses variable-length
// encoding, uint32 has a similar size as a single element byte[].
optional uint32 type = 1;
// The `autofill::ProfileTokenQuality::FormSignatureHash` has type uint8_t.
// Like for `type`, this should have the (non-existing) data type uint8.
optional uint32 form_hash = 2;
}
// Metadata that is attached to every token type.
message TokenMetadata {
optional VerificationStatus status = 1;
repeated Observation observations = 2;
// Stores a hash of the token value that this metadata is associated with.
// This is used by Autofill to detect changes of the token value by external
// integrators.
optional uint32 value_hash = 3;
}
// A token with a string interpretation of the value.
message StringToken {
optional string value = 1;
optional TokenMetadata metadata = 2;
}
// A globally unique, immutable UUID represented in lowercase.
optional string guid = 1;
reserved 2;
reserved "source";
// The number of time this profile has been used.
optional int64 use_count = 3;
// The last time this profile was used.
optional int64 use_date_unix_epoch_seconds = 4;
// The last time the profile was modified.
optional int64 date_modified_unix_epoch_seconds = 37;
// The BCP 47 language code used to format the address for display.
optional string language_code = 38;
// A user chosen profile label that is used to identify the profile.
// The value of the label can be freely chosen by the user.
optional string profile_label = 5;
// Tracks the application that initially created the profile. The integer
// represents a value in the server-side enum `BillableService`. A value of
// 70073 represents Chrome (enum value BILLABLE_SERVICE_CHROME_PAYMENTS).
optional int32 initial_creator_id = 39;
// Tracks the application that applied the last modification to the
// non-metadata content of the profile. It represents a value in the same
// `BillableService` enum.
// All StringTokens, and the `profile_label` are considered non-metadata.
optional int32 last_modifier_id = 40;
// Contact info name fields.
optional StringToken name_first = 7;
optional StringToken name_middle = 8;
optional StringToken name_last = 9;
// Sometimes the last name is composed of two names as it is common for
// Hispanic/Latinx names. In the unstructured representation of the last name,
// there may be even a conjunction between the first and the second last
// name. For example, the more-complete version of Pablo Picasso's surname is
// "Ruiz y Picasso" containing a first last name, a conjunction (the y) and a
// second last name.
optional StringToken name_last_first = 10;
optional StringToken name_last_conjunction = 11;
optional StringToken name_last_second = 12;
optional StringToken name_full = 13;
reserved 14;
reserved "name_full_with_honorific";
// Contact info additional fields.
optional StringToken email_address = 15;
optional StringToken company_name = 16;
// Address fields: These are user-provided values with no particular
// guarantees about their format.
optional StringToken address_city = 17;
optional StringToken address_state = 18;
optional StringToken address_zip = 19;
optional StringToken address_country = 20;
optional StringToken address_admin_level_2 = 43;
// Additional address fields for i18n.
// Includes all of the lines of a street address, including newlines, e.g.
// 123 Main Street,
// Apt. #42
optional StringToken address_street_address = 21;
// A sorting code is similar to a postal code. However, whereas a postal code
// normally refers to a single geographical location, a sorting code often
// does not. Instead, a sorting code is assigned to an organization, which
// might be geographically distributed. The most prominent example of a
// sorting code system is CEDEX in France.
optional StringToken address_sorting_code = 22;
// A dependent locality is a subunit of a locality, where a "locality" is
// roughly equivalent to a city. Examples of dependent localities include
// inner-city districts and suburbs.
optional StringToken address_dependent_locality = 23;
reserved 24;
reserved "address_language_code";
// The street name of the address, without a house number.
optional StringToken address_thoroughfare_name = 25;
// The house number, which may be alphanumeric.
optional StringToken address_thoroughfare_number = 26;
// Deprecated in M118.
reserved 27;
reserved "address_dependent_thoroughfare_name";
reserved 28;
reserved "address_thoroughfare_and_dependent_thoroughfare_name";
reserved 29;
reserved "address_premise_name";
// Contains the floor, staircase or apartment number within a building.
optional StringToken address_subpremise_name = 30;
// All the information related to the apartment. Normally a combination of the
// apartment type (address_apt_type) and number (address_apt_num).
// E.g. "Apt 5".
optional StringToken address_apt = 50;
// The apartment number within a building.
optional StringToken address_apt_num = 31;
// Information describing the type of apartment (e.g. Apt, Apartamento, Sala,
// Departamento).
optional StringToken address_apt_type = 51;
// The floor number within a building.
optional StringToken address_floor = 32;
// Contains both the street and house number.
optional StringToken address_street_location = 44;
optional StringToken address_landmark = 41;
optional StringToken address_overflow = 45;
// Indicates that the address is at the intersection between two streets.
// It is normally a combination of between_streets_1 and _2. This is a common
// way of writing addresses in Mexico.
optional StringToken address_between_streets = 42;
// The meaning of the field is similar to address_between_streets. The
// type should be used for "Entre calle 1" in MX addresses which also
// contain the "Entre calle 2" field.
optional StringToken address_between_streets_1 = 46;
// The meaning of the field is similar to address_between_streets. The
// type should be used for "Entre calle 2" in MX addresses which also
// contain the "Entre calle 1" field.
optional StringToken address_between_streets_2 = 47;
// Combination of types address_between_streets or address_landmark. Note that
// some address forms require the combination of both in a single field as
// opposed to each one separately.
optional StringToken address_between_streets_or_landmark = 48;
// Combination of types address_overflow and address_landmark. Note that some
// address forms require the combination of both in a single field as opposed
// to each one separately.
optional StringToken address_overflow_and_landmark = 49;
// Combination of types address_street_location and
// address_dependent_locality. Note that some address forms require the
// combination of both in a single field as opposed to each one separately.
optional StringToken address_street_location_and_locality = 52;
// Phone.
optional StringToken phone_home_whole_number = 33;
// Removed in M123.
reserved 34;
reserved "birthdate_day";
reserved 35;
reserved "birthdate_month";
reserved 36;
reserved "birthdate_year";
// Removed in M122.
reserved 6;
reserved "name_honorific";
}
@@ -0,0 +1,185 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message DataTypeProgressMarker {
// An integer identifying the data type whose progress is tracked by this
// marker. The legitimate values of this field correspond to the protobuf
// field numbers of all EntitySpecifics fields supported by the server.
// These values are externally declared in per-datatype .proto files.
optional int32 data_type_id = 1;
// An opaque-to-the-client sequence of bytes that the server may interpret
// as an indicator of the client's knowledge state. If this is empty or
// omitted by the client, it indicates that the client is initiating a
// a first-time sync of this datatype. Otherwise, clients must supply a
// value previously returned by the server in an earlier GetUpdatesResponse.
// These values are not comparable or generable on the client.
//
// The opaque semantics of this field are to afford server implementations
// some flexibility in implementing progress tracking. For instance,
// a server implementation built on top of a distributed storage service --
// or multiple heterogenous such services -- might need to supply a vector
// of totally ordered monotonic update timestamps, rather than a single
// monotonically increasing value. Other optimizations may also be
// possible if the server is allowed to embed arbitrary information in
// the progress token.
//
// Server implementations should keep the size of these tokens relatively
// small, on the order of tens of bytes, and they should remain small
// regardless of the number of items synchronized. (A possible bad server
// implementation would be for progress_token to contain a list of all the
// items ever sent to the client. Servers shouldn't do this.)
optional bytes token = 2;
reserved 3;
reserved "timestamp_token_for_migration";
reserved 4;
reserved "notification_hint";
// This field will be included only in GetUpdates with origin GU_TRIGGER.
optional GetUpdateTriggers get_update_triggers = 5;
// The garbage collection directive for this data type. The client should
// purge items locally based on this directive. Since this directive is
// designed to be sent from server only, the client should persist it locally
// as needed and avoid sending it to the server.
optional GarbageCollectionDirective gc_directive = 6;
}
// A single datatype's sync context. Allows the datatype to pass along
// datatype specific information with its own server backend.
message DataTypeContext {
// The type this context is associated with.
optional int32 data_type_id = 1;
// The context for the datatype.
optional bytes context = 2;
// The version of the context.
optional int64 version = 3;
}
message GarbageCollectionDirective {
enum Type {
UNKNOWN = 0;
VERSION_WATERMARK = 1;
DEPRECATED_AGE_WATERMARK = 2 [deprecated = true];
DEPRECATED_MAX_ITEM_COUNT = 3 [deprecated = true];
}
// Deprecated in M124. This field is unused because it would be hard to use
// `active_collaboration_ids`. The client should rely on the presence of the
// corresponding fields instead.
optional Type type = 1 [default = UNKNOWN, deprecated = true];
// This field specifies the watermark for the versions which should get
// garbage collected. The client should purge all sync entities when
// receiving any value of this. This is a change from previous behavior,
// where the client would only be required to purge items older than the
// specified watermark.
// TODO(crbug.com/877951): Rename this to make clear that whenever it's set,
// the client will delete ALL data, regardless of its value.
optional int64 version_watermark = 2;
reserved 3;
reserved "age_watermark_in_days";
reserved 4;
reserved "max_number_of_items";
// This field contains a list of active collaboration IDs which are available
// to the user. The client should stop tracking any shared sync entities with
// collaborations which are not in the list. Note that if the list is empty,
// an empty `collaboration_gc` is expected to be provided by the server.
// Introduced in M124.
message CollaborationGarbageCollection {
repeated string active_collaboration_ids = 1;
}
optional CollaborationGarbageCollection collaboration_gc = 5;
}
// This message communicates additional per-type information related to
// requests with origin GU_TRIGGER. This message is not relevant when any
// other origin value is used.
// Introduced in M29.
message GetUpdateTriggers {
// An opaque-to-the-client string of bytes, received through a notification,
// that the server may interpret as a hint about the location of the latest
// version of the data for this type.
// Introduced in M29.
repeated string notification_hint = 1;
// This flag is set if the client was forced to drop hints because the number
// of queued hints exceeded its limit. The oldest hints will be discarded
// first. Introduced in M29.
optional bool client_dropped_hints = 2;
// This flag is set when the client suspects that its list of invalidation
// hints may be incomplete. This may be the case if:
// - The client is syncing for the first time.
// - The client has just restarted and it was unable to keep track of
// invalidations that were received prior to the restart.
// - The client's connection to the invalidation server is currently or
// was recently broken.
//
// It's difficult to provide more details here. This is implemented by
// setting the flag to false whenever anything that might adversely affect
// notifications happens (eg. a crash, restart on a platform that doesn't
// support invalidation ack-tracking, transient invalidation error) and is
// unset only after we've experienced one successful sync cycle while
// notifications were enabled.
//
// This flag was introduced in M29.
optional bool invalidations_out_of_sync = 3;
// This counts the number of times the syncer has been asked to commit
// changes for this type since the last successful sync cycle. The number of
// nudges may not be related to the actual number of items modified. It
// often correlates with the number of user actions, but that's not always
// the case.
// Introduced in M29.
optional int64 local_modification_nudges = 4;
// This counts the number of times the syncer has been explicitly asked to
// fetch updates for this type since the last successful sync cycle. These
// explicit refresh requests should be relatively rare on most platforms, and
// associated with user actions. For example, at the time of this writing
// the most common (only?) source of refresh requests is when a user opens
// the new tab page on a platform that does not support sessions
// invalidations.
// Introduced in M29.
optional int64 datatype_refresh_nudges = 5;
// This flag is set if the invalidation server reports that it may have
// dropped some invalidations at some point. Introduced in M33.
optional bool server_dropped_hints = 6;
// This flag is set if this GetUpdate request is due at least in part due
// to the fact that this type has not finished initial sync yet, and the
// client would like to initialize itself with the server data.
//
// Only some types support performing an initial sync as part of a normal
// GetUpdate request. Many types must be in configure mode when fetching
// initial sync data.
//
// Introduced in M38.
optional bool initial_sync_in_progress = 7;
// This flag is set if this GetUpdate request is due to client receiving
// conflict response from server, so client needs to sync and then resolve
// conflict locally, and then commit again.
//
// Introduced in M42.
optional bool sync_for_resolve_conflict_in_progress = 8;
}
@@ -0,0 +1,225 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for nigori keys.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "sync_enums.proto";
import "brave_device_info_specifics.proto";
// Information about a device that is running a sync-enabled Chrome browser. We
// are mapping the per-client cache guid to more specific information about the
// device.
message DeviceInfoSpecifics {
// The cache_guid created to identify a sync client on this device.
optional string cache_guid = 1;
// A non-unique but human readable name to describe this client.
optional string client_name = 2;
// The platform of the device.
// Deprecated in M110, but still populated for backwards compatibility.
// Replaced by os_type + device_form_factor.
optional SyncEnums.DeviceType device_type = 3 [deprecated = true];
// The UserAgent used when contacting the Chrome Sync server.
// Only present when talking to the HTTP server.
optional string sync_user_agent = 4;
// The Chrome instance's version. Updated (if necessary) on every startup.
// DEPRECATED in M92. Still populated for backward compatibility.
optional string chrome_version = 5 [deprecated = true];
// Last time when pre-sync data on the device was saved. The device can be
// restored to state back to this time. In millisecond since UNIX epoch.
// DEPRECATED in M50.
optional int64 deprecated_backup_timestamp = 6 [deprecated = true];
// Device_id that is stable until user signs out. This device_id is used for
// annotating login scoped refresh token. Present only for Chrome Clients.
optional string signin_scoped_device_id = 7;
// This field is updated to be the current time periodically, and is also set
// to the current time whenever another field changes. By examining the
// difference between this field and the current time, it should be possible
// to reason about the inactivity of any device that was syncing at one time.
optional int64 last_updated_timestamp = 8;
// Device info fields that are specific to a feature. This is information that
// can not be derived from the other fields in the proto and are not general
// enough to be used by another feature.
optional FeatureSpecificFields feature_fields = 9;
// Device specific information for Sharing feature.
optional SharingSpecificFields sharing_fields = 10;
// Model of device.
optional string model = 11;
// Name of device manufacturer.
optional string manufacturer = 12;
// The interval in minutes with which |last_updated_timestamp| is updated to
// the sync servers if online and while sync is actively running (e.g.
// excludes backgrounded apps on Android). Introduced in M81. The legacy
// default was 1 day.
optional int32 pulse_interval_in_minutes = 13;
// Device specific information for Sync invalidations.
optional InvalidationSpecificFields invalidation_fields = 14;
// Information for contacting this device in order to perform security key
// operations.
optional PhoneAsASecurityKeySpecificFields paask_fields = 15;
// Unique hardware identifier string which details the HW combination of
// a ChromeOS device. This field will be empty on other OS devices.
optional string full_hardware_class = 16;
// Version information to pin-point which client implementation this is.
oneof client_version_info {
ChromeVersionInfo chrome_version_info = 17;
GooglePlayServicesVersionInfo google_play_services_version_info = 18;
}
// The OS type the device.
optional SyncEnums.OsType os_type = 19;
// The form factor of the device.
optional SyncEnums.DeviceFormFactor device_form_factor = 20;
optional BraveSpecificFields brave_fields = 1000;
}
// Feature specific information about the device that is running a sync-enabled
// Chrome browser. Adding to this proto is discouraged and should only be added
// when the information can not be derived more generally.
message FeatureSpecificFields {
// Tracks whether the SendTabToSelf feature is enabled on the device. For this
// to be true, two things must be true: (1) The receiving side of the feature
// must be enabled on the device (2) The user has enabled sync for this
// feature
optional bool send_tab_to_self_receiving_enabled = 1;
}
// Device specific information for Sharing feature. Used to send end-to-end
// encrypted message through FCM to other devices.
message SharingSpecificFields {
// FCM registration token of device subscribed using VAPID key.
// TODO(crbug.com/1012226): Deprecate when VAPID migration is over.
optional string vapid_fcm_token = 1;
// Public key for message encryption [RFC8291] using VAPID key.
// TODO(crbug.com/1012226): Deprecate when VAPID migration is over.
optional bytes vapid_p256dh = 2;
// Auth secret for message encryption [RFC8291] using VAPID key.
// TODO(crbug.com/1012226): Deprecate when VAPID migration is over.
optional bytes vapid_auth_secret = 3;
// Enum defining available Sharing features.
enum EnabledFeatures {
reserved 5;
reserved "PEER_CONNECTION";
UNKNOWN = 0;
// Supports Click to Call only via VAPID.
CLICK_TO_CALL_VAPID = 1;
// Supports Shared Clipboard only via VAPID.
SHARED_CLIPBOARD_VAPID = 2;
SMS_FETCHER = 3;
REMOTE_COPY = 4;
DISCOVERY = 6;
// Suppots Click to Call via both VAPID and sender ID.
CLICK_TO_CALL_V2 = 7;
// Suppots Shared Clipboard via both VAPID and sender ID.
SHARED_CLIPBOARD_V2 = 8;
// Supports optimization guide push notification.
OPTIMIZATION_GUIDE_PUSH_NOTIFICATION = 9;
}
// A list of enabled Sharing features.
repeated EnabledFeatures enabled_features = 4;
// Deprecated. Use sender_id_fcm_token_v2 for new clients.
optional string sender_id_fcm_token_deprecated = 5 [deprecated = true];
// Deprecated. Use sender_id_p256dh_v2 for new clients.
optional bytes sender_id_p256dh_deprecated = 6 [deprecated = true];
// Deprecated. Use sender_id_auth_secret_v2 for new clients.
optional bytes sender_id_auth_secret_deprecated = 7 [deprecated = true];
// FCM registration token of device subscribed using Sharing sender ID.
optional string sender_id_fcm_token_v2 = 8;
// Public key for message encryption [RFC8291] using Sharing sender ID.
optional bytes sender_id_p256dh_v2 = 9;
// Auth secret for message encryption [RFC8291] using Sharing sender ID.
optional bytes sender_id_auth_secret_v2 = 10;
}
// Device specific information telling how to send invalidations to this device.
message InvalidationSpecificFields {
// FCM registration token of device (using Sync sender ID).
optional string instance_id_token = 1;
// This device wants to receive only invalidations which are related to these
// types. The legitimate values of this field correspond to the protobuf field
// numbers of all EntitySpecifics fields supported by the server (see
// components/sync/protocol/sync.proto).
repeated int32 interested_data_type_ids = 2;
}
message PhoneAsASecurityKeySpecificFields {
// The encoded tunnel server in the same format as in the BLE advert. See
// device::cablev2::tunnelserver::DecodeDomain. (Actually a uint16 but
// proto doesn't have that as a type.)
optional uint32 tunnel_server_domain = 1;
// An FCM token that can be presented to the tunnel server to contact this
// device.
optional bytes contact_id = 2;
// A shared secret key, used to enable communication across devices via
// unauthenticated channels. Knowledge of this secret acts as proof that the
// remote end is authorized to talk to the device publishing this secret.
optional bytes secret = 3;
// An id that identifies |secret|. This, and |secret|, will be rotated and
// the ID allows this device to know which |secret| the counterparty is
// using.
optional fixed32 id = 4;
// A P-256, X9.62-encoded public key for this device. Specific to the Chrome
// instance.
optional bytes peer_public_key_x962 = 5;
// The credential ID for use when signing into accounts.google.com. This
// credential ID isn't published as a WebauthnCredentialSpecifics, unlike
// other passkeys, because it's auto-generated.
optional bytes google_credential_id = 6;
}
message ChromeVersionInfo {
// Version number as per https://www.chromium.org/developers/version-numbers.
optional string version_number = 1;
}
message GooglePlayServicesVersionInfo {
optional string apk_version_name = 1;
}
@@ -0,0 +1,25 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for the dictionary.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of Dictionary objects.
message DictionarySpecifics {
// A spelling which when typed is treated as a correctly spelled word.
optional string word = 1;
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Common sync protocol for encrypted data.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Encrypted sync data consists of two parts: a key name and a blob. Key name is
// the name of the key that was used to encrypt blob and blob is encrypted data
// itself.
//
// The reason we need to keep track of the key name is that a sync user can
// change their passphrase (and thus their encryption key) at any time. When
// that happens, we make a best effort to reencrypt all nodes with the new
// passphrase, but since we don't have transactions on the server-side, we
// cannot guarantee that every node will be reencrypted. As a workaround, we
// keep track of all keys, assign each key a name (by using that key to encrypt
// a well known string) and keep track of which key was used to encrypt each
// node.
message EncryptedData {
optional string key_name = 1;
optional string blob = 2; // base64-encoded.
}
@@ -0,0 +1,103 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "entity_specifics.proto";
import "unique_position.proto";
// Sync proto to store entity metadata in model type storage.
message EntityMetadata {
// A hash based on the client tag and model type.
// Used for various map lookups. Should always be available.
// Sent to the server as SyncEntity::client_tag_hash.
optional string client_tag_hash = 1;
// The entity's server-assigned ID.
//
// Prior to the item's first commit, we leave this value as an empty string.
// The initial ID for a newly created item has to meet certain uniqueness
// requirements, and we handle those on the sync thread.
optional string server_id = 2;
// Whether or not the entity is deleted.
optional bool is_deleted = 3;
// A version number used to track in-progress commits. Each local change
// increments this number.
optional int64 sequence_number = 4;
// The sequence number of the last item known to be successfully committed.
optional int64 acked_sequence_number = 5;
// The server version on which this item is based.
//
// If there are no local changes, this is the version of the entity as we see
// it here.
//
// If there are local changes, this is the version of the entity on which
// those changes are based.
optional int64 server_version = 6 [default = -1];
// Entity creation and modification timestamps. Assigned by the client and
// synced by the server, though the server usually doesn't bother to inspect
// their values. They are encoded as milliseconds since the Unix epoch.
optional int64 creation_time = 7;
optional int64 modification_time = 8;
// A hash of the current entity specifics value. Used to detect whether
// entity's specifics value has changed without having to keep specifics in
// memory.
optional string specifics_hash = 9;
// A hash of the last specifics known by both the client and server. Used to
// detect when local commits and remote updates are just for encryption. This
// value will be the empty string only in the following cases: the entity is
// in sync with the server, has never been synced, or is deleted.
optional string base_specifics_hash = 10;
// Used for positioning entities among their siblings. Relevant only for data
// types that support positions (e.g bookmarks). Refer to its definition in
// unique_position.proto for more information about its internal
// representation.
optional UniquePosition unique_position = 11;
// Used only for bookmarks. It's analogous to |specifics_hash| but it
// exclusively hashes the content of the favicon image, as represented in
// proto field BookmarkSpecifics.favicon, using base::PersistentHash().
optional fixed32 bookmark_favicon_hash = 12;
// Last specifics known by both the client and server. Used during commits to
// the server in order to prevent data loss caused by older clients dealing
// with unknown proto fields (fields that were introduced later). Datatypes
// (ModelTypeSyncBridge) may implement logic to trim down (or fully clear)
// this proto prior to caching, to avoid the memory and I/O overhead of
// dealing with an extra copy of the data. Introduced in M101.
optional EntitySpecifics possibly_trimmed_base_specifics = 13;
// If the entity is deleted, this field will record the chromium version
// (e.g. 117.0.5875.1) that originated the deletion. Introduced in M117.
optional string deleted_by_version = 14;
// Metadata used for shared data types. Must not be present for regular data
// types.
// Introduced in M124.
message CollaborationMetadata {
// Collaboration ID which the current entity belongs to.
optional string collaboration_id = 1;
}
optional CollaborationMetadata collaboration = 15;
}
@@ -0,0 +1,189 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "encryption.proto";
import "app_list_specifics.proto";
import "app_setting_specifics.proto";
import "app_specifics.proto";
import "arc_package_specifics.proto";
import "autofill_specifics.proto";
import "autofill_offer_specifics.proto";
import "autofill_wallet_credential_specifics.proto";
import "autofill_wallet_usage_specifics.proto";
import "bookmark_specifics.proto";
import "collaboration_group_specifics.proto";
import "compare_specifics.proto";
import "contact_info_specifics.proto";
import "device_info_specifics.proto";
import "dictionary_specifics.proto";
import "experiments_specifics.proto";
import "extension_setting_specifics.proto";
import "extension_specifics.proto";
import "favicon_image_specifics.proto";
import "favicon_tracking_specifics.proto";
import "history_delete_directive_specifics.proto";
import "history_specifics.proto";
import "managed_user_setting_specifics.proto";
import "managed_user_shared_setting_specifics.proto";
import "nigori_specifics.proto";
import "os_preference_specifics.proto";
import "os_priority_preference_specifics.proto";
import "password_sharing_invitation_specifics.proto";
import "password_specifics.proto";
import "plus_address_specifics.proto";
import "power_bookmark_specifics.proto";
import "preference_specifics.proto";
import "printer_specifics.proto";
import "printers_authorization_server_specifics.proto";
import "priority_preference_specifics.proto";
import "reading_list_specifics.proto";
import "saved_tab_group_specifics.proto";
import "search_engine_specifics.proto";
import "security_event_specifics.proto";
import "send_tab_to_self_specifics.proto";
import "segmentation_specifics.proto";
import "session_specifics.proto";
import "shared_tab_group_data_specifics.proto";
import "sharing_message_specifics.proto";
import "theme_specifics.proto";
import "typed_url_specifics.proto";
import "user_consent_specifics.proto";
import "user_event_specifics.proto";
import "web_apk_specifics.proto";
import "web_app_specifics.proto";
import "webauthn_credential_specifics.proto";
import "wifi_configuration_specifics.proto";
import "workspace_desk_specifics.proto";
message EntitySpecifics {
// If a datatype is encrypted, this field will contain the encrypted
// original EntitySpecifics. The extension for the datatype will continue
// to exist, but contain only the default values.
// Note that currently passwords employ their own legacy encryption scheme and
// do not use this field.
optional EncryptedData encrypted = 1;
// To add new datatype-specific fields to the protocol, extend
// EntitySpecifics. First, pick a non-colliding tag number by
// picking a Cr-Commit-Position of one of your past commits
// to src.chromium.org. Then, in a different protocol buffer
// definition, define your message type, and add an optional field
// to the list below using the unique tag value you selected.
//
// optional MyDatatypeSpecifics my_datatype = 32222;
//
// where:
// - 32222 is the non-colliding tag number you picked earlier.
// - MyDatatypeSpecifics is the type (probably a message type defined
// in your new .proto file) that you want to associate with each
// object of the new datatype.
// - my_datatype is the field identifier you'll use to access the
// datatype specifics from the code.
//
// Server implementations are obligated to preserve the contents of
// EntitySpecifics when it contains unrecognized fields. In this
// way, it is possible to add new datatype fields without having
// to update the server.
//
// Note: The tag selection process is based on legacy versions of the
// protocol which used protobuf extensions. We have kept the process
// consistent as the old values cannot change. The 5+ digit nature of the
// tags also makes them recognizable (individually and collectively) from
// noise in logs and debugging contexts, and creating a divergent subset of
// tags would only make things a bit more confusing.
oneof specifics_variant {
AutofillSpecifics autofill = 31729;
BookmarkSpecifics bookmark = 32904;
PreferenceSpecifics preference = 37702;
TypedUrlSpecifics typed_url = 40781;
ThemeSpecifics theme = 41210;
PasswordSpecifics password = 45873;
NigoriSpecifics nigori = 47745;
ExtensionSpecifics extension = 48119;
AppSpecifics app = 48364;
SessionSpecifics session = 50119;
AutofillProfileSpecifics autofill_profile = 63951;
SearchEngineSpecifics search_engine = 88610;
ExtensionSettingSpecifics extension_setting = 96159;
AppSettingSpecifics app_setting = 103656;
HistoryDeleteDirectiveSpecifics history_delete_directive = 150251;
DeviceInfoSpecifics device_info = 154522;
PriorityPreferenceSpecifics priority_preference = 163425;
DictionarySpecifics dictionary = 170540;
ManagedUserSettingSpecifics managed_user_setting = 186662;
AppListSpecifics app_list = 229170;
AutofillWalletSpecifics autofill_wallet = 306270;
WalletMetadataSpecifics wallet_metadata = 330441;
ArcPackageSpecifics arc_package = 340906;
PrinterSpecifics printer = 410745;
ReadingListSpecifics reading_list = 411028;
UserEventSpecifics user_event = 455206;
UserConsentSpecifics user_consent = 556014;
SendTabToSelfSpecifics send_tab_to_self = 601980;
SecurityEventSpecifics security_event = 600372;
WebApkSpecifics web_apk = 1117170;
WebAppSpecifics web_app = 673225;
WifiConfigurationSpecifics wifi_configuration = 662827;
OsPreferenceSpecifics os_preference = 702141;
OsPriorityPreferenceSpecifics os_priority_preference = 703915;
SharingMessageSpecifics sharing_message = 728866;
AutofillOfferSpecifics autofill_offer = 774329;
WorkspaceDeskSpecifics workspace_desk = 874841;
WebauthnCredentialSpecifics webauthn_credential = 895275;
HistorySpecifics history = 963985;
PrintersAuthorizationServerSpecifics printers_authorization_server = 974304;
SavedTabGroupSpecifics saved_tab_group = 1004874;
AutofillWalletUsageSpecifics autofill_wallet_usage = 1033580;
ContactInfoSpecifics contact_info = 1034378;
SegmentationSpecifics segmentation = 1026052;
PowerBookmarkSpecifics power_bookmark = 1073150;
IncomingPasswordSharingInvitationSpecifics
incoming_password_sharing_invitation = 1141935;
OutgoingPasswordSharingInvitationSpecifics
outgoing_password_sharing_invitation = 1142081;
AutofillWalletCredentialSpecifics autofill_wallet_credential = 1164238;
SharedTabGroupDataSpecifics shared_tab_group_data = 1239418;
CollaborationGroupSpecifics collaboration_group = 1259076;
CompareSpecifics compare = 1329438;
PlusAddressSpecifics plus_address = 1267844;
// When adding a new type, follow the docs below and keep this comment as
// the last entry.
// https://www.chromium.org/developers/design-documents/sync/integration-checklist/
// No-longer-supported data types, in various stages of removal.
// TODO(crbug.com/1395003): Eventually, these should all become "reserved"
// fields, or at least use "EmptySpecifics". But some of them are still
// needed by the server, e.g. to support older clients.
EmptySpecifics app_notification = 45184 [deprecated = true];
EmptySpecifics synced_notification = 153108 [deprecated = true];
EmptySpecifics synced_notification_app_info = 235816 [deprecated = true];
ExperimentsSpecifics experiments = 161496 [deprecated = true];
FaviconTrackingSpecifics favicon_tracking = 181534 [deprecated = true];
FaviconImageSpecifics favicon_image = 182019 [deprecated = true];
EmptySpecifics managed_user = 194582 [deprecated = true];
ManagedUserSharedSettingSpecifics managed_user_shared_setting = 202026
[deprecated = true];
EmptySpecifics managed_user_allowlist = 306060 [deprecated = true];
}
reserved 218175;
reserved "wifi_credential";
reserved 223759;
reserved "article";
reserved 545005;
reserved "mountain_share";
}
message EmptySpecifics {}
@@ -0,0 +1,45 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This API is not used in Chromium anymore, but the server still needs to
// support it for the benefit of older clients (Chrome versions up to and
// including M80 rely on it, see crbug.com/1009361).
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "experiments_specifics.proto";
// This request allows an unauthenticated client to check the status of the
// experiments which do not require user authentication. The status of an
// experiment for a specific client can be determined based on the user agent
// string and/or the client id sent in the HTTPS POST request.
message ExperimentStatusRequest {
// Enumerates the experiments for which the status should be returned. An
// experiment name must be the same as one of the field names specified in
// ExperimentsSpecifics. See sync/protocol/experiments_specifics.proto.
repeated string experiment_name = 1;
}
// Response to an experiment status request.
message ExperimentStatusResponse {
// Minimal time to wait before issuing another request.
optional int32 poll_interval_seconds = 1 [default = 3600];
// The experiments that the client has asked for, with each experiment
// containing exactly one experiment flag. The client can inspect the
// embedded flag to obtain the experiment status. Note that the number of
// experiments should be less than or equal to the number of experiment_name
// sent in the request since it is possible that there is no experiment
// matching an experiment_name.
repeated ExperimentsSpecifics experiment = 2;
}
@@ -0,0 +1,88 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This was a sync protocol datatype extension for experimental feature flags,
// also exposed via a separate ExperimentStatus API. As of M75, the datatype
// isn't used anymore, and as of M81 the ExperimentStatus API isn't used anymore
// either (see crbug.com/939819 and crbug.com/1009361). The proto definition
// needs to stay around for now so that the server can continue supporting these
// old clients .
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// A flag to enable support for keystore encryption.
message KeystoreEncryptionFlags {
optional bool enabled = 1;
}
// Whether history delete directives are enabled.
message HistoryDeleteDirectives {
optional bool enabled = 1;
}
// Whether this client should cull (delete) expired autofill
// entries when autofill sync is enabled.
message AutofillCullingFlags {
optional bool enabled = 1;
}
// Whether the favicon sync datatypes are enabled, and what parameters
// they should operate under.
message FaviconSyncFlags {
optional bool enabled = 1;
optional int32 favicon_sync_limit = 2 [default = 200];
}
// Flags for enabling the experimental no-precommit GU feature.
message PreCommitUpdateAvoidanceFlags {
optional bool enabled = 1;
}
// Flags for enabling the GCM feature.
message GcmChannelFlags {
optional bool enabled = 1;
}
// Flags for enabling the experimental enhanced bookmarks feature.
message EnhancedBookmarksFlags {
optional bool enabled = 1;
optional string extension_id = 2;
}
// Flags for enabling GCM channel for invalidations.
message GcmInvalidationsFlags {
optional bool enabled = 1;
}
// Flags for enabling wallet data syncing.
message WalletSyncFlags {
optional bool enabled = 1;
}
// Contains one flag or set of related flags. Each node of the experiments type
// will have a unique_client_tag identifying which flags it contains. By
// convention, the tag name should match the sub-message name.
message ExperimentsSpecifics {
optional KeystoreEncryptionFlags keystore_encryption = 1;
optional HistoryDeleteDirectives history_delete_directives = 2;
optional AutofillCullingFlags autofill_culling = 3;
optional FaviconSyncFlags favicon_sync = 4;
optional PreCommitUpdateAvoidanceFlags pre_commit_update_avoidance = 5;
optional GcmChannelFlags gcm_channel = 6;
// No longer used as of M43.
optional EnhancedBookmarksFlags obsolete_enhanced_bookmarks = 7;
// No longer used as of M72.
optional GcmInvalidationsFlags gcm_invalidations = 8;
// No longer used as of M51.
optional WalletSyncFlags obsolete_wallet_sync = 9;
}
@@ -0,0 +1,31 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for an extension setting.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of extension setting sync objects.
message ExtensionSettingSpecifics {
// Id of the extension the setting is for.
optional string extension_id = 1;
// Setting key.
optional string key = 2;
// Setting value serialized as JSON.
optional string value = 3;
}
@@ -0,0 +1,58 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for extensions.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of extension sync objects.
//
// Merge policy: the settings for the higher version number win; in
// the case of a tie, server wins.
message ExtensionSpecifics {
// Globally unique id for this extension.
optional string id = 1;
// The known installed version.
optional string version = 2;
// Auto-update URL to use for this extension. May be blank, in
// which case the default one (i.e., the one for the Chrome
// Extensions Gallery) is used.
optional string update_url = 3;
// Whether or not this extension is enabled.
optional bool enabled = 4;
// Whether or not this extension is enabled in incognito mode.
optional bool incognito_enabled = 5;
// DEPRECATED. See https://crbug.com/1233303.
optional string name = 6 [deprecated = true];
// Whether this extension was installed remotely, and hasn't been approved by
// a user in chrome yet.
optional bool remote_install = 7;
// DEPRECATED. See https://crbug.com/1014183.
optional bool installed_by_custodian = 8 [deprecated = true];
// DEPRECATED. See https://crbug.com/839681.
optional bool all_urls_enabled = 9 [deprecated = true];
// Bitmask of the set of reasons why the extension is disabled (see
// extensions::disable_reason::DisableReason). Only relevant when enabled ==
// false. Note that old clients (<M45) won't set this, even when enabled is
// false.
optional int32 disable_reasons = 10;
}
@@ -0,0 +1,49 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DEPRECATED: This data type is no longer synced.
// TODO(crbug.com/978775): Remove once the file is no longer used by the server.
// Sync protocol datatype extension for the favicon image specifics.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message FaviconData {
// The image data for the favicon (PNG encoded).
optional bytes favicon = 1;
// The favicon dimensions.
optional int32 width = 2;
optional int32 height = 3;
}
// Image data for favicons. Multiple resolutions are supported.
message FaviconImageSpecifics {
// The url of the favicon image.
optional string favicon_url = 1;
// The favicons associated with this source, one per scale type.
// Favicons images should be stored into the field that best corresponds to
// their scale. If multiple favicons are appropriate, the highest resolution
// that doesn't violate the maximum should be stored.
// A normal low-resolution web favicon (max resolution 16x16).
optional FaviconData favicon_web = 2;
// A slightly higher-resolution web favicon (max resolution 32x32).
optional FaviconData favicon_web_32 = 3;
// A high-resolution touch favicon (not precomposed, max resolution 64x64).
optional FaviconData favicon_touch_64 = 4;
// A high-resolution touch favicon (precomposed, max resolution 64x64).
optional FaviconData favicon_touch_precomposed_64 = 5;
}
@@ -0,0 +1,33 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DEPRECATED: This data type is no longer synced.
// TODO(crbug.com/978775): Remove once the file is no longer used by the server.
// Sync protocol datatype extension for the favicon tracking type.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Tracking info for of favicon images. These control expiration of images
// from sync based on recency, bookmark state, etc.
message FaviconTrackingSpecifics {
// The url of the favicon image.
optional string favicon_url = 1;
// The last time a page using this favicon was visited (in milliseconds
// since linux epoch).
optional int64 last_visit_time_ms = 3;
// Whether this favicon is currently bookmarked or not.
optional bool is_bookmarked = 4;
}
@@ -0,0 +1,106 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Security Events used for recording security related events.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// User reused their GAIA password on another website.
message GaiaPasswordReuse {
// Logged when we detect a password re-use event on a non-GAIA site.
// If the user hasn’t enabled SafeBrowsing, this will be the last event.
message PasswordReuseDetected {
message SafeBrowsingStatus {
// Is SafeBrowsing enabled?
optional bool enabled = 1;
// If SafeBrowsing is enabled, is the user opted-in to extended
// reporting or Scout?
enum ReportingPopulation {
REPORTING_POPULATION_UNSPECIFIED = 0;
NONE = 1;
EXTENDED_REPORTING = 2;
SCOUT = 3;
}
optional ReportingPopulation safe_browsing_reporting_population = 2;
}
optional SafeBrowsingStatus status = 1;
}
optional PasswordReuseDetected reuse_detected = 1;
message PasswordReuseLookup {
enum LookupResult {
UNSPECIFIED = 0;
// URL did match the password reuse allowlist.
// No further action required related to this re-use event.
ALLOWLIST_HIT = 1;
// The URL exists in the client’s cache.
// No further action required related to this re-use event.
// This event also logs the ReputationVerdict.
CACHE_HIT = 2;
// A valid response received from the SafeBrowsing service.
// This event also logs the ReputationVerdict.
REQUEST_SUCCESS = 3;
// Unable to get a valid response from the SafeBrowsing service.
REQUEST_FAILURE = 4;
// We won't be able to compute reputation for the URL e.g. local IP
// address, localhost, not-yet-assigned by ICANN gTLD, etc.
URL_UNSUPPORTED = 5;
// URL did match enterprise allowlist.
// No further action required related to this re-use event.
ENTERPRISE_ALLOWLIST_HIT = 6;
// Password reuse lookup is turned off by enterprise policy.
// No further action required related to this re-use event.
TURNED_OFF_BY_POLICY = 7;
}
optional LookupResult lookup_result = 1;
// The following two are only present for CACHE_HIT and REQUEST_SUCCESS.
// The verdict received from the Reputation service. This is set only
// if the user has SafeBrowsing enabled and we fetch the verdict from the
// cache or by sending a verdict request.
enum ReputationVerdict {
VERDICT_UNSPECIFIED = 0;
SAFE = 1;
LOW_REPUTATION = 2;
PHISHING = 3;
}
optional ReputationVerdict verdict = 2;
// PhishGuard token that identifies the verdict on the server.
optional bytes verdict_token = 3;
}
// Logged when we try to detect whether the password was reused on a
// Phishing or a Low-reputation site.
optional PasswordReuseLookup reuse_lookup = 2;
// Logged when the user interacts with the warning UI shown to encourage
// password change if the site is Phishing or Low-reputation.
message PasswordReuseDialogInteraction {
enum InteractionResult {
UNSPECIFIED = 0;
// The user took the action suggested by the warning prompt.
WARNING_ACTION_TAKEN = 1;
// The user clicked ignore in the warning prompt.
WARNING_ACTION_IGNORED = 2;
// The warning UI was ignored, i.e. not interacted with by the user.
// This could happen if the user navigates away from the page.
WARNING_UI_IGNORED = 3;
// The user clicked "Change Password" on chrome://settings page.
WARNING_ACTION_TAKEN_ON_SETTINGS = 4;
}
optional InteractionResult interaction_result = 1;
}
optional PasswordReuseDialogInteraction dialog_interaction = 3;
}
@@ -0,0 +1,27 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message GetUpdatesCallerInfo {
reserved 1;
reserved "source";
// True if notifications were enabled for this GetUpdateMessage.
// Note: Ideally this would be moved out of GetUpdatesCallerInfo, so that
// GetUpdatesCallerInfo itself can be removed.
optional bool notifications_enabled = 2;
}
@@ -0,0 +1,77 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for history delete directives.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// All timestamps below are from Sane Time (
// http://www.chromium.org/developers/design-documents/sane-time )
// and are in microseconds since the Unix epoch.
// Properties of history delete directive sync objects.
message HistoryDeleteDirectiveSpecifics {
// Exactly one of the fields below must be filled in. Otherwise, this
// delete directive must be ignored.
optional GlobalIdDirective global_id_directive = 1;
optional TimeRangeDirective time_range_directive = 2;
optional UrlDirective url_directive = 3;
}
message GlobalIdDirective {
// The global IDs of the navigations to delete.
repeated int64 global_id = 1;
// Time range for searching for navigations to delete. Client should delete
// all navigations to a URL between [start_time_usec, end_time_usec]
// if one of them matches a |global_id|.
optional int64 start_time_usec = 2;
optional int64 end_time_usec = 3;
}
message TimeRangeDirective {
// Both fields below must be filled in. Otherwise, this delete directive
// must be ignored.
// The time on or after which entries must be deleted.
optional int64 start_time_usec = 1;
// The time on or before which entries must be deleted.
optional int64 end_time_usec = 2;
// Optional field to filter the entries with the matching app ID.
// This field is set only by Android which has app-specific history feature
// that allows users to generate/delete history entries from Custom Tabs on
// app, not from BrApp.
optional string app_id = 3;
}
message UrlDirective {
// Both fields below must be filled in. Otherwise, this delete directive
// must be ignored.
// The URL that should be removed from history.
optional string url = 1;
// The time on or before which entries must be deleted.
// In microseconds since the Unix epoch.
optional int64 end_time_usec = 2;
// Optional field to filter the entries with the matching app ID.
// This field is set only by Android which has app-specific history feature
// that allows users to generate/delete history entries from Custom Tabs on
// app, not from BrApp.
optional string app_id = 3;
}
@@ -0,0 +1,154 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for history entries.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "sync_enums.proto";
// A history sync entity - this roughly represents one navigation, including its
// full redirect chain (but not referrals). Fields correspond to similarly named
// fields in history::VisitRow and history::URLRow.
message HistorySpecifics {
// Timestamp, also called "global ID" in some places (e.g. in delete
// directives). This uniquely identifies the entity.
optional int64 visit_time_windows_epoch_micros = 1;
// Cache GUID of the client that produced this entity, aka the "originator".
optional string originator_cache_guid = 2;
message RedirectEntry {
// ID of the visit on the originator client, unique within that client.
optional int64 originator_visit_id = 1;
// URL that was visited.
optional string url = 2;
// Title of the page corresponding to this URL.
optional string title = 3;
// True if the URL should NOT be used for auto-complete.
optional bool hidden = 4;
// The redirect type (if any).
optional SyncEnums.PageTransitionRedirectType redirect_type = 5;
}
// The redirect chain. The first entry is the URL the user originally
// navigated to; the last one is where they ended up. If there were no
// redirects, this has only one entry.
repeated RedirectEntry redirect_entries = 3;
// Whether the redirect chain in this entity is the continuation of a prior
// chain, and whether a continuation of this chain exists, in other entities.
// These are effectively the inverse of the CHAIN_START/CHAIN_END page
// transition qualifiers in Chrome. They are typically both false, since the
// whole chain is included in `redirect_entries`, but in some cases (notably,
// client redirects) a redirect chain may be split up across multiple
// entities.
optional bool redirect_chain_start_incomplete = 19;
optional bool redirect_chain_end_incomplete = 20;
// If this is set to true, then the redirect chain was too long and some
// entries were trimmed from the middle, so `redirect_entries` only
// corresponds to some of the first plus some of the last entries of the
// actual redirect chain.
optional bool redirect_chain_middle_trimmed = 21;
message PageTransition {
// The core transition type.
optional SyncEnums.PageTransition core_transition = 1 [default = LINK];
// Qualifiers:
// A supervised user tried to access this URL but was blocked.
optional bool blocked = 2;
// User used the Forward or Back button to navigate among browsing history.
optional bool forward_back = 3;
// User used the address bar to trigger this navigation.
optional bool from_address_bar = 4;
// User is navigating to the home page.
optional bool home_page = 5;
}
// The PageTransition for the navigation.
optional PageTransition page_transition = 4;
// The ID of the visit, on the originator client, that was a referrer for
// this one, or 0 if no referrer exists.
optional int64 originator_referring_visit_id = 5;
// The ID of the visit, on the originator client, that opened this one, or 0
// if no opener exists.
optional int64 originator_opener_visit_id = 6;
// The ID of the cluster that this visit belongs to on the originator client,
// or 0 if the originator client does not support setting this field or the
// visit does not belong to any cluster.
optional int64 originator_cluster_id = 22;
// The duration of the visit, in microseconds. Zero if the visit hasn't been
// finished (or the duration wasn't updated for some other reason).
optional int64 visit_duration_micros = 7;
// The kind of browser where this visit happened.
optional SyncEnums.BrowserType browser_type = 8;
// The SessionID of the window and tab where the visit happened. Can be used
// to link together possibly-related visits.
// Note: These are only unique per-client, so always use together with
// `originator_cache_guid`.
optional int32 window_id = 9;
optional int32 tab_id = 10;
// The ID for the task associated with this navigation, which is globally
// unique with high probability.
optional int64 task_id = 11;
// The IDs of ancestor tasks. These can be used to construct a tree of related
// visits.
optional int64 root_task_id = 12;
optional int64 parent_task_id = 13;
// The HTTP response code of the navigation.
optional int32 http_response_code = 14;
// The language of the content on the page, as an ISO 639 language code
// (usually two letters). May be "und" if the language couldn't be determined.
optional string page_language = 15;
// Whether a password field was found on the page.
optional SyncEnums.PasswordState password_state = 16;
// URL of the favicon for this page.
optional string favicon_url = 17;
// The URL of the referrer (indicated by `originator_referring_visit_id`), if
// any.
optional string referrer_url = 18;
// Visit has an image derived from public version of the page. This can be
// used to provide more visual experience.
optional bool has_url_keyed_image = 23;
message Category {
// ID of the category, this typically is an MID.
optional string id = 1;
// Weight of the category, this is an integer value informing the confidence
// of a visit belonging to this category.
optional int32 weight = 2;
}
// Categories tagged for the visit.
repeated Category categories = 24;
// Related search URLs for a Google SRP visit, to provide next steps to user.
repeated string related_searches = 25;
// ID of the app (non-BrApp) this entity was generated for, if any. Only
// visits originating from Android devices may have this set.
optional string app_id = 26;
}
@@ -0,0 +1,25 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message HistoryStatusRequest {}
// Response to a history status request.
message HistoryStatusResponse {
// Minimal time to wait before issuing another request.
optional int32 min_poll_interval_seconds = 1 [default = 3600];
// Indicates whether the history corpuses have any derived data for a user.
optional bool has_derived_data = 2;
}
@@ -0,0 +1,50 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol for persisting the loopback server state to disk.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "sync_entity.proto";
// Serialization of the LoopbackServerEntity and its ancestors.
message LoopbackServerEntity {
// Entity type mapping to one of the subclasses of LoopbackServerEntity.
enum Type {
UNKNOWN = 0;
BOOKMARK = 1;
PERMANENT = 2;
TOMBSTONE = 3;
UNIQUE = 4;
}
optional Type type = 1;
optional SyncEntity entity = 2;
optional int64 model_type = 3;
}
// Contains the loopback server state.
message LoopbackServerProto {
// The protocol buffer format version.
optional int64 version = 1;
optional int64 store_birthday = 2;
repeated LoopbackServerEntity entities = 3;
// All Keystore keys known to the server.
repeated bytes keystore_keys = 4;
// The last entity ID that was assigned to an entity.
optional int64 last_version_assigned = 5;
}
@@ -0,0 +1,25 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for managed user settings.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of managed user setting sync objects.
message ManagedUserSettingSpecifics {
optional string name = 1;
optional string value = 2;
}
@@ -0,0 +1,33 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for managed user shared settings.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of managed user shared setting sync objects.
message ManagedUserSharedSettingSpecifics {
// The MU ID for the managed user to whom the setting applies.
optional string mu_id = 1;
// The key of the setting.
optional string key = 2;
// The setting value. The setting is a JSON encoding of an arbitrary
// Javascript value.
optional string value = 3;
// This flag is set by the server to acknowledge that it has committed a
// change to a setting.
optional bool acknowledged = 4 [default = false];
}
@@ -0,0 +1,99 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "data_type_progress_marker.proto";
// Sync proto to store data type global metadata in model type storage.
message ModelTypeState {
enum InitialSyncState {
// Default value, typically used when there is no metadata (e.g. because
// Sync is disabled).
INITIAL_SYNC_STATE_UNSPECIFIED = 0;
// Note: Value "1" is reserved, in case we ever want to introduce an
// INITIAL_SYNC_STARTED state.
// Indicates that syncing has started and some, but not all updates from the
// initial download/sync have been delivered. This is only used for data
// types in ApplyUpdatesImmediatelyTypes().
INITIAL_SYNC_PARTIALLY_DONE = 2;
// Indicates that the initial sync (download + merge) has been completed.
INITIAL_SYNC_DONE = 3;
// Indicates that no initial sync is necessary, used for CommitOnlyTypes().
INITIAL_SYNC_UNNECESSARY = 4;
}
// The latest progress markers received from the server.
optional DataTypeProgressMarker progress_marker = 1;
// A data type context. Sent to the server in every commit or update
// request. May be updated by either responses from the server or requests
// made on the model thread. The interpretation of this value may be
// data-type specific. Many data types ignore it.
optional DataTypeContext type_context = 2;
// This value is set if this type's data should be encrypted on the server.
// If this key changes, the client will need to re-commit all of its local
// data to the server using the new encryption key.
optional string encryption_key_name = 3;
// Deprecated in M113 and replaced by `initial_sync_state`.
// As of M115, this is not populated anymore, but existing values are still
// migrated to `initial_sync_state`.
// TODO(crbug.com/1423338): Fully remove this field after a migration period.
optional bool initial_sync_done_deprecated = 4 [deprecated = true];
// Indicates the status of "initial sync", i.e. whether the first download
// cycle and initial merge are complete.
optional InitialSyncState initial_sync_state = 9;
// A GUID that identifies the committing sync client. It's persisted within
// the sync metadata and should be used to check the integrity of the
// metadata. Mismatches with the guid of the running client indicates invalid
// persisted sync metadata, because cache_guid is reset when sync is disabled,
// and disabling sync is supposed to clear sync metadata.
optional string cache_guid = 5;
// Syncing account ID, representing the user.
optional string authenticated_account_id = 6;
message Invalidation {
// Opaque field, which has to be provided as part of resulting GetUpdates
// back to the server.
optional bytes hint = 1;
// Version of invalidation, used to order incoming invalidations.
optional int64 version = 2;
}
// The latest unprocessed invalidations received from the server.
// All incoming invalidations are stored in this message and persist until
// they are used in GetUpdate() message.
repeated Invalidation invalidations = 7;
// This is relevant for the passwords datatype. This indicates that the
// initial sync flow (downling all passwords from the server) has been run at
// least once after the password notes features is enabled. It is used to
// enforce redownload of passwords upon upgrading the browser to a version
// that supports password notes if necessary. It is false by default and set
// to true upon downloading passwords to make sure this download is executed
// only once.
optional bool notes_enabled_before_initial_sync_for_passwords = 8;
}
@@ -0,0 +1,24 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Protocol messages used to record the state of the model type store for USS.
// At the time of writing, the model type store uses leveldb, a schemaless
// key-value store. This means that the database's schema is mostly implicit.
// This descriptor isn't intended to fully describe the schema, just keep track
// of which major changes have been applied.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message ModelTypeStoreSchemaDescriptor {
optional int64 version_number = 1;
}
@@ -0,0 +1,133 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "encryption.proto";
import "entity_metadata.proto";
import "model_type_state.proto";
import "nigori_specifics.proto";
message CrossUserSharingKeys {
// Asymmetric private-keys used for cross user sharing.
repeated CrossUserSharingPrivateKey private_key = 1;
}
message LocalNigoriKeyBag {
// Used for encrypting within the sync account boundary.
repeated NigoriKey key = 2;
}
message CryptographerData {
// Contains all known Nigori keys.
optional LocalNigoriKeyBag key_bag = 1;
// Default key is the key, that should be used for encryption. Can be empty
// in case we have pending keys (waiting for explicit passphrase, or client
// didn't received keystore keys).
optional string default_key_name = 2;
// Contains cross user sharing related keys.
optional CrossUserSharingKeys cross_user_sharing_keys = 3;
}
message CustomPassphraseKeyDerivationParams {
// Method used for deriving keys from custom passphrase.
optional NigoriSpecifics.KeyDerivationMethod
custom_passphrase_key_derivation_method = 1;
// Salt used for the derivation of the key from the custom passphrase. Should
// be set iff custom_passphrase_key_derivation_method == SCRYPT_8192_8_11.
optional string custom_passphrase_key_derivation_salt = 2;
}
message NigoriModel {
// Represents state of the cryptographer.
optional CryptographerData cryptographer_data = 1;
// Contains the name of the key, used for encryption of
// NigoriSpecifics.keystore_decryptor_token, should always be the name of the
// latest keystore key. Equals to cryptographer_data.default_key_name iff it
// is Nigori in full keystore mode (not in backward compatible keystore
// mode).
optional string current_keystore_key_name = 2;
// Contains keybag in encrypted form, should be empty once we were able to
// decrypt keybag from specifics.
optional EncryptedData pending_keys = 3;
// PassphraseType used for encryption. IMPLICIT_PASSPRHASE shouldn't be used
// here.
optional NigoriSpecifics.PassphraseType passphrase_type = 4
[default = UNKNOWN];
// The time (in UNIX epoch milliseconds) at which the keystore migration was
// performed.
optional int64 keystore_migration_time = 5;
// The time (in UNIX epoch milliseconds) at which a custom passphrase was
// set.
// Note: this field may not be set if the custom passphrase was applied before
// corresponding field in NigoriSpecifics was introduced.
optional int64 custom_passphrase_time = 6;
// Params used for deriving keys from custom passphrase. Should be set iff
// |passphrase_type| is CUSTOM_PASSPHRASE.
optional CustomPassphraseKeyDerivationParams
custom_passphrase_key_derivation_params = 7;
// Indicates whether we need to encrypt all encryptable user types.
optional bool encrypt_everything = 8;
// The list of encrypted UserEncryptableTypes, represented by their specifics
// field number.
repeated int32 encrypted_types_specifics_field_number = 9;
// Keystore keys are used to decrypt keystore-based Nigori. Should be
// persisted in order to not ask the keystore server for them during every
// browser startup. Due to backward compatibility requirements keys are
// always Base64 encoded.
repeated string keystore_key = 10;
// Encryptor keystore decryptor token. Used for decryption of keystore Nigori
// in case keystore keys arrived after NigoriSpecifics.
optional EncryptedData pending_keystore_decryptor_token = 11;
// Contains the name of the latest available trusted vault key that was used
// as the default encryption key. Resets when the client go out of pending
// decryption state and transits to other passphrase types.
optional string last_default_trusted_vault_key_name = 12;
// Some debug-only fields for passphrase type TRUSTED_VAULT_PASSPHRASE.
optional NigoriSpecifics.TrustedVaultDebugInfo trusted_vault_debug_info = 13;
// Current Public-key.
optional CrossUserSharingPublicKey cross_user_sharing_public_key = 14;
}
// Sync proto to store Nigori data in storage. Proto should be encrypted with
// os_crypt before storing it somewhere, because it contains sensitive data (
// nigori_model.cryptographer_data.key_bag and nigori_model.keystore_keys).
message NigoriLocalData {
// Global metadata.
optional ModelTypeState model_type_state = 1;
// Metadata for Nigori entity.
optional EntityMetadata entity_metadata = 2;
// Nigori model state.
optional NigoriModel nigori_model = 3;
}
@@ -0,0 +1,295 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for nigori keys.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "encryption.proto";
message NigoriKey {
// Note that M78 and before rely on the name being populated, at least for
// the main encrypted keybag within NigoriSpecifics.
optional string deprecated_name = 1 [deprecated = true];
optional bytes deprecated_user_key = 2 [deprecated = true];
optional bytes encryption_key = 3;
optional bytes mac_key = 4;
}
message CrossUserSharingPrivateKey {
// The key version, used for key rotation.
optional int32 version = 1;
// Private key on Curve25519 (32 bytes).
optional bytes x25519_private_key = 2;
}
message CrossUserSharingPublicKey {
// The key version, used for key rotation and consensus.
optional int32 version = 1;
// Public key on Curve25519 (32 bytes).
optional bytes x25519_public_key = 2;
}
message EncryptionKeys {
// Used for encrypting data in the user's account.
repeated NigoriKey key = 2;
// Used for encrypting/authenticating using Public-private cryptography
// primarily across user accounts, e.g. sharing invitations.
repeated CrossUserSharingPrivateKey cross_user_sharing_private_key = 3;
}
// Deprecated: use EncryptionKeys instead.
message NigoriKeyBag {
option deprecated = true;
// Used for encrypting data in the user's account.
repeated NigoriKey key = 2;
}
// Properties of nigori sync object.
message NigoriSpecifics {
// Encrypted proto of type EncryptionKeys.
optional EncryptedData encryption_keybag = 1;
// Once keystore migration is performed, we have to freeze the keybag so that
// older clients (that don't support keystore encryption) do not attempt to
// update the keybag.
// Previously |using_explicit_passphrase|.
optional bool keybag_is_frozen = 2;
// Obsolete encryption fields. These were deprecated due to legacy versions
// that understand their usage but did not perform encryption properly.
reserved 3;
reserved "deprecated_encrypt_bookmarks";
reserved 4;
reserved "deprecated_encrypt_preferences";
reserved 5;
reserved "deprecated_encrypt_autofill_profile";
reserved 6;
reserved "deprecated_encrypt_autofill";
reserved 7;
reserved "deprecated_encrypt_themes";
reserved 8;
reserved "deprecated_encrypt_typed_urls";
reserved 9;
reserved "deprecated_encrypt_extensions";
reserved 10;
reserved "deprecated_encrypt_sessions";
reserved 11;
reserved "deprecated_encrypt_apps";
reserved 12;
reserved "deprecated_encrypt_search_engines";
// Booleans corresponding to whether a datatype should be encrypted.
// Passwords and Wi-Fi configurations are always encrypted, so we don't need
// a field here. History delete directives need to be consumable by the
// server, and thus can't be encrypted.
// USS clients don't read these values, datatypes are either always encrypted,
// never encrypted or encrypted when |encrypt_everything| set to true.
// TODO(crbug.com/1360022): complete deprecation of encrypt_* fields (replace
// fields with reserved) once <M82 clients aren't supported.
optional bool encrypt_bookmarks = 13 [deprecated = true];
optional bool encrypt_preferences = 14 [deprecated = true];
optional bool encrypt_autofill_profile = 15 [deprecated = true];
optional bool encrypt_autofill = 16 [deprecated = true];
optional bool encrypt_themes = 17 [deprecated = true];
optional bool encrypt_typed_urls = 18 [deprecated = true];
optional bool encrypt_extensions = 19 [deprecated = true];
optional bool encrypt_sessions = 20 [deprecated = true];
optional bool encrypt_apps = 21 [deprecated = true];
optional bool encrypt_search_engines = 22 [deprecated = true];
// Deprecated on clients where tab sync is enabled by default.
reserved 23;
reserved "sync_tabs";
// If true, all current and future datatypes will be encrypted.
optional bool encrypt_everything = 24;
optional bool encrypt_extension_settings = 25 [deprecated = true];
optional bool encrypt_app_notifications = 26 [deprecated = true];
optional bool encrypt_app_settings = 27 [deprecated = true];
// User device information. Contains information about each device that has a
// sync-enabled Chrome browser connected to the user account.
// This has been moved to the DeviceInfo message.
reserved 28;
reserved "deprecated_device_information";
// Enable syncing favicons as part of tab sync.
optional bool sync_tab_favicons = 29;
// The state of the passphrase required to decrypt |encryption_keybag|.
enum PassphraseType {
// Proto enums should begin with an 'unknown' value for switch statements,
// unknown fields, etc.
UNKNOWN = 0;
// Gaia-based encryption passphrase. Legacy.
IMPLICIT_PASSPHRASE = 1;
// Keystore key encryption passphrase. Uses |keystore_bootstrap| to
// decrypt |encryption_keybag|.
KEYSTORE_PASSPHRASE = 2;
// Previous Gaia-based passphrase frozen and treated as a custom passphrase.
FROZEN_IMPLICIT_PASSPHRASE = 3;
// User provided custom passphrase.
CUSTOM_PASSPHRASE = 4;
// Encryption key provided by a trusted vault.
TRUSTED_VAULT_PASSPHRASE = 5;
}
// An |int| field is used instead of enum PassphraseType so we can better
// handle unknown values coming from later versions. Prior to M77, this was an
// enum so old clients will assume IMPLICIT_PASSPHRASE for values greater than
// 4.
optional int32 passphrase_type = 30 [default = 1];
// The keystore decryptor token blob. Encrypted with the keystore key, and
// contains the encryption key used to decrypt |encryption_keybag|.
// Only set if passphrase_state == KEYSTORE_PASSPHRASE.
optional EncryptedData keystore_decryptor_token = 31;
// The time (in milliseconds since Unix epoch) at which the keystore migration
// was performed.
optional int64 keystore_migration_time = 32;
// The time (in milliseconds since Unix epoch) at which a custom passphrase
// was set. Note: this field may not be set if the custom passphrase was
// applied before this field was introduced.
optional int64 custom_passphrase_time = 33;
// Boolean corresponding to whether custom spelling dictionary should be
// encrypted.
optional bool encrypt_dictionary = 34;
reserved 35;
reserved 36;
reserved 37;
reserved "encrypt_articles";
// Boolean corresponding to whether app list items should be encrypted.
optional bool encrypt_app_list = 38 [deprecated = true];
// Boolean corresponding to whether usage count and last use date of Wallet
// data should be encrypted.
optional bool encrypt_autofill_wallet_metadata = 39 [deprecated = true];
// Boolean indicating whether this node was originally missing a
// |keystore_migration_time| field value, and was fixed on the server by
// giving the field a value.
// THIS FIELD SHOULD ONLY BE SET BY THE SERVER.
optional bool server_only_was_missing_keystore_migration_time = 40;
// Boolean corresponding to whether arc pakcage items should be encrypted.
optional bool encrypt_arc_package = 41 [deprecated = true];
// Boolean corresponding to whether printer items should be encrypted.
optional bool encrypt_printers = 42 [deprecated = true];
// Boolean corresponding to whether reading list items should be encrypted.
optional bool encrypt_reading_list = 43 [deprecated = true];
reserved 44;
reserved "encrypt_mountain_shares";
enum KeyDerivationMethod {
// This comes from a <= M69 client, who does not know about the field
// (but implicitly uses PBKDF2_HMAC_SHA1_1003).
UNSPECIFIED = 0;
// PBKDF2-HMAC-SHA1 with 1003 iterations and constant hardcoded salt. Was
// implicitly used in <= M69.
PBKDF2_HMAC_SHA1_1003 = 1;
// scrypt with N = 2^13, r = 8, p = 11 and random salt. Was added in M70.
SCRYPT_8192_8_11 = 2;
}
// ID of the method used to derive the encryption key from a custom
// passphrase. Should be set only when |passphrase_type| is CUSTOM_PASSPHRASE
// and only based on CustomPassphraseKeyDerivationMethod. This field has been
// added in M70. All previous versions just ignore it, attempt to use
// PBKDF2_HMAC_SHA1_1003 and, thus, reject any passphrase if a different
// method has been used. The default corresponds to UNSPECIFIED. An |int|
// field is used so we can detect unknown values coming from later versions.
optional int32 custom_passphrase_key_derivation_method = 45 [default = 0];
// Base64-encoded salt used for the derivation of the key from the custom
// passphrase. Valid only if custom_passphrase_key_derivation_method ==
// SCRYPT_8192_8_11, ignored in other cases.
optional string custom_passphrase_key_derivation_salt = 46;
// Boolean corresponding to whether send tab should be encrypted.
optional bool encrypt_send_tab_to_self = 47 [deprecated = true];
// Boolean corresponding to whether Web Apps data should be encrypted.
optional bool encrypt_web_apps = 48 [deprecated = true];
// Boolean corresponding to whether OS preferences should be encrypted.
optional bool encrypt_os_preferences = 49 [deprecated = true];
// Information useful for auto-upgrade to trusted vault.
message AutoUpgradeDebugInfo {
enum AutoUpgradeState {
STATE_UNSPECIFIED = 0;
AUTO_UPGRADED = 1;
CONTROL = 2;
VALIDATION = 3;
}
// Current state of an account after it was processed by auto-upgrade.
optional AutoUpgradeState auto_upgrade_state = 1;
// Each eligibility group can be ramped in different stages (e.g. 1%,
// 10%, 100%).
// Every ramp stage is differentiated with its own entry in the enum.
enum EligibilityGroup {
GROUP_UNSPECIFIED = 0;
TEST_GROUP_1 = 1;
}
// ID of eligibility group account belongs to. Only use IDs defined
// within EligibilityGroup enum.
optional int32 eligibility_group_id = 2;
}
// Some debug-only fields for passphrase type TRUSTED_VAULT_PASSPHRASE.
message TrustedVaultDebugInfo {
// The time (in milliseconds since Unix epoch) at which the trusted vault
// migration was performed.
optional int64 migration_time = 1;
// The version (aka key epoch) corresponding to the Nigori keybag.
optional int32 key_version = 2;
// DebugInfo written by a process that auto-upgrades to trusted vault.
optional AutoUpgradeDebugInfo auto_upgrade_debug_info = 3;
// The time (in milliseconds since Unix epoch) last update of the auto
// upgrade debug info was made.
optional int64
auto_upgrade_debug_info_modification_time_unix_epoch_millis = 4;
}
optional TrustedVaultDebugInfo trusted_vault_debug_info = 50;
reserved 51;
reserved "encrypt_workspace_desk";
reserved 52;
reserved "encrypt_printers_authorization_servers";
// Current cross user sharing Public-key to expose in server registry
// for sharing offers outside account boundary.
optional CrossUserSharingPublicKey cross_user_sharing_public_key = 53;
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message NoteEntity {
enum TargetType {
TARGET_TYPE_UNSPECIFIED = 0;
// Page target type makes the note global to the page. It does not associate
// with any user selection.
TARGET_TYPE_PAGE = 1;
}
// Plain text of the note.
optional string plain_text = 1;
// Rich text of the note. It will be used when plain_text is empty and the UI
// supports rich text.
optional string rich_text = 2;
// The type of the note target. Currently only PAGE target type is supported.
/* required */ optional TargetType target_type = 3;
// The current version of the note. If this version is newer than the note
// version of the browser the note won't be displayed.
/* required */ optional int32 current_note_version = 4;
}
@@ -0,0 +1,28 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for Chrome OS preferences.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "preference_specifics.proto";
// Properties of a synced Chrome OS system preference. Uses PreferenceSpecifics
// instead of declaring name and value fields to allow more code sharing in the
// client (e.g. all PrefModelAssociators can operate on PreferenceSpecifics).
message OsPreferenceSpecifics {
optional PreferenceSpecifics preference = 1;
}
@@ -0,0 +1,28 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for Chrome OS priority preferences.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "preference_specifics.proto";
// Properties of a Chrome OS priority preference. Uses PreferenceSpecifics
// instead of declaring name and value fields to allow more code sharing
// in the client (e.g. all PrefModelAssociators can use PreferenceSpecifics).
message OsPriorityPreferenceSpecifics {
optional PreferenceSpecifics preference = 1;
}
@@ -0,0 +1,134 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "nigori_specifics.proto";
message PasswordSharingInvitationData {
// Contains password fields required for sending. See PasswordSpecificsData
// for field descriptions.
message PasswordGroupElementData {
// See PasswordSpecificsData::Scheme for values.
optional int32 scheme = 2;
optional string signon_realm = 3;
optional string origin = 4;
optional string username_element = 5;
optional string password_element = 7;
optional string display_name = 8;
optional string avatar_url = 9;
}
message PasswordGroupData {
optional string username_value = 1;
optional string password_value = 2;
repeated PasswordGroupElementData element_data = 3;
}
reserved 1;
optional PasswordGroupData password_group_data = 2;
}
// Contains user profile information.
message UserDisplayInfo {
// Primary email address of the user.
optional string email = 1;
// The user's full name.
optional string display_name = 2;
// Portrait photo of the user.
optional string profile_image_url = 3;
}
message UserInfo {
// Obfuscated Gaia ID.
optional string user_id = 1;
optional UserDisplayInfo user_display_info = 2;
// Latest user's public key registered on the server.
optional CrossUserSharingPublicKey cross_user_sharing_public_key = 3;
}
// Incoming invitations for password sending.
message IncomingPasswordSharingInvitationSpecifics {
// Unique client tag for the invitation. This does *not* have to be the same
// GUID as for the outgoing invitation.
optional string guid = 1;
// Profile information about the sender of the password. Sender's public key
// is used to authenticate the sender for `encrypted_key_for_recipient`.
optional UserInfo sender_info = 2;
// Encrypted PasswordSharingInvitationData using recipient's public key
// corresponding to `recipient_key_version` and sender's private key to
// authenticate the sender, see https://www.rfc-editor.org/rfc/rfc9180.html.
optional bytes encrypted_password_sharing_invitation_data = 3;
// An unsynced field for use internally on the client. This field should
// never be set in any network-based communications because it contains
// unencrypted material.
optional PasswordSharingInvitationData client_only_unencrypted_data = 4;
optional uint32 recipient_key_version = 6;
reserved 5;
}
// Outgoing invitations for password sending.
message OutgoingPasswordSharingInvitationSpecifics {
// Unique client tag for the invitation, generated by the client.
optional string guid = 1;
// Recipient's user identifier (obfuscated Gaia ID).
optional string recipient_user_id = 2;
// Encrypted PasswordSharingInvitationData using recipient's public key
// corresponding to `recipient_key_version` and sender's private key to
// authenticate the sender, see https://www.rfc-editor.org/rfc/rfc9180.html.
optional bytes encrypted_password_sharing_invitation_data = 3;
// An unsynced field for use internally on the client. This field should
// never be set in any network-based communications because it contains
// unencrypted material.
optional PasswordSharingInvitationData client_only_unencrypted_data = 4;
optional uint32 recipient_key_version = 6;
// Version of Public key of the sender which is used to authenticate the
// sender of the password. Must be equal to the latest committed version.
optional uint32 sender_key_version = 7;
reserved 5;
}
// Used for the server to return fine-grained commit errors back to the client.
message OutgoingPasswordSharingInvitationCommitError {
// This enum is used in histograms. Entries should not be renumbered and
// numeric values should never be reused. Also remember to update in
// tools/metrics/histograms/enums.xml.
enum ErrorCode {
UNKNOWN = 0;
// Sender's public key version is different on the server.
PUBLIC_KEY_VERSION_MISMATCH = 1;
// The sender and the recipient are not in the same family according to the
// Google family service.
NOT_FAMILY_MEMBER = 2;
}
optional ErrorCode error_code = 1;
}
@@ -0,0 +1,41 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "password_sharing_invitation_specifics.proto";
// A message to obtain a list of recipients for sending a password.
message PasswordSharingRecipientsRequest {
// The request returns family members for the user and that is why no
// additional fields are required.
}
message PasswordSharingRecipientsResponse {
enum PasswordSharingRecipientsResult {
UNKNOWN = 0;
// The user is a member of a family and the request succeeded.
SUCCESS = 1;
// Not a family member, used to distinguish from a family with
// only one member.
NOT_FAMILY_MEMBER = 2;
}
optional PasswordSharingRecipientsResult result = 1;
// List of possible recipients for sending a password. Note that public key
// may be absent if a recipient can’t receive a password (e.g. due to an older
// Chrome version).
repeated UserInfo recipients = 2;
}
@@ -0,0 +1,367 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for password data.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "encryption.proto";
// These are the properties that get serialized into the |encrypted| field of
// PasswordSpecifics. They correspond to fields in
// password_manager::PasswordForm.
//
// Sync unique tag is calculated as
// EscapePath(origin) + "|" +
// EscapePath(username_element) + "|" +
// EscapePath(username_value) + "|" +
// EscapePath(password_element) + "|" +
// EscapePath(signon_realm)
// where '+' is the string concatenation operation. EscapePath escapes a partial
// or complete file/pathname. This includes non-printable, non-7bit, and
// (including space) the following characters ' "#%:<>?[\]^`{|}'. The space
// character is encoded as '%20'.
// A simple example of PasswordSpecificsData, as created by Chrome for a web
// form that the user submitted. E.g. federated credentials or credentials for
// Android apps may have different fields set.
// PasswordSpecificsData {
// action: "https://example.com/",
// avatar_url: "",
// blacklisted: false,
// date_created: 13305119271948046,
// date_last_used: 13305119270102691,
// date_password_modified_windows_epoch_micros: 13305119271949070,
// display_name: "",
// federation_url: "",
// notes: {},
// origin: "https://rsolomakhin.github.io/autofill/",
// password_element: "password",
// password_issues: {},
// password_value: "pwd",
// scheme: 0,
// signon_realm: "https://rsolomakhin.github.io/",
// times_used: 0,
// type: 0,
// username_element: "username",
// username_value: "Superman"
// }
// All the strings are encoded with UTF-8. URLs are encoded in Punycode.
message PasswordIssues {
message PasswordIssue {
// Timestamp set by a client detecting the issue for the first time.
// Number of microseconds since Windows epoch (1601).
// This can be unset even if is_muted is set in a few cases in
// storage (for a time mutes were written without setting this
// field - fixed starting 2021-11-10).
optional uint64 date_first_detection_windows_epoch_micros = 1;
// Whether the issue was muted by user.
optional bool is_muted = 2;
// Whether the backend should notify the user about this issue.
// Set to true if the user hasn't already seen a client notification for
// this issue (e.g. a leak detection prompt in Chrome). The backend sending
// notifications does not reset this field. All other sources can write this
// in both `PasswordSpecificsData` and `PasswordSpecificsMetadata` and do
// so.
optional bool trigger_notification_from_backend_on_detection = 3;
}
optional PasswordIssue leaked_password_issue = 1;
optional PasswordIssue reused_password_issue = 2;
optional PasswordIssue weak_password_issue = 3;
optional PasswordIssue phished_password_issue = 4;
}
message PasswordSpecificsData {
// The different types of the saved credential.
enum Scheme {
// SCHEME_HTML, the credential represents either a parsed HTML form, or an
// android credential or a password saved through Credential Management API
// (https://w3c.github.io/webappsec/specs/credentialmanagement/).
SCHEME_HTML = 0;
// SCHEME_BASIC, basic access http authentication.
SCHEME_BASIC = 1;
// SCHEME_DIGEST, digest access authentication.
SCHEME_DIGEST = 2;
// SCHEME_OTHER, another proxy access authentication.
SCHEME_OTHER = 3;
// USERNAME_ONLY, partial credentials saved on Android that contain only
// username and miss the password.
USERNAME_ONLY = 4;
}
// See the enum above.
optional int32 scheme = 1;
// Signon realm stores information on where the saved password was stored, and
// where it's supposed to be filled again.
//
// It can take various formats depending on the exact circumstances where it
// was recorded. Note that the format is *not* guaranteed to be a valid URL or
// URI:
//
// * For parsed web forms and normal passwords saved through Credential
// Manager
// API: <http-scheme>://<url-host>[:<url-port>]/
//
// where
// <http-scheme> is one of "http" or "https"
// <url-host> is the host for which the password was stored
// <url-port> is the option port on the host
// The signon realm is a valid URL in this case with an empty path.
// Examples:
// http://www.example.com/
// https://127.0.0.1/
// http://www.google.com:8080/
// http://192.168.1.254/
// https://accounts.google.com/
//
// * For Android apps saved through Autofill with Google:
// android://<hash-of-cert>@<package-name>/
// where
// <hash-of-cert> is the base64 encoded SHA512 of the app's public
// certificate <package-name> is the app's package name
// Examples:
// android://kCyQDzpaoAX2gs-1zdGPKNAeICb8LzRFOxa4NCq0jO8c8d_NFS_q-Y35bU3Nq3GmFV2lLurmNvIZa6YPYZwmWg==@com.pinterest/
// android://mNUCvTnoWBkzIhSSkVj-uzAdK42YagmCmyUtPoC6JPmYAN3wKpmTdIRsdJtz6pzNBye8XL7nBbEcx-y9CJeo9A==@com.twitter.android.lite/
//
// * For federated credentials:
// federation://<origin_host>/<federation_host>
// where
// <origin_host> is the host for which the login information was stored
// <federation_host> is the host of the federation provider that was
// used to sign in
// Examples:
// federation://www.example.com/accounts.google.com
// federation://uk.trustpilot.com/www.facebook.com
//
// * For proxy auth:
// <proxy-host>[:<proxy_port>]/<auth-realm>
// where
// <proxy-host> is the host of the proxy for which the password was
// stored
// <proxy-port> is the port of the proxy
// <auth-realm> is a string provided by the proxy during authentication.
// It can contain spaces.
// Examples:
// proxy2.eq.edu.au:80/MISldap
// proxy.det.nsw.edu.au:8080/NSW Department of Education
// 10.47.2.250:3128/Squid Proxy Server CPUT
// default.go2https.com:443/(******Get password from vpnso.com/account/
// *****)
//
// * For HTTP basic auth:
// <http-scheme>://<url-host>[:<url-port>]/<auth-realm>
// where
// <http-scheme> is one of "http" or "https"
// <url-host> is the host for which the password was stored
// <url-port> is the option port on the host
// <auth-realm> is a string provided by the host during authentication.
// It can contain spaces.
// Examples:
// http://192.168.1.1/Broadband Router
// http://192.168.0.1/TP-LINK Wireless N Router WR841N
// http://192.168.1.1/index.htm
// https://www.edge.asic.gov.au/ASIC eBusiness
optional string signon_realm = 2;
// For parsed web forms and Credential Management API:
// url-scheme://url-host[:url-port]/path
// For Android: "android://<hash of cert>@<package name>/"
// For proxy/HTTP auth: url-scheme://url-host[:url-port]/path
optional string origin = 3;
// Only for web-parsed forms - the action target of the form:
// url-scheme://url-host[:url-port]/path
optional string action = 4;
// Only for web-parsed forms - the name of the element containing username.
optional string username_element = 5;
// For all: the username.
// For blacklisted forms: <empty>.
optional string username_value = 6;
// Only for web-parsed forms - the name of the element containing password.
optional string password_element = 7;
// For all: the password.
// For federated logins and blacklisted forms: <empty>
optional string password_value = 8;
// Deprecated: http://crbug.com/413020
// True if the credential was saved for a HTTPS session with a valid SSL cert.
// Ignored for Android apps.
optional bool ssl_valid = 9 [deprecated = true];
// True for the last credential used for logging in on a given site.
// Deprecated in M81.
optional bool preferred = 10 [deprecated = true];
// Time when the credential was created. Amount of microseconds since 1601.
optional int64 date_created = 11;
// True, if user chose permanently not to save the credentials for the form.
optional bool blacklisted = 12;
// kFormSubmission(0), user manually filled the username and the password
// in the form.
// kGenerated(1), the credential was auto generated.
// kApi(2), the credential was generated from Credential Management API.
// kManuallyAdded(3), user manually created the password credential
// via Settings.
// kImported(4), the credential was imported using the import flow.
// kReceivedViaSharing(5), the credential has been received via the password
// sharing feature.
optional int32 type = 13;
// Number of times this login was used for logging in using an HTML form.
// Chrome uses this field to distinguish log-in and sign-up forms.
optional int32 times_used = 14;
// A human readable name of the account holder. Set by CredentialManager API
// and Android.
optional string display_name = 15;
// A URL of the avatar for the credential. Set by CredentialManager API and
// Android.
optional string avatar_url = 16;
// A URL of the IdP used to verify the credential. Set by Credential Manager
// API and Android.
optional string federation_url = 17;
// Time when the credential was last used. This covers *successful* logins to
// the website, and explicit updates to the password. It does *not* cover if
// the password just gets filled but not actually submitted, or if the login
// failed.
// Note that password consumers other than Chrome (e.g. Google Play Services)
// might not update this at all.
// Amount of microseconds since 1601, aka Windows epoch.
optional int64 date_last_used = 18;
// Set if an issue was detected that puts this password at risk. All the
// clients are expected to clear the field when the password value is updated.
// 'reused' part can be additionally reset when the analysis on the entire
// password store is completed.
optional PasswordIssues password_issues = 19;
// Time when the |password_value| was last modified. For new credentials it
// should be set to |date_created|. For subsequent updates the timestamp is
// changed if and only if the new password value was saved.
// Number of microseconds since Windows epoch (1601).
optional int64 date_password_modified_windows_epoch_micros = 20;
message Notes {
message Note {
// The display name must be unique within the scope of a password.
optional string unique_display_name = 1;
// The user-defined value of the note.
optional string value = 2;
// The creation time of the note. Number of microseconds since 1601.
optional int64 date_created_windows_epoch_micros = 3;
// Whether the value of the note is not displayed in plain text by
// default.
optional bool hide_by_default = 4;
}
repeated Note note = 1;
}
reserved 21;
// Set of extra notes that the user attached to the password. The presence of
// this field, even with an empty Notes message, becomes the authoritative
// value for notes and would disregard whatever `encrypted_notes_backup`
// contains.
optional Notes notes = 22;
// For credentials that have been shared by another user, this field captures
// the sender email. It's empty for credentials that weren't received via
// password sharing feature.
optional string sender_email = 23;
// Similar to `sender_email` but for the sender name.
optional string sender_name = 24;
// The timestamp when the password was received via sharing feature from
// another user.
optional int64 date_received_windows_epoch_micros = 25;
// Whether the user has been already notified that they received this password
// from another user via the password sharing feature. This is synced to avoid
// showing the notification on multiple devices.
optional bool sharing_notification_displayed = 26;
// Similar to `sender_email` but for the url of the sender profile image.
optional string sender_profile_image_url = 27;
}
// Contains the password specifics metadata which simplifies its lookup.
message PasswordSpecificsMetadata {
// The signon realm for the credential. For more details, see the
// `signon_realm` field in PasswordSpecificsData.
optional string url = 1;
// True, if user chose permanently not to save the credentials for the form.
// Introduced in M82. Copy from PasswordSpecificsData.blacklisted.
optional bool blacklisted = 2;
// Copy from PasswordSpecificsData.date_last_used.
// Introduced in M112.
optional int64 date_last_used_windows_epoch_micros = 3;
// Copy from PasswordSpecificsData.password_issues. Introduced in M114.
optional PasswordIssues password_issues = 4;
// Copy from PasswordSpecificsData.type. Introduced in M119.
optional int32 type = 13;
}
// Properties of password sync objects.
message PasswordSpecifics {
// The actual password data. Contains an encrypted PasswordSpecificsData
// message.
optional EncryptedData encrypted = 1;
// An unsynced field for use internally on the client. This field should
// never be set in any network-based communications because it contains
// unencrypted material.
optional PasswordSpecificsData client_only_encrypted_data = 2;
// Password related metadata, which is sent to the server side. The field
// should never be set for full encryption users. If encryption is enabled,
// this field must be cleared.
optional PasswordSpecificsMetadata unencrypted_metadata = 3;
reserved 4;
// An encrypted backup of the notes field inside the PasswordSpecificsData.
// The Sync server preserves the contents of this field across commits from
// legacy clients that don't set this field. It is the responsibility of Sync
// clients to populate the contents of PasswordSpecificsData notes fields
// using the contents of this field. This should be deprecated together with
// the logic for preserving it on the server when clients without support for
// the |notes| field are no longer allowed by the server (below support
// version horizon).
//
// Encryption key considerations:
// a) For commits, the client must use the same key for both encrypted blobs.
// b) For handling getupdates, the two keys may NOT necessarily match the
// encryption key used, as in theory the new blob could be "behind" if key
// rotation took place. As of today, it is safe to assume that if
// |encrypted| is decryptable by a client, then |encrypted_notes_backup|
// must be decryptable too (i.e. the Nigori keybag should include older
// versions of the key). But not the other way round.
//
// If both `encrypted_notes_backup` and the `notes` in `encrypted` are
// populated, the one in notes is considered the authoritative value.
optional EncryptedData encrypted_notes_backup = 5;
}
@@ -0,0 +1,28 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "entity_specifics.proto";
// Sync proto to store entity data similar to what the legacy Directory used
// to store, used to persist data locally and never sent through the wire.
//
// Because it's conceptually similar to SyncEntity (actual protocol) and it's
// unclear how big this'll grow, we've kept compatibility with SyncEntity by
// using the same field numbers.
message PersistedEntityData {
// See corresponding fields in SyncEntity for details.
optional string name = 8;
optional EntitySpecifics specifics = 21;
}
@@ -0,0 +1,29 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message PlusAddressSpecifics {
// A stable, unique identifier, generated by the PlusAddress backend.
optional int64 profile_id = 1;
// The origin that the `plus_email` is generated for.
optional string facet = 2;
optional PlusEmail plus_email = 3;
}
message PlusEmail {
optional string email_address = 1;
}
@@ -0,0 +1,59 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "note_entity.proto";
// Data structure dedicated to each power type.
// Should keep the field numbers of specifics in sync with the ones in PowerType
// below.
message PowerEntity {
oneof entity {
// Used for notes.
NoteEntity note_entity = 2;
}
}
message PowerBookmarkSpecifics {
// PowerType is persisted to disk and therefore shouldn't be reoderered or
// changed.
// Should keep the field numbers of PowerType in sync with the ones in
// entity above.
enum PowerType {
// All powers should have type so this should be unused.
POWER_TYPE_UNSPECIFIED = 0;
// Used for testing.
POWER_TYPE_MOCK = 1;
// Used for notes.
POWER_TYPE_NOTE = 2;
}
// The primary key for local storage.
/* required */ optional string guid = 1;
// Powers associated with a URL.
/* required */ optional string url = 2;
// Tracks the type of the power.
/* required */ optional PowerType power_type = 3;
// Tracks when this was added.
/* required */ optional int64 creation_time_usec = 4;
// Tracks when this was last modified.
/* required */ optional int64 update_time_usec = 5;
// Power entity.
/* required */ optional PowerEntity power_entity = 100;
}
@@ -0,0 +1,25 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for preferences.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of preference sync objects.
message PreferenceSpecifics {
optional string name = 1;
optional string value = 2;
}
@@ -0,0 +1,83 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for printer data.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// User PPD configuration
message PrinterPPDReference {
// Url for user provided file. Overrides other fields.
optional string user_supplied_ppd_url = 1;
// Retired fields
optional string effective_manufacturer = 2 [deprecated = true];
optional string effective_model = 3 [deprecated = true];
// String identifying the type of printer, used to look up a ppd to drive the
// printer.
optional string effective_make_and_model = 4;
// True if the printer should be automatically configured, false otherwise.
optional bool autoconf = 5 [default = false];
}
message PrinterSpecifics {
// Printer record GUID
optional string id = 1;
// User visible name. Any string.
optional string display_name = 2;
// User visible description. Any string.
optional string description = 3;
// Printer manufacturer. Should be a known manufacturuer.
// Deprecated in favor of make_and_model.
optional string manufacturer = 4 [deprecated = true];
// Printer model. Should match a known model for the manufacturer.
// Deprecated in favor of make_and_model.
optional string model = 5 [deprecated = true];
// Universal Resource Identifier for the printer on the network. usb:// will
// be the scheme for USB printers. Example
// ipp://address.example:port/queue/queue/queue.
optional string uri = 6;
// Universally Unique Identifier provided by the printer. Used for unique
// identification of printers in a zeroconf environment.
optional string uuid = 7;
// PPDData was deprecated in favor of PPDReference format.
optional bytes ppd = 8 [deprecated = true];
// Structure representing the user's ppd configuration.
optional PrinterPPDReference ppd_reference = 9;
// Timestamp when printer was last updated.
optional int64 updated_timestamp = 10;
// The make and model of the printer in one string. The typical arrangement
// for this is '<make> <model>'. This aligns with the typical formatting of
// the IPP attribute printer-make-and-model.
optional string make_and_model = 11;
// Universal Resource Identifier for the print server on the network. This
// will only be populated if the printer is from a print server. Example
// ipp://address.example:port/
optional string print_server_uri = 12;
}
@@ -0,0 +1,27 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for printer data.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Represents trusted Authorization Server for printers.
message PrintersAuthorizationServerSpecifics {
// Universal Resource Identifier for the authorization server on the network.
// The scheme must be https. Example: https://address.example:port/path/path.
// This field must be unique and is required.
optional string uri = 1;
}
@@ -0,0 +1,26 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for priority preferences.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "preference_specifics.proto";
// Properties of a synced priority preference.
message PriorityPreferenceSpecifics {
optional PreferenceSpecifics preference = 1;
}
@@ -0,0 +1,43 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for the reading list items.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Sync Reading list entry. This proto contains the fields synced for a reading
// list entry. It must be kept synced with the reading_list.ReadingListLocal
// protobuf.
message ReadingListSpecifics {
// entry_id and url must be equal.
optional string entry_id = 1;
optional string title = 2;
optional string url = 3;
optional int64 creation_time_us = 4;
optional int64 update_time_us = 5;
optional int64 first_read_time_us = 7;
optional int64 update_title_time_us = 8;
optional int32 estimated_read_time_seconds = 9;
enum ReadingListEntryStatus {
UNREAD = 0;
READ = 1;
UNSEEN = 2;
}
// If the field is not present, it defaults to UNSEEN.
optional ReadingListEntryStatus status = 6;
}
@@ -0,0 +1,85 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Sync protocol datatype extension for saved tab groups. Saved Tab Groups are
// tab groups which can be recalled between browser sessions, and if sync is
// turned on, cross device. A saved tab group contains tabs which are stored as
// a SavedTabGroupSpecifics separately (SavedTabGroupTab). This is due to
// limitations from the sync service on the size of an individual sync entity.
message SavedTabGroup {
// These colors map to tab_groups::TabGroupColorId. They DO NOT match the enum
// integer values due to "kGrey" being in the "Unspecified" field.
enum SavedTabGroupColor {
SAVED_TAB_GROUP_COLOR_UNSPECIFIED = 0;
SAVED_TAB_GROUP_COLOR_GREY = 1;
SAVED_TAB_GROUP_COLOR_BLUE = 2;
SAVED_TAB_GROUP_COLOR_RED = 3;
SAVED_TAB_GROUP_COLOR_YELLOW = 4;
SAVED_TAB_GROUP_COLOR_GREEN = 5;
SAVED_TAB_GROUP_COLOR_PINK = 6;
SAVED_TAB_GROUP_COLOR_PURPLE = 7;
SAVED_TAB_GROUP_COLOR_CYAN = 8;
SAVED_TAB_GROUP_COLOR_ORANGE = 9;
}
// The position of the SavedTabGroup in any visual display. This also
// represents the index in the SavedTabGroupModel of the group.
optional int64 position = 1;
// The displayed title of the group, shown on the tab group and the saved tab
// group button.
optional string title = 2;
// The color of the group, mapped to tab_groups::TabGroupColorId.
optional SavedTabGroupColor color = 3;
}
// Sync protocol datatype extension for saved tab group tabs. SavedTabGroupTab
// are the sync representation of a tab in a saved tab group. they are stored as
// separate entities due to size limitations of sync entities.
message SavedTabGroupTab {
// An id that links the saved tab group tab to it's saved tab group.
optional string group_guid = 1;
// The position in the Tab Strip the tab is located relative to the start of
// the tab group. This is also the index in the saved_tabs_ vector in the
// SavedTabGroup in the SavedTabGroupModel, which is updated by the
// TabStripModel.
optional int64 position = 2;
// Tab Data used for constructing the tab.
optional string url = 3;
// The displayed title of the tab, shown on the saved tab group button's
// context menu.
optional string title = 4;
}
message SavedTabGroupSpecifics {
// An id that refers to the sync saved tab group object.
optional string guid = 1;
// Timestamps for CRUD operations.
optional int64 creation_time_windows_epoch_micros = 2;
optional int64 update_time_windows_epoch_micros = 3;
oneof entity {
SavedTabGroup group = 4;
SavedTabGroupTab tab = 5;
}
}
@@ -0,0 +1,98 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for custom search engines.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
// Fields that are not used anymore should be marked [deprecated = true].
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of custom search engine sync objects.
message SearchEngineSpecifics {
// The description of the search engine.
optional string short_name = 1;
// The search engine keyword for omnibox access.
optional string keyword = 2;
// A URL to the favicon to show in the search engines options page.
optional string favicon_url = 3;
// The actual parameterized search engine query URL.
optional string url = 4;
// A flag signifying whether it is safe to automatically modify this search
// engine entry or not.
optional bool safe_for_autoreplace = 5;
// The URL to the OSD file this search engine entry came from.
optional string originating_url = 6;
// The date this search engine entry was created. A UTC timestamp with units
// in microseconds.
optional int64 date_created = 7;
// A list of supported input encodings.
optional string input_encodings = 8;
// Obsolete field. This used to represent whether or not this entry is shown
// in the list of default search engines.
optional bool deprecated_show_in_default_list = 9 [deprecated = true];
// The parameterized URL that provides suggestions as the user types.
optional string suggestions_url = 10;
// The ID associated with the prepopulate data this search engine comes from.
// Set to zero if it was not prepopulated.
optional int32 prepopulate_id = 11;
// DEPRECATED: Whether to autogenerate a keyword for the search engine or not.
// Do not write to this field in the future. We preserve this for now so we
// can read the field in order to migrate existing data that sets this bit.
optional bool autogenerate_keyword = 12 [deprecated = true];
// ID 13 reserved - previously used by |logo_id|, now deprecated.
optional string instant_url = 15 [deprecated = true];
// ID 16 reserved - previously used by |id|, now deprecated.
// The last time this entry was modified by a user. A UTC timestamp with units
// in microseconds.
optional int64 last_modified = 17;
// The primary identifier of this search engine entry for Sync.
optional string sync_guid = 18;
// A list of URL patterns that can be used, in addition to |url|, to extract
// search terms from a URL.
repeated string alternate_urls = 19;
optional string search_terms_replacement_key = 20 [deprecated = true];
// The parameterized URL that provides image results according to the image
// content or image URL provided by user.
optional string image_url = 21;
// The following post_params are comma-separated lists used to specify the
// post parameters for the corresponding search URL.
optional string search_url_post_params = 22;
optional string suggestions_url_post_params = 23;
optional string instant_url_post_params = 24 [deprecated = true];
optional string image_url_post_params = 25;
// The parameterized URL for a search provider specified new tab page.
optional string new_tab_url = 26;
enum ActiveStatus {
// The default state when a SE is auto-added. Unspecified SE are inactive.
ACTIVE_STATUS_UNSPECIFIED = 0;
// The SE is active and can be triggered via the omnibox.
ACTIVE_STATUS_TRUE = 1;
// The SE has been manually set to inactive by the user.
ACTIVE_STATUS_FALSE = 2;
}
// Whether a search engine is 'active' and can be triggered via the omnibox by
// typing in the relevant keyword.
optional ActiveStatus is_active = 27;
// The ID associated with the starter pack engine. This is set to 0 if not a
// starter pack engine.
optional int32 starter_pack_id = 28;
reserved 14;
reserved "created_by_policy";
}
@@ -0,0 +1,29 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for security events.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "gaia_password_reuse.proto";
message SecurityEventSpecifics {
// The specific security event to record.
oneof event { GaiaPasswordReuse gaia_password_reuse_event = 1; }
// Time of event, as measured by client in microseconds since Windows epoch.
optional int64 event_time_usec = 2;
}
@@ -0,0 +1,113 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for segmentations.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Sync data proto for storing segmentation data. Keyed by the combination of
// cache_guid (a Sync client id) and segmentation key.
//
// The segmentation platform is a platform that uses intelligence and machine
// learning to guide developers for building purpose-built user experience for
// specific segments of users. See go/chrome-segmentation for more details.
message SegmentationSpecifics {
// The selected segment by the segmentation scheme.
message SegmentSelectionResult {
// The string ID that refers to the segment the user was assigned to, e.g.,
// 'highly_engaged_user'.
optional string selected_segment = 1;
// Expiry time of selection result. Represents time from windows epoch in
// seconds. Expired results are ignored by clients.
optional int64 expiry_time_windows_epoch_seconds = 2;
// Time when the segmentation data is updated. Used to weigh results by
// recency. Represents time from windows epoch in seconds.
optional int64 last_updated_time_windows_epoch_seconds = 3;
}
// Metadata about the client device as used by the segmentation platform.
message DeviceMetadata {
// The cache_guid created to identify a sync client on this device.
// Reuses the same Sync guid.
optional string cache_guid = 1;
enum PlatformType {
PLATFORM_TYPE_UNSPECIFIED = 0;
PLATFORM_WINDOWS = 1;
PLATFORM_MAC = 2;
PLATFORM_LINUX = 3;
PLATFORM_CHROMEOS_ASH = 4;
PLATFORM_ANDROID = 5;
PLATFORM_IOS = 6;
PLATFORM_CHROMEOS_LACROS = 7;
}
// The OS platform of the device.
optional PlatformType platform_type = 2;
}
// Model execution data including segment scores and related metadata, e.g.,
// model version.
message ModelExecutionData {
// Output of model.
message ModelOutput {
// When outputting multiple scores from a single model, this is the
// segment label for each output.
optional string label = 1;
// Raw segment scores provided by the ML model or the heuristic model.
// The score is derived based on a combination of UMA histograms,
// user actions or UKM and URLs visited. The score can be treated as a
// probability of the user liking a feature, like feed, or NTP.
optional float score = 2;
// A rank defined by the segmentation scheme.
optional int32 rank = 3;
}
// A string ID that identifies a model.
optional string model_id = 1;
// A model may output one or multiple scores, one score for each segment
// label.
repeated ModelOutput model_outputs = 2;
// Timestamp when the ML model was executed.
// Represents time from windows epoch in seconds.
optional int64 execution_time_windows_epoch_seconds = 3;
// Expiry timestamp for the model scores.
// Represents time from windows epoch in seconds.
optional int64 score_expiry_time_windows_epoch_seconds = 4;
// The version of the ML model that was run.
optional int32 model_version = 5;
}
// The key used to identify the type of segmentation associated with a
// feature, e.g. 'user_engagement'.
optional string segmentation_key = 1;
optional SegmentSelectionResult segment_selection_result = 2;
optional DeviceMetadata device_metadata = 3;
// One or more model execution data associated with each segment for the
// segmentation key.
repeated ModelExecutionData model_execution_data = 4;
}
@@ -0,0 +1,47 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for the reading list items.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Send Tab To Self list entry. This proto contains the fields synced to send
// a url across devices.
message SendTabToSelfSpecifics {
// A random unique identifier for each shared tab.
// Required.
optional string guid = 5;
// The name of the tab being shared.
optional string title = 1;
// The URL of the tab being shared.
// Required.
optional string url = 2;
// The time the tab was shared as measured by the client in microseconds since
// the windows epoch.
optional int64 shared_time_usec = 3;
// A non-unique but human readable name to describe this client, used in UI.
optional string device_name = 4;
// The stable Device_id of the device that this tab was shared with.
// Required.
optional string target_device_sync_cache_guid = 7;
// A boolean to designate if the shared tab been opened on the target device.
optional bool opened = 8;
// Whether the notification for this proto been dismissed.
optional bool notification_dismissed = 9;
reserved "navigation_time_usec";
reserved 6;
}
@@ -0,0 +1,198 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for sessions.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "sync_enums.proto";
message SessionSpecifics {
// Unique id for the client. M89 and higher use sync's cache GUID (client ID)
// to populate this tag for *new* sessions.
optional string session_tag = 1;
optional SessionHeader header = 2;
optional SessionTab tab = 3;
// The local tab id used by sync. Unique across all nodes for that client.
optional int32 tab_node_id = 4 [default = -1];
}
// Properties of session sync objects.
message SessionHeader {
// Each session is composed of windows.
repeated SessionWindow window = 2;
// A non-unique but human-readable name to describe this client.
optional string client_name = 3;
// The form factor of device.
optional SyncEnums.DeviceFormFactor device_form_factor = 5;
// The type of device.
// Mandatory until M115. Replaced by device_form_factor afterwards, but still
// populated for backward compatibility.
optional SyncEnums.DeviceType device_type = 4 [deprecated = true];
}
message SessionWindow {
// Unique (to the owner) id for this window.
optional int32 window_id = 1;
// Index of the selected tab in tabs; -1 if no tab is selected.
optional int32 selected_tab_index = 2 [default = -1];
// Type of the window.
optional SyncEnums.BrowserType browser_type = 3 [default = TYPE_TABBED];
// The tabs that compose a window (correspond to tab id's).
repeated int32 tab = 4;
}
message SessionTab {
// Unique (to the owner) id for this tab.
optional int32 tab_id = 1 [default = -1];
// The unique id for the window this tab belongs to.
optional int32 window_id = 2;
// Visual index of the tab within its window. There may be gaps in these
// values.
optional int32 tab_visual_index = 3 [default = -1];
// Identifies the index of the current navigation in navigations. For
// example, if this is 2 it means the current navigation is navigations[2].
optional int32 current_navigation_index = 4 [default = -1];
// True if the tab is pinned.
optional bool pinned = 5 [default = false];
// If non-empty, this tab is an app tab and this is the id of the extension.
optional string extension_app_id = 6;
// Tabs are navigated, and the navigation data is here.
repeated TabNavigation navigation = 7;
optional SyncEnums.BrowserType browser_type = 13;
// Timestamp for when this tab was last activated.
// Corresponds to WebContents::GetLastActiveTime().
optional int64 last_active_time_unix_epoch_millis = 14;
// The favicon for the current url the tab is displaying. Either empty
// or a valid PNG encoded favicon.
optional bytes favicon = 8 [deprecated = true];
// The type of favicon. For now only normal web favicons are supported.
enum FaviconType { TYPE_WEB_FAVICON = 1; }
optional FaviconType favicon_type = 9 [deprecated = true];
// The url of the actual favicon (as opposed to the page using the favicon).
optional string favicon_source = 11 [deprecated = true];
// Ids of the currently assigned variations which should be sent to sync.
repeated uint64 variation_id = 12 [deprecated = true];
}
message TabNavigation {
// The virtual URL, when nonempty, will override the actual URL of the page
// when we display it to the user.
optional string virtual_url = 2;
// The referring URL, which can be empty.
optional string referrer = 3;
// The title of the page.
optional string title = 4;
// The core transition type.
optional SyncEnums.PageTransition page_transition = 6 [default = LINK];
// If this transition was triggered by a redirect, the redirect type.
optional SyncEnums.PageTransitionRedirectType redirect_type = 7;
// The unique navigation id (within this client).
optional int32 unique_id = 8;
// Timestamp for when this navigation last occurred (in client time).
// If the user goes back/forward in history the timestamp may refresh.
optional int64 timestamp_msec = 9;
// User used the Forward or Back button to navigate among browsing history.
optional bool navigation_forward_back = 10;
// User used the address bar to trigger this navigation.
optional bool navigation_from_address_bar = 11;
// User is navigating to the home page.
optional bool navigation_home_page = 12;
// The id for this navigation, which is globally unique with high
// probability.
optional int64 global_id = 15;
// The favicon url associated with this page.
optional string favicon_url = 17;
// The status code from the last navigation.
optional int32 http_status_code = 20;
// Correct referrer policy. Valid enums are defined in
// third_party/WebKit/public/platform/WebReferrerPolicy.h.
optional int32 correct_referrer_policy = 25 [default = 1];
// Whether the Password Manager saw a password field on the page.
optional SyncEnums.PasswordState password_state = 26;
// The start/end of a redirect chain. Deprecated in M108 because they were
// always set to false in practice, and don't make sense in the first place -
// this message corresponds to a complete chain.
optional bool navigation_chain_start = 13 [deprecated = true];
optional bool navigation_chain_end = 14 [deprecated = true];
// Search terms extracted from the URL. Deprecated in M64.
optional string search_terms = 16 [deprecated = true];
enum BlockedState {
STATE_ALLOWED = 1;
STATE_BLOCKED = 2;
}
// Whether access to the URL was allowed or blocked. Deprecated in M121
// because it was unused.
optional BlockedState blocked_state = 18
[deprecated = true, default = STATE_ALLOWED];
// Referrer policy. Old, broken value. Deprecated in M61.
optional int32 obsolete_referrer_policy = 21 [deprecated = true];
// True if created from restored navigation entry that hasn't been loaded.
// Deprecated in M121 because it was unused.
optional bool is_restored = 22 [deprecated = true];
// The chain of redirections for this navigation, from the original URL
// through the last URL that redirected. Deprecated in M121 because it was
// unused.
repeated NavigationRedirect navigation_redirect = 23 [deprecated = true];
// The last URL traversed when different from the virtual_url. Deprecated in
// M121 because it was unused.
optional string last_navigation_redirect_url = 24 [deprecated = true];
// The id for the task associated with this navigation, which is globally
// unique with high probability. Deprecated in M121 because it was unused.
optional int64 task_id = 27 [deprecated = true];
// Task ids of all ancestor navigations, which can be from other tabs, from
// root to parent. Deprecated in M121 because it was unused.
repeated int64 ancestor_task_id = 28 [deprecated = true];
// When a history entry is replaced (e.g. history.replaceState()), this
// contained some information about the entry prior to being replaced.
// Deprecated in M121 because it was unused.
optional ReplacedNavigation replaced_navigation = 29 [deprecated = true];
// The page language as determined by its textual content. An ISO 639 language
// code (two letters, except for Chinese where a localization is necessary).
// Deprecated in M121 because it was unused.
optional string page_language = 30 [deprecated = true];
reserved 1;
reserved "index";
reserved 5;
reserved "state";
reserved 19;
reserved "content_pack_categories";
}
// Navigation information for a single redirection within a single navigation.
message NavigationRedirect {
// A URL that redirected while navigating to the virtual_url.
optional string url = 1;
}
// Subset of TabNavigation fields representing a navigation that was later
// replaced in history (e.g. history.replaceState()), which allows tracking
// information about the original navigation prior to the first replacement.
message ReplacedNavigation {
optional string first_committed_url = 1;
optional int64 first_timestamp_msec = 2;
optional SyncEnums.PageTransition first_page_transition = 3;
}
@@ -0,0 +1,70 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for shared tab groups and tabs.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "unique_position.proto";
message SharedTabGroupDataSpecifics {
// Unique identifier for this entity.
optional string guid = 1;
// TODO(b/319521964): consider to rename and add a comment to clarify the
// content.
optional string last_modification_author = 2;
oneof entity {
SharedTabGroup tab_group = 3;
SharedTab tab = 4;
}
}
message SharedTabGroup {
// The displayed title of the group, shown on the tab group.
optional string title = 1;
// These colors map to tab_groups::TabGroupColorId. They DO NOT match the enum
// integer values due to "kGrey" being in the "Unspecified" field.
enum Color {
UNSPECIFIED = 0;
GREY = 1;
BLUE = 2;
RED = 3;
YELLOW = 4;
GREEN = 5;
PINK = 6;
PURPLE = 7;
CYAN = 8;
ORANGE = 9;
}
// The color of the tab group, mapped to tab_groups::TabGroupColorId.
optional Color color = 2;
}
message SharedTab {
// The URL of the page.
optional string url = 1;
// The title of the page.
optional string title = 2;
// The URL to the favicon of the page.
optional string favicon_url = 3;
// The GUID of the SharedTabGroup this is a member of.
optional string shared_tab_group_guid = 4;
// The UniquePosition within a SharedTabGroup.
optional UniquePosition unique_position = 5;
}
@@ -0,0 +1,87 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for sharing message.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message SharingMessageSpecifics {
// Unique identifier of message.
optional string message_id = 1;
message ChannelConfiguration {
message FCMChannelConfiguration {
// FCM registration token of target device.
optional string token = 1;
// Time to live for a FCM message (in seconds) - if specified, the message
// will expire based on the TTL.
optional int32 ttl = 2;
// Priority level of a FCM message. 5 = normal, 10 = high.
optional int32 priority = 3;
}
oneof channel_configuration {
// FCM channel configuration. Message will be delivered as a FCM message.
FCMChannelConfiguration fcm = 1;
// Opaque server channel configuration. Message will be delivered through
// server channel.
bytes server = 2;
}
}
optional ChannelConfiguration channel_configuration = 2;
// Payload encrypted using the target user keys according to WebPush
// encryption scheme. The payload has to be a valid
// chrome/browser/sharing/proto/sharing_message.proto serialized using
// SerializeToString.
optional bytes payload = 3;
}
// Used for the server to return fine grained commit errors back to the client.
message SharingMessageCommitError {
// This enum is used in histograms. Entries should not be renumbered and
// numeric values should never be reused. Also remember to update in
// tools/metrics/histograms/enums.xml SyncSharingMessageCommitErrorCode enum.
enum ErrorCode {
NONE = 0;
INVALID_ARGUMENT = 1;
NOT_FOUND = 2;
INTERNAL = 3;
UNAVAILABLE = 4;
RESOURCE_EXHAUSTED = 5;
UNAUTHENTICATED = 6;
PERMISSION_DENIED = 7;
// Client-specific error codes.
SYNC_TURNED_OFF = 8;
SYNC_NETWORK_ERROR = 9;
// Deprecated UMA bucket, prior to splitting between SYNC_SERVER_ERROR and
// SYNC_AUTH_ERROR.
DEPRECATED_SYNC_SERVER_OR_AUTH_ERROR = 10;
// Message wasn't committed before timeout.
SYNC_TIMEOUT = 11;
// Error code for server error or unparsable server response.
SYNC_SERVER_ERROR = 12;
// Auth error when communicating with the server.
SYNC_AUTH_ERROR = 13;
}
optional ErrorCode error_code = 1;
}
+505
View File
@@ -0,0 +1,505 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol for communication between sync client and server.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}. If you add new Specifics proto,
// also update proto_value_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "client_commands.proto";
import "client_debug_info.proto";
import "data_type_progress_marker.proto";
import "get_updates_caller_info.proto";
import "password_sharing_invitation_specifics.proto";
import "sync_entity.proto";
import "sync_enums.proto";
import "sharing_message_specifics.proto";
// This message contains diagnostic information used to correlate
// commit-related traffic with extensions-related mutations to the
// data models in chromium. It plays no functional role in
// processing this CommitMessage.
message ChromiumExtensionsActivity {
// The human-readable ID identifying the extension responsible
// for the traffic reported in this ChromiumExtensionsActivity.
optional string extension_id = 1;
// How many times the extension successfully invoked a write
// operation through the bookmarks API since the last CommitMessage.
optional uint32 bookmark_writes_since_last_commit = 2;
}
// Client specific configuration information.
message ClientConfigParams {
// The set of data types this client has enabled. Note that this does not
// include proxy types, as they do not have protocol field numbers and are
// placeholder types that implicitly enable protocol types.
repeated int32 enabled_type_ids = 1;
// Whether the PROXY_TABS proxy datatype is enabled on this client.
// Deprecated and not populated anymore since M120.
optional bool tabs_datatype_enabled = 2 [deprecated = true];
// Whether the account(s) present in the content area's cookie jar match the
// chrome account. If multiple accounts are present in the cookie jar, a
// mismatch implies all of them are different from the chrome account.
optional bool cookie_jar_mismatch = 3;
// Indicates that the client is not aware of any other active clients
// interested in the committed data types. This flag shows that it is not
// necessary to send invalidations for the committed data. A client is
// considered active if it's DeviceInfo has updated recent enough. This flag
// does not take into account whether standalone invalidations are enabled (as
// opposed to |single_client_with_standalone_invalidations|). However, it's
// set depending on interested data types of other devices, e.g. if there are
// other devices but they are not interested in SESSION data type, and current
// commit request contains only SESSION, it will be set to true.
// Introduced in M88.
optional bool single_client = 4;
// A list of FCM registration tokens which are obtained from other clients.
// This list is used by the server to send invalidations to all other clients.
// If the list is empty, the server should treat this as "there is no
// information about other clients". In practice, this happens by the next
// causes:
// 1. This is the old client which doesn't set this field.
// 2. There are too many active devices and the list would have too many
// items.
// 3. An empty list could also mean that the current client is the only
// client. This case should be covered by the
// |single_client_with_standalone_invalidations| field instead (otherwise it
// could be mixed up with older clients). The server doesn't have to use this
// field and can ignore it. Note that this list does not take into account
// interested data types from the other clients.
// Introduced in M89.
repeated string devices_fcm_registration_tokens = 5;
// Similar to |single_client| but takes into account only clients with enabled
// sync standalone invalidations. When set to true, there are no other clients
// with sync standalone invalidations interested in the committing types and
// hence it's not necessary to send out standalone invalidations (it may still
// be necessary to send out invalidations using the legacy system, see
// |single_client| instead).
// Introduced in M105.
optional bool single_client_with_standalone_invalidations = 6;
// Similar to |devices_fcm_registration_tokens| but takes into account clients
// which are subscribed to the data types which are committed in current
// commit request.
// A list of FCM registration tokens which are obtained from other clients.
// This list is used by the server to send invalidations to all other clients.
// If the list is empty, the server should treat this as "there is no
// information about other clients". In practice, this happens by the next
// causes:
// 1. This is the old client which doesn't set this field.
// 2. There are too many active devices and the list would have too many
// items.
// 3. An empty list could also mean that the current client is the only
// client. This case should be covered by the
// |single_client_with_standalone_invalidations| field instead (otherwise it
// could be mixed up with older clients). The server doesn't have to use this
// field and can ignore it.
// Introduced in M105.
repeated string fcm_registration_tokens_for_interested_clients = 7;
// Similar to |single_client| but takes into account only clients which are
// subscribed to old invalidations (i.e. not subscribed to sync standalone
// invalidations). When set to true, there are no other clients subscribed to
// old invalidations. Note that opposed to |single_client|, this flag does not
// always take into account the list of interested data types (because
// interested data types were introduced in M89). When the list is empty, a
// client is considered to be subscribed to all data types. Note that "single
// client" is a bit misleading since this will also be true if there are no
// clients with old invalidations at all.
// Introduced in M110.
optional bool single_client_with_old_invalidations = 8;
}
message CommitMessage {
repeated SyncEntity entries = 1;
// A GUID that identifies the committing sync client. This value will be
// returned as originator_cache_guid for any new items.
optional string cache_guid = 2;
repeated ChromiumExtensionsActivity extensions_activity = 3;
// The configuration of this client at commit time. Used by the server to
// make commit-time decisions about how to process datatypes that might
// involve server-side interaction, and e.g require explicit user intent for
// syncing a particular data type regardless of whether a commit for that
// datatype is currently being sent up.
optional ClientConfigParams config_params = 4;
// Set of optional per-client datatype contexts.
repeated DataTypeContext client_contexts = 5;
// This field need to be 256 bytes if set. This attempts to mitigate CRIME
// attacks when sync communicate from client to server with compression. So if
// compression is used, this need to set a 256 random ASCII bytes. If no
// compression, this field should not be set. The server can ignore the
// padding.
optional string padding = 6;
}
message GetUpdatesMessage {
// Indicates the reason for the GetUpdatesMessage.
// This was *mostly* deprecated in M29. GetUpdatesOrigin is the new way to
// encode the reason for the GetUpdates request, but some parts of the server
// still rely on this field. It also still contains the
// "notifications_enabled" flag which needs to be moved elsewhere before this
// can be fully removed. See https://crbug.com/510165.
optional GetUpdatesCallerInfo caller_info = 2;
// Indicates whether related folders should be fetched.
optional bool fetch_folders = 3 [default = true];
// Per-datatype progress marker.
//
// With the exception of certain configuration or initial sync requests, the
// client should include one instance of this field for each enabled data
// type.
repeated DataTypeProgressMarker from_progress_marker = 6;
// Indicates whether the response should be sent in chunks. This may be
// needed for devices with limited memory resources. If true, the response
// will include one or more ClientToServerResponses, with the first one
// containing GetUpdatesMetadataResponse, and the remaining ones, if any,
// containing GetUpdatesStreamingResponse. These ClientToServerResponses are
// delimited by a length prefix, which is encoded as a varint.
optional bool streaming = 7 [default = false];
// Whether the client needs the server to provide an encryption key for this
// account.
// Note: this should typically only be set on the first GetUpdates a client
// requests. Clients are expected to persist the encryption key from then on.
// The allowed frequency for requesting encryption keys is much lower than
// other datatypes, so repeated usage will likely result in throttling.
optional bool need_encryption_key = 8 [default = false];
// This value is an updated version of the GetUpdatesCallerInfo's
// GetUpdatesSource. It describes the reason for the GetUpdate request.
// Introduced in M29.
optional SyncEnums.GetUpdatesOrigin get_updates_origin = 9;
// Whether this GU also serves as a retry GU. Any GU that happens after
// retry timer timeout is a retry GU effectively.
optional bool is_retry = 10 [default = false];
// Set of optional per-client datatype contexts.
repeated DataTypeContext client_contexts = 11;
reserved 1;
reserved "from_timestamp";
reserved 4;
reserved "requested_types";
reserved 5;
reserved "batch_size";
reserved 1000;
reserved "create_mobile_bookmarks_folder";
}
// Message from a client asking the server to clear its data. This causes the
// server to generate a new store birthday, which allows dealing reliably with
// in-flight requests (in particular commits) from other clients.
message ClearServerDataMessage {
// No arguments needed as the store birthday and user identifier are part of
// an enclosing message.
}
// Response to a ClearServerData request.
message ClearServerDataResponse {
// No result fields necessary. Success/failure is indicated in
// ClientToServerResponse.
}
// The client must preserve, store, and resend the chip bag with
// every request. The server depends on the chip bag in order
// to precisely choreograph a client-server state machines.
//
// Because the client stores and sends this data on every request,
// the contents of the chip bag should be kept relatively small.
//
// If the server does not return a chip bag, the client must assume
// that there has been no change to the chip bag. The client must
// resend the bag of chips it had prior on the next request.
//
// The client must make the chip bag durable if and only if it
// processes the response from the server.
message ChipBag {
// Server chips are deliberately oqaque, allowing the server
// to encapsulate its state machine logic.
optional bytes server_chips = 1;
}
// Information about the syncer's state.
message ClientStatus {
// Flag to indicate if the client has detected hierarchy conflcits. The flag
// is left unset if update application has not been attempted yet.
//
// The server should attempt to resolve any hierarchy conflicts when this flag
// is set. The client may not assume that any particular action will be
// taken. There is no guarantee the problem will be addressed in a reasonable
// amount of time.
// TODO(crbug.com/1315573): Deprecated in M103.
optional bool hierarchy_conflict_detected = 1 [deprecated = true];
// Whether the client has full sync (or, sync the feature) enabled or not.
optional bool is_sync_feature_enabled = 2;
}
message ClientToServerMessage {
// |share| field is only used on the server for logging and can sometimes
// contain empty string. It is still useful for logging username when it can't
// be derived from access token in case of auth error.
required string share = 1;
optional int32 protocol_version = 2 [default = 99];
enum Contents {
COMMIT = 1;
GET_UPDATES = 2;
DEPRECATED_3 = 3;
DEPRECATED_4 = 4;
CLEAR_SERVER_DATA = 5;
}
// Each ClientToServerMessage contains one request defined by the
// message_contents. Each type has a corresponding message field that will be
// present iff the message is of that type. E.g. a commit message will have a
// message_contents of COMMIT and its commit field will be present.
required Contents message_contents = 3;
optional CommitMessage commit = 4;
optional GetUpdatesMessage get_updates = 5;
reserved 6;
reserved "authenticate";
// Opaque server-provided ID representing an "epoch" of the server-side data.
// Clients must hand this opaque ID back to the server as part of all requests
// within the same sync session (i.e. for all requests to the server except
// the very first GetUpdates request). See analogous field
// ClientToServerResponse.store_birthday for more details about its lifetime.
optional string store_birthday = 7;
reserved 8;
reserved "sync_problem_detected";
reserved 9;
// Client side state information for debugging purpose.
// This is only sent on the first getupdates of every sync cycle,
// as an optimization to save bandwidth.
optional DebugInfo debug_info = 10;
// Per-client state for use by the server. Sent with every message sent to the
// server.
optional ChipBag bag_of_chips = 11;
// Google API key.
optional string api_key = 12;
// Client's self-reported state.
// The client should set this on every message sent to the server, though its
// member fields may often be unset.
optional ClientStatus client_status = 13;
// The ID that our invalidation client used to identify itself to the server.
// Sending the ID here allows the server to not send notifications of our own
// changes to our invalidator.
// The field was deprecated in M117 and is no longer populated.
optional string invalidator_client_id = 14 [deprecated = true];
// Identifies this ClientToServerMessage as a clear server data request. This
// field is present when message_contents is CLEAR_SERVER_DATA.
optional ClearServerDataMessage clear_server_data = 15;
}
message CommitResponse {
enum ResponseType {
SUCCESS = 1;
CONFLICT = 2; // You're out of date; update and check your data
// TODO(ncarter): What's the difference between RETRY and TRANSIENT_ERROR?
RETRY = 3; // Someone has a conflicting, non-expired session open
INVALID_MESSAGE = 4; // What the client sent was invalid, and trying again
// won't help.
OVER_QUOTA = 5; // This operation would put you, or you are, over quota
TRANSIENT_ERROR = 6; // Something went wrong; try again in a bit
}
repeated group EntryResponse = 1 {
required ResponseType response_type = 2;
// Sync servers may also return a new ID for an existing item, indicating
// a new entry's been created to hold the data the client's sending up.
optional string id_string = 3;
reserved 4;
reserved "parent_id_string";
reserved 5;
reserved "position_in_parent";
// The item's current version.
optional int64 version = 6;
reserved 7;
reserved "name";
reserved 8;
reserved "non_unique_name";
optional string error_message = 9;
// Last modification time (in milliseconds since Unix epoch). Allows the
// server to override the client-supplied mtime during a commit operation.
// TODO(crbug.com/1182252): Delete this field too.
optional int64 mtime = 10 [deprecated = true];
message DatatypeSpecificError {
oneof datatype_error {
SharingMessageCommitError sharing_message_error = 1;
OutgoingPasswordSharingInvitationCommitError
outgoing_password_sharing_invitation_error = 2;
}
}
// Datatype specific error (if any).
optional DatatypeSpecificError datatype_specific_error = 11;
}
}
message GetUpdatesResponse {
// New sync entries that the client should apply.
repeated SyncEntity entries = 1;
reserved 2;
reserved "new_timestamp";
reserved 3;
reserved "newest_timestamp";
// Approximate count of changes remaining - use this for UI feedback.
// If present and zero, this estimate is firm: the server has no changes
// after the current batch.
optional int64 changes_remaining = 4;
// Opaque, per-datatype timestamp-like tokens. Clients should retain and
// persist the values returned in this field, and present them back to the
// server to indicate the starting point for future update requests.
//
// This will be sent only if the client provided |from_progress_marker|
// in the update request.
//
// The server may provide a new progress marker even if this is the end of
// the batch, or if there were no new updates on the server; and the client
// must save these. If the server does not provide a |new_progress_marker|
// value for a particular datatype, when the request provided a
// |from_progress_marker| value for that datatype, the client should
// interpret this to mean "no change from the previous state" and retain its
// previous progress-marker value for that datatype.
repeated DataTypeProgressMarker new_progress_marker = 5;
// The current encryption keys associated with this account. Will be set if
// the GetUpdatesMessage in the request had need_encryption_key == true or
// the server has updated the set of encryption keys (e.g. due to a key
// rotation).
repeated bytes encryption_keys = 6;
// Set of optional datatype contexts server mutations.
repeated DataTypeContext context_mutations = 7;
}
message ClientToServerResponse {
optional CommitResponse commit = 1;
optional GetUpdatesResponse get_updates = 2;
reserved 3;
reserved "authenticate";
// Up until protocol_version 24, the default was SUCCESS which made it
// impossible to add new enum values since older clients would parse any
// out-of-range value as SUCCESS. Starting with 25, unless explicitly set,
// the error_code will be UNKNOWN so that clients know when they're
// out-of-date. Note also that when using protocol_version < 25,
// TRANSIENT_ERROR is not supported. Instead, the server sends back a HTTP
// 400 error code. This is deprecated now.
optional SyncEnums.ErrorType error_code = 4 [default = UNKNOWN];
optional string error_message = 5;
// Opaque server-provided ID representing an "epoch" of the server-side data,
// referred to as "birthday" or "store birthday". This ID remains fixed until
// server-side data gets cleared/reset (e.g. via ClearServerDataMessage),
// which clients experience as NOT_MY_BIRTHDAY error, and involves clearing
// all local sync metadata including the cached store birthday.
//
// This mechanism allows the server to implement clear-data/reset
// functionality that reliably identifies and deletes sync entities uploaded
// before the clear-data/reset event (e.g. via ClearServerDataMessage).
// Furthermore, it allows the server to deal reliably with in-flight changes
// from other clients upon clear-data event, because all writes issued with an
// outdated birthday (which in-flight writes would use) can be detected by the
// server.
optional string store_birthday = 6;
optional ClientCommand client_command = 7;
reserved 8;
reserved "profiling_data";
reserved 9;
reserved 10;
reserved "stream_metadata";
reserved 11;
reserved "stream_data";
// The data types whose storage has been migrated. Present when the value of
// error_code is MIGRATION_DONE.
repeated int32 migrated_data_type_id = 12;
message Error {
optional SyncEnums.ErrorType error_type = 1 [default = UNKNOWN];
optional string error_description = 2;
reserved 3;
reserved "url";
optional SyncEnums.Action action = 4 [default = UNKNOWN_ACTION];
// Currently meaningful if |error_type| is throttled or partial_failure.
// In the throttled case, if this field is absent then the whole client
// (all datatypes) is throttled.
// In the partial_failure case, this field denotes partial failures. The
// client should retry those datatypes with exponential backoff.
repeated int32 error_data_type_ids = 5;
}
optional Error error = 13;
// The new per-client state for this client. If set, should be persisted and
// sent with any subsequent ClientToServerMessages.
optional ChipBag new_bag_of_chips = 14;
// Present if this ClientToServerResponse is in response to a ClearServerData
// request.
optional ClearServerDataResponse clear_server_data = 15;
}
// A message to notify the server of certain sync events. Idempotent. Send these
// to the /event endpoint.
message EventRequest {
optional SyncDisabledEvent sync_disabled = 1;
}
message EventResponse {}
// A message indicating that the sync engine has been disabled on a client.
message SyncDisabledEvent {
// The GUID that identifies the sync client.
optional string cache_guid = 1;
// The store birthday that the client was using before disabling sync.
optional string store_birthday = 2;
}
+235
View File
@@ -0,0 +1,235 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "entity_specifics.proto";
import "unique_position.proto";
message SyncEntity {
// This item's identifier. In a commit of a new item, this will be a
// client-generated ID. If the commit succeeds, the server will generate
// a globally unique ID and return it to the committing client in the
// CommitResponse.EntryResponse. In the context of a GetUpdatesResponse,
// |id_string| is always the server generated ID. The original
// client-generated ID is preserved in the |originator_client_id| field.
// Present in both GetUpdatesResponse and CommitMessage.
// Prior to M123, this field was empty for commit-only types.
optional string id_string = 1;
// An id referencing this item's parent in the hierarchy. In a
// CommitMessage, it is accepted for this to be a client-generated temporary
// ID if there was a new created item with that ID appearing earlier
// in the message. In all other situations, it is a server ID.
// Present in both GetUpdatesResponse and CommitMessage.
//
// Starting with M99, this field is optional and used for legacy bookmarks
// only:
// 1. When processing GetUpdatesResponse, it is unused for modern data created
// or reuploaded by M94 or above, which populates the parent's GUID in
// BookmarkSpecifics (which is sufficient).
// 2. When issuing CommitMessage, the field is populated for compatibility
// with clients before M99.
optional string parent_id_string = 2;
reserved 3;
reserved "old_parent_id";
// The version of this item -- a monotonically increasing value that is
// maintained by for each item. If zero in a CommitMessage, the server
// will interpret this entity as a newly-created item and generate a
// new server ID and an initial version number. If nonzero in a
// CommitMessage, this item is treated as an update to an existing item, and
// the server will use |id_string| to locate the item. Then, if the item's
// current version on the server does not match |version|, the commit will
// fail for that item. The server will not update it, and will return
// a result code of CONFLICT. In a GetUpdatesResponse, |version| is
// always positive and indentifies the revision of the item data being sent
// to the client.
// Present in both GetUpdatesResponse and CommitMessage.
// WARNING: This field used to be required before M60. Any client before this
// will fail to deserialize if this field is missing.
optional int64 version = 4;
// Last modification time, in milliseconds since Unix epoch.
// Present in both GetUpdatesResponse and CommitMessage.
optional int64 mtime = 5;
// Creation time, in milliseconds since Unix epoch.
// Present in both GetUpdatesResponse and CommitMessage.
optional int64 ctime = 6;
// The name of this item.
// Historical note:
// Since November 2010, this value is no different from non_unique_name.
// Before then, server implementations would maintain a unique-within-parent
// value separate from its base, "non-unique" value. Clients had not
// depended on the uniqueness of the property since November 2009; it was
// removed from Chromium by http://codereview.chromium.org/371029 .
// Present in both GetUpdatesResponse and CommitMessage.
// WARNING: This field used to be required before M60. Any client before this
// will fail to deserialize if this field is missing.
optional string name = 7;
// The name of this item. Same as |name|.
// |non_unique_name| should take precedence over the |name| value if both
// are supplied. For efficiency, clients and servers should avoid setting
// this redundant value.
// Present in both GetUpdatesResponse and CommitMessage.
optional string non_unique_name = 8;
reserved 9;
reserved "sync_timestamp";
// If present, this tag identifies this item as being a uniquely
// instanced item. The server ensures that there is never more
// than one entity in a user's store with the same tag value.
// This value is used to identify and find e.g. the "Bookmark Bar" folder
// without relying on a particular ID or name.
//
// This variant of the tag is created by the server, so clients can't create
// an item with a tag using this field.
//
// Use client_tag_hash if you want to create one from the client.
//
// An item can't have both a client_tag_hash and
// a server_defined_unique_tag.
//
// Present only in GetUpdatesResponse.
optional string server_defined_unique_tag = 10;
reserved 11;
reserved "BookmarkData";
reserved 12;
reserved "bookmark_folder";
reserved 13;
reserved "bookmark_url";
reserved 14;
reserved "bookmark_favicon";
// Ancient fields, predecessors for |unique_position|, deprecated with M26 and
// still supported to deal with old incoming data. See field |unique_position|
// for details as well as the data-upgrading implementation in
// GetUniquePositionFromSyncEntity().
optional int64 position_in_parent = 15 [deprecated = true];
optional string insert_after_item_id = 16 [deprecated = true];
reserved 17;
reserved "extended_attributes";
// If true, indicates that this item has been (or should be) deleted.
// Present in both GetUpdatesResponse and CommitMessage.
optional bool deleted = 18 [default = false];
// A unique ID that identifies the the sync client who initially committed
// this entity. This value corresponds to |cache_guid| in CommitMessage.
// This field, along with |originator_client_item_id|, can be used to
// reunite the original with its official committed version in the case
// where a client does not receive or process the commit response for
// some reason.
//
// Present only in GetUpdatesResponse.
//
// This field is also used in determining the unique identifier used in
// bookmarks' unique_position field.
optional string originator_cache_guid = 19;
// Item ID as generated by the client that initially created this entity. Used
// exclusively for bookmarks (other datatypes use client_tag_hash).
// There are three generation of bookmarks that have populated this field
// differently, depending on which version of the browser created the
// bookmark:
// 1. For bookmarks created before M44 (2015), the field got populated with an
// ID that is locally unique, but not globally unique (usually a negative
// number).
// 2. For bookmarks created between M45 and M51, both inclusive, the field got
// populated with a globally unique GUID in uppercase form.
// 3. For bookmarks created with M52 or above, the field gets populated with
// a globally unique GUID in lowercase form.
//
// Present only in GetUpdatesResponse.
optional string originator_client_item_id = 20;
// Extensible container for datatype-specific data.
// This became available in version 23 of the protocol.
optional EntitySpecifics specifics = 21;
// Indicate whether this is a folder or not. Available in version 23+.
optional bool folder = 22 [default = false];
// A client defined unique hash for this entity.
// Analogous to server_defined_unique_tag, but allows the client to determine
// an immutable attribute of the entity in hashed form that makes it unique
// per user (i.e. at most one entity may exist per client tag hash).
//
// This value must be of the form base64(SHA1(client_tag)) where the
// client_tag is a value derived from the contents of an entity in a
// type-specific way. Since the hashed contents of the client_tag are exposed
// to the server via this field, take care when defining it that there is
// enough entropy to mask any secrets that the server shouldn't learn.
//
// The difference between server_defined_unique_tag and
// client_tag_hash is the creator of the entity. Server defined
// tags are entities created by the server at account creation,
// while client defined tags are entities created by the client at any time.
//
// During GetUpdates, a sync entity update will come back with ONE of:
// a) Originator item and cache id - for bookmarks created using old versions
// of the browser (latest M93, before https://crrev.com/c/2945119).
// b) Server tag - If server committed the item as unique
// c) Client tag - If client committed the item as unique
//
// May be present in CommitMessages for the initial creation of an entity.
// If present in Commit updates for the entity, it will be ignored.
//
// May be returned in GetUpdatesMessage and sent up in CommitMessage.
optional string client_tag_hash = 23;
// Field 23 was previously named client_defined_unique_tag, avoid reuse.
reserved "client_defined_unique_tag";
reserved 24;
reserved "ordinal_in_parent";
// Introduced in M26, represents ordering among entities, in practice used for
// bookmarks only. Clients should not assume it is always populated in
// GetUpdatesMessage due to the following caveats:
// 1. Tombstones and permanent folders do not populate it (ordering is
// irrelevant).
// 2. It may remain unset by future versions of the client, as long as the
// field with the same name is populated inside BookmarkSpecifics. M94 and
// above populate both for backward compatibility reasons, but when support
// for M93 is retired, modern clients at the time may stop populating this
// field.
// 3. Very old data (last committed by M25 or below, before the field was
// introduced) does not include this field, and in that case the legacy
// fields |position_in_parent| or |insert_after_item_id| must be honored
// instead.
//
// May be returned in GetUpdatesMessage and sent up in CommitMessage.
optional UniquePosition unique_position = 25;
// This used to be a list of sync attachment IDs, but it was never launched
// and the code has been removed as of M66.
reserved 26;
reserved "attachment_id";
// Metadata used for shared data types. Must not be present for regular data
// types.
// Introduced in M124.
message CollaborationMetadata {
// Collaboration ID which the current entity belongs to.
optional string collaboration_id = 1;
}
optional CollaborationMetadata collaboration = 27;
}
+225
View File
@@ -0,0 +1,225 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol for communication between sync client and server.
// If you change or add any enums in this file, update
// proto_enum_conversions.{h, cc}.
// If you deprecate enum or enum values, mark any corresponding entry in
// tools/metrics/histograms/enums.xml as <obsolete>.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message SyncEnums {
// These events are sent by the DebugInfo class for singleton events.
enum SingletonDebugEventType {
// Connection status change. Note this gets generated even during a
// successful connection.
CONNECTION_STATUS_CHANGE = 1;
// Client received an updated token.
UPDATED_TOKEN = 2;
// Cryptographer needs passphrase.
PASSPHRASE_REQUIRED = 3;
// Passphrase was accepted by cryptographer.
PASSPHRASE_ACCEPTED = 4;
// Sync Initialization is complete.
INITIALIZATION_COMPLETE = 5;
// Server sent stop syncing permanently. This event should never be seen by
// the server in the absence of bugs.
STOP_SYNCING_PERMANENTLY = 6;
// Client has finished encrypting all data.
// DEPRECATED_ENCRYPTION_COMPLETE = 7;
// Client received an actionable error.
ACTIONABLE_ERROR = 8;
// Set of encrypted types has changed.
ENCRYPTED_TYPES_CHANGED = 9;
// The encryption passphrase state changed.
PASSPHRASE_TYPE_CHANGED = 10;
// A new keystore encryption token was persisted. Deprecated 11/2021.
DEPRECATED_KEYSTORE_TOKEN_UPDATED = 11;
// The datatype manager has finished an at least partially successful
// configuration and is once again syncing with the server.
CONFIGURE_COMPLETE = 12;
// A new cryptographer bootstrap token was generated.
DEPRECATED_BOOTSTRAP_TOKEN_UPDATED = 13;
// Cryptographer needs trusted vault decryption keys.
TRUSTED_VAULT_KEY_REQUIRED = 14;
// Cryptographer no longer needs trusted vault decryption keys.
TRUSTED_VAULT_KEY_ACCEPTED = 15;
}
// See ui/base/page_transition_types.h for detailed information on the
// values of PageTransition and PageTransitionRedirectType below.
// Types of transitions between pages.
enum PageTransition {
LINK = 0;
TYPED = 1;
AUTO_BOOKMARK = 2;
AUTO_SUBFRAME = 3;
MANUAL_SUBFRAME = 4;
GENERATED = 5;
AUTO_TOPLEVEL = 6;
FORM_SUBMIT = 7;
RELOAD = 8;
KEYWORD = 9;
KEYWORD_GENERATED = 10;
// The below two were mistakenly added but never properly used. They are
// actually transition qualifiers, and are set independently of other
// qualifiers and of the main transitions. See session_specifics.proto for
// the list of synced transition qualifiers.
// DEPRECATED_CHAIN_START = 12;
// DEPRECATED_CHAIN_END = 13;
}
// Types of redirects that triggered a transition.
enum PageTransitionRedirectType {
CLIENT_REDIRECT = 1;
SERVER_REDIRECT = 2;
}
enum ErrorType {
SUCCESS = 0;
// DEPRECATED_ACCESS_DENIED = 1;
// Returned when the server and client disagree on the store birthday. This
// should be interpreted as all local sync metadata requiring cleanup,
// obviously including the locally-cached store birthday.
NOT_MY_BIRTHDAY = 2;
// Returned when the store has exceeded the allowed bandwidth utilization.
THROTTLED = 3;
// DEPRECATED_AUTH_EXPIRED = 4;
// DEPRECATED_USER_NOT_ACTIVATED = 5;
// DEPRECATED_AUTH_INVALID = 6;
// DEPRECATED_CLEAR_PENDING = 7;
// A transient error occurred (e.g. a backend timeout). The client should
// try again later.
TRANSIENT_ERROR = 8;
// A server-side migration has taken place for one or more data types. The
// client should clear the cache for these data types only and then re-sync
// with a server.
MIGRATION_DONE = 9;
// An administrator disabled sync for this domain.
DISABLED_BY_ADMIN = 10;
// Deprecated in M50.
// DEPRECATED_USER_ROLLBACK = 11;
// Returned when the client wants to update several data types, but some of
// them failed (e.g. throttled).
PARTIAL_FAILURE = 12;
// Returned when the server detects that this client's sync metadata is
// obsolete. The client should reset local sync metadata and restart
// syncing.
CLIENT_DATA_OBSOLETE = 13;
// Returned when the server detects that the encryption state (Nigori,
// keystore keys) has been reset/overridden, which means the local
// Nigori-related state is obsolete and should be cleared.
// Introduced in M84.
ENCRYPTION_OBSOLETE = 14;
// Unknown value. This should never be explicitly used; it is the default
// value when an out-of-date client parses a value it doesn't recognize.
UNKNOWN = 100;
}
enum Action {
UPGRADE_CLIENT = 0; // Upgrade the client to latest version.
// DEPRECATED_CLEAR_USER_DATA_AND_RESYNC = 1;
// DEPRECATED_ENABLE_SYNC_ON_ACCOUNT = 2;
// DEPRECATED_STOP_AND_RESTART_SYNC = 3;
// DEPRECATED_DISABLE_SYNC_ON_CLIENT = 4;
UNKNOWN_ACTION = 5; // This is the default.
}
// New enums to describe the device type were introduced; OsType and
// FormFactor. Deprecated 12/2022.
// TODO(1395353): Remove deprecated values from DeviceType enum.
enum DeviceType {
TYPE_UNSET = 0 [deprecated = true];
TYPE_WIN = 1 [deprecated = true];
TYPE_MAC = 2 [deprecated = true];
TYPE_LINUX = 3 [deprecated = true];
TYPE_CROS = 4 [deprecated = true];
TYPE_OTHER = 5 [deprecated = true];
TYPE_PHONE = 6 [deprecated = true];
TYPE_TABLET = 7 [deprecated = true];
}
// Types of device OS.
enum OsType {
OS_TYPE_UNSPECIFIED = 0;
OS_TYPE_WINDOWS = 1;
OS_TYPE_MAC = 2;
OS_TYPE_LINUX = 3;
OS_TYPE_CHROME_OS_ASH = 4;
OS_TYPE_ANDROID = 5;
OS_TYPE_IOS = 6;
OS_TYPE_CHROME_OS_LACROS = 7;
OS_TYPE_FUCHSIA = 8;
}
// Types of device form factors.
enum DeviceFormFactor {
DEVICE_FORM_FACTOR_UNSPECIFIED = 0;
DEVICE_FORM_FACTOR_DESKTOP = 1;
DEVICE_FORM_FACTOR_PHONE = 2;
DEVICE_FORM_FACTOR_TABLET = 3;
}
// Types of browsers; used for history-related data types.
enum BrowserType {
BROWSER_TYPE_UNKNOWN = 0;
TYPE_TABBED = 1; // A normal tabbed window.
TYPE_POPUP = 2; // A popup window.
TYPE_CUSTOM_TAB = 3; // A custom tab within an app.
}
// The "password state" is the presence/absence of a password field on a
// website; used for history-related data types.
enum PasswordState {
PASSWORD_STATE_UNKNOWN = 0;
NO_PASSWORD_FIELD = 1;
HAS_PASSWORD_FIELD = 2;
}
// This is the successor to GetUpdatesSource. It merges the "normal mode"
// values (LOCAL, NOTIFICATION and DATATYPE_REFRESH), which were never really
// mutually exclusive to being with, into the GU_TRIGGER value. It also
// drops support for some old values that are not supported by newer clients.
//
// Mind the gaps: Some values are intentionally unused because we want to
// keep the values in sync with GetUpdatesSource as much as possible. Please
// don't add any values < 12 unless there's a good reason for it.
//
// Introduced in M28.
enum GetUpdatesOrigin {
UNKNOWN_ORIGIN = 0; // The source was not set by the caller.
PERIODIC = 4; // The source of the update was periodic polling.
NEWLY_SUPPORTED_DATATYPE = 7; // The client is in configuration mode
// because it's syncing all datatypes, and
// support for a new datatype was recently
// released via a software auto-update.
MIGRATION = 8; // The client is in configuration mode because a
// MIGRATION_DONE error previously returned by the
// server necessitated resynchronization.
NEW_CLIENT = 9; // The client is in configuration mode because the
// user enabled sync for the first time. Not to be
// confused with FIRST_UPDATE.
RECONFIGURATION = 10; // The client is in configuration mode because the
// user opted to sync a different set of datatypes.
GU_TRIGGER = 12; // The client is in 'normal' mode. It may have several
// reasons for requesting an update. See the per-type
// GetUpdateTriggers message for more details.
RETRY = 13; // A retry GU to pick up updates missed by last GU due to
// replication delay, missing hints, etc.
PROGRAMMATIC = 14; // A GU to programmatically enable/disable a
// datatype, often due to error handling.
}
}
@@ -0,0 +1,39 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// The payload coming from the server for all invalidated data types.
message SyncInvalidationsPayload {
message DataTypeInvalidation {
// The legitimate values of this field correspond to the protobuf field
// numbers of all EntitySpecifics fields supported by the server (see
// components/sync/protocol/sync.proto).
optional int32 data_type_id = 1;
}
// This field contains information about each data type which needs to be
// updated. Note that it may contain data types which current client is not
// interested in.
repeated DataTypeInvalidation data_type_invalidations = 1;
// Opaque field, which has to be provided as part of resulting GetUpdates
// back to the server.
optional bytes hint = 2;
// Version of invalidation, used to order incoming invalidations.
optional int64 version = 3;
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Protocol messages used only for testing.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message UnknownFieldsTestA {
required bool foo = 1;
}
message UnknownFieldsTestB {
required bool foo = 1;
required bool bar = 2;
}
@@ -0,0 +1,49 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for themes.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of theme sync objects.
message ThemeSpecifics {
// If set, we're using a extension and all custom_* fields should be
// present. If not set, and all custom_* fields should be omitted.
optional bool use_custom_theme = 1;
// This field is only relevant on platforms that have a distinction between
// the system theme and the default theme, but other platforms must be careful
// to pass through the value of this flag.
//
// If true, we use the system theme by default (i.e., when we don't use a
// custom theme) for platforms that make a distinction between the default
// theme and the system theme. Has no effect if use_custom_theme is true.
optional bool use_system_theme_by_default = 2;
// TODO(gayane): Migrate custom_* fields to a separate message.
// Extension specific fields. Ignored if |autogenerated_theme| is set.
optional string custom_theme_name = 3;
optional string custom_theme_id = 4;
optional string custom_theme_update_url = 5;
message AutogeneratedTheme {
// SkColor used to autogenerate theme.
optional uint32 color = 1;
}
// If set, autogenerated theme is used.
optional AutogeneratedTheme autogenerated_theme = 6;
}
@@ -0,0 +1,42 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for typed urls.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Properties of typed_url sync objects - fields correspond to similarly named
// fields in history::URLRow.
message TypedUrlSpecifics {
// Actual URL that was typed.
optional string url = 1;
// Title of the page corresponding to this URL.
optional string title = 2;
// True if the URL should NOT be used for auto-complete.
optional bool hidden = 4;
// Timestamps for all visits to this URL.
repeated int64 visits = 7 [packed = true];
// The PageTransition::Type for each of the visits in the |visit| array. Both
// arrays must be the same length.
repeated int32 visit_transitions = 8 [packed = true];
reserved 3;
reserved "typed_count";
reserved 5;
reserved "visit";
reserved 6;
reserved "visited_count";
}
@@ -0,0 +1,84 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Protobuf representation of the UniquePosition class.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// A UniquePosition is a string of bytes.
//
// Unique positions are unique per-item, since they are guaranteed to end with a
// fixed-length suffix that is unique per-item. The position string may not end
// with a '\0' byte.
//
// Prior to the suffix is a series of arbitrary bytes of arbitrary length.
// Items under the same parent are positioned relative to each other by a
// lexicographic comparison of their UniquePosition values.
message UniquePosition {
// History:
//
// Unique positions were first introduced in M28. This change was rolled out
// in such a way that it would try to maintain backwards compatibilty with
// clients that understood only the old int64-based positions.
//
// At first, clients supported only the 'value' field. This version never
// made it to stable. We later added support for the 'compressed_value'
// field, and clients would populate either one or the other.
//
// In M30, we added the custom_compressed_v1 representation. This
// representation was better than the previous implementations in almost every
// way. However, we could not use it right away, since older clients would
// not understand it. We decided to write both the old-style ('value' or
// 'custom_compressed') representation and the 'custom_compressed_v1'
// repersentations to every protobuf during the transition period. Protobufs
// written during this transition period would be readable by clients who
// understand at least one of the two formats.
//
// In M33, we dropped support for writing the backwards-compatibility fields.
// Protobufs written by this version or later are not be intelligible by
// clients with version M29 or older. Those clients will end up making use of
// the old int64 position fallback mechanism.
// The uncompressed string of bytes representing the position.
//
// Deprecated. See history note above.
optional bytes value = 1 [deprecated = true];
// The client may choose to write a compressed position to this field instead
// of populating the 'value' above. If it chooses to use compression, the
// 'value' field above must be empty. The position value will be compressed
// with gzip and stored in the compressed_value field. The position's
// uncompressed length must be specified and written to the
// uncompressed_length field.
//
// Deprecated. See history note above.
optional bytes compressed_value = 2 [deprecated = true];
optional uint64 uncompressed_length = 3 [deprecated = true];
// This encoding uses compression scheme designed especially for unique
// positions. It has the property that X < Y precisely when Compressed(X) <
// Compressed(Y), which is very useful when the most common operation is to
// compare these positions against each other. Their values may remain
// compressed in memory.
//
// The compression scheme is implemented and documented in
// sync/core_impl/base/unique_position.cc.
//
// As of M30, this is the preferred encoding. Newer clients may continue to
// populate the 'value' and 'compressed_value' fields to ensure backwards
// compatibility, but they will always try to read from this field first.
optional bytes custom_compressed_v1 = 4;
}
@@ -0,0 +1,113 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for user consents.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "user_consent_types.proto";
// Next id: 17
message UserConsentSpecifics {
// ===========================================================================
// Fields common to all Chrome User Consents.
// ===========================================================================
// The UI language Chrome is using, represented as the IETF language tag
// defined in BCP 47. The region subtag is not included when it adds no
// distinguishing information to the language tag (e.g. both "en-US"
// and "fr" are correct here).
optional string locale = 4;
// The local time on the client when the user consent was recorded. The time
// as measured by client is given in microseconds since Windows epoch. This
// is needed since user consent recording may happen when a client is
// offline.
optional int64 client_consent_time_usec = 12;
// ===========================================================================
// The specific consent type. Add new User Consent types to
// user_consent_types.proto.
// ===========================================================================
oneof consent {
UserConsentTypes.SyncConsent sync_consent = 7;
UserConsentTypes.ArcBackupAndRestoreConsent arc_backup_and_restore_consent =
8;
UserConsentTypes.ArcGoogleLocationServiceConsent
arc_location_service_consent = 9;
UserConsentTypes.ArcPlayTermsOfServiceConsent
arc_play_terms_of_service_consent = 10;
UserConsentTypes.UnifiedConsent unified_consent = 13 [deprecated = true];
UserConsentTypes.AssistantActivityControlConsent
assistant_activity_control_consent = 14;
UserConsentTypes.AccountPasswordsConsent account_passwords_consent = 15;
}
reserved "arc_metrics_and_usage_consent";
reserved 11;
reserved "autofill_assistant_consent";
reserved 16;
// ===========================================================================
// Client only fields.
// ===========================================================================
// TODO(markusheintz): Refactor the code so that these fields can be moved out
// of this message.
// The account ID of the user who gave the consent. This field is used
// by UserEventService to distinguish consents from different users,
// as UserConsent does not get deleted when a user signs out. However,
// it should be cleared before being sent over the wire, as the UserEvent
// is sent over an authenticated channel, so this information would be
// redundant.
//
// For semantics and usage of the |account_id| in the signin codebase,
// see IdentityManager::GetPrimaryAccountId() or CoreAccountId.
optional string account_id = 6;
// ===========================================================================
// Deprecated fields. Please do not use them!
// They have to remain in the proto because the server must continue
// supporting legacy consent formats like this.
// ===========================================================================
// Which feature does the consent apply to?
enum Feature {
FEATURE_UNSPECIFIED = 0;
CHROME_SYNC = 1;
PLAY_STORE = 2;
BACKUP_AND_RESTORE = 3;
GOOGLE_LOCATION_SERVICE = 4;
CHROME_UNIFIED_CONSENT = 5;
// TODO(markusheintz): ASSISTANT_ACTIVITY_CONTROL was only added for
// compatibility with the Feature enum in UserEventSpecifics.UserConsent.
// Delete this value once the value is deleted from the other proto.
ASSISTANT_ACTIVITY_CONTROL = 6;
}
optional Feature feature = 1 [deprecated = true];
// Ids of the strings of the consent text presented to the user.
repeated int32 description_grd_ids = 2 [deprecated = true];
// Id of the string of the UI element the user clicked when consenting.
optional int32 confirmation_grd_id = 3 [deprecated = true];
// Was the consent for |feature| given or not given (denied/revoked)?
optional UserConsentTypes.ConsentStatus status = 5 [deprecated = true];
}
@@ -0,0 +1,173 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Various user consent types used when reporting consent.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message UserConsentTypes {
// The status of a particular User Consent. The status describes the state
// of the consent as it is: given, not_given.
enum ConsentStatus {
// The status is unspecified e.g. because the user was not asked to
// consent yet or an error happened or the decision was delayed.
CONSENT_STATUS_UNSPECIFIED = 0;
// The consent is not given. This means the user either revoked the
// consent or did not grant it in the first place.
NOT_GIVEN = 1;
// The consent is given. This means the user did grant the consent.
GIVEN = 2;
}
// ===========================================================================
// Keep sorted alphabetically.
// Don't forget to update proto_visitors.h and potentially
// proto_enum_conversions.{h, cc}. Never TBR proto changes, because they need
// to be manually imported on the Sync server.
// ===========================================================================
// The user consent for the ARC Backup And Restore feature. The consent
// status is determined by the corresponding setting on the ARC setup dialog.
message ArcBackupAndRestoreConsent {
// Ids of the strings of the setting text presented to the user.
repeated int32 description_grd_ids = 1;
// Id of the string of the UI element the user clicked when consenting.
optional int32 confirmation_grd_id = 2;
optional ConsentStatus status = 3;
}
// The user consent for the Google Location Service. The consent status is
// determined by the corresponding setting on the ARC setup dialog.
message ArcGoogleLocationServiceConsent {
// Ids of the strings of the setting text presented to the user.
repeated int32 description_grd_ids = 1;
// Id of the string of the UI element the user clicked when consenting.
optional int32 confirmation_grd_id = 2;
optional ConsentStatus status = 3;
}
// The user consent for the Play terms of service is determined by the user
// action on the ARC setup dialog that presents the Play Terms of Service.
message ArcPlayTermsOfServiceConsent {
// The length of the UTF-8 encoded string of the Play Terms of Service
// text. The length is given in number of bytes.
optional int32 play_terms_of_service_text_length = 1;
// The SHA1 hash of UTF-8 encoded string of the Play Terms of Service
// displayed to the user.
optional bytes play_terms_of_service_hash = 2;
// GRD Ids of the strings on the consent UI that was shown to the user.
repeated int32 description_grd_ids = 5;
// Id of the string of the UI element the user clicked when consenting.
optional int32 confirmation_grd_id = 3;
// The status of the Play Terms of Service consent. This specifies whether
// the consent was given or not given/revoked.
optional ConsentStatus status = 4;
// Enum describing different user flow during which the ARC Play Terms of
// service consent can be recorded.
enum ConsentFlow {
// The ARC setup flow on a CROS device.
SETUP = 1;
// The user flow for changing the ARC setting on a CROS device.
SETTING_CHANGE = 2;
}
// The user flow during which the consent was recorded. This is necessary in
// order to create the correct UI context and Event for the backend.
optional ConsentFlow consent_flow = 6;
}
// The User Consent for Chrome Sync is determined by the user action on the
// corresponding Sync Consent dialog.
message SyncConsent {
// Ids of the strings of the sync consent text presented to the user.
repeated int32 description_grd_ids = 1;
// Id of the string of the UI element the user clicked in order to confirm
// and close the sync consent dialog.
optional int32 confirmation_grd_id = 2;
// The status of the sync consent. This specifies whether the consent was
// given or not given/revoked.
optional ConsentStatus status = 3;
}
// The unified User Consent for Chrome is determined by the user action on the
// corresponding Chrome Consent dialog.
message UnifiedConsent {
// Ids of the strings of the unified Chrome consent text presented to the
// user.
repeated int32 description_grd_ids = 1;
// Id of the string of the UI element the user clicked in order to confirm
// and close the unified Chrome consent dialog.
optional int32 confirmation_grd_id = 2;
// The status of the unified Chrome consent. This specifies whether the
// consent was given or not given/revoked.
optional ConsentStatus status = 3;
}
// The User Consent for Assistant activity control is determined by the user
// action on the Assistant opt-in flow.
message AssistantActivityControlConsent {
// An opaque token to for ui auditing, which strengthens the audit trail.
optional bytes ui_audit_key = 1;
// The status of the Assistant activity control consent. This specifies
// whether the consent was given or not given.
optional ConsentStatus status = 2;
// Enum denoting different Assistant activity control setting type.
enum SettingType {
// Legacy clients (< M93) did not set a SettingType; for those,
// UNSPECIFIED should be interpreted as ALL. Newer clients should always
// set it explicitly.
SETTING_TYPE_UNSPECIFIED = 0;
// All Settings.
ALL = 1;
WEB_AND_APP_ACTIVITY = 2;
DEVICE_APPS = 3;
}
// The activity control setting which the consent was given or not given to.
// Clients before M93 did not set this field. Such consents should be
// treated as applying to ALL.
optional SettingType setting_type = 3;
}
// The User Consent for downloading and using passwords stored in the user's
// Google Account. Determined during the opt-in flow for the feature.
message AccountPasswordsConsent {
// Ids of the strings of the consent text presented to the user.
repeated int32 description_grd_ids = 1;
// Id of the string of the UI element the user clicked in order to confirm
// the consent dialog.
optional int32 confirmation_grd_id = 2;
// The status of the consent. This specifies whether the consent was given
// or not given/revoked.
optional ConsentStatus status = 3;
}
}
@@ -0,0 +1,142 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for user events.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "gaia_password_reuse.proto";
message UserEventSpecifics {
// Time of event, as measured by client in microseconds since Windows epoch.
optional int64 event_time_usec = 1;
// The |global_id| field of the associated navigation, if there is one.
optional int64 navigation_id = 2;
// On startup we generate a new random id to identify which FieldTrialEvents
// other events belonged to.
optional fixed64 session_id = 3;
// 4-7 reserved for future scalars.
// Used for testing and debugging EventLog system.
message Test {}
// Language detection output.
message LanguageDetection {
option deprecated = true;
message Language {
// ISO 639 language code will be used.
optional string language_code = 1;
// Whether the detected language is reliable, note this is determined by
// the CLD3.
optional bool is_reliable = 2;
}
// Top n languages. Typically we just log the top language, but for page
// that we're not confident about, we may log up to 3 top languages in
// descending order.
repeated Language detected_languages = 1;
// Adopted language code is the code of final determined language.
// It will be stored only if it's different from the first detected
// language.
optional string adopted_language_code = 2;
}
// User translated a page or interacted with translate suggestion.
message Translation {
option deprecated = true;
// Source language of the translation.
optional string from_language_code = 1;
// Target language of the translation.
optional string to_language_code = 2;
enum Interaction {
UNKNOWN = 0;
ACCEPT = 1;
DECLINE = 2;
// This happens when user scroll or click outside the UI without
// translation.
IGNORED = 3;
// This happens when user choose to close the translation window without
// translation.
DISMISSED = 4;
// User manually entered either language.
// In this case, from_language_code and to_language_code will be user
// chosen values.
MANUAL = 5;
// User choose to revert the translation, in this case, from_language_code
// and to_language_code will be previous chosen values.
TRANSLATION_REVERTED = 6;
// Automatically triggered translation.
// User sets always translate in user settings.
AUTO_TRANSLATION_BY_PREF = 7;
// User navigated through a click from a translated page.
AUTO_TRANSLATION_BY_LINK = 8;
// Failed to initialize the translate script, this can happen for iOS due
// to CSPs.
INITIALIZATION_ERROR = 9;
}
optional Interaction interaction = 3;
}
// Logged when the user logs into Google, and at least once per 28d.
message GaiaPasswordCaptured {
enum EventTrigger {
UNSPECIFIED = 0;
// Event added because user logged in.
USER_LOGGED_IN = 1;
// Event added because 28d timer fired.
EXPIRED_28D_TIMER = 2;
}
optional EventTrigger event_trigger = 1;
}
message FlocIdComputed {
enum EventTrigger {
UNSPECIFIED = 0;
// Event added because the floc id is computed for the 1st floc session.
NEW = 1;
// Event added because the floc id is re-computed due to a long period of
// time has passed since the last computation.
REFRESHED = 2;
// Event added because the floc id is re-computed due to history deletion.
HISTORY_DELETE = 3;
}
reserved 1;
reserved "event_trigger";
// If not set, it means that the floc is disabled.
optional uint64 floc_id = 2;
}
oneof event {
Test test_event = 8;
LanguageDetection language_detection_event = 10 [deprecated = true];
Translation translation_event = 11 [deprecated = true];
// Happens when a user types their Google account password on another site.
GaiaPasswordReuse gaia_password_reuse_event = 104;
GaiaPasswordCaptured gaia_password_captured_event = 15;
FlocIdComputed floc_id_computed_event = 16;
}
reserved "field_trial_event";
reserved 9;
reserved "user_consent";
reserved 12;
}
@@ -0,0 +1,52 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update
// components/sync/protocol/proto_visitors.h and potentially
// components/sync/protocol/proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Sync & local data: Information about web app icon.
message WebApkIconInfo {
enum Purpose {
UNSPECIFIED = 0;
// Suitable for any purpose.
ANY = 1;
// Designed for masking.
MASKABLE = 2;
// Suitable for monochrome purposes.
MONOCHROME = 3;
}
// The size of the square app icon, in raw pixels.
optional int32 size_in_px = 1;
// The URL of the app icon.
optional string url = 2;
// The purpose or context in which the icon should be used.
optional Purpose purpose = 3;
}
// WebApk data.
message WebApkSpecifics {
optional string manifest_id = 1;
optional string start_url = 2;
optional string name = 3;
optional uint32 theme_color = 4;
optional string scope = 5;
repeated WebApkIconInfo icon_infos = 6;
// Time when this WebApk was last used or installed.
// Number of microseconds since 1601, aka Windows epoch.
optional int64 last_used_time_windows_epoch_micros = 7;
}
@@ -0,0 +1,98 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update
// components/sync/protocol/proto_visitors.h and potentially
// components/sync/protocol/proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// Sync & local data: Information about web app icon.
message WebAppIconInfo {
enum Purpose {
UNSPECIFIED = 0;
// Suitable for any purpose.
ANY = 1;
// Designed for masking.
MASKABLE = 2;
// Suitable for monochrome purposes.
MONOCHROME = 3;
}
// The size of the square app icon, in raw pixels.
optional int32 size_in_px = 1;
// The URL of the app icon.
optional string url = 2;
// The purpose or context in which the icon should be used.
optional Purpose purpose = 3;
}
// WebApp data. This is a synced part of
// chrome/browser/web_applications/proto/web_app.proto data.
message WebAppSpecifics {
// This enum should be a subset of the DisplayMode enum in
// chrome/browser/web_applications/proto/web_app.proto and
// third_party/blink/public/mojom/manifest/display_mode.mojom
enum UserDisplayMode {
UNSPECIFIED = 0;
BROWSER = 1;
// MINIMAL_UI is never serialized.
STANDALONE = 3;
// FULLSCREEN is never serialized.
// WINDOW_CONTROLS_OVERLAY is never serialized.
TABBED = 6;
// BORDERLESS is never serialized.
// PICTURE_IN_PICTURE is never serialized.
}
optional string start_url = 1;
optional string name = 2;
// User-selected display setting, which may be used if no platform-specific
// setting is available (eg. on Windows, Mac, and Linux). If absent,
// user_display_mode_cros may be used as fallback (if populated). On pre-M122
// devices this field is used by all platforms (ie. ChromeOS, Windows, Mac,
// Linux) and always replaces any locally-stored value, with
// absent/unspecified falling back to standalone.
optional UserDisplayMode user_display_mode_default = 3;
reserved "user_display_mode_non_cros";
reserved "user_display_mode";
optional uint32 theme_color = 4;
optional string scope = 5;
repeated WebAppIconInfo icon_infos = 6;
// Used to store the page number that the app is displayed on in
// chrome://apps.
optional string user_page_ordinal = 7;
// Used to store the in-page ranking for ordering apps in its given
// |user_page_ordinal| page.
optional string user_launch_ordinal = 8;
// Used to store id specified in the manifest. This is a path that is relative
// to the start_url, similar to how the id field is parsed in
// https://www.w3.org/TR/appmanifest/#id-member, except this field does not
// include a scheme or origin. This is only the path after the origin,
// excluding the first "/".
// Note: If this field is not set, then the manifest_id is generated using the
// start_url in GenerateManifestIdFromStartUrlOnly. This is different than if
// this is set to "", which means the manifest_id will be set to the origin of
// the start_url.
optional string relative_manifest_id = 9;
// User-selected display setting for ChromeOS.
// If absent, user_display_mode_default may be used as fallback (if
// populated).
// Introduced in M122.
optional UserDisplayMode user_display_mode_cros = 10;
}
@@ -0,0 +1,173 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
// WebauthnCredentialSpecifics is an entity that backs a WebAuthn
// PublicKeyCredential. Since it contains the authenticator’s view of this
// object, it has a private key rather than a public key.
// (https://www.w3.org/TR/webauthn-2/#iface-pkcredential).
//
// Names of fields are taken from WebAuthn where possible. E.g.
// user.displayName in WebAuthn becomes user_display_name here.
//
// All fields are immutable after creation except for user_name and
// user_display_name, which may be updated by a user.
message WebauthnCredentialSpecifics {
// Sync's ID for this entity (sometimes called the client unique tag), 16
// random bytes. This value is used within Sync to identify this entity. The
// credential ID is not used because the (hashed) sync_id is exposed to the
// Sync server, and we don’t want Google to be able to map a credential ID to
// an account. Password entities construct this value from the concatenation
// of many fields and depend on the fact that the server only sees a hash of
// it. But the only high-entropy secret here is the private key, which will
// have different encryption in the future, and private keys are not the sort
// of data to copy into other fields. Therefore this independent value is
// provided to form the client's ID.
optional bytes sync_id = 1;
// The credential ID, 16 random bytes. This is a value surfaced in
// the WebAuthn API (https://www.w3.org/TR/webauthn-2/#credential-id).
optional bytes credential_id = 2;
// An RP ID is a WebAuthn concept:
// https://www.w3.org/TR/webauthn-2/#rp-id. It’s usually a domain name,
// although in non-Web contexts it can be a URL with a non-Web scheme.
optional string rp_id = 3;
// The user ID, which is also called a “user handle” in WebAuthn
// (https://www.w3.org/TR/webauthn-2/#user-handle), is an RP-specific
// identifier that is up to 64-bytes long. An authenticator conceptually only
// stores a single credential for a given (rp_id, user_id) pair, but there
// may be several credentials in Sync. They are prioritised using
// newly_shadowed_credential_ids and creation_time. See below.
//
// (We wish to be able to retain several entities for a single (rp_id,
// user_id) pair because there’s an edge case where we may wish to revert to
// an older entry and thus need to keep the older entry around in Sync. The
// revert could happen on a different device too.)
optional bytes user_id = 4;
// The id of credentials with the same (rp_id, user_id) that were
// shadowed by the creation of this entity.
//
// A credential is shadowed if one or more other credentials (from the same
// account, and with the same (rp_id, user_id)) include its credential_id in
// their list of shadowed IDs. Shadowed credentials are ignored when finding
// a credential to sign with. If there is more than one candidate remaining
// after filtering shadowed credentials then the most recently created (based
// on creation_time) is used.
//
// The reason for all this is that sites can replace a credential by creating
// another one with the same (rp_id, user_id) pair. However, we don't
// immediately know whether the WebAuthn response reached the website's
// server. Consider a user with a poor internet connection. Javascript in the
// site's origin triggers a credential creation that “overwrites” an existing
// credential, but the Javascript is unable to send the new public key to the
// website's server. The user is now locked out: the old credential has been
// over-written but the website's server doesn't know about the new one.
//
// Thus we wish to keep “overwritten” credentials around for a while to allow
// for some sort of recovery. In the simple case, a new credential shadows
// the single, previous old credential. We could depend on creation_time, but
// client clocks aren't always accurate, thus this field.
//
// In complicated cases two devices might race to replace a credential, in
// which case (after mutual syncing) two candidate credentials exist for the
// same (rp_id, user_id) pair because neither shadows the other. In this case
// we pick the newest based on |creation_time| but it's quite possible that
// some recovery will be needed because the website's server thinks the other
// one is correct.
//
// A generation counter isn't used because a single device might replace a
// series of credentials as it tries to update the website's server. But that
// doesn't mean that it should dominate a different device that replaced it
// only once, but later.
repeated bytes newly_shadowed_credential_ids = 5;
// The local time on the device when this credential was created. Given in
// milliseconds since the UNIX epoch. This is used to break ties between
// credentials. See newly_shadowed_credential_ids.
optional int64 creation_time = 6;
// The human-readable account identifier. Usually an email address. This is
// mutable.
// https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialentity-name
optional string user_name = 7;
// The human-readable name. Usually a legal name. This is mutable.
// https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialuserentity-displayname.
optional string user_display_name = 8;
// Reserving obsolete field.
reserved 10;
// Credentials may optionally be enabled for Secure Payment Confirmation[1] on
// third-party sites. This is opt-in at creation time.
//
// [1] https://www.w3.org/TR/secure-payment-confirmation/
optional bool third_party_payments_support = 11;
// Time when this passkey was last successfully asserted. Number of
// microseconds since 1601, aka Windows epoch. This mirrors the
// `date_last_used` field in PasswordSpecificsData.
optional int64 last_used_time_windows_epoch_micros = 13;
message Encrypted {
// The bytes of the private key, in PKCS#8 format.
optional bytes private_key = 1;
// The secret for implementing the hmac-secret extension
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-hmac-secret-extension
optional bytes hmac_secret = 2;
// The contents of the credential's credBlob.
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-credBlob-extension
optional bytes cred_blob = 3;
// The contents of the credential's largeBlob value(*). Unlike with security
// keys, largeBlob data is not stored in a single lump for all credentials,
// but as per-credential data. This data is presented to the authenticator
// over CTAP and thus has already had the required DEFLATE compression
// applied by the remote platform. The uncompressed size of this data is in
// the next field.
//
// (*) "large" with respect to embedded devices. Maximum length is 2KiB for
// Google Password Manager.
//
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#authenticatorLargeBlobs
optional bytes large_blob = 4;
// The claimed uncompressed size of the DEFLATE-compressed data in
// `large_blob`. This corresponds to the `origSize` field from the spec:
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#large-blob
optional uint64 large_blob_uncompressed_size = 5;
}
// The "version" (sometimes called the epoch) of the key that `encrypted_data`
// is encrypted with. This allows trial decryption to be avoided when present.
optional int32 key_version = 14;
oneof encrypted_data {
// The bytes of the private key, encrypted with a feature-specific security
// domain.
bytes private_key = 9;
// A serialized, `Encrypted` message, encrypted with a feature-specific
// security domain.
bytes encrypted = 12;
}
}
@@ -0,0 +1,94 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for Wi-Fi configuration items.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
message WifiConfigurationSpecifics {
// SSID encoded to hex, letters should be upper case and 0x prefix should be
// omitted. For example, ssid "network" would be provided as "6E6574776F726B".
optional bytes hex_ssid = 1;
enum SecurityType {
SECURITY_TYPE_UNSPECIFIED = 0;
SECURITY_TYPE_NONE = 1;
SECURITY_TYPE_WEP = 2;
SECURITY_TYPE_PSK = 3; // WPA-PSK or RSN-PSK
}
optional SecurityType security_type = 2;
// The passphrase can be ASCII, UTF-8, or a string of hex digits.
optional bytes passphrase = 3;
enum AutomaticallyConnectOption {
AUTOMATICALLY_CONNECT_UNSPECIFIED = 0;
AUTOMATICALLY_CONNECT_DISABLED = 1;
AUTOMATICALLY_CONNECT_ENABLED = 2;
}
optional AutomaticallyConnectOption automatically_connect = 4;
enum IsPreferredOption {
IS_PREFERRED_UNSPECIFIED = 0;
IS_PREFERRED_DISABLED = 1;
IS_PREFERRED_ENABLED = 2;
}
optional IsPreferredOption is_preferred = 5;
enum MeteredOption {
METERED_OPTION_UNSPECIFIED = 0;
METERED_OPTION_NO = 1;
METERED_OPTION_YES = 2;
// Allows the device to use heuristics to determine if network is metered.
METERED_OPTION_AUTO = 3;
}
optional MeteredOption metered = 6;
message ProxyConfiguration {
enum ProxyOption {
PROXY_OPTION_UNSPECIFIED = 0;
PROXY_OPTION_DISABLED = 1;
// Use a Proxy Auto-config(PAC) Url, set in proxy_url
PROXY_OPTION_AUTOMATIC = 2;
// Uses Web Proxy Auto-Discovery Protocol (WPAD) to discover the proxy
// settings using DHCP/DNS.
PROXY_OPTION_AUTODISCOVERY = 3;
// User sets details in manual_proxy_configuration.
PROXY_OPTION_MANUAL = 4;
}
optional ProxyOption proxy_option = 1;
// Only set if PROXY_OPTION_AUTOMATIC.
optional string autoconfiguration_url = 2;
message ManualProxyConfiguration {
optional string http_proxy_url = 1;
optional int32 http_proxy_port = 2;
optional string secure_http_proxy_url = 3;
optional int32 secure_http_proxy_port = 4;
optional string socks_host_url = 5;
optional int32 socks_host_port = 6;
repeated string excluded_domains = 7;
}
// Only set if PROXY_OPTION_MANUAL.
optional ManualProxyConfiguration manual_proxy_configuration = 3;
}
optional ProxyConfiguration proxy_configuration = 7;
enum DnsOption {
DNS_OPTION_UNSPECIFIED = 0;
DNS_OPTION_DEFAULT_DHCP = 1;
DNS_OPTION_CUSTOM = 2;
}
optional DnsOption dns_option = 10;
// List of DNS servers to be used when set to DNS_OPTION_CUSTOM. Up to 4.
repeated string custom_dns = 8;
// The last time this configuration was connected to before being synced. It
// will only be updated when the configuration is changed. This is represented
// with the UNIX timestamp, ms since epoch.
optional int64 last_connected_timestamp = 9;
}
@@ -0,0 +1,291 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Sync protocol datatype extension for desks on Chrome OS.
// If you change or add any fields in this file, update proto_visitors.h and
// potentially proto_enum_conversions.{h, cc}.
syntax = "proto2";
option go_package = "./sync_pb";
option java_multiple_files = true;
option java_package = "org.chromium.components.sync.protocol";
option optimize_for = LITE_RUNTIME;
package sync_pb;
import "sync_enums.proto";
// A workspace desk entry. This proto contains the fields synced to represent
// a saved desk in user's workspace.
message WorkspaceDeskSpecifics {
reserved "created_time_usec";
// A random unique identifier for each desk.
// Required.
optional string uuid = 1;
// The name of the desk being saved.
optional string name = 2;
// The time this snapshot of desk was created.
optional int64 created_time_windows_epoch_micros = 3;
// The time this snapshot of desk was updated.
optional int64 updated_time_windows_epoch_micros = 5;
optional Desk desk = 4;
// The type of a desk.
optional DeskType desk_type = 6;
// The cache guid of the syncing client. Used to identify a unique device
// that's syncing data.
optional string client_cache_guid = 7;
// The form factor of the device.
optional SyncEnums.DeviceFormFactor device_form_factor = 8;
// Represent a snapshot of the information on a desk.
message Desk {
// The apps in this template.
repeated App apps = 1;
}
// A Launchable app in template.
message App {
// The on-display location of this window.
optional WindowBound window_bound = 1;
optional WindowState window_state = 2;
// The z-index of this app relative to other apps in this template.
// App with larger z-index appears in front of app with smaller z-index.
optional int32 z_index = 3;
optional AppOneOf app = 4;
// window_id used to instantiate proto as a restore_data object. The
// restore_data object in question can be found at:
// //components/app_restore/restore_data.h
optional int32 window_id = 5;
// Display ID this app is on.
optional int64 display_id = 6;
// State of a window before it was minimized. Empty/unset for non-minimized
// windows.
optional WindowState pre_minimized_window_state = 7;
// Container this app was launched from. Empty/unset for Arc apps.
optional LaunchContainer container = 8;
// Window open disposition, defines how a window opens. Empty/unset for Arc
// apps.
optional WindowOpenDisposition disposition = 9;
// Name of the app which is used by the browser to determine whether to
// launch an app as an app or a tab. Does not contain a human readable name
// but contains the app ID and a prefix.
optional string app_name = 10;
// Human readable title of the app.
optional string title = 11;
// This is used to determine the size of a snapped window is in clamshell
// mode. Empty/unset for non-snapped windows. This should be a value between
// 0 and 100.
optional int32 snap_percentage = 12;
// The override URL can be specified by apps to override the default URL at
// launch.
optional string override_url = 13;
}
message AppOneOf {
oneof app {
BrowserAppWindow browser_app_window = 1;
ChromeApp chrome_app = 2;
ProgressiveWebApp progress_web_app = 3;
ArcApp arc_app = 4;
}
}
// A Chrome or Lacros Browser app window.
message BrowserAppWindow {
message BrowserAppTab {
// The last committed URL of the main frame of the tab.
optional string url = 1;
// The title of the tab.
optional string title = 2;
}
// Structure representing a tab group associated with this window.
message TabGroup {
// Zero-based index within the browser app tabs of the first tab in the
// group.
optional int32 first_index = 1;
// Zero-based Index wotjom the browser app tabs of the last tab in the
// group.
optional int32 last_index = 2;
// User readable title of the tab group.
optional string title = 3;
// Color associated with this tab group.
optional TabGroupColor color = 4;
// Indicates whether the tab group is collapsed.
optional bool is_collapsed = 5;
}
// The tabs in this browser window.
repeated BrowserAppTab tabs = 1;
// The index of the currently active tab.
optional int32 active_tab_index = 2;
// Indicates whether to show as a dedicated app window.
optional bool show_as_app = 3;
// Tab groups associated with this window.
repeated TabGroup tab_groups = 4;
// The index of the first non-pinned tab.
optional int32 first_non_pinned_tab_index = 5;
}
// A Chrome App window.
message ChromeApp {
// The Chrome App's App ID.
optional string app_id = 1;
reserved 2;
}
// A PWA window.
message ProgressiveWebApp {
// The PWA's App ID.
optional string app_id = 1;
reserved 2;
}
// An Arc App window.
message ArcApp {
// The Arc App's ID.
optional string app_id = 1;
// Arc window information. This maps
// to ArcExtraInfo in a WindowInfo struct.
optional WindowSize minimum_size = 2;
optional WindowSize maximum_size = 3;
reserved 4;
optional WindowBound bounds_in_root = 5;
// maps to gfx::Size. Width of the window, including the frame,
// in pixels.
message WindowSize {
optional int32 width = 1;
optional int32 height = 2;
}
}
message WindowBound {
// The offset of the window from the top edge of the screen in pixels.
optional int32 top = 1;
// The offset of the window from the left edge of the screen in pixels.
optional int32 left = 2;
// The width of the window, including the frame, in pixels.
optional int32 width = 3;
// The height of the window, including the frame, in pixels.
optional int32 height = 4;
}
// Enumeration for the container an app was launched from as used by
// (components/services/app_services/public/mojom/types.mojom:490).
enum LaunchContainer {
LAUNCH_CONTAINER_UNSPECIFIED = 0;
LAUNCH_CONTAINER_WINDOW = 1;
LAUNCH_CONTAINER_PANEL_DEPRECATED = 2;
LAUNCH_CONTAINER_TAB = 3;
LAUNCH_CONTAINER_NONE = 4;
}
// The state of a generic window.
enum WindowState {
UNKNOWN_WINDOW_STATE = 0;
// Normal window state (not minimized, maximized, or fullscreen).
NORMAL = 1;
// Minimized window state.
MINIMIZED = 2;
// Maximized window state.
MAXIMIZED = 3;
// Fullscreen window state.
FULLSCREEN = 4;
// Snapped to primary half of the screen. Primary half is on the left in
// landscape screen orientation and top in portrait screen orientation.
PRIMARY_SNAPPED = 5;
// Snapped to secondary half of the screen. Secondary half is on the right
// in landscape screen orientation and bottom in portrait screen
// orientation.
SECONDARY_SNAPPED = 6;
// Floated window state.
FLOATED = 7;
}
// Enumeration for how a window opens. Maps to base::WindowOpenDisposition.
// (found in ui/base/window_open_disposition.h:10). Does not include
// MAX_VALUE as MAX_VALUE is equivalent to NEW_PICTURE_IN_PICTURE. All
// comments are copied verbatim from aforementioned header.
enum WindowOpenDisposition {
UNKNOWN = 0;
CURRENT_TAB = 1;
// Indicates that only one tab with the URL shoul exist in the same window.
SINGLETON_TAB = 2;
NEW_FOREGROUND_TAB = 3;
NEW_BACKGROUND_TAB = 4;
NEW_POPUP = 5;
NEW_WINDOW = 6;
SAVE_TO_DISK = 7;
OFF_THE_RECORD = 8;
IGNORE_ACTION = 9;
// Activates an existing tab containing the URL, rather than navigating.
// This is similar to SINGLETON_TAB, but searches across all windows
// from the current profile and anymity (instead of tjust the current one);
// closes the current tab on switching if the current ta was the NTP with
// no session history; and behaves like CURRENT_TAB instead of
// NEW_FOREGROUND_TAB when no existing tab is found.
SWITCH_TO_TAB = 10;
// creates a new document picture-in-picture window showing a child WebView.
NEW_PICTURE_IN_PICTURE = 11;
}
// Enumerates the type of desk, namely to differentiate desks between desks
// saved for desk templates and desks saved for save and recall.
enum DeskType {
UNKNOWN_TYPE = 0;
TEMPLATE = 1;
SAVE_AND_RECALL = 2;
FLOATING_WORKSPACE = 3;
}
// Enumerates the possible colors that a tab group can have. This mirrors
// the enumeration that can be found in
// components/tab_groups/tab_group_color.h
enum TabGroupColor {
UNKNOWN_COLOR = 0;
GREY = 1;
BLUE = 2;
RED = 3;
YELLOW = 4;
GREEN = 5;
PINK = 6;
PURPLE = 7;
CYAN = 8;
ORANGE = 9;
}
}
+131
View File
@@ -0,0 +1,131 @@
use axum::http::HeaderMap;
use base64::{Engine, engine::general_purpose::URL_SAFE};
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
use regex::Regex;
use std::{env, sync::LazyLock};
use thiserror::Error;
use crate::util::now_millis;
pub const TOKEN_MAX_DURATION: i64 = 86_400 * 1_000;
const BEARER_PREFIX: &str = "Bearer ";
static TOKEN_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-fA-F0-9]+)\|([a-fA-F0-9]+)\|([a-fA-F0-9]+)$").unwrap());
#[derive(Debug, Error)]
pub enum AuthError {
#[error("not a valid token")]
InvalidToken,
#[error("invalid token format")]
InvalidTokenFormat,
#[error("token is expired")]
Expired,
#[error("signature verification failed")]
Signature,
#[error("this client ID is blocked")]
Blocked,
#[error("{0}")]
Decode(String),
}
pub fn authenticate(token: &str) -> Result<String, AuthError> {
let decoded = URL_SAFE
.decode(token)
.map_err(|err| AuthError::Decode(format!("error doing base64 decoding: {err}")))?;
let decoded = std::str::from_utf8(&decoded)
.map_err(|err| AuthError::Decode(format!("error decoding token as utf8: {err}")))?;
let captures = TOKEN_RE
.captures(decoded)
.ok_or(AuthError::InvalidTokenFormat)?;
let timestamp_hex = &captures[1];
let signed_timestamp_hex = &captures[2];
let public_key_hex = &captures[3];
let public_key = hex::decode(public_key_hex)
.map_err(|err| AuthError::Decode(format!("error decoding hex string: {err}")))?;
let timestamp_bytes = hex::decode(timestamp_hex)
.map_err(|err| AuthError::Decode(format!("error decoding hex string: {err}")))?;
let signature_bytes = hex::decode(signed_timestamp_hex)
.map_err(|err| AuthError::Decode(format!("error decoding hex string: {err}")))?;
let key = VerifyingKey::from_bytes(
public_key
.as_slice()
.try_into()
.map_err(|_| AuthError::InvalidTokenFormat)?,
)
.map_err(|_| AuthError::InvalidTokenFormat)?;
let signature =
Signature::from_slice(&signature_bytes).map_err(|_| AuthError::InvalidTokenFormat)?;
key.verify(&timestamp_bytes, &signature)
.map_err(|_| AuthError::Signature)?;
let timestamp = std::str::from_utf8(&timestamp_bytes)
.map_err(|err| AuthError::Decode(format!("error parsing timestamp: {err}")))?
.parse::<i64>()
.map_err(|err| AuthError::Decode(format!("error parsing timestamp: {err}")))?;
if (now_millis() - timestamp).abs() > TOKEN_MAX_DURATION {
return Err(AuthError::Expired);
}
let public_key_lc = public_key_hex.to_ascii_lowercase();
let blocked = env::var("BLOCKED_CLIENT_IDS").unwrap_or_default();
if blocked
.split(',')
.any(|id| id.trim().eq_ignore_ascii_case(&public_key_lc))
{
return Err(AuthError::Blocked);
}
Ok(public_key_lc)
}
pub fn authorize(headers: &HeaderMap) -> Result<String, AuthError> {
let token = headers
.get("authorization")
.and_then(|value| value.to_str().ok())
.ok_or(AuthError::InvalidToken)?;
let token = token
.strip_prefix(BEARER_PREFIX)
.ok_or(AuthError::InvalidToken)?;
if token.is_empty() {
return Err(AuthError::InvalidToken);
}
authenticate(token).map_err(|err| AuthError::Decode(format!("error authorizing: {err}")))
}
#[cfg(test)]
mod tests {
use super::*;
use base64::engine::general_purpose::URL_SAFE;
use ed25519_dalek::{Signer, SigningKey};
fn generate_token(timestamp: i64) -> (String, String) {
let key = SigningKey::from_bytes(&[7u8; 32]);
let timestamp_bytes = timestamp.to_string().into_bytes();
let signature = key.sign(&timestamp_bytes);
let public_key_hex = hex::encode(key.verifying_key().to_bytes());
let token_hex = format!(
"{}|{}|{}",
hex::encode(timestamp_bytes),
hex::encode(signature.to_bytes()),
public_key_hex
);
(URL_SAFE.encode(token_hex), public_key_hex)
}
#[test]
fn authenticate_accepts_valid_token() {
let (token, public_key_hex) = generate_token(now_millis());
let client_id = authenticate(&token).expect("token should authenticate");
assert_eq!(client_id, public_key_hex);
}
#[test]
fn authenticate_rejects_expired_token() {
let (token, _) = generate_token(now_millis() - TOKEN_MAX_DURATION - 1);
assert!(matches!(authenticate(&token), Err(AuthError::Expired)));
}
}
+84
View File
@@ -0,0 +1,84 @@
use std::{
collections::HashMap,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
const TTL: Duration = Duration::from_secs(600);
#[derive(Clone, Default)]
pub struct Cache {
inner: Arc<Mutex<CacheInner>>,
}
#[derive(Default)]
struct CacheInner {
type_mtimes: HashMap<String, (i64, Instant)>,
interim_counts: HashMap<String, i32>,
}
impl Cache {
pub fn get_type_mtime_key(client_id: &str, data_type: i32) -> String {
format!("{client_id}#{data_type}")
}
fn interim_count_key(client_id: &str, count_type: &str) -> String {
format!("{client_id}#interim_{count_type}")
}
pub fn get_interim_count(&self, client_id: &str, count_type: &str, clear_cache: bool) -> i32 {
let key = Self::interim_count_key(client_id, count_type);
let mut inner = self.inner.lock().expect("cache lock poisoned");
let count = *inner.interim_counts.get(&key).unwrap_or(&0);
if clear_cache {
inner.interim_counts.remove(&key);
}
count
}
pub fn increment_interim_count(
&self,
client_id: &str,
count_type: &str,
subtract: bool,
) -> i32 {
let key = Self::interim_count_key(client_id, count_type);
let mut inner = self.inner.lock().expect("cache lock poisoned");
let entry = inner.interim_counts.entry(key).or_default();
if subtract {
*entry -= 1;
} else {
*entry += 1;
}
*entry
}
pub fn set_type_mtime(&self, client_id: &str, data_type: i32, mtime: i64) {
let key = Self::get_type_mtime_key(client_id, data_type);
self.inner
.lock()
.expect("cache lock poisoned")
.type_mtimes
.insert(key, (mtime, Instant::now() + TTL));
}
pub fn is_type_mtime_updated(&self, client_id: &str, data_type: i32, token: i64) -> bool {
let key = Self::get_type_mtime_key(client_id, data_type);
let mut inner = self.inner.lock().expect("cache lock poisoned");
let Some((mtime, expires_at)) = inner.type_mtimes.get(&key).copied() else {
return true;
};
if Instant::now() > expires_at {
inner.type_mtimes.remove(&key);
return true;
}
mtime > token
}
pub fn del_type_mtime_keys(&self, keys: &[String]) {
let mut inner = self.inner.lock().expect("cache lock poisoned");
for key in keys {
inner.type_mtimes.remove(key);
}
}
}
+722
View File
@@ -0,0 +1,722 @@
#![allow(deprecated)]
use std::{collections::HashMap, env};
use prost::Message;
use thiserror::Error;
use uuid::Uuid;
use crate::{
cache::Cache,
datastore::{
DbSyncEntity, HISTORY_DELETE_DIRECTIVE_TYPE_ID, HISTORY_TYPE_ID, SqliteDatastore,
create_db_sync_entity, create_pb_sync_entity,
},
proto::{
BookmarkSpecifics, ClearServerDataMessage, ClearServerDataResponse, ClientCommand,
ClientToServerMessage, ClientToServerResponse, CommitMessage, CommitResponse,
DataTypeProgressMarker, EntitySpecifics, GetUpdatesMessage, GetUpdatesResponse,
NigoriSpecifics, SyncEntity, client_to_server_message, commit_response, entity_specifics,
sync_enums,
},
util::{now_millis, put_varint, read_varint},
};
const STORE_BIRTHDAY: &str = "1";
const MAX_COMMIT_BATCH_SIZE: i32 = 90;
const SET_SYNC_POLL_INTERVAL: i32 = 30;
const NIGORI_TYPE_ID: i32 = 47_745;
const DEVICE_INFO_TYPE_ID: i32 = 154_522;
const HISTORY_COUNT_TYPE: &str = "history";
const NORMAL_COUNT_TYPE: &str = "normal";
const MAX_ACTIVE_DEVICES: i32 = 50;
const HIGH_MAX_ACTIVE_DEVICES: i32 = 100;
const MAX_GU_BATCH_SIZE: i64 = 500;
const MAX_CLIENT_OBJECT_QUOTA: i32 = 50_000;
const MAX_CLIENT_HISTORY_OBJECT_QUOTA: i32 = 30_000;
#[derive(Debug, Error)]
pub enum CommandError {
#[error("{0}")]
Invalid(String),
#[error(transparent)]
Datastore(#[from] crate::datastore::DatastoreError),
}
type ErrorType = sync_enums::ErrorType;
pub fn handle_client_to_server_message(
cache: &Cache,
pb: &ClientToServerMessage,
db: &SqliteDatastore,
client_id: &str,
) -> Result<ClientToServerResponse, CommandError> {
let mut rsp = ClientToServerResponse {
store_birthday: Some(STORE_BIRTHDAY.to_string()),
client_command: Some(ClientCommand {
set_sync_poll_interval: Some(SET_SYNC_POLL_INTERVAL),
max_commit_batch_size: Some(MAX_COMMIT_BATCH_SIZE),
..ClientCommand::default()
}),
..ClientToServerResponse::default()
};
match client_to_server_message::Contents::try_from(pb.message_contents).map_err(|_| {
CommandError::Invalid("unsupported message type of ClientToServerMessage".to_string())
})? {
client_to_server_message::Contents::GetUpdates => {
let mut gu_rsp = GetUpdatesResponse::default();
let msg = pb.get_updates.as_ref().ok_or_else(|| {
CommandError::Invalid("nil GetUpdatesMessage is received".to_string())
})?;
let (code, err) = handle_get_updates_request(cache, msg, &mut gu_rsp, db, client_id);
rsp.get_updates = Some(gu_rsp);
rsp.error_code = Some(code as i32);
if let Some(err) = err {
rsp.error_message = Some(err);
}
}
client_to_server_message::Contents::Commit => {
let mut commit_rsp = CommitResponse::default();
let (code, err) =
handle_commit_request(cache, pb.commit.as_ref(), &mut commit_rsp, db, client_id);
rsp.commit = Some(commit_rsp);
rsp.error_code = Some(code as i32);
if let Some(err) = err {
rsp.error_message = Some(err);
}
}
client_to_server_message::Contents::ClearServerData => {
let (code, err) = handle_clear_server_data_request(
cache,
db,
pb.clear_server_data.as_ref(),
client_id,
);
rsp.clear_server_data = Some(ClearServerDataResponse {});
rsp.error_code = Some(code as i32);
if let Some(err) = err {
rsp.error_message = Some(err);
}
}
_ => {
return Err(CommandError::Invalid(
"unsupported message type of ClientToServerMessage".to_string(),
));
}
}
Ok(rsp)
}
fn handle_get_updates_request(
cache: &Cache,
gu_msg: &GetUpdatesMessage,
gu_rsp: &mut GetUpdatesResponse,
db: &SqliteDatastore,
client_id: &str,
) -> (ErrorType, Option<String>) {
let is_new_client =
gu_msg.get_updates_origin == Some(sync_enums::GetUpdatesOrigin::NewClient as i32);
let is_poll = gu_msg.get_updates_origin == Some(sync_enums::GetUpdatesOrigin::Periodic as i32);
if is_new_client {
let mut active_devices = 0;
loop {
let (has_changes_remaining, sync_entities) = match db.get_updates_for_type(
DEVICE_INFO_TYPE_ID,
0,
false,
client_id,
max_gu_batch_size(),
) {
Ok(result) => result,
Err(err) => {
return (
ErrorType::TransientError,
Some(format!(
"error getting updates for type {DEVICE_INFO_TYPE_ID}: {err}"
)),
);
}
};
for entity in sync_entities {
if !entity.deleted.unwrap_or(false) {
active_devices += 1;
}
if has_reached_device_limit(active_devices, client_id) {
return (
ErrorType::Throttled,
Some("exceed limit of active devices in a chain".to_string()),
);
}
}
if !has_changes_remaining {
break;
}
}
if let Err(err) = insert_server_defined_unique_entities(db, client_id) {
return (
ErrorType::TransientError,
Some(format!("error creating server defined entitiies: {err}")),
);
}
}
let mut changes_remaining = 0i64;
gu_rsp.changes_remaining = Some(changes_remaining);
if gu_msg.from_progress_marker.is_empty() {
return (ErrorType::Success, None);
}
let fetch_folders = gu_msg.fetch_folders.unwrap_or(true);
let max_size = max_gu_batch_size();
gu_rsp.new_progress_marker = Vec::with_capacity(gu_msg.from_progress_marker.len());
gu_rsp.entries = Vec::with_capacity(max_size as usize);
for from_marker in &gu_msg.from_progress_marker {
let mut new_marker = DataTypeProgressMarker {
data_type_id: from_marker.data_type_id,
token: from_marker
.token
.as_ref()
.filter(|token| !token.is_empty())
.cloned()
.or_else(|| Some(put_varint(0))),
..DataTypeProgressMarker::default()
};
let data_type = from_marker.data_type_id.unwrap_or_default();
if data_type == NIGORI_TYPE_ID && is_new_client {
gu_rsp.encryption_keys = vec![b"1234".to_vec()];
}
if gu_rsp.entries.len() as i64 >= max_size {
gu_rsp.new_progress_marker.push(new_marker);
continue;
}
let token_bytes = new_marker.token.clone().unwrap_or_default();
let Some(token) = read_varint(&token_bytes) else {
return (
ErrorType::TransientError,
Some("failed at decoding token value".to_string()),
);
};
if is_poll && !cache.is_type_mtime_updated(client_id, data_type, token) {
gu_rsp.new_progress_marker.push(new_marker);
continue;
}
let remaining_size = max_size - gu_rsp.entries.len() as i64;
let (has_changes_remaining, entities) = match db.get_updates_for_type(
data_type,
token,
fetch_folders,
client_id,
remaining_size,
) {
Ok(result) => result,
Err(err) => {
return (
ErrorType::TransientError,
Some(format!("error getting updates for type {data_type}: {err}")),
);
}
};
if is_new_client && data_type == NIGORI_TYPE_ID && token == 0 && entities.is_empty() {
return (
ErrorType::TransientError,
Some("nigori root folder entity is not ready yet".to_string()),
);
}
if has_changes_remaining {
changes_remaining = 1;
gu_rsp.changes_remaining = Some(changes_remaining);
}
let mut last_mtime = None;
for entity in entities {
match create_pb_sync_entity(&entity) {
Ok(pb_entity) => {
last_mtime = Some(entity.mtime);
gu_rsp.entries.push(pb_entity);
if gu_rsp.entries.len() as i64 >= max_size {
break;
}
}
Err(err) => {
return (
ErrorType::TransientError,
Some(format!(
"error creating protobuf sync entity from DB entity: {err}"
)),
);
}
}
}
if let Some(mtime) = last_mtime {
new_marker.token = Some(put_varint(mtime));
}
if changes_remaining != 1 {
cache.set_type_mtime(client_id, data_type, last_mtime.unwrap_or(token));
}
gu_rsp.new_progress_marker.push(new_marker);
}
(ErrorType::Success, None)
}
fn handle_commit_request(
cache: &Cache,
commit_msg: Option<&CommitMessage>,
commit_rsp: &mut CommitResponse,
db: &SqliteDatastore,
client_id: &str,
) -> (ErrorType, Option<String>) {
let Some(commit_msg) = commit_msg else {
return (
ErrorType::TransientError,
Some("nil commitMsg is received".to_string()),
);
};
if commit_msg.entries.is_empty() {
return (ErrorType::Success, None);
}
let mut item_counts = match db.get_client_item_count(client_id) {
Ok(counts) => counts,
Err(err) => {
return (
ErrorType::TransientError,
Some(format!("error getting client's item count: {err}")),
);
}
};
let mut new_normal_count = cache.get_interim_count(client_id, NORMAL_COUNT_TYPE, false);
let mut new_history_count = cache.get_interim_count(client_id, HISTORY_COUNT_TYPE, false);
let current_normal_count = item_counts.item_count;
let current_history_count = item_counts.sum_history_counts();
let boosted_quota_addition = if current_history_count > MAX_CLIENT_HISTORY_OBJECT_QUOTA {
(MAX_CLIENT_OBJECT_QUOTA - MAX_CLIENT_HISTORY_OBJECT_QUOTA)
.min(current_history_count - MAX_CLIENT_HISTORY_OBJECT_QUOTA)
} else {
0
};
commit_rsp.entryresponse = Vec::with_capacity(commit_msg.entries.len());
let mut id_map = HashMap::<String, String>::new();
let mut type_mtime_map = HashMap::<i32, i64>::new();
let mut size_monitor = ItemSizeMonitor::default();
for entry in &commit_msg.entries {
let mut entry_rsp = commit_response::EntryResponse::default();
let mut entity =
match create_db_sync_entity(entry, commit_msg.cache_guid.as_deref(), client_id) {
Ok(entity) => entity,
Err(err) => {
entry_rsp.response_type = commit_response::ResponseType::InvalidMessage as i32;
entry_rsp.error_message = Some(format!(
"Cannot convert protobuf sync entity to DB format: {err}"
));
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
};
size_monitor.observe(entity.data_type, entry);
if let Some(parent_id) = entity.parent_id.clone() {
if let Some(server_parent_id) = id_map.get(&parent_id) {
entity.parent_id = Some(server_parent_id.clone());
}
}
let old_version = entity.version;
let mut is_update_op = old_version != 0;
let is_history_related = entity.data_type == HISTORY_TYPE_ID
|| entity.data_type == HISTORY_DELETE_DIRECTIVE_TYPE_ID;
entity.version = entity.mtime;
if entity.data_type == HISTORY_TYPE_ID {
let Some(tag) = entity.client_defined_unique_tag.as_deref() else {
entry_rsp.response_type = commit_response::ResponseType::InvalidMessage as i32;
entry_rsp.error_message =
Some("History sync entity is missing client tag".to_string());
commit_rsp.entryresponse.push(entry_rsp);
continue;
};
match db.has_item(client_id, tag) {
Ok(exists) => is_update_op = exists,
Err(err) => {
entry_rsp.response_type = commit_response::ResponseType::TransientError as i32;
entry_rsp.error_message =
Some(format!("Insert history sync entity failed: {err}"));
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
}
}
if !is_update_op {
if current_normal_count + current_history_count + new_normal_count + new_history_count
>= MAX_CLIENT_OBJECT_QUOTA + boosted_quota_addition
{
entry_rsp.response_type = commit_response::ResponseType::OverQuota as i32;
entry_rsp.error_message = Some(format!(
"There are already {} non-deleted objects in store",
current_normal_count + current_history_count
));
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
if !is_history_related
|| current_history_count + new_history_count < MAX_CLIENT_HISTORY_OBJECT_QUOTA
{
match db.insert_sync_entity(&entity) {
Ok((conflict, err_msg)) if conflict => {
entry_rsp.response_type = commit_response::ResponseType::Conflict as i32;
entry_rsp.error_message = err_msg;
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
Ok(_) => {
if let Some(originator_id) = entity.originator_client_item_id.clone() {
id_map.insert(originator_id, entity.id.clone());
}
if is_history_related {
new_history_count =
cache.increment_interim_count(client_id, HISTORY_COUNT_TYPE, false);
} else {
new_normal_count =
cache.increment_interim_count(client_id, NORMAL_COUNT_TYPE, false);
}
}
Err(err) => {
entry_rsp.response_type =
commit_response::ResponseType::TransientError as i32;
entry_rsp.error_message = Some(format!("Insert sync entity failed: {err}"));
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
}
}
} else {
match db.update_sync_entity(&entity, old_version) {
Ok((true, _)) => {
entry_rsp.response_type = commit_response::ResponseType::Conflict as i32;
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
Ok((false, deleted)) => {
if deleted {
if is_history_related {
new_history_count =
cache.increment_interim_count(client_id, HISTORY_COUNT_TYPE, true);
} else {
new_normal_count =
cache.increment_interim_count(client_id, NORMAL_COUNT_TYPE, true);
}
}
}
Err(err) => {
entry_rsp.response_type = commit_response::ResponseType::TransientError as i32;
entry_rsp.error_message = Some(format!("Update sync entity failed: {err}"));
commit_rsp.entryresponse.push(entry_rsp);
continue;
}
}
}
type_mtime_map.insert(entity.data_type, entity.mtime);
entry_rsp.response_type = commit_response::ResponseType::Success as i32;
entry_rsp.id_string = Some(entity.id.clone());
entry_rsp.version = Some(entity.version);
entry_rsp.mtime = Some(entity.mtime);
commit_rsp.entryresponse.push(entry_rsp);
}
size_monitor.log_warnings();
new_normal_count = cache.get_interim_count(client_id, NORMAL_COUNT_TYPE, true);
new_history_count = cache.get_interim_count(client_id, HISTORY_COUNT_TYPE, true);
for (data_type, mtime) in type_mtime_map {
cache.set_type_mtime(client_id, data_type, mtime);
}
if let Err(err) =
db.update_client_item_count(&mut item_counts, new_normal_count, new_history_count)
{
tracing::error!(error = %err, "Update client item count failed");
}
(ErrorType::Success, None)
}
fn handle_clear_server_data_request(
cache: &Cache,
db: &SqliteDatastore,
_msg: Option<&ClearServerDataMessage>,
client_id: &str,
) -> (ErrorType, Option<String>) {
if let Err(err) = db.disable_sync_chain(client_id) {
return (ErrorType::TransientError, Some(err.to_string()));
}
let entities = match db.clear_server_data(client_id) {
Ok(entities) => entities,
Err(err) => return (ErrorType::TransientError, Some(err.to_string())),
};
let keys = entities
.into_iter()
.map(|entity| Cache::get_type_mtime_key(&entity.client_id, entity.data_type))
.collect::<Vec<_>>();
cache.del_type_mtime_keys(&keys);
(ErrorType::Success, None)
}
fn insert_server_defined_unique_entities(
db: &SqliteDatastore,
client_id: &str,
) -> Result<(), CommandError> {
if db.has_server_defined_unique_tag(client_id, "google_chrome_nigori")? {
return Ok(());
}
let nigori = EntitySpecifics {
encrypted: None,
specifics_variant: Some(entity_specifics::SpecificsVariant::Nigori(
NigoriSpecifics::default(),
)),
};
let bookmark = EntitySpecifics {
encrypted: None,
specifics_variant: Some(entity_specifics::SpecificsVariant::Bookmark(
BookmarkSpecifics::default(),
)),
};
let mut entities = Vec::new();
entities.push(create_server_defined_unique_entity(
"Nigori",
"google_chrome_nigori",
client_id,
"0",
nigori,
)?);
let bookmark_root = create_server_defined_unique_entity(
"Bookmarks",
"google_chrome_bookmarks",
client_id,
"0",
bookmark.clone(),
)?;
let bookmark_root_id = bookmark_root.id.clone();
entities.push(bookmark_root);
for (name, tag) in [
("Other Bookmarks", "other_bookmarks"),
("Synced Bookmarks", "synced_bookmarks"),
("Bookmark Bar", "bookmark_bar"),
] {
entities.push(create_server_defined_unique_entity(
name,
tag,
client_id,
&bookmark_root_id,
bookmark.clone(),
)?);
}
db.insert_sync_entities_with_server_tags(&entities)?;
Ok(())
}
fn create_server_defined_unique_entity(
name: &str,
server_defined_tag: &str,
client_id: &str,
parent_id: &str,
specifics: EntitySpecifics,
) -> Result<DbSyncEntity, CommandError> {
let now = now_millis();
let pb_entity = SyncEntity {
ctime: Some(now),
mtime: Some(now),
deleted: Some(false),
folder: Some(true),
name: Some(name.to_string()),
server_defined_unique_tag: Some(server_defined_tag.to_string()),
version: Some(1),
parent_id_string: Some(parent_id.to_string()),
id_string: Some(Uuid::new_v4().to_string()),
specifics: Some(specifics),
..SyncEntity::default()
};
Ok(create_db_sync_entity(&pb_entity, None, client_id)?)
}
fn has_reached_device_limit(active_devices: i32, client_id: &str) -> bool {
let high_limit = env::var("HIGH_DEVICE_LIMIT_CLIENT_IDS")
.unwrap_or_default()
.split(',')
.any(|id| id.trim().eq_ignore_ascii_case(client_id));
let limit = if high_limit {
HIGH_MAX_ACTIVE_DEVICES
} else {
MAX_ACTIVE_DEVICES
};
active_devices >= limit
}
fn max_gu_batch_size() -> i64 {
MAX_GU_BATCH_SIZE
}
#[derive(Default)]
struct ItemSizeMonitor {
stats_by_type: HashMap<i32, DataTypeStats>,
}
#[derive(Default)]
struct DataTypeStats {
count: usize,
max_size: usize,
}
impl ItemSizeMonitor {
fn observe(&mut self, data_type: i32, msg: &SyncEntity) {
let size = msg.encoded_len();
if size <= 400 * 1024 {
return;
}
let stats = self.stats_by_type.entry(data_type).or_default();
stats.count += 1;
stats.max_size = stats.max_size.max(size);
}
fn log_warnings(&self) {
for (data_type, stats) in &self.stats_by_type {
tracing::warn!(
data_type,
count = stats.count,
max_size = stats.max_size,
threshold_bytes = 400 * 1024,
"Commit entries exceeded size threshold"
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{datastore::SqliteDatastore, util::read_varint};
const CLIENT_ID: &str = "client";
const CACHE_GUID: &str = "cache-guid";
const BOOKMARK_TYPE: i32 = 32_904;
fn bookmark_specifics() -> EntitySpecifics {
EntitySpecifics {
encrypted: None,
specifics_variant: Some(entity_specifics::SpecificsVariant::Bookmark(
BookmarkSpecifics::default(),
)),
}
}
fn nigori_specifics() -> EntitySpecifics {
EntitySpecifics {
encrypted: None,
specifics_variant: Some(entity_specifics::SpecificsVariant::Nigori(
NigoriSpecifics::default(),
)),
}
}
fn commit_entity(id: &str, version: i64, specifics: EntitySpecifics) -> SyncEntity {
SyncEntity {
id_string: Some(id.to_string()),
name: Some(id.to_string()),
version: Some(version),
deleted: Some(false),
folder: Some(false),
specifics: Some(specifics),
..SyncEntity::default()
}
}
#[test]
fn commit_then_get_updates_round_trips_entities() {
let db = SqliteDatastore::in_memory().expect("sqlite");
let cache = Cache::default();
let msg = ClientToServerMessage {
share: String::new(),
message_contents: client_to_server_message::Contents::Commit as i32,
commit: Some(CommitMessage {
entries: vec![
commit_entity("bookmark-1", 0, bookmark_specifics()),
commit_entity("nigori-1", 0, nigori_specifics()),
],
cache_guid: Some(CACHE_GUID.to_string()),
..CommitMessage::default()
}),
..ClientToServerMessage::default()
};
let rsp = handle_client_to_server_message(&cache, &msg, &db, CLIENT_ID).expect("commit");
assert_eq!(rsp.error_code, Some(ErrorType::Success as i32));
let commit_rsp = rsp.commit.expect("commit response");
assert_eq!(commit_rsp.entryresponse.len(), 2);
assert!(
commit_rsp
.entryresponse
.iter()
.all(|entry| entry.response_type == commit_response::ResponseType::Success as i32)
);
let gu_msg = ClientToServerMessage {
share: String::new(),
message_contents: client_to_server_message::Contents::GetUpdates as i32,
get_updates: Some(GetUpdatesMessage {
fetch_folders: Some(false),
get_updates_origin: Some(sync_enums::GetUpdatesOrigin::GuTrigger as i32),
from_progress_marker: vec![
DataTypeProgressMarker {
data_type_id: Some(NIGORI_TYPE_ID),
token: Some(put_varint(0)),
..DataTypeProgressMarker::default()
},
DataTypeProgressMarker {
data_type_id: Some(BOOKMARK_TYPE),
token: Some(put_varint(0)),
..DataTypeProgressMarker::default()
},
],
..GetUpdatesMessage::default()
}),
..ClientToServerMessage::default()
};
let rsp = handle_client_to_server_message(&cache, &gu_msg, &db, CLIENT_ID).expect("gu");
assert_eq!(rsp.error_code, Some(ErrorType::Success as i32));
let gu_rsp = rsp.get_updates.expect("gu response");
assert_eq!(gu_rsp.changes_remaining, Some(0));
assert_eq!(gu_rsp.entries.len(), 2);
assert_eq!(gu_rsp.new_progress_marker.len(), 2);
assert!(
gu_rsp.new_progress_marker.iter().all(|marker| read_varint(
marker.token.as_deref().unwrap_or_default()
)
.unwrap()
> 0)
);
}
}
+778
View File
@@ -0,0 +1,778 @@
use prost::Message;
use rusqlite::{
Connection, OptionalExtension, Transaction, params, params_from_iter,
types::{ToSqlOutput, Value},
};
use std::sync::{Arc, Mutex};
use thiserror::Error;
use uuid::Uuid;
use crate::{
proto::{EntitySpecifics, SyncEntity, UniquePosition},
util::{now_millis, now_secs, read_key_varint},
};
pub const HISTORY_TYPE_ID: i32 = 963_985;
pub const HISTORY_DELETE_DIRECTIVE_TYPE_ID: i32 = 150_251;
pub const HISTORY_EXPIRATION_INTERVAL_SECS: i64 = 14 * 24 * 60 * 60;
pub const CURRENT_COUNT_VERSION: i32 = 2;
const PERIOD_DURATION_SECS: i64 = HISTORY_EXPIRATION_INTERVAL_SECS / 4;
const REASON_DELETED: &str = "deleted";
#[derive(Debug, Error)]
pub enum DatastoreError {
#[error("{0}")]
Invalid(String),
#[error(transparent)]
Sql(#[from] rusqlite::Error),
#[error(transparent)]
Decode(#[from] prost::DecodeError),
#[error(transparent)]
Encode(#[from] prost::EncodeError),
}
pub type Result<T> = std::result::Result<T, DatastoreError>;
#[derive(Clone)]
pub struct SqliteDatastore {
conn: Arc<Mutex<Connection>>,
}
#[derive(Clone, Debug)]
pub struct DbSyncEntity {
pub client_id: String,
pub id: String,
pub parent_id: Option<String>,
pub version: i64,
pub mtime: i64,
pub ctime: i64,
pub name: Option<String>,
pub non_unique_name: Option<String>,
pub server_defined_unique_tag: Option<String>,
pub deleted: Option<bool>,
pub originator_cache_guid: Option<String>,
pub originator_client_item_id: Option<String>,
pub specifics: Vec<u8>,
pub data_type: i32,
pub folder: Option<bool>,
pub client_defined_unique_tag: Option<String>,
pub unique_position: Option<Vec<u8>>,
pub data_type_mtime: String,
pub expiration_time: Option<i64>,
}
#[derive(Clone, Debug, Default)]
pub struct ClientItemCounts {
pub client_id: String,
pub id: String,
pub item_count: i32,
pub history_item_count_period1: i32,
pub history_item_count_period2: i32,
pub history_item_count_period3: i32,
pub history_item_count_period4: i32,
pub last_period_change_time: i64,
pub version: i32,
}
impl ClientItemCounts {
pub fn sum_history_counts(&self) -> i32 {
self.history_item_count_period1
+ self.history_item_count_period2
+ self.history_item_count_period3
+ self.history_item_count_period4
}
}
impl SqliteDatastore {
pub fn open(path: &str) -> Result<Self> {
let conn = Connection::open(path)?;
conn.pragma_update(None, "journal_mode", "WAL")?;
conn.pragma_update(None, "foreign_keys", "ON")?;
let store = Self {
conn: Arc::new(Mutex::new(conn)),
};
store.migrate()?;
Ok(store)
}
#[cfg(test)]
pub fn in_memory() -> Result<Self> {
let conn = Connection::open_in_memory()?;
let store = Self {
conn: Arc::new(Mutex::new(conn)),
};
store.migrate()?;
Ok(store)
}
fn migrate(&self) -> Result<()> {
let conn = self.conn.lock().expect("sqlite lock poisoned");
conn.execute_batch(
r#"
CREATE TABLE IF NOT EXISTS sync_entities (
client_id TEXT NOT NULL,
id TEXT NOT NULL,
parent_id TEXT,
version INTEGER NOT NULL,
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL,
name TEXT,
non_unique_name TEXT,
server_defined_unique_tag TEXT,
deleted INTEGER,
originator_cache_guid TEXT,
originator_client_item_id TEXT,
specifics BLOB NOT NULL,
data_type INTEGER NOT NULL,
folder INTEGER,
client_defined_unique_tag TEXT,
unique_position BLOB,
data_type_mtime TEXT NOT NULL,
expiration_time INTEGER,
PRIMARY KEY (client_id, id)
);
CREATE INDEX IF NOT EXISTS idx_sync_entities_updates
ON sync_entities (client_id, data_type, mtime);
CREATE TABLE IF NOT EXISTS unique_tags (
client_id TEXT NOT NULL,
tag_kind TEXT NOT NULL,
tag TEXT NOT NULL,
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL,
PRIMARY KEY (client_id, tag_kind, tag)
);
CREATE TABLE IF NOT EXISTS item_counts (
client_id TEXT PRIMARY KEY,
id TEXT NOT NULL,
item_count INTEGER NOT NULL,
history_item_count_period1 INTEGER NOT NULL,
history_item_count_period2 INTEGER NOT NULL,
history_item_count_period3 INTEGER NOT NULL,
history_item_count_period4 INTEGER NOT NULL,
last_period_change_time INTEGER NOT NULL,
version INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS disabled_chains (
client_id TEXT PRIMARY KEY,
reason TEXT NOT NULL,
mtime INTEGER NOT NULL,
ctime INTEGER NOT NULL
);
"#,
)?;
Ok(())
}
pub fn insert_sync_entity(&self, entity: &DbSyncEntity) -> Result<(bool, Option<String>)> {
let mut conn = self.conn.lock().expect("sqlite lock poisoned");
let tx = conn.transaction()?;
if entity.client_defined_unique_tag.is_some() && entity.data_type != HISTORY_TYPE_ID {
if let Err(err) = insert_tag(
&tx,
&entity.client_id,
"Client",
entity.client_defined_unique_tag.as_deref().unwrap(),
) {
if is_constraint(&err) {
return Ok((true, Some(err.to_string())));
}
return Err(err);
}
}
if let Err(err) = insert_entity(&tx, entity) {
if is_constraint(&err) {
return Ok((true, Some(err.to_string())));
}
return Err(err);
}
tx.commit()?;
Ok((false, None))
}
pub fn insert_sync_entities_with_server_tags(&self, entities: &[DbSyncEntity]) -> Result<()> {
let mut conn = self.conn.lock().expect("sqlite lock poisoned");
let tx = conn.transaction()?;
for entity in entities {
let tag = entity.server_defined_unique_tag.as_deref().ok_or_else(|| {
DatastoreError::Invalid("server-defined entity is missing tag".to_string())
})?;
insert_tag(&tx, &entity.client_id, "Server", tag)?;
insert_entity(&tx, entity)?;
}
tx.commit()?;
Ok(())
}
pub fn update_sync_entity(
&self,
entity: &DbSyncEntity,
old_version: i64,
) -> Result<(bool, bool)> {
let mut conn = self.conn.lock().expect("sqlite lock poisoned");
let tx = conn.transaction()?;
let old: Option<(i64, Option<bool>)> = tx
.query_row(
"SELECT version, deleted FROM sync_entities WHERE client_id = ?1 AND id = ?2",
params![entity.client_id, entity.id],
|row| Ok((row.get(0)?, row.get(1)?)),
)
.optional()?;
let Some((stored_version, old_deleted)) = old else {
return Ok((true, false));
};
if entity.data_type != HISTORY_TYPE_ID && stored_version != old_version {
return Ok((true, false));
}
update_entity(&tx, entity)?;
let was_deleted = old_deleted.unwrap_or(false);
let deleted_now = entity.deleted.unwrap_or(was_deleted);
let newly_deleted = !was_deleted && deleted_now;
if newly_deleted
&& entity.client_defined_unique_tag.is_some()
&& entity.data_type != HISTORY_TYPE_ID
{
tx.execute(
"DELETE FROM unique_tags WHERE client_id = ?1 AND tag_kind = 'Client' AND tag = ?2",
params![entity.client_id, entity.client_defined_unique_tag],
)?;
}
tx.commit()?;
Ok((false, newly_deleted))
}
pub fn get_updates_for_type(
&self,
data_type: i32,
client_token: i64,
fetch_folders: bool,
client_id: &str,
max_size: i64,
) -> Result<(bool, Vec<DbSyncEntity>)> {
let limit = max_size.saturating_add(1);
let now = now_secs();
let conn = self.conn.lock().expect("sqlite lock poisoned");
let mut sql = String::from(
"SELECT client_id, id, parent_id, version, mtime, ctime, name, non_unique_name, \
server_defined_unique_tag, deleted, originator_cache_guid, originator_client_item_id, \
specifics, data_type, folder, client_defined_unique_tag, unique_position, \
data_type_mtime, expiration_time \
FROM sync_entities \
WHERE client_id = ?1 AND data_type = ?2 AND mtime > ?3 \
AND (expiration_time IS NULL OR expiration_time <= 0 OR expiration_time >= ?4)",
);
if !fetch_folders {
sql.push_str(" AND folder = 0");
}
sql.push_str(" ORDER BY mtime ASC LIMIT ?5");
let mut stmt = conn.prepare(&sql)?;
let mut rows = stmt.query_map(
params![client_id, data_type, client_token, now, limit],
row_to_entity,
)?;
let mut entities = Vec::new();
for row in rows.by_ref() {
entities.push(row?);
}
let has_changes_remaining = entities.len() as i64 > max_size;
if has_changes_remaining {
entities.truncate(max_size as usize);
}
Ok((has_changes_remaining, entities))
}
pub fn has_server_defined_unique_tag(&self, client_id: &str, tag: &str) -> Result<bool> {
let conn = self.conn.lock().expect("sqlite lock poisoned");
let exists: Option<i32> = conn
.query_row(
"SELECT 1 FROM unique_tags WHERE client_id = ?1 AND tag_kind = 'Server' AND tag = ?2",
params![client_id, tag],
|row| row.get(0),
)
.optional()?;
Ok(exists.is_some())
}
pub fn has_item(&self, client_id: &str, id: &str) -> Result<bool> {
let conn = self.conn.lock().expect("sqlite lock poisoned");
let exists: Option<i32> = conn
.query_row(
"SELECT 1 FROM sync_entities WHERE client_id = ?1 AND id = ?2",
params![client_id, id],
|row| row.get(0),
)
.optional()?;
Ok(exists.is_some())
}
pub fn get_client_item_count(&self, client_id: &str) -> Result<ClientItemCounts> {
let conn = self.conn.lock().expect("sqlite lock poisoned");
let mut counts = conn
.query_row(
"SELECT client_id, id, item_count, history_item_count_period1, \
history_item_count_period2, history_item_count_period3, history_item_count_period4, \
last_period_change_time, version FROM item_counts WHERE client_id = ?1",
params![client_id],
|row| {
Ok(ClientItemCounts {
client_id: row.get(0)?,
id: row.get(1)?,
item_count: row.get(2)?,
history_item_count_period1: row.get(3)?,
history_item_count_period2: row.get(4)?,
history_item_count_period3: row.get(5)?,
history_item_count_period4: row.get(6)?,
last_period_change_time: row.get(7)?,
version: row.get(8)?,
})
},
)
.optional()?
.unwrap_or_else(|| ClientItemCounts {
client_id: client_id.to_string(),
id: client_id.to_string(),
..ClientItemCounts::default()
});
drop(conn);
self.init_real_counts_and_update_history_counts(&mut counts)?;
Ok(counts)
}
fn init_real_counts_and_update_history_counts(
&self,
counts: &mut ClientItemCounts,
) -> Result<()> {
let now = now_secs();
if counts.version < CURRENT_COUNT_VERSION {
if counts.item_count > 0 {
let conn = self.conn.lock().expect("sqlite lock poisoned");
let history_count: i32 = conn.query_row(
"SELECT COUNT(*) FROM sync_entities WHERE client_id = ?1 \
AND data_type IN (?2, ?3) AND deleted = 0",
params![
counts.client_id,
HISTORY_TYPE_ID,
HISTORY_DELETE_DIRECTIVE_TYPE_ID
],
|row| row.get(0),
)?;
let normal_count: i32 = conn.query_row(
"SELECT COUNT(*) FROM sync_entities WHERE client_id = ?1 \
AND data_type NOT IN (?2, ?3) AND deleted = 0",
params![
counts.client_id,
HISTORY_TYPE_ID,
HISTORY_DELETE_DIRECTIVE_TYPE_ID
],
|row| row.get(0),
)?;
counts.history_item_count_period1 = 0;
counts.history_item_count_period2 = 0;
counts.history_item_count_period3 = 0;
counts.history_item_count_period4 = history_count;
counts.item_count = normal_count;
}
counts.last_period_change_time = now;
counts.version = CURRENT_COUNT_VERSION;
return Ok(());
}
let elapsed = now - counts.last_period_change_time;
if elapsed >= PERIOD_DURATION_SECS {
let change_count = elapsed / PERIOD_DURATION_SECS;
for _ in 0..change_count {
counts.history_item_count_period1 = counts.history_item_count_period2;
counts.history_item_count_period2 = counts.history_item_count_period3;
counts.history_item_count_period3 = counts.history_item_count_period4;
counts.history_item_count_period4 = 0;
}
counts.last_period_change_time += PERIOD_DURATION_SECS * change_count;
}
Ok(())
}
pub fn update_client_item_count(
&self,
counts: &mut ClientItemCounts,
new_normal_item_count: i32,
new_history_item_count: i32,
) -> Result<()> {
counts.history_item_count_period4 += new_history_item_count;
counts.item_count += new_normal_item_count;
let conn = self.conn.lock().expect("sqlite lock poisoned");
conn.execute(
"INSERT INTO item_counts (
client_id, id, item_count, history_item_count_period1, history_item_count_period2,
history_item_count_period3, history_item_count_period4, last_period_change_time, version
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
ON CONFLICT(client_id) DO UPDATE SET
id = excluded.id,
item_count = excluded.item_count,
history_item_count_period1 = excluded.history_item_count_period1,
history_item_count_period2 = excluded.history_item_count_period2,
history_item_count_period3 = excluded.history_item_count_period3,
history_item_count_period4 = excluded.history_item_count_period4,
last_period_change_time = excluded.last_period_change_time,
version = excluded.version",
params![
counts.client_id,
counts.id,
counts.item_count,
counts.history_item_count_period1,
counts.history_item_count_period2,
counts.history_item_count_period3,
counts.history_item_count_period4,
counts.last_period_change_time,
counts.version,
],
)?;
Ok(())
}
pub fn clear_server_data(&self, client_id: &str) -> Result<Vec<DbSyncEntity>> {
let mut conn = self.conn.lock().expect("sqlite lock poisoned");
let tx = conn.transaction()?;
let entities = {
let mut stmt = tx.prepare(
"SELECT client_id, id, parent_id, version, mtime, ctime, name, non_unique_name, \
server_defined_unique_tag, deleted, originator_cache_guid, originator_client_item_id, \
specifics, data_type, folder, client_defined_unique_tag, unique_position, \
data_type_mtime, expiration_time FROM sync_entities WHERE client_id = ?1",
)?;
stmt.query_map(params![client_id], row_to_entity)?
.collect::<std::result::Result<Vec<_>, _>>()?
};
tx.execute(
"DELETE FROM sync_entities WHERE client_id = ?1",
params![client_id],
)?;
tx.execute(
"DELETE FROM unique_tags WHERE client_id = ?1",
params![client_id],
)?;
tx.execute(
"DELETE FROM item_counts WHERE client_id = ?1",
params![client_id],
)?;
tx.commit()?;
Ok(entities)
}
pub fn disable_sync_chain(&self, client_id: &str) -> Result<()> {
let now = now_millis();
let conn = self.conn.lock().expect("sqlite lock poisoned");
conn.execute(
"INSERT INTO disabled_chains (client_id, reason, mtime, ctime) VALUES (?1, ?2, ?3, ?3)
ON CONFLICT(client_id) DO UPDATE SET reason = excluded.reason, mtime = excluded.mtime",
params![client_id, REASON_DELETED, now],
)?;
Ok(())
}
pub fn is_sync_chain_disabled(&self, client_id: &str) -> Result<bool> {
let conn = self.conn.lock().expect("sqlite lock poisoned");
let exists: Option<i32> = conn
.query_row(
"SELECT 1 FROM disabled_chains WHERE client_id = ?1",
params![client_id],
|row| row.get(0),
)
.optional()?;
Ok(exists.is_some())
}
}
fn is_constraint(err: &DatastoreError) -> bool {
matches!(
err,
DatastoreError::Sql(rusqlite::Error::SqliteFailure(e, _))
if e.code == rusqlite::ErrorCode::ConstraintViolation
)
}
fn insert_tag(tx: &Transaction<'_>, client_id: &str, tag_kind: &str, tag: &str) -> Result<()> {
let now = now_millis();
tx.execute(
"INSERT INTO unique_tags (client_id, tag_kind, tag, mtime, ctime) VALUES (?1, ?2, ?3, ?4, ?4)",
params![client_id, tag_kind, tag, now],
)?;
Ok(())
}
fn insert_entity(tx: &Transaction<'_>, entity: &DbSyncEntity) -> Result<()> {
tx.execute(
"INSERT INTO sync_entities (
client_id, id, parent_id, version, mtime, ctime, name, non_unique_name,
server_defined_unique_tag, deleted, originator_cache_guid, originator_client_item_id,
specifics, data_type, folder, client_defined_unique_tag, unique_position,
data_type_mtime, expiration_time
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19)",
params![
entity.client_id,
entity.id,
entity.parent_id,
entity.version,
entity.mtime,
entity.ctime,
entity.name,
entity.non_unique_name,
entity.server_defined_unique_tag,
entity.deleted,
entity.originator_cache_guid,
entity.originator_client_item_id,
entity.specifics,
entity.data_type,
entity.folder,
entity.client_defined_unique_tag,
entity.unique_position,
entity.data_type_mtime,
entity.expiration_time,
],
)?;
Ok(())
}
fn update_entity(tx: &Transaction<'_>, entity: &DbSyncEntity) -> Result<()> {
let mut sql = String::from(
"UPDATE sync_entities SET version = ?1, mtime = ?2, specifics = ?3, data_type_mtime = ?4",
);
let mut values = vec![
Value::Integer(entity.version),
Value::Integer(entity.mtime),
Value::Blob(entity.specifics.clone()),
Value::Text(entity.data_type_mtime.clone()),
];
push_optional(
&mut sql,
&mut values,
"unique_position",
entity.unique_position.clone().map(Value::Blob),
);
push_optional(
&mut sql,
&mut values,
"parent_id",
entity.parent_id.clone().map(Value::Text),
);
push_optional(
&mut sql,
&mut values,
"name",
entity.name.clone().map(Value::Text),
);
push_optional(
&mut sql,
&mut values,
"non_unique_name",
entity.non_unique_name.clone().map(Value::Text),
);
push_optional(
&mut sql,
&mut values,
"deleted",
entity.deleted.map(|v| Value::Integer(i64::from(v))),
);
push_optional(
&mut sql,
&mut values,
"folder",
entity.folder.map(|v| Value::Integer(i64::from(v))),
);
let client_idx = values.len() + 1;
let id_idx = values.len() + 2;
sql.push_str(&format!(
" WHERE client_id = ?{client_idx} AND id = ?{id_idx}"
));
values.push(Value::Text(entity.client_id.clone()));
values.push(Value::Text(entity.id.clone()));
tx.execute(&sql, params_from_iter(values.iter().map(value_as_tosql)))?;
Ok(())
}
fn push_optional(sql: &mut String, values: &mut Vec<Value>, column: &str, value: Option<Value>) {
if let Some(value) = value {
values.push(value);
let idx = values.len();
sql.push_str(&format!(", {column} = ?{idx}"));
}
}
fn value_as_tosql(value: &Value) -> ToSqlOutput<'_> {
ToSqlOutput::Borrowed(value.into())
}
fn row_to_entity(row: &rusqlite::Row<'_>) -> rusqlite::Result<DbSyncEntity> {
Ok(DbSyncEntity {
client_id: row.get(0)?,
id: row.get(1)?,
parent_id: row.get(2)?,
version: row.get(3)?,
mtime: row.get(4)?,
ctime: row.get(5)?,
name: row.get(6)?,
non_unique_name: row.get(7)?,
server_defined_unique_tag: row.get(8)?,
deleted: row.get(9)?,
originator_cache_guid: row.get(10)?,
originator_client_item_id: row.get(11)?,
specifics: row.get(12)?,
data_type: row.get(13)?,
folder: row.get(14)?,
client_defined_unique_tag: row.get(15)?,
unique_position: row.get(16)?,
data_type_mtime: row.get(17)?,
expiration_time: row.get(18)?,
})
}
pub fn create_db_sync_entity(
entity: &SyncEntity,
cache_guid: Option<&str>,
client_id: &str,
) -> Result<DbSyncEntity> {
let id_string = entity.id_string.clone().ok_or_else(|| {
DatastoreError::Invalid("validate SyncEntity error: empty IdString".to_string())
})?;
let version = entity.version.ok_or_else(|| {
DatastoreError::Invalid("validate SyncEntity error: empty Version".to_string())
})?;
let specifics = entity.specifics.as_ref().ok_or_else(|| {
DatastoreError::Invalid("validate SyncEntity error: nil Specifics".to_string())
})?;
let specifics_bytes = specifics.encode_to_vec();
let data_type = infer_specifics_data_type(&specifics_bytes)?;
let unique_position = entity.unique_position.as_ref().map(Message::encode_to_vec);
let mut id = id_string.clone();
let mut originator_cache_guid = None;
let mut originator_client_item_id = None;
if let Some(cache_guid) = cache_guid {
if version == 0 {
id = Uuid::new_v4().to_string();
}
originator_cache_guid = Some(cache_guid.to_string());
originator_client_item_id = Some(id_string);
}
if data_type == HISTORY_TYPE_ID {
id = entity.client_tag_hash.clone().ok_or_else(|| {
DatastoreError::Invalid("history entity is missing client_tag_hash".to_string())
})?;
}
let now = now_millis();
let expiration_time =
if data_type == HISTORY_TYPE_ID || data_type == HISTORY_DELETE_DIRECTIVE_TYPE_ID {
Some(now_secs() + HISTORY_EXPIRATION_INTERVAL_SECS)
} else {
None
};
let deleted = if version == 0 {
Some(entity.deleted.unwrap_or(false))
} else {
entity.deleted
};
let folder = if version == 0 {
Some(entity.folder.unwrap_or(false))
} else {
entity.folder
};
Ok(DbSyncEntity {
client_id: client_id.to_string(),
id,
parent_id: entity.parent_id_string.clone(),
version,
mtime: now,
ctime: entity.ctime.unwrap_or(now),
name: entity.name.clone(),
non_unique_name: entity.non_unique_name.clone(),
server_defined_unique_tag: entity.server_defined_unique_tag.clone(),
deleted,
originator_cache_guid,
originator_client_item_id,
specifics: specifics_bytes,
data_type,
folder,
client_defined_unique_tag: entity.client_tag_hash.clone(),
unique_position,
data_type_mtime: format!("{data_type}#{now}"),
expiration_time,
})
}
pub fn create_pb_sync_entity(entity: &DbSyncEntity) -> Result<SyncEntity> {
let specifics = EntitySpecifics::decode(entity.specifics.as_slice())?;
let unique_position = entity
.unique_position
.as_ref()
.map(|bytes| UniquePosition::decode(bytes.as_slice()))
.transpose()?;
Ok(SyncEntity {
id_string: Some(entity.id.clone()),
parent_id_string: entity.parent_id.clone(),
version: Some(entity.version),
mtime: Some(entity.mtime),
ctime: Some(entity.ctime),
name: entity.name.clone(),
non_unique_name: entity.non_unique_name.clone(),
server_defined_unique_tag: entity.server_defined_unique_tag.clone(),
deleted: entity.deleted,
originator_cache_guid: entity.originator_cache_guid.clone(),
originator_client_item_id: entity.originator_client_item_id.clone(),
specifics: Some(specifics),
folder: entity.folder,
client_tag_hash: entity.client_defined_unique_tag.clone(),
unique_position,
..SyncEntity::default()
})
}
fn infer_specifics_data_type(bytes: &[u8]) -> Result<i32> {
let mut pos = 0usize;
while pos < bytes.len() {
let (key, key_len) = read_key_varint(&bytes[pos..])
.ok_or_else(|| DatastoreError::Invalid("invalid entity specifics key".to_string()))?;
pos += key_len;
let field_number = key >> 3;
let wire_type = key & 0x07;
if field_number > 1 {
return i32::try_from(field_number).map_err(|_| {
DatastoreError::Invalid("datatype field number is out of range".to_string())
});
}
match wire_type {
0 => {
let (_, len) = read_key_varint(&bytes[pos..]).ok_or_else(|| {
DatastoreError::Invalid("invalid entity specifics varint".to_string())
})?;
pos += len;
}
1 => pos = pos.saturating_add(8),
2 => {
let (len, len_len) = read_key_varint(&bytes[pos..]).ok_or_else(|| {
DatastoreError::Invalid("invalid entity specifics length".to_string())
})?;
pos += len_len + len as usize;
}
5 => pos = pos.saturating_add(4),
_ => {
return Err(DatastoreError::Invalid(
"unsupported entity specifics wire type".to_string(),
));
}
}
}
Err(DatastoreError::Invalid(
"entity specifics has no datatype variant".to_string(),
))
}
+231
View File
@@ -0,0 +1,231 @@
mod auth;
mod cache;
mod command;
mod datastore;
mod proto;
mod util;
use std::{
env,
io::Read,
net::SocketAddr,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
time::Duration,
};
use axum::{
Router,
body::Bytes,
extract::{DefaultBodyLimit, State},
http::{HeaderMap, HeaderValue, StatusCode},
response::{IntoResponse, Response},
routing::{get, post},
};
use flate2::read::GzDecoder;
use prost::Message;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
use crate::{
cache::Cache,
command::handle_client_to_server_message,
datastore::SqliteDatastore,
proto::{ClientToServerMessage, ClientToServerResponse, sync_enums},
util::now_millis,
};
const PAYLOAD_LIMIT_10_MB: usize = 10 * 1024 * 1024;
#[derive(Clone)]
struct AppState {
cache: Cache,
db: SqliteDatastore,
health_active: Arc<AtomicBool>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")))
.with(tracing_subscriber::fmt::layer())
.init();
let sqlite_path = env::var("SQLITE_PATH").unwrap_or_else(|_| "brave-sync.sqlite3".to_string());
let db = SqliteDatastore::open(&sqlite_path)?;
let state = AppState {
cache: Cache::default(),
db,
health_active: Arc::new(AtomicBool::new(true)),
};
let app = Router::new()
.route("/", get(heartbeat))
.route("/health-check", get(health_check))
.route("/metrics", get(metrics))
.route("/v2/command/", post(command_handler))
.layer(DefaultBodyLimit::max(PAYLOAD_LIMIT_10_MB))
.layer(TraceLayer::new_for_http())
.with_state(state.clone());
let addr: SocketAddr = env::var("LISTEN_ADDR")
.unwrap_or_else(|_| "0.0.0.0:8295".to_string())
.parse()?;
let listener = tokio::net::TcpListener::bind(addr).await?;
tracing::info!(%addr, sqlite_path, "server starting up");
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal(state.health_active))
.await?;
Ok(())
}
async fn heartbeat() -> Response {
with_sane_time(StatusCode::OK.into_response())
}
async fn metrics() -> Response {
with_sane_time(
(
StatusCode::OK,
"# metrics are not implemented in the Rust port\n",
)
.into_response(),
)
}
async fn health_check(State(state): State<AppState>) -> Response {
if state.health_active.load(Ordering::Relaxed) {
with_sane_time((StatusCode::OK, "ok\n").into_response())
} else {
with_sane_time(StatusCode::GONE.into_response())
}
}
async fn command_handler(
State(state): State<AppState>,
headers: HeaderMap,
body: Bytes,
) -> Response {
let client_id = match auth::authorize(&headers) {
Ok(client_id) => client_id,
Err(err) => {
tracing::error!(error = %err, "Authorization failed");
return with_sane_time(
(
StatusCode::UNAUTHORIZED,
StatusCode::UNAUTHORIZED.to_string(),
)
.into_response(),
);
}
};
match state.db.is_sync_chain_disabled(&client_id) {
Ok(true) => {
let rsp = ClientToServerResponse {
error_code: Some(sync_enums::ErrorType::DisabledByAdmin as i32),
..ClientToServerResponse::default()
};
return protobuf_response(rsp);
}
Ok(false) => {}
Err(err) => {
tracing::error!(error = %err, "disabled-chain lookup failed");
return with_sane_time(
(
StatusCode::INTERNAL_SERVER_ERROR,
"unable to complete request",
)
.into_response(),
);
}
}
let body = if headers
.get("content-encoding")
.and_then(|value| value.to_str().ok())
== Some("gzip")
{
let mut decoder = GzDecoder::new(body.as_ref());
let mut out = Vec::with_capacity(body.len());
let mut limited = decoder.by_ref().take((PAYLOAD_LIMIT_10_MB + 1) as u64);
if let Err(err) = limited.read_to_end(&mut out) {
tracing::error!(error = %err, "Create gzip reader failed");
return with_sane_time(
(
StatusCode::INTERNAL_SERVER_ERROR,
"Create gzip reader failed",
)
.into_response(),
);
}
if out.len() > PAYLOAD_LIMIT_10_MB {
return with_sane_time(
(StatusCode::PAYLOAD_TOO_LARGE, "payload too large").into_response(),
);
}
out
} else {
body.to_vec()
};
let msg = match ClientToServerMessage::decode(body.as_slice()) {
Ok(msg) => msg,
Err(err) => {
tracing::error!(error = %err, "Unmarshall ClientToServerMessage failed");
return with_sane_time(
(StatusCode::INTERNAL_SERVER_ERROR, "Unmarshal error").into_response(),
);
}
};
match handle_client_to_server_message(&state.cache, &msg, &state.db, &client_id) {
Ok(rsp) => protobuf_response(rsp),
Err(err) => {
tracing::error!(error = %err, "Handle command message failed");
with_sane_time((StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())
}
}
}
fn protobuf_response(rsp: ClientToServerResponse) -> Response {
let mut out = Vec::new();
if let Err(err) = rsp.encode(&mut out) {
tracing::error!(error = %err, "Marshall ClientToServerResponse failed");
return with_sane_time(
(StatusCode::INTERNAL_SERVER_ERROR, "Marshal Error").into_response(),
);
}
with_sane_time((StatusCode::OK, out).into_response())
}
fn with_sane_time(mut response: Response) -> Response {
let millis = now_millis().to_string();
if let Ok(value) = HeaderValue::from_str(&millis) {
response.headers_mut().insert("Sane-Time-Millis", value);
}
response
}
async fn shutdown_signal(health_active: Arc<AtomicBool>) {
#[cfg(unix)]
{
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("install SIGTERM handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = sigterm.recv() => {},
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
tracing::info!("shutdown signal received, disabling health check");
health_active.store(false, Ordering::Relaxed);
tokio::time::sleep(Duration::from_secs(60)).await;
}
+5
View File
@@ -0,0 +1,5 @@
#![allow(clippy::all)]
#![allow(dead_code)]
#![allow(deprecated)]
include!(concat!(env!("OUT_DIR"), "/sync_pb.rs"));
+64
View File
@@ -0,0 +1,64 @@
use std::time::{SystemTime, UNIX_EPOCH};
pub fn now_millis() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as i64
}
pub fn now_secs() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64
}
pub fn put_varint(mut x: i64) -> Vec<u8> {
let mut buf = Vec::with_capacity(10);
while x < -64 || x >= 64 {
buf.push((x as u8 & 0x7f) | 0x80);
x >>= 7;
}
buf.push(x as u8 & 0x7f);
while buf.len() < 10 {
buf.push(0);
}
buf
}
pub fn read_varint(buf: &[u8]) -> Option<i64> {
let mut x = 0i64;
let mut s = 0u32;
for (i, b) in buf.iter().copied().enumerate() {
if b < 0x80 {
if i == 9 && b > 1 {
return None;
}
x |= (b as i64) << s;
if s < 64 && (b & 0x40) != 0 {
x |= !0i64 << (s + 7);
}
return Some(x);
}
x |= ((b & 0x7f) as i64) << s;
s += 7;
}
None
}
pub fn read_key_varint(buf: &[u8]) -> Option<(u64, usize)> {
let mut x = 0u64;
let mut s = 0u32;
for (i, b) in buf.iter().copied().enumerate() {
if b < 0x80 {
if i == 9 && b > 1 {
return None;
}
return Some((x | ((b as u64) << s), i + 1));
}
x |= ((b & 0x7f) as u64) << s;
s += 7;
}
None
}