Added only/never repeated string fields to ManifestEntry proto. Updated convert.go for round-trip. Targeting test in convert_test.go. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
145 lines
4.4 KiB
Protocol Buffer
145 lines
4.4 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package sgard.v1;
|
|
|
|
option go_package = "github.com/kisom/sgard/sgardpb";
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
|
|
// ManifestEntry mirrors manifest.Entry from the YAML model.
|
|
message ManifestEntry {
|
|
string path = 1;
|
|
string hash = 2;
|
|
string type = 3; // "file", "directory", "link"
|
|
string mode = 4;
|
|
string target = 5;
|
|
google.protobuf.Timestamp updated = 6;
|
|
string plaintext_hash = 7; // SHA-256 of plaintext (encrypted entries only)
|
|
bool encrypted = 8;
|
|
bool locked = 9; // repo-authoritative; restore always overwrites
|
|
repeated string only = 10; // per-machine targeting: only apply on matching
|
|
repeated string never = 11; // per-machine targeting: never apply on matching
|
|
}
|
|
|
|
// KekSlot describes a single KEK source for unwrapping the DEK.
|
|
message KekSlot {
|
|
string type = 1; // "passphrase" or "fido2"
|
|
int32 argon2_time = 2;
|
|
int32 argon2_memory = 3; // KiB
|
|
int32 argon2_threads = 4;
|
|
string credential_id = 5; // base64, fido2 only
|
|
string salt = 6; // base64
|
|
string wrapped_dek = 7; // base64
|
|
}
|
|
|
|
// Encryption holds the encryption configuration.
|
|
message Encryption {
|
|
string algorithm = 1;
|
|
map<string, KekSlot> kek_slots = 2;
|
|
}
|
|
|
|
// Manifest mirrors the top-level manifest.Manifest.
|
|
message Manifest {
|
|
int32 version = 1;
|
|
google.protobuf.Timestamp created = 2;
|
|
google.protobuf.Timestamp updated = 3;
|
|
string message = 4;
|
|
repeated ManifestEntry files = 5;
|
|
Encryption encryption = 6;
|
|
}
|
|
|
|
// BlobChunk is one piece of a streamed blob. The first chunk for a given
|
|
// hash carries the hash field; subsequent chunks omit it.
|
|
message BlobChunk {
|
|
string hash = 1; // SHA-256 hex, present on the first chunk of each blob
|
|
bytes data = 2; // up to 64 KiB per chunk
|
|
}
|
|
|
|
// Push messages.
|
|
|
|
message PushManifestRequest {
|
|
Manifest manifest = 1;
|
|
}
|
|
|
|
message PushManifestResponse {
|
|
enum Decision {
|
|
DECISION_UNSPECIFIED = 0;
|
|
ACCEPTED = 1; // server is older; push proceeds
|
|
REJECTED = 2; // server is newer; client should pull
|
|
UP_TO_DATE = 3; // timestamps match; nothing to do
|
|
}
|
|
Decision decision = 1;
|
|
repeated string missing_blobs = 2; // hashes the server needs
|
|
google.protobuf.Timestamp server_updated = 3;
|
|
}
|
|
|
|
message PushBlobsRequest {
|
|
BlobChunk chunk = 1;
|
|
}
|
|
|
|
message PushBlobsResponse {
|
|
int32 blobs_received = 1;
|
|
}
|
|
|
|
// Pull messages.
|
|
|
|
message PullManifestRequest {}
|
|
|
|
message PullManifestResponse {
|
|
Manifest manifest = 1;
|
|
}
|
|
|
|
message PullBlobsRequest {
|
|
repeated string hashes = 1; // blobs the client needs
|
|
}
|
|
|
|
message PullBlobsResponse {
|
|
BlobChunk chunk = 1;
|
|
}
|
|
|
|
// Prune messages.
|
|
|
|
message PruneRequest {}
|
|
|
|
message PruneResponse {
|
|
int32 blobs_removed = 1;
|
|
}
|
|
|
|
// Auth messages.
|
|
|
|
message AuthenticateRequest {
|
|
bytes nonce = 1; // 32-byte nonce (server-provided or client-generated)
|
|
int64 timestamp = 2; // Unix seconds
|
|
bytes signature = 3; // SSH signature over (nonce || timestamp)
|
|
string public_key = 4; // SSH public key in authorized_keys format
|
|
}
|
|
|
|
message AuthenticateResponse {
|
|
string token = 1; // JWT valid for 30 days
|
|
}
|
|
|
|
// ReauthChallenge is embedded in Unauthenticated error details when a
|
|
// token is expired but was previously valid. The client signs this
|
|
// challenge to obtain a new token without generating its own nonce.
|
|
message ReauthChallenge {
|
|
bytes nonce = 1; // server-generated 32-byte nonce
|
|
int64 timestamp = 2; // server's current Unix timestamp
|
|
}
|
|
|
|
// GardenSync is the sgard remote sync service.
|
|
service GardenSync {
|
|
// Authenticate exchanges an SSH-signed challenge for a JWT token.
|
|
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse);
|
|
|
|
// Push flow: send manifest, then stream missing blobs.
|
|
rpc PushManifest(PushManifestRequest) returns (PushManifestResponse);
|
|
rpc PushBlobs(stream PushBlobsRequest) returns (PushBlobsResponse);
|
|
|
|
// Pull flow: get manifest, then stream requested blobs.
|
|
rpc PullManifest(PullManifestRequest) returns (PullManifestResponse);
|
|
rpc PullBlobs(PullBlobsRequest) returns (stream PullBlobsResponse);
|
|
|
|
// Prune removes orphaned blobs on the server.
|
|
rpc Prune(PruneRequest) returns (PruneResponse);
|
|
}
|