Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar-ui

crates/gunnar-ui-server/proto/gunnar/v1/refs.proto

syntax = "proto3";

package gunnar.v1;

import "gunnar/v1/common.proto";

// Read access to a repository's refs, commit graph and objects.
//
// This service is READ-ONLY by construction. Refs move over the SSH listener
// through receive-pack and its policy gate, never over gRPC. There is no
// `Refs.Set`, and adding one would route a push around every gate.
service Refs {
  rpc List(ListRefsRequest) returns (ListRefsResponse);
  rpc Get(GetRefRequest) returns (GetRefResponse);
  // Commit walk, paged. Served from the Arrow commit table of the `git` znippy
  // package once the `git` package's `__gunnar_graph__` lands (wave 9b): a scan, not a walk.
  rpc Log(LogRequest) returns (LogResponse);
  rpc ReadObject(ReadObjectRequest) returns (ReadObjectResponse);
  // One LFS object out of the repository's LFS store, by the sha256 its
  // POINTER names. A file kept in LFS is a pointer in the tree, so a caller
  // that reads the tree — a console drawing a README's link to a PDF — gets
  // 131 bytes of text where the file should be, and no way to ask for the
  // file. `git lfs` fetches these over the SSH adapter or the HTTP batch API;
  // this is the same bytes for a caller that already speaks gRPC and holds
  // Read on the repo. It is a whole small object, not a transfer adapter:
  // see ReadLfsObjectRequest for the ceiling, and gunnar-lfs for the wires
  // that move a firmware image.
  rpc ReadLfsObject(ReadLfsObjectRequest) returns (ReadLfsObjectResponse);
  rpc Diff(DiffRequest) returns (DiffResponse);
  // Who wrote each line of a file at a revision, and a literal string across
  // one revision's files. These run the SAME walks `gunnar blame` / `gunnar
  // search` run — but against the RUNNING server's already-open store, so an
  // operator can blame/search a SEALED appliance with no shell, where the
  // store-local CLI verbs (an exclusive lock the live server holds) cannot run
  // at all. Read on the repo, like every verb here.
  rpc Blame(BlameRequest) returns (BlameResponse);
  rpc Search(SearchRequest) returns (SearchResponse);
}

message ListRefsRequest {
  RepoPath repo = 1;
  // e.g. "refs/heads/", matched as a literal prefix, not a glob.
  string prefix = 2;
  uint32 page_size = 3;
  string page_token = 4;
  // Resolve annotated tags to the commit they name, filling `peeled_oid`.
  bool peel = 5;
}
message ListRefsResponse {
  repeated RefTarget refs = 1;
  string next_page_token = 2;
}

message GetRefRequest {
  RepoPath repo = 1;
  // Full ref name ("refs/heads/main") or "HEAD".
  string ref_name = 2;
  bool peel = 3;
}
message GetRefResponse {
  RefTarget ref_target = 1;
}

message LogRequest {
  RepoPath repo = 1;
  // A full ref name or a hex oid. Empty means the repo's default branch.
  string start = 2;
  uint32 page_size = 3;
  string page_token = 4;
  // Only commits that CHANGED this path (a file or a directory), judged
  // against each commit's FIRST parent — the same commits `git log -- <path>`
  // shows without rename-following or history simplification. Empty means no
  // filter. The walk behind a filtered page is bounded, so a quiet path deep
  // in a long history can come back short of `page_size` without being the
  // end of history.
  string path = 5;
  // Walk from EVERY branch and tag tip (refs/heads/*, refs/tags/*, and HEAD)
  // at once instead of from `start`, so commits that live only on a side
  // branch show up. Same order (newest committer time first) and the same
  // `page_size` cap as a single-start walk. `start` must be empty when this is
  // set; a caller continuing a page passes a frontier oid in `start` instead.
  bool all_refs = 6;
}
message LogResponse {
  repeated Commit commits = 1;
  string next_page_token = 2;
}

message ReadObjectRequest {
  RepoPath repo = 1;
  // Full hex oid. Abbreviated oids are a UI convenience resolved elsewhere;
  // this field is exact (abbreviations are never on the git wire).
  string oid = 2;
  // 0 means "the whole object". A larger object is truncated, not refused, and
  // `truncated` is set on the response.
  uint64 max_bytes = 3;
}
message ReadObjectResponse {
  string oid = 1;
  ObjectKind kind = 2;
  // The FULL uncompressed size, even when `data` is truncated.
  uint64 size = 3;
  bytes data = 4;
  bool truncated = 5;
}

