Define archive control v1 protocol
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/common.proto";
|
||||
import "archive_control/v1/job.proto";
|
||||
import "archive_control/v1/route.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
enum ClientFeature {
|
||||
CLIENT_FEATURE_UNSPECIFIED = 0;
|
||||
CLIENT_FEATURE_INVENTORY_CHUNKS = 1;
|
||||
CLIENT_FEATURE_CONTENT_TREE = 2;
|
||||
CLIENT_FEATURE_ROUTE_PROVISIONING = 3;
|
||||
CLIENT_FEATURE_HARD_LINK = 4;
|
||||
CLIENT_FEATURE_REFLINK = 5;
|
||||
CLIENT_FEATURE_SPARSE_FILES = 6;
|
||||
CLIENT_FEATURE_PARTFILE_MIGRATION = 7;
|
||||
CLIENT_FEATURE_PARTFILE_MERGE = 8;
|
||||
CLIENT_FEATURE_QB_STOPPED_ADD = 9;
|
||||
CLIENT_FEATURE_EVICTION = 10;
|
||||
CLIENT_FEATURE_DB_BACKUP = 11;
|
||||
}
|
||||
|
||||
message FilesystemCapabilities {
|
||||
string root_name = 1;
|
||||
bool readable = 2;
|
||||
bool writable = 3;
|
||||
bool hard_link = 4;
|
||||
bool reflink = 5;
|
||||
bool sparse_files = 6;
|
||||
string detail = 7;
|
||||
}
|
||||
|
||||
message ClientCapabilities {
|
||||
repeated ClientFeature features = 1;
|
||||
repeated ServiceHealth services = 2;
|
||||
repeated FilesystemCapabilities filesystems = 3;
|
||||
repeated LocalRoute routes = 4;
|
||||
string syncthing_device_id = 5;
|
||||
repeated string syncthing_advertised_addresses = 6;
|
||||
string qbittorrent_version = 7;
|
||||
string qbittorrent_web_api_version = 8;
|
||||
string libtorrent_version = 9;
|
||||
string syncthing_version = 10;
|
||||
repeated string supported_partfile_formats = 11;
|
||||
uint32 max_envelope_bytes = 12;
|
||||
}
|
||||
|
||||
message ActiveJobCursor {
|
||||
string job_id = 1;
|
||||
uint64 job_revision = 2;
|
||||
uint64 last_event_sequence = 3;
|
||||
JobState state = 4;
|
||||
bool committed = 5;
|
||||
}
|
||||
|
||||
// RegisterRequest must be the first application message on each WebSocket.
|
||||
// shared_token is forbidden in every other message and must never be logged.
|
||||
message RegisterRequest {
|
||||
ProtocolVersion protocol_version = 1;
|
||||
ClientIdentity client = 2;
|
||||
string connection_instance_id = 3;
|
||||
string shared_token = 4;
|
||||
ClientCapabilities capabilities = 5;
|
||||
repeated ActiveJobCursor active_jobs = 6;
|
||||
}
|
||||
|
||||
enum RegistrationStatus {
|
||||
REGISTRATION_STATUS_UNSPECIFIED = 0;
|
||||
REGISTRATION_STATUS_ACCEPTED = 1;
|
||||
REGISTRATION_STATUS_REJECTED = 2;
|
||||
}
|
||||
|
||||
message RegisterResponse {
|
||||
RegistrationStatus status = 1;
|
||||
ProtocolVersion negotiated_version = 2;
|
||||
string connection_id = 3;
|
||||
google.protobuf.Duration heartbeat_interval = 4;
|
||||
google.protobuf.Duration heartbeat_timeout = 5;
|
||||
Error error = 6;
|
||||
}
|
||||
|
||||
message Heartbeat {
|
||||
uint64 sequence = 1;
|
||||
}
|
||||
|
||||
message HeartbeatAck {
|
||||
uint64 sequence = 1;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// ProtocolVersion is negotiated during connection registration. A different
|
||||
// major version is incompatible; minor versions are feature-negotiated.
|
||||
message ProtocolVersion {
|
||||
uint32 major = 1;
|
||||
uint32 minor = 2;
|
||||
}
|
||||
|
||||
enum ClientRole {
|
||||
CLIENT_ROLE_UNSPECIFIED = 0;
|
||||
CLIENT_ROLE_ARCHIVE = 1;
|
||||
CLIENT_ROLE_CACHE = 2;
|
||||
}
|
||||
|
||||
enum HealthState {
|
||||
HEALTH_STATE_UNSPECIFIED = 0;
|
||||
HEALTH_STATE_HEALTHY = 1;
|
||||
HEALTH_STATE_DEGRADED = 2;
|
||||
HEALTH_STATE_UNHEALTHY = 3;
|
||||
HEALTH_STATE_UNKNOWN = 4;
|
||||
}
|
||||
|
||||
enum ErrorCode {
|
||||
ERROR_CODE_UNSPECIFIED = 0;
|
||||
ERROR_CODE_INVALID_ARGUMENT = 1;
|
||||
ERROR_CODE_UNAUTHENTICATED = 2;
|
||||
ERROR_CODE_PROTOCOL_VERSION_MISMATCH = 3;
|
||||
ERROR_CODE_CLIENT_ID_CONFLICT = 4;
|
||||
ERROR_CODE_NOT_FOUND = 5;
|
||||
ERROR_CODE_CONFLICT = 6;
|
||||
ERROR_CODE_STALE_STATE = 7;
|
||||
ERROR_CODE_UNSUPPORTED = 8;
|
||||
ERROR_CODE_PRECONDITION_FAILED = 9;
|
||||
ERROR_CODE_RESOURCE_EXHAUSTED = 10;
|
||||
ERROR_CODE_UNAVAILABLE = 11;
|
||||
ERROR_CODE_TIMEOUT = 12;
|
||||
ERROR_CODE_CANCELLED = 13;
|
||||
ERROR_CODE_INTEGRITY_CHECK_FAILED = 14;
|
||||
ERROR_CODE_PATH_CONFLICT = 15;
|
||||
ERROR_CODE_PERMISSION_DENIED = 16;
|
||||
ERROR_CODE_MANUAL_INTERVENTION_REQUIRED = 17;
|
||||
ERROR_CODE_INTERNAL = 18;
|
||||
}
|
||||
|
||||
message Error {
|
||||
ErrorCode code = 1;
|
||||
string message = 2;
|
||||
bool retryable = 3;
|
||||
map<string, string> details = 4;
|
||||
}
|
||||
|
||||
message ServiceHealth {
|
||||
string service = 1;
|
||||
HealthState state = 2;
|
||||
string version = 3;
|
||||
string api_version = 4;
|
||||
string detail = 5;
|
||||
google.protobuf.Timestamp checked_at = 6;
|
||||
}
|
||||
|
||||
message ClientIdentity {
|
||||
string client_id = 1;
|
||||
string display_name = 2;
|
||||
ClientRole role = 3;
|
||||
}
|
||||
|
||||
message PageRequest {
|
||||
uint32 page_size = 1;
|
||||
string page_token = 2;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/common.proto";
|
||||
import "archive_control/v1/inventory.proto";
|
||||
import "archive_control/v1/job.proto";
|
||||
import "archive_control/v1/resource.proto";
|
||||
import "archive_control/v1/route.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message AssignJobCommand {
|
||||
JobDefinition job = 1;
|
||||
uint64 expected_job_revision = 2;
|
||||
uint64 expected_last_event_sequence = 3;
|
||||
}
|
||||
|
||||
message ExecuteStepCommand {
|
||||
string job_id = 1;
|
||||
uint64 expected_job_revision = 2;
|
||||
JobStepKind step = 3;
|
||||
uint32 attempt = 4;
|
||||
uint64 expected_last_event_sequence = 5;
|
||||
}
|
||||
|
||||
message CancelJobCommand {
|
||||
string job_id = 1;
|
||||
uint64 expected_job_revision = 2;
|
||||
string reason = 3;
|
||||
uint64 expected_last_event_sequence = 4;
|
||||
}
|
||||
|
||||
message EnsureRouteCommand {
|
||||
EnsureRouteSpec route = 1;
|
||||
}
|
||||
|
||||
message RequestJobSnapshotCommand {
|
||||
repeated string job_ids = 1;
|
||||
}
|
||||
|
||||
message Command {
|
||||
string command_id = 1;
|
||||
google.protobuf.Timestamp created_at = 2;
|
||||
oneof payload {
|
||||
AssignJobCommand assign_job = 10;
|
||||
ExecuteStepCommand execute_step = 11;
|
||||
CancelJobCommand cancel_job = 12;
|
||||
EnsureRouteCommand ensure_route = 13;
|
||||
InventoryQuery inventory_query = 14;
|
||||
RequestJobSnapshotCommand request_job_snapshot = 15;
|
||||
}
|
||||
}
|
||||
|
||||
enum CommandAckStatus {
|
||||
COMMAND_ACK_STATUS_UNSPECIFIED = 0;
|
||||
COMMAND_ACK_STATUS_ACCEPTED = 1;
|
||||
COMMAND_ACK_STATUS_DUPLICATE = 2;
|
||||
COMMAND_ACK_STATUS_REJECTED = 3;
|
||||
}
|
||||
|
||||
// CommandAck confirms durable acceptance/rejection, not completion. Completion
|
||||
// is reported by JobEvent, InventoryChunk, RouteUpdate, or JobSnapshot.
|
||||
message CommandAck {
|
||||
string command_id = 1;
|
||||
CommandAckStatus status = 2;
|
||||
Error error = 3;
|
||||
}
|
||||
|
||||
enum JobEventType {
|
||||
JOB_EVENT_TYPE_UNSPECIFIED = 0;
|
||||
JOB_EVENT_TYPE_ASSIGNED = 1;
|
||||
JOB_EVENT_TYPE_STEP_STARTED = 2;
|
||||
JOB_EVENT_TYPE_PROGRESS = 3;
|
||||
JOB_EVENT_TYPE_WAITING = 4;
|
||||
JOB_EVENT_TYPE_STALLED = 5;
|
||||
JOB_EVENT_TYPE_STEP_SUCCEEDED = 6;
|
||||
JOB_EVENT_TYPE_COMMITTED = 7;
|
||||
JOB_EVENT_TYPE_CANCELLING = 8;
|
||||
JOB_EVENT_TYPE_ROLLBACK_STARTED = 9;
|
||||
JOB_EVENT_TYPE_ROLLBACK_SUCCEEDED = 10;
|
||||
JOB_EVENT_TYPE_CLEANUP_REQUIRED = 11;
|
||||
JOB_EVENT_TYPE_SUCCEEDED = 12;
|
||||
JOB_EVENT_TYPE_FAILED = 13;
|
||||
JOB_EVENT_TYPE_CANCELLED = 14;
|
||||
}
|
||||
|
||||
message JobEvent {
|
||||
string event_id = 1;
|
||||
string job_id = 2;
|
||||
uint64 sequence = 3;
|
||||
uint64 job_revision = 4;
|
||||
JobEventType type = 5;
|
||||
JobState state = 6;
|
||||
bool committed = 7;
|
||||
JobProgress progress = 8;
|
||||
Error error = 9;
|
||||
ResourceStateFingerprint observed_resource = 10;
|
||||
Placement observed_placement = 11;
|
||||
google.protobuf.Timestamp occurred_at = 12;
|
||||
}
|
||||
|
||||
message JobSnapshot {
|
||||
JobRecord job = 1;
|
||||
uint64 last_event_sequence = 2;
|
||||
repeated string in_flight_command_ids = 3;
|
||||
}
|
||||
|
||||
message ClientStateSnapshot {
|
||||
string snapshot_id = 1;
|
||||
repeated JobSnapshot active_jobs = 2;
|
||||
repeated LocalRoute routes = 3;
|
||||
repeated ServiceHealth services = 4;
|
||||
google.protobuf.Timestamp observed_at = 5;
|
||||
}
|
||||
|
||||
message RouteUpdate {
|
||||
string command_id = 1;
|
||||
LocalRoute route = 2;
|
||||
RouteVerification verification = 3;
|
||||
Error error = 4;
|
||||
string update_id = 5;
|
||||
// Monotonic for this command_id; duplicate sequences must have identical
|
||||
// content and gaps are reconciled through ClientStateSnapshot.
|
||||
uint64 sequence = 6;
|
||||
}
|
||||
|
||||
message ProtocolError {
|
||||
Error error = 1;
|
||||
string offending_message_id = 2;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/client.proto";
|
||||
import "archive_control/v1/common.proto";
|
||||
import "archive_control/v1/control.proto";
|
||||
import "archive_control/v1/inventory.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// Envelope is the only top-level WebSocket application message. UUID fields
|
||||
// use lowercase canonical UUID text. correlation_id links a response/event to
|
||||
// the initiating envelope when applicable.
|
||||
message Envelope {
|
||||
ProtocolVersion protocol_version = 1;
|
||||
string message_id = 2;
|
||||
string correlation_id = 3;
|
||||
google.protobuf.Timestamp sent_at = 4;
|
||||
oneof payload {
|
||||
RegisterRequest register_request = 10;
|
||||
RegisterResponse register_response = 11;
|
||||
Heartbeat heartbeat = 12;
|
||||
HeartbeatAck heartbeat_ack = 13;
|
||||
Command command = 14;
|
||||
CommandAck command_ack = 15;
|
||||
InventoryChunk inventory_chunk = 16;
|
||||
JobEvent job_event = 17;
|
||||
JobSnapshot job_snapshot = 18;
|
||||
ClientStateSnapshot client_state_snapshot = 19;
|
||||
RouteUpdate route_update = 20;
|
||||
ProtocolError protocol_error = 21;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/common.proto";
|
||||
import "archive_control/v1/resource.proto";
|
||||
|
||||
enum InventoryScope {
|
||||
INVENTORY_SCOPE_UNSPECIFIED = 0;
|
||||
INVENTORY_SCOPE_RESOURCE_SUMMARIES = 1;
|
||||
INVENTORY_SCOPE_RESOURCE_LOOKUP = 2;
|
||||
INVENTORY_SCOPE_CONTENT_TREE = 3;
|
||||
INVENTORY_SCOPE_PLACEMENTS = 4;
|
||||
}
|
||||
|
||||
message InventoryQuery {
|
||||
string query_id = 1;
|
||||
InventoryScope scope = 2;
|
||||
repeated ResourceId resource_ids = 3;
|
||||
PageRequest page = 4;
|
||||
string page_filter = 5;
|
||||
bool selected_complete_only = 6;
|
||||
string expected_revision = 7;
|
||||
}
|
||||
|
||||
message ResourceSummaryChunk {
|
||||
repeated ResourceSummary resources = 1;
|
||||
}
|
||||
|
||||
message ContentTreeChunk {
|
||||
ResourceId resource_id = 1;
|
||||
repeated ContentTreeEntry entries = 2;
|
||||
string resource_content_revision = 3;
|
||||
}
|
||||
|
||||
message PlacementChunk {
|
||||
repeated Placement placements = 1;
|
||||
}
|
||||
|
||||
// InventoryChunk is an atomic-snapshot stream. Receivers commit results only
|
||||
// after a last_chunk=true chunk arrives for the same snapshot and revision.
|
||||
message InventoryChunk {
|
||||
string query_id = 1;
|
||||
string snapshot_id = 2;
|
||||
string revision = 3;
|
||||
uint32 chunk_index = 4;
|
||||
bool last_chunk = 5;
|
||||
string next_page_token = 6;
|
||||
Error error = 7;
|
||||
oneof payload {
|
||||
ResourceSummaryChunk resource_summaries = 10;
|
||||
ContentTreeChunk content_tree = 11;
|
||||
PlacementChunk placements = 12;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/common.proto";
|
||||
import "archive_control/v1/resource.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
enum JobOperation {
|
||||
JOB_OPERATION_UNSPECIFIED = 0;
|
||||
JOB_OPERATION_ARCHIVE = 1;
|
||||
JOB_OPERATION_UNARCHIVE = 2;
|
||||
JOB_OPERATION_EVICT_CACHE = 3;
|
||||
}
|
||||
|
||||
enum JobState {
|
||||
JOB_STATE_UNSPECIFIED = 0;
|
||||
JOB_STATE_QUEUED = 1;
|
||||
JOB_STATE_PREPARING = 2;
|
||||
JOB_STATE_RUNNING = 3;
|
||||
JOB_STATE_WAITING = 4;
|
||||
JOB_STATE_STALLED = 5;
|
||||
JOB_STATE_CANCELLING = 6;
|
||||
JOB_STATE_ROLLING_BACK = 7;
|
||||
JOB_STATE_CLEANUP_REQUIRED = 8;
|
||||
JOB_STATE_SUCCEEDED = 9;
|
||||
JOB_STATE_FAILED = 10;
|
||||
JOB_STATE_CANCELLED = 11;
|
||||
}
|
||||
|
||||
enum JobStepKind {
|
||||
JOB_STEP_KIND_UNSPECIFIED = 0;
|
||||
JOB_STEP_KIND_ROUTE_SETUP = 1;
|
||||
JOB_STEP_KIND_PREFLIGHT = 2;
|
||||
JOB_STEP_KIND_SOURCE_STAGE = 3;
|
||||
JOB_STEP_KIND_SYNCTHING_TRANSFER = 4;
|
||||
JOB_STEP_KIND_TARGET_MATERIALIZE = 5;
|
||||
JOB_STEP_KIND_QB_VERIFY = 6;
|
||||
JOB_STEP_KIND_STAGING_CLEANUP = 7;
|
||||
JOB_STEP_KIND_VERIFY_ARCHIVE_COVERAGE = 8;
|
||||
JOB_STEP_KIND_QB_REMOVE_ENTRY = 9;
|
||||
JOB_STEP_KIND_SAFE_FILE_UNLINK = 10;
|
||||
JOB_STEP_KIND_ROLLBACK = 11;
|
||||
}
|
||||
|
||||
enum StepState {
|
||||
STEP_STATE_UNSPECIFIED = 0;
|
||||
STEP_STATE_NOT_STARTED = 1;
|
||||
STEP_STATE_QUEUED = 2;
|
||||
STEP_STATE_RUNNING = 3;
|
||||
STEP_STATE_WAITING = 4;
|
||||
STEP_STATE_STALLED = 5;
|
||||
STEP_STATE_SUCCEEDED = 6;
|
||||
STEP_STATE_FAILED = 7;
|
||||
STEP_STATE_CANCELLED = 8;
|
||||
}
|
||||
|
||||
message TransferJobSpec {
|
||||
string source_client_id = 1;
|
||||
string target_client_id = 2;
|
||||
string route_id = 3;
|
||||
SelectionSet requested_files = 4;
|
||||
ResourceStateFingerprint source_fingerprint = 5;
|
||||
ResourceStateFingerprint target_baseline_fingerprint = 6;
|
||||
// Absent for a new target placement. Presence distinguishes an absent
|
||||
// placement from an existing placement whose generation is invalidly zero.
|
||||
optional uint64 expected_target_placement_generation = 7;
|
||||
SelectionSet target_baseline_files = 8;
|
||||
SelectionSet transfer_delta_files = 9;
|
||||
uint64 requested_logical_bytes = 10;
|
||||
uint64 transfer_delta_logical_bytes = 11;
|
||||
}
|
||||
|
||||
message ArchiveCoverage {
|
||||
string archive_client_id = 1;
|
||||
SelectionSet covered_files = 2;
|
||||
uint64 placement_generation = 3;
|
||||
ResourceStateFingerprint fingerprint = 4;
|
||||
}
|
||||
|
||||
message EvictionJobSpec {
|
||||
string cache_client_id = 1;
|
||||
SelectionSet files_to_evict = 2;
|
||||
uint64 cache_placement_generation = 3;
|
||||
ResourceStateFingerprint cache_fingerprint = 4;
|
||||
repeated ArchiveCoverage archive_coverage = 5;
|
||||
}
|
||||
|
||||
// JobDefinition is immutable after acceptance. Runtime state and calculated
|
||||
// transfer deltas belong in JobRecord and TransferManifest.
|
||||
message JobDefinition {
|
||||
string job_id = 1;
|
||||
string idempotency_key = 2;
|
||||
JobOperation operation = 3;
|
||||
ResourceId resource_id = 4;
|
||||
string resource_display_name = 5;
|
||||
google.protobuf.Timestamp created_at = 6;
|
||||
oneof spec {
|
||||
TransferJobSpec transfer = 10;
|
||||
EvictionJobSpec eviction = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message JobProgress {
|
||||
JobStepKind step = 1;
|
||||
uint32 display_step_number = 2;
|
||||
uint32 display_step_total = 3;
|
||||
StepState state = 4;
|
||||
double fraction_complete = 5;
|
||||
uint64 bytes_complete = 6;
|
||||
uint64 bytes_total = 7;
|
||||
double approximate_bytes_per_second = 8;
|
||||
string detail = 9;
|
||||
google.protobuf.Timestamp last_progress_at = 10;
|
||||
// Weighted progress across all visible steps. This remains available while
|
||||
// a job is waiting or stalled; fraction_complete is for the current step.
|
||||
double overall_fraction_complete = 11;
|
||||
}
|
||||
|
||||
message JobRecord {
|
||||
JobDefinition definition = 1;
|
||||
JobState state = 2;
|
||||
bool committed = 3;
|
||||
uint64 revision = 4;
|
||||
JobProgress progress = 5;
|
||||
Error error = 6;
|
||||
google.protobuf.Timestamp updated_at = 7;
|
||||
google.protobuf.Timestamp committed_at = 8;
|
||||
google.protobuf.Timestamp finished_at = 9;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// ResourceId models BitTorrent v1, v2, and hybrid torrent identity. Hex values
|
||||
// are lowercase and have exact SHA-1/SHA-256 lengths. At least one is required.
|
||||
message ResourceId {
|
||||
string info_hash_v1_hex = 1;
|
||||
string info_hash_v2_hex = 2;
|
||||
}
|
||||
|
||||
// FileIndexRange is inclusive at both ends. Ranges in a SelectionSet must be
|
||||
// sorted, non-overlapping, and coalesced.
|
||||
message FileIndexRange {
|
||||
uint32 first = 1;
|
||||
uint32 last = 2;
|
||||
}
|
||||
|
||||
message SelectionSet {
|
||||
repeated FileIndexRange ranges = 1;
|
||||
}
|
||||
|
||||
enum TorrentRuntimeState {
|
||||
TORRENT_RUNTIME_STATE_UNSPECIFIED = 0;
|
||||
TORRENT_RUNTIME_STATE_STOPPED = 1;
|
||||
TORRENT_RUNTIME_STATE_QUEUED = 2;
|
||||
TORRENT_RUNTIME_STATE_CHECKING = 3;
|
||||
TORRENT_RUNTIME_STATE_DOWNLOADING = 4;
|
||||
TORRENT_RUNTIME_STATE_SEEDING = 5;
|
||||
TORRENT_RUNTIME_STATE_STALLED = 6;
|
||||
TORRENT_RUNTIME_STATE_ERROR = 7;
|
||||
TORRENT_RUNTIME_STATE_MISSING = 8;
|
||||
}
|
||||
|
||||
enum ContentEntryType {
|
||||
CONTENT_ENTRY_TYPE_UNSPECIFIED = 0;
|
||||
CONTENT_ENTRY_TYPE_FILE = 1;
|
||||
CONTENT_ENTRY_TYPE_DIRECTORY = 2;
|
||||
CONTENT_ENTRY_TYPE_PADDING_FILE = 3;
|
||||
}
|
||||
|
||||
message TorrentFile {
|
||||
uint32 file_index = 1;
|
||||
string canonical_path = 2;
|
||||
uint64 logical_bytes = 3;
|
||||
uint64 allocated_bytes = 4;
|
||||
uint64 completed_bytes = 5;
|
||||
bool selected = 6;
|
||||
bool sparse = 7;
|
||||
bool padding = 8;
|
||||
}
|
||||
|
||||
// ContentTreeEntry is a flat tree record. Directory selections are expanded by
|
||||
// the source into available_file_indices before a job is created.
|
||||
message ContentTreeEntry {
|
||||
string canonical_path = 1;
|
||||
string parent_path = 2;
|
||||
string display_name = 3;
|
||||
ContentEntryType type = 4;
|
||||
optional uint32 file_index = 5;
|
||||
SelectionSet available_file_indices = 6;
|
||||
uint64 available_logical_bytes = 7;
|
||||
uint32 available_file_count = 8;
|
||||
// Present for FILE and PADDING_FILE entries. The duplicated canonical_path
|
||||
// must match this tree entry; receivers reject inconsistent records.
|
||||
TorrentFile file = 9;
|
||||
}
|
||||
|
||||
message ResourceSummary {
|
||||
ResourceId resource_id = 1;
|
||||
// qb_torrent_id is meaningful only to the reporting qBittorrent instance.
|
||||
string qb_torrent_id = 2;
|
||||
string display_name = 3;
|
||||
TorrentRuntimeState runtime_state = 4;
|
||||
SelectionSet selected_files = 5;
|
||||
SelectionSet selected_complete_files = 6;
|
||||
uint64 selected_logical_bytes = 7;
|
||||
uint64 selected_complete_bytes = 8;
|
||||
uint64 total_logical_bytes = 9;
|
||||
uint32 total_file_count = 10;
|
||||
string content_revision = 11;
|
||||
bool canonical_paths = 12;
|
||||
google.protobuf.Timestamp observed_at = 13;
|
||||
}
|
||||
|
||||
// ResourceStateFingerprint is an optimistic concurrency token for active jobs.
|
||||
message ResourceStateFingerprint {
|
||||
ResourceId resource_id = 1;
|
||||
string client_id = 2;
|
||||
string qb_torrent_id = 3;
|
||||
string content_revision = 4;
|
||||
SelectionSet selected_files = 5;
|
||||
SelectionSet selected_complete_files = 6;
|
||||
TorrentRuntimeState runtime_state = 7;
|
||||
string save_path_fingerprint = 8;
|
||||
google.protobuf.Timestamp observed_at = 9;
|
||||
}
|
||||
|
||||
enum PlacementState {
|
||||
PLACEMENT_STATE_UNSPECIFIED = 0;
|
||||
PLACEMENT_STATE_PRESENT = 1;
|
||||
PLACEMENT_STATE_MERGING = 2;
|
||||
PLACEMENT_STATE_EVICTING = 3;
|
||||
PLACEMENT_STATE_ABSENT = 4;
|
||||
PLACEMENT_STATE_ERROR = 5;
|
||||
PLACEMENT_STATE_UNKNOWN = 6;
|
||||
}
|
||||
|
||||
message Placement {
|
||||
ResourceId resource_id = 1;
|
||||
string client_id = 2;
|
||||
PlacementState state = 3;
|
||||
SelectionSet verified_files = 4;
|
||||
uint64 verified_logical_bytes = 5;
|
||||
uint64 generation = 6;
|
||||
ResourceStateFingerprint fingerprint = 7;
|
||||
google.protobuf.Timestamp verified_at = 8;
|
||||
string created_by_job_id = 9;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
enum SyncthingFolderType {
|
||||
SYNCTHING_FOLDER_TYPE_UNSPECIFIED = 0;
|
||||
SYNCTHING_FOLDER_TYPE_SEND_RECEIVE = 1;
|
||||
SYNCTHING_FOLDER_TYPE_SEND_ONLY = 2;
|
||||
SYNCTHING_FOLDER_TYPE_RECEIVE_ONLY = 3;
|
||||
SYNCTHING_FOLDER_TYPE_OTHER = 4;
|
||||
}
|
||||
|
||||
enum RouteState {
|
||||
ROUTE_STATE_UNSPECIFIED = 0;
|
||||
ROUTE_STATE_DISCOVERED = 1;
|
||||
ROUTE_STATE_PROVISIONING = 2;
|
||||
ROUTE_STATE_VERIFYING = 3;
|
||||
ROUTE_STATE_READY = 4;
|
||||
ROUTE_STATE_PAUSED = 5;
|
||||
ROUTE_STATE_UNHEALTHY = 6;
|
||||
ROUTE_STATE_UNSUPPORTED = 7;
|
||||
}
|
||||
|
||||
// LocalRoute is a client's non-secret view of one Syncthing folder. route_id is
|
||||
// the Syncthing folder ID. Archive Control routes must have exactly two devices.
|
||||
message LocalRoute {
|
||||
string route_id = 1;
|
||||
string local_relative_path = 2;
|
||||
SyncthingFolderType folder_type = 3;
|
||||
string local_syncthing_device_id = 4;
|
||||
repeated string peer_syncthing_device_ids = 5;
|
||||
RouteState state = 6;
|
||||
bool writable = 7;
|
||||
bool sparse_supported = 8;
|
||||
bool archive_control_created = 9;
|
||||
string detail = 10;
|
||||
google.protobuf.Timestamp observed_at = 11;
|
||||
}
|
||||
|
||||
message RouteDescriptor {
|
||||
string route_id = 1;
|
||||
string archive_client_id = 2;
|
||||
string cache_client_id = 3;
|
||||
string archive_syncthing_device_id = 4;
|
||||
string cache_syncthing_device_id = 5;
|
||||
RouteState state = 6;
|
||||
google.protobuf.Timestamp verified_at = 7;
|
||||
string detail = 8;
|
||||
}
|
||||
|
||||
message EnsureRouteSpec {
|
||||
string route_id = 1;
|
||||
string peer_client_id = 2;
|
||||
string peer_syncthing_device_id = 3;
|
||||
repeated string peer_addresses = 4;
|
||||
string local_relative_path = 5;
|
||||
uint32 setup_timeout_seconds = 6;
|
||||
}
|
||||
|
||||
message RouteVerification {
|
||||
string route_id = 1;
|
||||
string nonce = 2;
|
||||
bool local_nonce_seen_by_peer = 3;
|
||||
bool peer_nonce_seen_locally = 4;
|
||||
RouteState state = 5;
|
||||
string detail = 6;
|
||||
google.protobuf.Timestamp observed_at = 7;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package archive_control.v1;
|
||||
|
||||
import "archive_control/v1/resource.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
enum MaterializationMethod {
|
||||
MATERIALIZATION_METHOD_UNSPECIFIED = 0;
|
||||
MATERIALIZATION_METHOD_HARD_LINK = 1;
|
||||
MATERIALIZATION_METHOD_REFLINK = 2;
|
||||
MATERIALIZATION_METHOD_COPY = 3;
|
||||
MATERIALIZATION_METHOD_PREEXISTING_REUSED = 4;
|
||||
}
|
||||
|
||||
enum ArtifactKind {
|
||||
ARTIFACT_KIND_UNSPECIFIED = 0;
|
||||
ARTIFACT_KIND_TORRENT_FILE = 1;
|
||||
ARTIFACT_KIND_LIBTORRENT_PARTFILE = 2;
|
||||
ARTIFACT_KIND_OTHER_AUXILIARY = 3;
|
||||
}
|
||||
|
||||
message ManifestFile {
|
||||
uint32 file_index = 1;
|
||||
string payload_relative_path = 2;
|
||||
string target_canonical_path = 3;
|
||||
uint64 logical_bytes = 4;
|
||||
uint64 allocated_bytes = 5;
|
||||
bool sparse = 6;
|
||||
MaterializationMethod source_staging_method = 7;
|
||||
MaterializationMethod target_method = 8;
|
||||
bool target_preexisted = 9;
|
||||
}
|
||||
|
||||
message ManifestArtifact {
|
||||
ArtifactKind kind = 1;
|
||||
string payload_relative_path = 2;
|
||||
uint64 logical_bytes = 3;
|
||||
uint64 allocated_bytes = 4;
|
||||
bool sparse = 5;
|
||||
string format = 6;
|
||||
string sha256_hex = 7;
|
||||
}
|
||||
|
||||
// TransferManifest is written as canonical protobuf JSON into the isolated
|
||||
// Syncthing job namespace. Paths are always relative to configured safe roots.
|
||||
message TransferManifest {
|
||||
uint32 manifest_version = 1;
|
||||
string job_id = 2;
|
||||
ResourceId resource_id = 3;
|
||||
string source_client_id = 4;
|
||||
string target_client_id = 5;
|
||||
string route_id = 6;
|
||||
SelectionSet requested_files = 7;
|
||||
SelectionSet target_baseline_files = 8;
|
||||
SelectionSet transfer_delta_files = 9;
|
||||
repeated ManifestFile files = 10;
|
||||
repeated ManifestArtifact artifacts = 11;
|
||||
ResourceStateFingerprint source_fingerprint = 12;
|
||||
ResourceStateFingerprint target_baseline_fingerprint = 13;
|
||||
google.protobuf.Timestamp created_at = 14;
|
||||
}
|
||||
|
||||
// ReadyMarker is written last. A destination trusts no manifest until the
|
||||
// marker exists and its digest matches the manifest bytes.
|
||||
message ReadyMarker {
|
||||
string job_id = 1;
|
||||
string manifest_sha256_hex = 2;
|
||||
google.protobuf.Timestamp ready_at = 3;
|
||||
}
|
||||
Reference in New Issue
Block a user