// Connect (crpc) contract for the gitrpc JSON-RPC server. // // 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." 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 { // Create a repo (git init --bare). Reuses the namespace's NamespaceID if it // exists, else mints a new one. rpc CreateRepo(CreateRepoRequest) 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); // 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); // Daemon status + config: startup params, listen locations, start time / // uptime, and the state DB URI (gitctl uses db_uri to connect directly for // inspection reads -- there is no list RPC on this service). Diagnostics only; // does not touch the DB. 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 CreateRepoRequest { string ns = 1; string name = 2; } 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 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; } // ---- ManageService Status (diagnostics) ---- // StatusResponse is the daemon's config + runtime snapshot. db_uri is the state // DB connection string (sqlite:// or postgres://) gitctl connects to // directly for inspection reads; it carries credentials when postgres, so the // manage port must stay localhost-only/trusted. 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) string db_uri = 6; // -db state 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 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 }