message ReadLfsObjectRequest {
  RepoPath repo = 1;
  // The sha256 out of the pointer file, lowercase hex, 64 characters — the
  // name LFS stores the object under. Not a git oid: an LFS object is not in
  // the object database at all.
  string oid = 2;
  // The largest response the caller will take. 0 means the server's own
  // ceiling, and anything above it is lowered to it.
  //
  // **An object past the ceiling is REFUSED, never truncated.** A prefix of a
  // git blob is a readable prefix of a file; a prefix of an LFS object is a
  // broken PDF, and a caller that cannot tell the difference would serve one.
  // A client that wants the whole of a large object wants `git lfs`, whose
  // adapters stream it — see the `gunnar-lfs` crate — or a RANGED read below.
  uint64 max_bytes = 3;
  // **A range of the object**, for a caller that streams one larger than the
  // ceiling as consecutive ranges (a console handing a browser a 6 MB
  // picture), or wants only its head (a picture's size is in its header).
  //
  // Both 0 — the fields absent, as every client before them sends — is the
  // whole-or-refused read above, unchanged. Otherwise this is a ranged read:
  // the bytes from `offset`, at most `length` of them (0 = up to the
  // ceiling), ending early at the object's end. The ceiling applies to the
  // RANGE: a `length` over it is refused, never cut. An `offset` past the end
  // of the object is refused (OutOfRange), naming the object's size.
  uint64 offset = 4;
  uint64 length = 5;
}
message ReadLfsObjectResponse {
  string oid = 1;
  // The WHOLE object's size. On a whole read it is also `data`'s length; on a
  // ranged read `data` is the range, and `offset` + `data`'s length = `size`
  // says it was the last one.
  uint64 size = 2;
  bytes data = 3;
  // Where `data` starts in the object — the request's `offset`, echoed. A
  // server older than ranged reads leaves it 0 and answers whole-or-refused,
  // which is how a client asking for `offset` > 0 detects one.
  uint64 offset = 4;
}

enum ChangeKind {
  CHANGE_KIND_UNSPECIFIED = 0;
  CHANGE_KIND_ADDED = 1;
  CHANGE_KIND_MODIFIED = 2;
  CHANGE_KIND_DELETED = 3;
  CHANGE_KIND_RENAMED = 4;
  CHANGE_KIND_COPIED = 5;
  CHANGE_KIND_TYPE_CHANGED = 6;
}

message FileChange {
  ChangeKind kind = 1;
  string path = 2;
  // Set for RENAMED and COPIED only.
  string old_path = 3;
  string old_oid = 4;
  string new_oid = 5;
  uint32 old_mode = 6;
  uint32 new_mode = 7;
  uint64 insertions = 8;
  uint64 deletions = 9;
  bool binary = 10;
}

message DiffRequest {
  RepoPath repo = 1;
  // Tree-ish oids or ref names.
  string from = 2;
  string to = 3;
  // Only paths under these prefixes. Empty means the whole tree.
  repeated string path_prefixes = 4;
}
message DiffResponse {
  repeated FileChange changes = 1;
}

message BlameRequest {
  RepoPath repo = 1;
  // A full ref name, "HEAD", or a hex oid. Empty means the repo's default
  // branch — resolved the way Log resolves `start`.
  string rev = 2;
  // The file to blame, as a path within the revision's tree.
  string path = 3;
}
// One line's attribution, in file order.
message BlameLine {
  // The full hex oid of the commit that last touched this line.
  string commit = 1;
  // The commit author's display name, lossily decoded (git does not promise a
  // name is UTF-8).
  string author = 2;
  // The line's bytes at the blamed revision, without its trailing newline.
  bytes line = 3;
}
message BlameResponse {
  repeated BlameLine lines = 1;
}

message SearchRequest {
  RepoPath repo = 1;
  // A full ref name, "HEAD", or a hex oid. Empty means the default branch.
  string rev = 2;
  // A LITERAL string, not a regular expression. An empty pattern is refused.
  string pattern = 3;
  // Files larger than this are skipped without being read. 0 means the
  // server's default (1 MiB).
  uint64 max_file_bytes = 4;
  // Stop after this many matches. 0 means the server's default (1000).
  uint32 max_matches = 5;
}
// One matching line.
message SearchMatch {
  // The file's path within the tree, `/`-joined.
  string path = 1;
  // The one-based line number, the way an editor and `grep` count.
  uint32 line_number = 2;
  // The line's bytes, without its trailing newline.
  bytes line = 3;
}
message SearchResponse {
  repeated SearchMatch matches = 1;
}