Files
sgard/proto/sgard/v1/sgard.proto
Kyle Isom 2ff9fe2f50 Step 31: Proto + sync update for targeting.
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>
2026-03-24 22:55:02 -07:00

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);
}