transport.proto
This Kit feature is not available yet.
In proto/mkit/transport/v1/
Preview
Binary · 22 KiB// SPDX-License-Identifier: MIT OR Apache-2.0
//
// mkit.transport.v1 — the canonical Connect remote protocol.
//
// TransportService is the single Connect service every remote mkit
// deployment target implements. `mkit-server` and the native (non-wasm)
// CLI client compile against this one proto, with no per-target dialect. It maps
// one-to-one onto the verbs of the
// `Transport` trait (rust/crates/mkit-core/src/protocol.rs), the same
// trait `mkit-transport-http`/`-s3`/`-ssh`/`-enc` already implement.
// See docs/specs/SPEC-TRANSPORT-CONNECT.md for the full normative
// contract (error-code mapping, CAS semantics, streaming design,
// deployment-target consumption).
//
// Design lineage: Gitaly-style resource RPCs (unary verbs stay unary;
// only pack transfer streams). `PackChunk` is a byte-for-byte
// duplicate of `mkit.rpc.v1.ssh.PackChunk` (same field numbers, same
// types) so the wire bytes a client streams are identical regardless
// of which transport carries them — SSH, enc, or Connect. This is the
// same "duplicate now, wire-numbers pinned, extract later" pattern
// `mkit.repo.v1.RefExpectation` already uses against `ssh.proto`
// (apps/repo-worker/proto/mkit/repo/v1/repo.proto); a future issue
// (tracked as mkit#679) extracts both into a shared
// `mkit/common/v1/refs.proto` once the buf workspace exists
// (mkit#677) to import across module boundaries.
//
// The service implementation is `mkit-server`'s `connect` binding over
// its pipeline; the
// client is `mkit-transport-connect`'s `ConnectTransport`.
edition = "2023";
package mkit.transport.v1;
// ----------------------------------------------------------------------------
// CAS semantics for UpdateRef / AdvanceRefs. Wire numbers are load-bearing
// and match mkit.rpc.v1.ssh.RefExpectation (rust/crates/mkit-rpc/proto/
// ssh.proto) and mkit.repo.v1.RefExpectation (apps/repo-worker/proto/mkit/
// repo/v1/repo.proto) byte-for-byte — do NOT renumber; new variants append.
enum RefExpectation {
// Protocol error if sent (the proto default). Servers MUST reject with
// Connect code `invalid_argument`.
REF_EXPECTATION_UNSPECIFIED = 0;
// "Clobber" / last-writer-wins — Transport::RefWriteCondition::Any.
// `expected_id` MUST be empty.
REF_EXPECTATION_ANY = 1;
// "Create only" — Transport::RefWriteCondition::Missing. The ref MUST
// NOT already exist. `expected_id` MUST be empty.
REF_EXPECTATION_MISSING = 2;
// Compare-and-swap — Transport::RefWriteCondition::Match(expected).
// `expected_id` MUST be the 32-byte current value.
REF_EXPECTATION_MATCH = 3;
}
// A single ref and the object it points at. Matches ssh.proto /
// repo.proto RefEntry byte-for-byte.
message RefEntry {
string name = 1; // e.g. "refs/heads/main" (SPEC-REFS §3 grammar)
bytes object_id = 2; // raw 32-byte BLAKE3 digest
}
// Outcome of a two-ref AdvanceRefs call. Mirrors
// mkit_core::protocol::AdvanceOutcome exactly (three variants, no
// zero/UNSPECIFIED — a call either completes or returns a Connect error,
// so there is no "unset" outcome on the wire).
enum AdvanceOutcome {
ADVANCE_OUTCOME_UNSPECIFIED = 0;
// Both refs were updated (transactionally, if the server advertises
// atomic advance; packmap-then-head otherwise — see SPEC-TRANSPORT-CONNECT
// §4).
ADVANCE_OUTCOME_COMMITTED = 1;
// The head precondition did not hold (the branch moved under us).
ADVANCE_OUTCOME_HEAD_CONFLICT = 2;
// The packmap precondition did not hold (a concurrent pusher advanced
// the chain first).
ADVANCE_OUTCOME_PACKMAP_CONFLICT = 3;
}
// ----------------------------------------------------------------------------
// Pack transfer. PackChunk duplicates mkit.rpc.v1.ssh.PackChunk's field
// layout exactly (same field numbers 1-4, same types) so chunk bytes are
// wire-identical to the SSH/enc framing — no new chunking format.
message PackChunk {
bytes pack_id = 1; // raw 32-byte BLAKE3 digest
uint64 offset = 2; // MUST equal the next expected byte offset (no gaps)
bytes data = 3; // chunk payload
bool last = 4; // true on the final chunk of the stream
}
// ----------------------------------------------------------------------------
// TransportService — the full remote surface, Connect over HTTP.
//
// Endpoint paths follow the Connect convention
// POST /mkit.transport.v1.TransportService/<Method>. `upload_blob` /
// `download_blob` / `write_ref` are NOT separate RPCs: they are
// Transport-trait-level default methods that delegate to UploadPack /
// DownloadPack / UpdateRef(ANY) before any transport is invoked (see
// rust/crates/mkit-core/src/protocol.rs), so the wire never distinguishes
// "pack" from "auxiliary blob", or "unconditional write" from "CAS write
// with expectation=ANY".
service TransportService {
// List readable repository names in a namespace (STC §7.10).
rpc ListRepos(ListReposRequest) returns (ListReposResponse);
// List refs whose full name starts with `prefix`. Returned names have
// `prefix` stripped, per SPEC-REFS §4. An empty prefix lists every ref.
rpc ListRefs(ListRefsRequest) returns (ListRefsResponse);
// Read a ref's current value. `exists = false` if absent.
rpc ReadRef(ReadRefRequest) returns (ReadRefResponse);
// CAS ref write. Connect code `failed_precondition` on a CAS mismatch
// (`MISSING` on an existing ref, or `MATCH` on a different current
// value); `invalid_argument` on `REF_EXPECTATION_UNSPECIFIED`. See
// SPEC-TRANSPORT-CONNECT §3 — the response never carries the current
// ref value; a client that needs to disambiguate MUST follow up with
// ReadRef.
rpc UpdateRef(UpdateRefRequest) returns (UpdateRefResponse);
// Atomically advance a branch's head ref and packmap ref together,
// each under its own CAS precondition. See SPEC-TRANSPORT-CONNECT §4.
rpc AdvanceRefs(AdvanceRefsRequest) returns (AdvanceRefsResponse);
// HEAD-check a pack by digest.
rpc PackExists(PackExistsRequest) returns (PackExistsResponse);
// Client-streaming pack upload. The first message on the stream MUST be
// `header`; every subsequent message MUST be `chunk`, in ascending
// contiguous `offset` order, ending with a `chunk.last = true` message.
// The server MUST reject a stream whose received byte count does not
// equal `header.total_bytes` or whose BLAKE3 does not equal
// `header.pack_id`, and MUST NOT store partial bytes on rejection. See
// SPEC-TRANSPORT-CONNECT §6.
rpc UploadPack(stream UploadPackRequest) returns (UploadPackResponse);
// Server-streaming pack download. The first message on the stream MUST
// be `header`; every subsequent message MUST be `chunk`, ending with a
// `chunk.last = true` message. Connect code `not_found` (raised before
// any message is sent) if the digest is absent. See
// SPEC-TRANSPORT-CONNECT §6.
rpc DownloadPack(DownloadPackRequest) returns (stream DownloadPackResponse);
// Discover deployment capabilities without authentication (SPEC-TRANSPORT-CONNECT §2.1).
rpc GetServerInfo(GetServerInfoRequest) returns (GetServerInfoResponse);
// Fetch a retained writer-view storage receipt (SPEC-SERVER §15).
rpc GetReceipt(GetReceiptRequest) returns (GetReceiptResponse);
// Reserve an upload for a ref (SPEC-TRANSPORT-CONNECT §7.6).
rpc BeginUpload(BeginUploadRequest) returns (BeginUploadResponse);
// Stream one ticketed upload part (SPEC-TRANSPORT-CONNECT §7.6).
rpc UploadPart(stream UploadPartRequest) returns (UploadPartResponse);
// Complete a multipart upload using its receipts (SPEC-TRANSPORT-CONNECT §7.6).
rpc CompleteUpload(CompleteUploadRequest) returns (CompleteUploadResponse);
// Namespace grant epoch RPCs (SPEC-WRITE-GRANTS §5.3).
rpc GetGrantEpoch(GetGrantEpochRequest) returns (GetGrantEpochResponse);
rpc SetGrantEpoch(SetGrantEpochRequest) returns (SetGrantEpochResponse);
// Outside auth-v2; the setter verifies a deployment-authority statement.
rpc GetAuthorityGeneration(GetAuthorityGenerationRequest) returns (GetAuthorityGenerationResponse);
rpc SetAuthorityGeneration(SetAuthorityGenerationRequest) returns (SetAuthorityGenerationResponse);
// Repository visibility and URL tokens (SPEC-WRITE-GRANTS §9.1, §9.4).
rpc SetRepoVisibility(SetRepoVisibilityRequest) returns (SetRepoVisibilityResponse);
rpc IssueObjectUrl(IssueObjectUrlRequest) returns (IssueObjectUrlResponse);
}
// Namespace listing; grant holders receive the public listing view.
message ListReposRequest {
string namespace = 1;
string page_token = 2;
uint32 page_size = 3; // 0 means 100; values above 100 are invalid.
string name_prefix = 4; // Raw ASCII name prefix, not stripped in responses.
}
message RepoEntry {
string name = 1;
RepoVisibility visibility = 2;
}
message ListReposResponse {
repeated RepoEntry repos = 1;
string next_page_token = 2;
}
// ----------------------------------------------------------------------------
// Refs.
message ListRefsRequest {
string prefix = 1;
// Requested page bound, capped by the server (SPEC-TRANSPORT-CONNECT §7.9).
uint32 page_size = 2;
// Opaque continuation token; empty starts a listing (SPEC-TRANSPORT-CONNECT §7.9).
string page_token = 3;
}
message ListRefsResponse {
repeated RefEntry refs = 1;
// Empty ends the listing (SPEC-TRANSPORT-CONNECT §7.9).
string next_page_token = 2;
// Active notices associated with visible refs on this page (SPEC-SERVER §14.6).
repeated RefRedaction ref_redactions = 3;
}
// Notice associated with one visible ref; RefEntry's pinned wire layout is unchanged.
message RefRedaction {
string ref = 1; // Full ref name, not the prefix-stripped ListRefs display name.
RedactionNotice notice = 2;
}
message ReadRefRequest {
string name = 1;
}
message ReadRefResponse {
bool exists = 1;
bytes object_id = 2; // 32-byte BLAKE3 digest; meaningful only when exists
// Active notices for this visible ref; empty when absent (SPEC-SERVER §14.6).
repeated RedactionNotice redaction_notices = 3;
}
message UpdateRefRequest {
string name = 1;
RefExpectation expectation = 2; // REQUIRED; UNSPECIFIED is a protocol error
bytes expected_id = 3; // 32 bytes for MATCH; empty for ANY/MISSING
bytes new_id = 4; // 32-byte BLAKE3 digest to write
// Delete the ref under its CAS precondition (SPEC-TRANSPORT-CONNECT §7.8).
bool delete = 5;
}
message UpdateRefResponse {
// DSSE storage-receipt envelope JSON; empty when receipts are disabled.
// CAS and validation failures remain Connect errors (STC §3).
bytes receipt = 1;
}
message AdvanceRefsRequest {
string head_ref = 1;
RefExpectation head_expectation = 2;
bytes head_expected_id = 3;
bytes head_new_id = 4;
string packmap_ref = 5;
RefExpectation packmap_expectation = 6;
bytes packmap_expected_id = 7;
bytes packmap_new_id = 8;
// Upload tickets consumed by this advance (SPEC-TRANSPORT-CONNECT §7.6).
repeated bytes ticket_ids = 9;
// Delete both refs under their CAS preconditions (SPEC-TRANSPORT-CONNECT §7.8).
bool delete = 10;
// Copy this repository's authoritative published branch pair. When set,
// head_ref is the destination; all other explicit update fields, tickets
// and delete must be absent. Both destination refs use MISSING.
string source_ref = 11;
// Optional source-tip CAS, meaningful only with source_ref (32 bytes).
bytes expected_tip = 12;
}
message AdvanceRefsResponse {
AdvanceOutcome outcome = 1;
// DSSE storage-receipt envelope JSON; empty on conflict or when disabled.
bytes receipt = 2;
}
// A signed, writer-view read. Exactly one selector is required (STC §2.2).
message GetReceiptRequest {
oneof selector {
AdvanceReceiptSelector advance = 1;
LeaseReceiptSelector lease = 2;
}
}
message AdvanceReceiptSelector {
string ref = 1; // full ref name
uint64 advance_sequence = 2; // 0 selects the latest committed advance
}
message LeaseReceiptSelector {
string ref = 1; // empty selects the repository or namespace lease scope
uint64 lease_version = 2; // 0 selects the latest committed version
bool namespace_scope = 3; // with empty ref, selects signed repository's namespace
}
message GetReceiptResponse {
bytes receipt = 1; // complete DSSE storage-receipt envelope JSON
}
// ----------------------------------------------------------------------------
// Packs.
message PackExistsRequest {
bytes pack_id = 1;
}
message PackExistsResponse {
bool exists = 1;
}
// Announces an upload before any PackChunk. `total_bytes` is required: the
// server uses it to reject oversized uploads before storage and to verify
// the following PackChunk stream ends exactly at the declared byte count.
message UploadPackHeader {
bytes pack_id = 1;
uint64 total_bytes = 2;
// Opaque authenticated upload ticket (SPEC-TRANSPORT-CONNECT §7.6).
bytes ticket_token = 3;
}
message UploadPackRequest {
oneof body {
UploadPackHeader header = 1;
PackChunk chunk = 2;
}
}
message UploadPackResponse {
// Empty body is success.
}
message DownloadPackRequest {
bytes pack_id = 1;
}
// Sent once before the first chunk on a download.
message DownloadPackHeader {
uint64 total_bytes = 1;
}
message DownloadPackResponse {
oneof body {
DownloadPackHeader header = 1;
PackChunk chunk = 2;
}
}
// ----------------------------------------------------------------------------
// Discovery.
// Empty discovery request; X-Repository is optional (SPEC-TRANSPORT-CONNECT §2.1).
message GetServerInfoRequest {}
// Deployment capabilities, independent of repository existence (SPEC-TRANSPORT-CONNECT §2.1).
message GetServerInfoResponse {
// Wire package: "mkit.transport.v1" (SPEC-TRANSPORT-CONNECT §2.1).
string protocol = 1;
// Transport specification version: 2 (SPEC-TRANSPORT-CONNECT §2.1).
uint32 spec_version = 2;
// Largest accepted pack length (SPEC-TRANSPORT-CONNECT §2.1).
uint64 max_pack_bytes = 3;
// Power of two, at least 8 MiB (SPEC-TRANSPORT-CONNECT §2.1, §7.6).
uint64 part_size = 4;
// Largest number of parts per upload (SPEC-TRANSPORT-CONNECT §2.1, §7.6).
uint32 max_parts = 5;
// Largest ref count per page (SPEC-TRANSPORT-CONNECT §2.1, §7.9).
uint32 max_list_refs_page_size = 6;
// Smaller packs may skip BeginUpload; zero with admission or multiple repositories (SPEC-TRANSPORT-CONNECT §2.1).
uint64 begin_upload_threshold_bytes = 7;
// Whether AdvanceRefs commits both refs atomically (SPEC-TRANSPORT-CONNECT §2.1, §4).
bool atomic_advance = 8;
// Whether pushed packs are decoded and verified (SPEC-TRANSPORT-CONNECT §2.1).
bool indexed_mode = 9;
// Whether admission can challenge requests (SPEC-TRANSPORT-CONNECT §2.1, §5.1).
bool admission = 10;
// Raw 32-byte current receipt+notice public key; empty only if unconfigured.
bytes receipt_public_key = 11;
// 64-hex BLAKE3 of receipt_public_key; empty only if unconfigured.
string receipt_key_id = 12;
// Accepted owner signature schemes; empty until M2 (SPEC-TRANSPORT-CONNECT §2.1).
repeated string grant_schemes = 13;
// "allowlist", "any", or "single-repository" (SPEC-TRANSPORT-CONNECT §2.1, §7.5).
string namespace_policy = 14;
// Fixed index prefix fan-out; default 16 (SPEC-TRANSPORT-CONNECT §2.1, §7.9).
uint32 index_fanout = 15;
uint32 max_delta_chain_depth = 16; // SPEC-SERVER §9.8; 0 when indexed mode is off
// Whether storage leases are enforced; STC §2.1 and SPEC-SERVER §12.
bool leases = 17;
// Whether async inspection is configured; writers MUST sign reads to see
// non-held pending content. Held content is hidden from every caller,
// including with sync holds when false (SPEC-SERVER §§10–11).
bool async_inspection = 18;
// Maximum distinct inspected objects in one advance; absent when no inspector
// is configured (SPEC-TRANSPORT-CONNECT §2.1; SPEC-SERVER §11.1).
uint32 inspection_max_objects = 19;
}
// ----------------------------------------------------------------------------
// Uploads (tickets and parts).
// Reserve an upload; the repository comes only from X-Repository (SPEC-TRANSPORT-CONNECT §7.4, §7.6).
message BeginUploadRequest {
// The ref this upload will advance (SPEC-TRANSPORT-CONNECT §7.6).
string ref = 1;
// Pack's 32-byte BLAKE3 digest (SPEC-TRANSPORT-CONNECT §7.6).
bytes pack_id = 2;
// Total pack length (SPEC-TRANSPORT-CONNECT §7.6).
uint64 bytes = 3;
}
// Repository membership or an upload reservation (SPEC-TRANSPORT-CONNECT §7.6).
message BeginUploadResponse {
// Exactly one BeginUpload outcome (SPEC-TRANSPORT-CONNECT §7.6).
oneof result {
// The pack is already a repository member (SPEC-TRANSPORT-CONNECT §7.6).
AlreadyPresent already_present = 1;
// The upload reservation and session (SPEC-TRANSPORT-CONNECT §7.6).
UploadTicket ticket = 2;
}
}
// Indicates existing pack membership (SPEC-TRANSPORT-CONNECT §7.6).
message AlreadyPresent {}
// Upload reservation bound by an opaque authenticated token (SPEC-TRANSPORT-CONNECT §7.6).
message UploadTicket {
// 32-byte id; its hex form is <ticket> in part: commitments (SPEC-TRANSPORT-CONNECT §7.6).
bytes id = 1;
// Uniform non-final part length (SPEC-TRANSPORT-CONNECT §7.6).
uint64 part_size = 2;
// Expiry in Unix milliseconds, as in auth v2 (SPEC-TRANSPORT-CONNECT §7.6).
int64 expires_unix_ms = 3;
// Opaque server-authenticated ticket token (SPEC-TRANSPORT-CONNECT §7.6, "Ticket token").
bytes token = 4;
}
// First message of one part's stream (SPEC-TRANSPORT-CONNECT §7.6).
message UploadPartHeader {
// Opaque server-authenticated ticket token (SPEC-TRANSPORT-CONNECT §7.6).
bytes ticket_token = 1;
// Zero-based part index (SPEC-TRANSPORT-CONNECT §7.6).
uint32 index = 2;
}
// Header followed by ordered part bytes (SPEC-TRANSPORT-CONNECT §7.6).
message UploadPartRequest {
// One message in the part stream (SPEC-TRANSPORT-CONNECT §7.6).
oneof msg {
// First message only (SPEC-TRANSPORT-CONNECT §7.6).
UploadPartHeader header = 1;
// Following messages carry the part's bytes in order (SPEC-TRANSPORT-CONNECT §7.6).
bytes chunk = 2;
}
}
// Acknowledges one uploaded part (SPEC-TRANSPORT-CONNECT §7.6).
message UploadPartResponse {
// Opaque server-authenticated part receipt (SPEC-TRANSPORT-CONNECT §7.6).
bytes receipt = 1;
}
// Finish an upload after all parts (SPEC-TRANSPORT-CONNECT §7.6).
message CompleteUploadRequest {
// Opaque server-authenticated ticket token (SPEC-TRANSPORT-CONNECT §7.6).
bytes ticket_token = 1;
// Part receipts in part-index order (SPEC-TRANSPORT-CONNECT §7.6).
repeated bytes receipts = 2;
}
// Empty body is successful completion (SPEC-TRANSPORT-CONNECT §7.6).
message CompleteUploadResponse {}
// Grants, visibility and URL tokens (SPEC-WRITE-GRANTS).
// Namespace RPC; it carries no X-Repository (SPEC-WRITE-GRANTS §5.3).
message GetGrantEpochRequest {
string namespace = 1;
}
// An absent stored epoch is 0 (SPEC-WRITE-GRANTS §5.3).
message GetGrantEpochResponse {
uint64 epoch = 1;
}
// The single §4.2-encoded owner statement is at most 8,192 bytes. No auth v2
// envelope or replay entry applies (SPEC-WRITE-GRANTS §5.3).
message SetGrantEpochRequest {
string signed_statement = 1;
}
// Stored epoch after revocation completion (SPEC-WRITE-GRANTS §5.3).
message SetGrantEpochResponse {
uint64 epoch = 1;
}
enum RepoVisibility {
REPO_VISIBILITY_UNSPECIFIED = 0;
REPO_VISIBILITY_PUBLIC = 1;
REPO_VISIBILITY_PRIVATE = 2;
}
// Repository comes only from X-Repository (SPEC-TRANSPORT-CONNECT §7.4).
// A grant never authorizes either mode (SPEC-WRITE-GRANTS §9.1).
message SetRepoVisibilityRequest {
oneof mode {
// Auth v2 body: owner key or authority; UNSPECIFIED is invalid_argument.
RepoVisibility visibility = 1;
// Owner-signed mkit-repo-visibility:v1 in §4.2 encoding; no auth v2.
string signed_statement = 2;
}
}
message SetRepoVisibilityResponse {}
// Signed read; repository comes only from X-Repository (§9.4; STC §7.4).
message IssueObjectUrlRequest {
oneof target {
// 32-byte id maps to object:<64 lowercase hex>.
bytes object_id = 1;
// Maps to path:<ref>:<base64url(path)>.
RefPath ref_path = 2;
}
// 0 requests the default; otherwise clamped to url_token_ttl (§9.4).
uint32 ttl_seconds = 3;
}
// Full ref name and UTF-8 path of 0..1,024 bytes; empty names the root tree.
message RefPath {
string ref = 1;
string path = 2;
}
message IssueObjectUrlResponse {
string token = 1;
int64 expires_unix_ms = 2;
}
// ----------------------------------------------------------------------------
// Errors/details.
// Detail on a challenged unary request (SPEC-TRANSPORT-CONNECT §5.1).
message AdmissionChallenge {
repeated Challenge challenges = 1; // 1 to 8 entries, in the server's order of preference
string description = 2; // text for a person; at most 512 bytes of UTF-8
}
// One opaque admission challenge (SPEC-TRANSPORT-CONNECT §5.1).
message Challenge {
string scheme = 1; // lowercase token: [a-z0-9][a-z0-9.-]{0,63}
string value = 2; // opaque to mkit; at most 8,192 bytes
}
// Detail on an `unavailable` AdvanceRefs whose consumed pack is still under
// verification (SPEC-TRANSPORT-CONNECT §7.6; SPEC-SERVER §9.5).
message PendingVerification {
uint32 retry_after_ms = 1; // server's suggested poll interval; bounded tiny-pack work may send under 1000 (SPEC-SERVER §9.5)
}
// Detail on an `unavailable` answer whose repository membership is not yet
// visible inside the relay-lag window (SPEC-SERVER §9.4; SPEC-TRANSPORT-CONNECT
// §5). The condition is carried by the type; the message has no fields and
// retrying with the same nonce is safe.
message MembershipNotVisible {}
// Signed takedown notice used as a Connect error detail and on visible refs.
// The sole field is the exact DSSE JSON envelope (SPEC-SERVER §14.6).
message RedactionNotice {
bytes envelope = 1;
}
// Optional namespace authority fence; SPEC-SERVER §6.2.1.
message GetAuthorityGenerationRequest {
string namespace = 1;
}
message GetAuthorityGenerationResponse {
uint64 generation = 1;
}
// Bounded, canonical deployment-authority statement; never an owner fallback.
message SetAuthorityGenerationRequest {
string signed_statement = 1;
}
message SetAuthorityGenerationResponse {
uint64 generation = 1;
}