532 lines
15 KiB
Protocol Buffer
532 lines
15 KiB
Protocol Buffer
// This is a faithful translation of the hand-written JSON-RPC method set in
|
|
// handlers.go (registerRepoMethods + registerWriteMethods) plus the gitcmd
|
|
// result types those handlers return verbatim. Every rpc below maps 1:1 to a
|
|
// "repo.<name>" JSON-RPC method (noted in each comment); every message field
|
|
// keeps the snake_case name of the corresponding Go struct json tag so the
|
|
// wire shape matches what gitrpc serializes today.
|
|
//
|
|
// The server is path-driven and stateless: every method takes the repository
|
|
// NAME (owner/name.git) in `repo`; the server resolves it to an on-disk path
|
|
// and has already authorized the call before the handler runs. Auth/ACL and
|
|
// name->path resolution live in the caller (simpleconsole), not here.
|
|
//
|
|
// Generate Go + TS with protoc-gen-es (TS) / protoc-gen-go + protoc-gen-connect-go:
|
|
// protoc -I . --go_out=. --go_opt=paths=source_relative \
|
|
// --connect_go_out=. --connect_go_opt=paths=source_relative \
|
|
// --es_out=./gen --es_opt=target=ts \
|
|
// --plugin=protoc-gen-es=./node_modules/.bin/protoc-gen-es \
|
|
// gitrpc.proto
|
|
|
|
syntax = "proto3";
|
|
|
|
package simplegit.v1;
|
|
|
|
import "google/protobuf/empty.proto";
|
|
import "google/protobuf/timestamp.proto";
|
|
|
|
option go_package = "simplegit/gitrpc/v1;v1";
|
|
|
|
// GitService is the git kernel facade: pure git operations over a resolved,
|
|
// already-authorized on-disk repo path. Bound localhost-only; no identity store.
|
|
service GitService {
|
|
// repo.listBranches
|
|
rpc ListBranches(RepoRequest) returns (ListBranchesResponse);
|
|
|
|
// repo.listTags
|
|
rpc ListTags(RepoRequest) returns (ListTagsResponse);
|
|
|
|
// repo.listCommits
|
|
rpc ListCommits(ListCommitsRequest) returns (ListCommitsResponse);
|
|
|
|
// repo.listCommitsByPath
|
|
rpc ListCommitsByPath(ListCommitsByPathRequest) returns (ListCommitsResponse);
|
|
|
|
// repo.getCommit
|
|
rpc GetCommit(GetCommitRequest) returns (CommitDetail);
|
|
|
|
// repo.getCommitDiff
|
|
rpc GetCommitDiff(GetCommitDiffRequest) returns (DiffResult);
|
|
|
|
// repo.getTree
|
|
rpc GetTree(GetTreeRequest) returns (Tree);
|
|
|
|
// repo.getBlob
|
|
rpc GetBlob(GetBlobRequest) returns (Blob);
|
|
|
|
// repo.compare
|
|
rpc Compare(CompareRequest) returns (CompareResult);
|
|
|
|
// repo.blame
|
|
rpc Blame(BlameRequest) returns (BlameResult);
|
|
|
|
// repo.getContributors
|
|
rpc GetContributors(GetContributorsRequest) returns (GetContributorsResponse);
|
|
|
|
// repo.getStats
|
|
rpc GetStats(RepoRequest) returns (RepoStats);
|
|
|
|
// repo.countObjects
|
|
rpc CountObjects(RepoRequest) returns (CountObjectsResult);
|
|
|
|
// repo.createFile
|
|
rpc CreateFile(CreateFileRequest) returns (FileCommitResult);
|
|
|
|
// repo.deleteFile
|
|
rpc DeleteFile(DeleteFileRequest) returns (FileCommitResult);
|
|
|
|
// repo.merge
|
|
rpc Merge(MergeRequest) returns (MergeResult);
|
|
}
|
|
|
|
// ManageService is the management surface backed by the self-contained state DB
|
|
// (state.IStateMut): repo lifecycle + ACL grants. Bound to a SEPARATE localhost
|
|
// port (-manage) from GitService; authenticated with a console system token
|
|
// (the Updater / console is the caller). Unlike GitService these mutate the
|
|
// state DB, not just git.
|
|
service ManageService {
|
|
// Execute a whitelisted state/ctrl database operation. Arguments are
|
|
// positional strings interpreted by the selected operation.
|
|
rpc Ctrl(CtrlRequest) returns (CtrlResponse);
|
|
// Create an empty namespace. Password login is configured separately.
|
|
rpc CreateNS(CreateNSRequest) returns (google.protobuf.Empty);
|
|
// Delete an empty namespace and its namespace-level auth data.
|
|
rpc DeleteNS(DeleteNSRequest) returns (google.protobuf.Empty);
|
|
// Create a repo when absent, or update its mutable properties when present.
|
|
rpc UpsertRepo(UpsertRepoRequest) returns (google.protobuf.Empty);
|
|
// OK if the repo is registered; NotFound otherwise.
|
|
rpc ExistRepo(ExistRepoRequest) returns (google.protobuf.Empty);
|
|
// Remove a repo (DB row + repo-targeted ACL grants + on-disk dir).
|
|
rpc DeleteRepo(DeleteRepoRequest) returns (google.protobuf.Empty);
|
|
// Move/rename a repo (transfer and/or rename).
|
|
rpc MoveRepo(MoveRepoRequest) returns (google.protobuf.Empty);
|
|
// Rename a namespace (all its repos); NamespaceID is stable.
|
|
rpc MoveNS(MoveNSRequest) returns (google.protobuf.Empty);
|
|
// Create or rotate the password-login projection for a namespace.
|
|
rpc UserPasswordUpsert(UserPasswordUpsertRequest) returns (google.protobuf.Empty);
|
|
// Remove a namespace's password-login projection. Repos and namespace remain.
|
|
rpc UserDelete(UserDeleteRequest) returns (google.protobuf.Empty);
|
|
|
|
// Grant an SSH key perm on a namespace; registers the key if new. Returns the ACL id.
|
|
rpc ACLUpsertSSHKeyOnNS(ACLUpsertSSHKeyOnNSRequest) returns (ACLIDResponse);
|
|
// Grant a PAT perm on a namespace; creates the PAT if new. Returns the ACL id.
|
|
rpc ACLUpsertPATOnNS(ACLUpsertPATOnNSRequest) returns (ACLIDResponse);
|
|
// Grant an SSH key perm on a repo; registers the key if new. Returns the ACL id.
|
|
rpc ACLUpsertSSHKeyOnRepo(ACLUpsertSSHKeyOnRepoRequest) returns (ACLIDResponse);
|
|
// Grant a PAT perm on a repo; creates the PAT if new. Returns the ACL id.
|
|
rpc ACLUpsertPATOnRepo(ACLUpsertPATOnRepoRequest) returns (ACLIDResponse);
|
|
// Remove a single ACL grant by id.
|
|
rpc ACLDelete(ACLDeleteRequest) returns (google.protobuf.Empty);
|
|
// Change a grant's permission without resubmitting its credential.
|
|
rpc ACLSetPerm(ACLSetPermRequest) returns (google.protobuf.Empty);
|
|
|
|
// Daemon status + config. Database connection details are intentionally not
|
|
// exposed; management reads use Ctrl.
|
|
rpc Status(google.protobuf.Empty) returns (StatusResponse);
|
|
}
|
|
|
|
// ---------- shared request messages ----------
|
|
|
|
// repoParams: the repository name (owner/name.git). JSON key is "repo" so it
|
|
// never collides with the in-repo file "path" param several methods also carry.
|
|
message RepoRequest {
|
|
string repo = 1;
|
|
}
|
|
|
|
message ListCommitsRequest {
|
|
string repo = 1;
|
|
string ref = 2; // "" -> HEAD (server-side default)
|
|
int32 limit = 3;
|
|
}
|
|
|
|
message ListCommitsByPathRequest {
|
|
string repo = 1;
|
|
string ref = 2;
|
|
string path = 3;
|
|
int32 limit = 4;
|
|
}
|
|
|
|
message GetCommitRequest {
|
|
string repo = 1;
|
|
string sha = 2;
|
|
}
|
|
|
|
message GetCommitDiffRequest {
|
|
string repo = 1;
|
|
string sha = 2;
|
|
}
|
|
|
|
message GetTreeRequest {
|
|
string repo = 1;
|
|
string ref = 2;
|
|
string path = 3; // "" for repo root
|
|
}
|
|
|
|
message GetBlobRequest {
|
|
string repo = 1;
|
|
string ref = 2;
|
|
string path = 3;
|
|
}
|
|
|
|
message CompareRequest {
|
|
string repo = 1;
|
|
string base = 2; // "" -> HEAD
|
|
string head = 3; // "" -> HEAD
|
|
}
|
|
|
|
message BlameRequest {
|
|
string repo = 1;
|
|
string ref = 2;
|
|
string path = 3;
|
|
}
|
|
|
|
message GetContributorsRequest {
|
|
string repo = 1;
|
|
string ref = 2; // "" -> default branch
|
|
}
|
|
|
|
// Write-path requests carry the caller-resolved commit identity
|
|
// (author_name / author_email): the server has no identity store. `strategy`
|
|
// selects the gitcmd implementation ("plumbing" default, "temprepo" for the
|
|
// Gitea-style temp-clone+push that runs hooks).
|
|
|
|
message CreateFileRequest {
|
|
string repo = 1;
|
|
string branch = 2;
|
|
string path = 3;
|
|
string content = 4;
|
|
string message = 5;
|
|
string author_name = 6;
|
|
string author_email = 7;
|
|
string expected_sha = 8; // atomic ref update against this branch tip
|
|
string strategy = 9; // "" / "plumbing" / "temprepo"
|
|
}
|
|
|
|
message DeleteFileRequest {
|
|
string repo = 1;
|
|
string branch = 2;
|
|
string path = 3;
|
|
string message = 4;
|
|
string author_name = 5;
|
|
string author_email = 6;
|
|
string expected_sha = 7;
|
|
string strategy = 8;
|
|
}
|
|
|
|
message MergeRequest {
|
|
string repo = 1;
|
|
string base = 2;
|
|
string head = 3;
|
|
string message = 4;
|
|
string author_name = 5;
|
|
string author_email = 6;
|
|
bool no_ff = 7;
|
|
string strategy = 8;
|
|
}
|
|
|
|
// ---- ManageService requests ----
|
|
|
|
message CreateNSRequest {
|
|
string ns = 1;
|
|
}
|
|
|
|
message DeleteNSRequest {
|
|
string ns = 1;
|
|
}
|
|
|
|
message UpsertRepoRequest {
|
|
string ns = 1;
|
|
string name = 2;
|
|
// is_private seeds the repo's privacy flag in simplegit's state DB. simplegit
|
|
// enforces it at the git-transport layer: a private repo denies read (clone)
|
|
// to anyone without an ACL grant, so the console's isPrivate must be
|
|
// propagated here (it can't be enforced console-side - simplegit is the git
|
|
// kernel that owns the bare repo + its access checks).
|
|
bool is_private = 3;
|
|
}
|
|
|
|
message ExistRepoRequest {
|
|
string ns = 1;
|
|
string name = 2;
|
|
}
|
|
|
|
message DeleteRepoRequest {
|
|
string ns = 1;
|
|
string name = 2;
|
|
}
|
|
|
|
message MoveRepoRequest {
|
|
string src = 1; // old ns/name
|
|
string dst = 2; // new ns/name
|
|
}
|
|
|
|
message MoveNSRequest {
|
|
string src = 1; // old namespace
|
|
string dst = 2; // new namespace
|
|
}
|
|
|
|
message UserPasswordUpsertRequest {
|
|
string ns = 1;
|
|
string password = 2;
|
|
}
|
|
|
|
message UserDeleteRequest {
|
|
string ns = 1;
|
|
}
|
|
|
|
message ACLUpsertSSHKeyOnNSRequest {
|
|
string ns = 1;
|
|
string key = 2; // authorized-keys text
|
|
string perm = 3; // read | write | admin
|
|
}
|
|
|
|
message ACLUpsertPATOnNSRequest {
|
|
string ns = 1;
|
|
string pat = 2; // plaintext PAT
|
|
string perm = 3;
|
|
}
|
|
|
|
message ACLUpsertSSHKeyOnRepoRequest {
|
|
string reponame = 1; // ns/name
|
|
string key = 2;
|
|
string perm = 3;
|
|
}
|
|
|
|
message ACLUpsertPATOnRepoRequest {
|
|
string reponame = 1;
|
|
string pat = 2;
|
|
string perm = 3;
|
|
}
|
|
|
|
message ACLDeleteRequest {
|
|
int64 acl_id = 1;
|
|
}
|
|
|
|
message ACLSetPermRequest {
|
|
int64 acl_id = 1;
|
|
string perm = 2;
|
|
}
|
|
|
|
// ---- ManageService Status (diagnostics) ----
|
|
|
|
// StatusResponse is the daemon's config + runtime snapshot.
|
|
message StatusResponse {
|
|
string root = 1; // -root (git root)
|
|
string rpc_addr = 2; // -rpc listen
|
|
string ssh_addr = 3; // -ssh listen
|
|
string manage_addr = 4; // -manage listen
|
|
string auth_url = 5; // -auth RBAC center ("" if skip-auth)
|
|
reserved 6; // formerly db_uri
|
|
bool skip_auth = 7; // -skip-auth
|
|
google.protobuf.Timestamp started_at = 8; // daemon start time
|
|
int64 uptime_seconds = 9; // seconds since started_at
|
|
}
|
|
|
|
// ---------- scalar/repeated response wrappers ----------
|
|
// (proto rpc returns must be a message; these wrap the non-message returns.)
|
|
|
|
message ACLIDResponse {
|
|
int64 acl_id = 1;
|
|
}
|
|
|
|
message CtrlRequest {
|
|
string op = 1;
|
|
repeated string args = 2;
|
|
}
|
|
|
|
message CtrlResponse {
|
|
bytes result = 1; // JSON encoded operation result
|
|
}
|
|
|
|
message ListBranchesResponse {
|
|
repeated Branch branches = 1;
|
|
}
|
|
|
|
message ListTagsResponse {
|
|
repeated Tag tags = 1;
|
|
}
|
|
|
|
message ListCommitsResponse {
|
|
repeated Commit commits = 1;
|
|
}
|
|
|
|
message GetContributorsResponse {
|
|
repeated Contributor contributors = 1;
|
|
}
|
|
|
|
// ---------- data messages (gitcmd result types) ----------
|
|
|
|
// gitcmd.Commit. Pinned snake_case json so renaming Go fields does not drift
|
|
// the wire contract.
|
|
message Commit {
|
|
string id = 1; // full 40-char SHA
|
|
string short_sha = 2; // 7-char abbreviated SHA
|
|
string message = 3; // full commit message
|
|
string author = 4; // author name
|
|
string author_email = 5; // author email
|
|
google.protobuf.Timestamp when = 6; // author timestamp
|
|
repeated string parents = 7; // parent SHAs
|
|
}
|
|
|
|
// gitcmd.CommitDetail extends Commit with file change statistics. Commit fields
|
|
// are nested (proto has no struct embedding) rather than flattened.
|
|
message CommitDetail {
|
|
Commit commit = 1;
|
|
DiffStats stats = 2;
|
|
repeated FileDiff files = 3;
|
|
}
|
|
|
|
message DiffStats {
|
|
int32 additions = 1;
|
|
int32 deletions = 2;
|
|
int32 total = 3;
|
|
int32 files = 4;
|
|
}
|
|
|
|
message FileDiff {
|
|
string path = 1;
|
|
int32 additions = 2;
|
|
int32 deletions = 3;
|
|
string status = 4; // A (added), M (modified), D (deleted), R (renamed)
|
|
}
|
|
|
|
message Branch {
|
|
string name = 1;
|
|
string sha = 2;
|
|
string short_sha = 3;
|
|
string message = 4;
|
|
string author = 5;
|
|
google.protobuf.Timestamp when = 6;
|
|
bool is_default = 7;
|
|
}
|
|
|
|
message Tag {
|
|
string name = 1;
|
|
string sha = 2;
|
|
string short_sha = 3;
|
|
string message = 4;
|
|
string tagger = 5;
|
|
google.protobuf.Timestamp tag_date = 6;
|
|
bool is_annotated = 7;
|
|
}
|
|
|
|
message TreeEntry {
|
|
string name = 1;
|
|
string path = 2;
|
|
string type = 3; // "blob" (file) or "tree" (directory)
|
|
string mode = 4; // git mode, e.g. "100644", "040000"
|
|
int64 size = 5; // file size in bytes (0 for directories)
|
|
string sha = 6;
|
|
}
|
|
|
|
message Tree {
|
|
string ref = 1;
|
|
string path = 2;
|
|
repeated TreeEntry entries = 3;
|
|
}
|
|
|
|
message Blob {
|
|
string ref = 1;
|
|
string path = 2;
|
|
string content = 3; // raw file content (text or base64-encoded binary)
|
|
string encoding = 4; // "text" or "base64"
|
|
int64 size = 5;
|
|
string sha = 6;
|
|
bool is_binary = 7;
|
|
}
|
|
|
|
message DiffLine {
|
|
string type = 1; // "context", "add", "delete"
|
|
string text = 2; // line content without the leading sigil
|
|
}
|
|
|
|
message Hunk {
|
|
int32 old_start = 1; // 0 if the old side is absent
|
|
int32 old_count = 2;
|
|
int32 new_start = 3; // 0 if the new side is absent
|
|
int32 new_count = 4;
|
|
repeated DiffLine lines = 5;
|
|
}
|
|
|
|
message FileChange {
|
|
string path = 6; // resulting path (new path; old path if deleted)
|
|
string old_path = 1; // set only on rename/copy
|
|
string status = 2; // A / M / D / R
|
|
bool is_binary = 3;
|
|
int32 additions = 4;
|
|
int32 deletions = 5;
|
|
repeated Hunk hunks = 7;
|
|
}
|
|
|
|
message DiffResult {
|
|
repeated FileChange files = 1;
|
|
}
|
|
|
|
message CompareResult {
|
|
string base = 1;
|
|
string head = 2;
|
|
int32 ahead = 3; // commits reachable from head but not base
|
|
int32 behind = 4; // commits reachable from base but not head
|
|
repeated Commit commits = 5; // base..head, newest first
|
|
DiffResult diff = 6; // combined file diff base..head
|
|
}
|
|
|
|
message BlameLine {
|
|
string sha = 1;
|
|
string short_sha = 2;
|
|
string author = 3;
|
|
string email = 4;
|
|
google.protobuf.Timestamp when = 5;
|
|
int32 line_no = 6; // 1-based line number in the blamed file
|
|
string content = 7;
|
|
string summary = 8; // commit subject line
|
|
}
|
|
|
|
message BlameResult {
|
|
string ref = 1;
|
|
string path = 2;
|
|
repeated BlameLine lines = 3;
|
|
}
|
|
|
|
message Contributor {
|
|
string author = 1;
|
|
string email = 2;
|
|
int32 commits = 3;
|
|
int32 additions = 4;
|
|
int32 deletions = 5;
|
|
int32 total_lines = 6;
|
|
}
|
|
|
|
message RepoStats {
|
|
int32 total_commits = 1;
|
|
int32 total_branches = 2;
|
|
int32 total_tags = 3;
|
|
int64 total_size = 4; // git object size in bytes (count-objects)
|
|
repeated Contributor contributors = 5;
|
|
google.protobuf.Timestamp first_commit = 6;
|
|
google.protobuf.Timestamp last_commit = 7;
|
|
}
|
|
|
|
message CountObjectsResult {
|
|
int64 count = 1; // loose objects
|
|
int64 size = 2; // loose objects size, bytes
|
|
int64 in_pack = 3; // objects inside packs
|
|
int64 packs = 4; // number of packs
|
|
int64 size_pack = 5; // packed objects size, bytes
|
|
int64 garbage = 6; // garbage objects
|
|
int64 size_garbage = 7; // garbage size, bytes
|
|
}
|
|
|
|
// MergeResult: exactly one of merge_commit (clean/FF) or conflicts (conflict)
|
|
// is meaningful.
|
|
message MergeResult {
|
|
string base = 1;
|
|
string head = 2;
|
|
Commit merge_commit = 3; // nil on conflict; on FF, head's tip
|
|
bool fast_forward = 4;
|
|
repeated string conflicts = 5; // conflicting paths (merge_commit nil)
|
|
}
|
|
|
|
message FileCommitResult {
|
|
Commit commit = 1; // the new commit that now tips the branch
|
|
}
|