{
  "openapi": "3.1.0",
  "info": {
    "title": "zuka",
    "version": "0.1.0",
    "summary": "Git hosting whose primary client is an agent.",
    "description": "Bare git repositories with a REST API, the git smart-HTTP wire protocol, and SSH.\n\nAuthentication is a bearer token on `/v1/*`, and HTTP Basic on the git paths where the username is ignored and the password is the token. SSH authenticates with a registered public key.\n\nErrors on `/v1/*` are RFC 9457 `application/problem+json` with a stable `type` URI; clients discriminate on `type` and read extension members, never on `detail`. The git paths under `/{account}/{repo}.git/` deliberately answer in `text/plain` instead, because git prints the response body straight at the user.\n\nAccount and repository names are case-folded: `Alice/Site` and `alice/site` are the same resource.\n\nSSH is also served. Authentication is a registered public key; the SSH username is ignored and the account is derived from which key matched. A session may only exec `git-upload-pack` or `git-receive-pack` \u2014 there is no shell, no pty and no subsystem. Register keys with `POST /v1/keys`.\n\nSSH serves `git-upload-pack`, `git-receive-pack` and `git-upload-archive`, so `git archive --remote` works alongside clone, fetch and push.\n\nStorage quotas and per-credential rate limits apply to `/v1` and `/mcp`. Over-quota blocks writes only \u2014 reads and clones keep working. The git transport is not rate limited; it is bounded by a concurrency semaphore instead.\n\nList endpoints return `{items, truncated}`. There is no cursor: a previous draft emitted `next_cursor: null` on lists that had been cut short, which told clients the list had ended when it had not.",
    "license": {
      "name": "Apache-2.0"
    }
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8790",
      "description": "Local standalone"
    }
  ],
  "tags": [
    {
      "name": "service",
      "description": "Health and discovery."
    },
    {
      "name": "account",
      "description": "The authenticated identity."
    },
    {
      "name": "repositories",
      "description": "Repository lifecycle and refs."
    },
    {
      "name": "keys",
      "description": "SSH public keys."
    },
    {
      "name": "git",
      "description": "The git wire protocol. Not REST."
    },
    {
      "name": "mcp",
      "description": "Model Context Protocol surface for agents."
    },
    {
      "name": "ci",
      "description": "Continuous integration runs."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/healthz": {
      "get": {
        "tags": [
          "service"
        ],
        "operationId": "health",
        "summary": "Liveness and disk headroom",
        "description": "Returns 503 when free space is below the reserve, because writes are refused in that state.",
        "responses": {
          "200": {
            "description": "Healthy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "Below the disk reserve; writes are refused."
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "service"
        ],
        "operationId": "openapi",
        "summary": "This document",
        "responses": {
          "200": {
            "description": "The OpenAPI document."
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "getAccount",
        "summary": "The authenticated identity",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The account, its scopes, and any repository confinement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "operationId": "listKeys",
        "summary": "List SSH public keys",
        "description": "Requires an unconfined admin token: a repo-scoped token must not manage credentials that outlive its confinement.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The account's keys. The public key body is never returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Key"
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "Whether the result was cut short by a limit."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "keys"
        ],
        "operationId": "addKey",
        "summary": "Register an SSH public key",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddKey"
              },
              "examples": {
                "laptop": {
                  "value": {
                    "title": "laptop",
                    "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJd8kRTIB0hLbrLZ0hBqUFwjBjHwzMbrUsGVLXCBYqEd",
                    "read_only": false,
                    "repos": []
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Key"
                }
              }
            }
          },
          "400": {
            "description": "Not a supported public key. `authorized_keys` option prefixes such as `command=` are refused.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "That public key is already registered."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "keys"
        ],
        "operationId": "deleteKey",
        "summary": "Remove an SSH public key",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such key for this account."
          }
        }
      }
    },
    "/v1/repos": {
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "listRepos",
        "summary": "List repositories",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Repositories this identity can read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Repository"
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "Whether the result was cut short by a limit."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "repositories"
        ],
        "operationId": "createRepo",
        "summary": "Create a repository",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRepo"
              },
              "examples": {
                "minimal": {
                  "value": {
                    "name": "site"
                  }
                },
                "full": {
                  "value": {
                    "name": "site",
                    "default_branch": "refs/heads/main",
                    "description": "the shop"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Repository"
                }
              }
            }
          },
          "400": {
            "description": "Invalid name or default branch.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "A repository with that name exists. Names are case-folded, so `Site` collides with `site`."
          },
          "507": {
            "description": "Below the disk reserve."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "413": {
            "description": "Repository quota exceeded. The problem body carries `limit` and `used`."
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "getRepo",
        "summary": "Read a repository",
        "description": "A repository the caller cannot read answers 404, not 403 \u2014 a 403 would confirm it exists.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The repository.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Repository"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "repositories"
        ],
        "operationId": "deleteRepo",
        "summary": "Delete a repository",
        "description": "Moves the repository aside rather than removing it; it is recoverable until the sweep runs. Deleting an absent repository is 404, not 204 \u2014 an agent that typos a name must not be told it deleted something.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "repositories"
        ],
        "operationId": "patchRepo",
        "summary": "Update repository settings",
        "description": "RFC 7396 merge patch: an absent field is unchanged, `null` clears.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/merge-patch+json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "default_branch": {
                    "type": "string",
                    "description": "Must already exist, or HEAD would point at nothing and clones would land detached."
                  },
                  "protected_refs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Refs that may not be rewritten. Empty means none \u2014 rewriting is ordinary git and is allowed by default."
                  }
                }
              },
              "examples": {
                "protect the deploy branch": {
                  "value": {
                    "protected_refs": [
                      "refs/heads/main"
                    ]
                  }
                },
                "clear the description": {
                  "value": {
                    "description": null
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Repository"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ref name."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`default_branch` does not exist in this repository."
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/refs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "listRefs",
        "summary": "List branches, tags and notes",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Refs with their object ids.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Ref"
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "Whether the result was cut short by a limit."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/{account}/{repo}.git/info/refs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "service",
          "in": "query",
          "required": true,
          "description": "Exactly one occurrence. A repeated `service` parameter is refused, because this server and the CGI resolve duplicates differently and the authorization decision would not match the service that runs.",
          "schema": {
            "type": "string",
            "enum": [
              "git-upload-pack",
              "git-receive-pack"
            ]
          }
        }
      ],
      "get": {
        "tags": [
          "git"
        ],
        "operationId": "gitInfoRefs",
        "summary": "Ref advertisement",
        "description": "The git smart-HTTP handshake. Not REST, and errors are `text/plain`. `service=git-receive-pack` requires write access, because advertising for a push is itself a write signal.\n\nAuthentication is HTTP Basic: the username is ignored, the password is an API token.",
        "security": [
          {
            "basicAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ref advertisement.",
            "content": {
              "application/x-git-upload-pack-advertisement": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Carries `WWW-Authenticate: Basic`, without which git fails instead of prompting.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Plain text, never problem+json."
          },
          "404": {
            "description": "Plain text, never problem+json."
          }
        }
      }
    },
    "/{account}/{repo}.git/git-upload-pack": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        }
      ],
      "post": {
        "tags": [
          "git"
        ],
        "operationId": "gitUploadPack",
        "summary": "Fetch or clone",
        "description": "Streamed end to end; nothing buffers the pack.",
        "security": [
          {
            "basicAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-git-upload-pack-request": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Packfile.",
            "content": {
              "application/x-git-upload-pack-result": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Plain text with a Basic challenge."
          },
          "404": {
            "description": "Plain text."
          }
        }
      }
    },
    "/{account}/{repo}.git/git-receive-pack": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        }
      ],
      "post": {
        "tags": [
          "git"
        ],
        "operationId": "gitReceivePack",
        "summary": "Push",
        "description": "Push, including force-push. Rewriting a branch is allowed by default \u2014 `rebase`, `commit --amend` and `filter-branch` are ordinary git and this service does not block them; git's reflog is the recovery path. Protection is opt-in per ref via `protected_refs`, and is enforced identically over SSH.",
        "security": [
          {
            "basicAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-git-receive-pack-request": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Push report. A rejected ref update is reported inside this body, not as an HTTP status.",
            "content": {
              "application/x-git-receive-pack-result": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Plain text with a Basic challenge."
          },
          "403": {
            "description": "Plain text."
          },
          "413": {
            "description": "Pack exceeds the maximum, or the account is over its storage quota. Over-quota blocks writes only; clones keep working."
          },
          "507": {
            "description": "Below the disk reserve."
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/tree/{path}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "path",
          "in": "path",
          "required": true,
          "allowReserved": true,
          "description": "Directory within the tree. Empty for the root. Components equal to `.git` are refused in every case and Unicode variant.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "ref",
          "in": "query",
          "required": false,
          "description": "Fully-qualified ref (`refs/heads/main`) or a full object id. Defaults to the repository's default branch. A query parameter rather than a path segment because both refs and paths contain slashes, so `/raw/{ref}/{path}` has no unique parse.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "getTree",
        "summary": "List a directory",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Entries, with paths addressable as given.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TreeEntry"
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "Whether the result was cut short by a limit."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid path or ref."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/raw/{path}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "path",
          "in": "path",
          "required": true,
          "allowReserved": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "ref",
          "in": "query",
          "required": false,
          "description": "Fully-qualified ref (`refs/heads/main`) or a full object id. Defaults to the repository's default branch. A query parameter rather than a path segment because both refs and paths contain slashes, so `/raw/{ref}/{path}` has no unique parse.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "getRaw",
        "summary": "Read a file's bytes",
        "description": "Always `application/octet-stream` with `nosniff`, `Content-Disposition: attachment` and a sandbox CSP: this returns attacker-controlled bytes on the same origin as the API. Symlinks are refused rather than followed. Immutable when `ref` is a full object id.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes.",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid path, or the target is a symlink."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "description": "Blob exceeds the inline limit; fetch it over the git transport."
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/commits": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "ref",
          "in": "query",
          "required": false,
          "description": "Fully-qualified ref (`refs/heads/main`) or a full object id. Defaults to the repository's default branch. A query parameter rather than a path segment because both refs and paths contain slashes, so `/raw/{ref}/{path}` has no unique parse.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "path",
          "in": "query",
          "required": false,
          "description": "Limit the log to commits touching this path.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "limit",
          "in": "query",
          "required": false,
          "schema": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "default": 50
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "listCommits",
        "summary": "Commit log",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Commits, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Commit"
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "Whether the result was cut short by a limit."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid ref, path or limit."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/commits/{sha}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "sha",
          "in": "path",
          "required": true,
          "description": "Full object id, 40 or 64 hex characters. Abbreviations and ref names are refused so a value cannot be read as a git argument.",
          "schema": {
            "type": "string",
            "pattern": "^[0-9a-f]{40,64}$"
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "getCommit",
        "summary": "One commit and its changed paths",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The commit plus its name-status summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "commit": {
                      "$ref": "#/components/schemas/Commit"
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "status": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a full object id."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/compare": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "base",
          "in": "query",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "head",
          "in": "query",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "compare",
        "summary": "What changed between two points",
        "description": "Diff from the merge base, matching `git diff base...head` \u2014 'what head added', not 'how the two differ'. Both endpoints are query parameters because refs contain slashes.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Commits, changed paths and line counts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "base": {
                      "type": "string"
                    },
                    "head": {
                      "type": "string"
                    },
                    "merge_base": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "commits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Commit"
                      }
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Change"
                      }
                    },
                    "additions": {
                      "type": "integer"
                    },
                    "deletions": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid revision."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/search": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "q",
          "in": "query",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Literal text, not a regular expression. Passed after `-e` so it cannot be read as a flag."
        },
        {
          "name": "path",
          "in": "query",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "ref",
          "in": "query",
          "schema": {
            "type": "string"
          },
          "description": "Fully-qualified ref or full object id. Defaults to the repository's default branch."
        },
        {
          "name": "limit",
          "in": "query",
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "search",
        "summary": "Search file contents",
        "description": "Binary files are skipped. No matches is a 200 with an empty list, not a 404.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Matches with file, line number and content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "line": {
                            "type": "integer"
                          },
                          "content": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`q` missing or empty."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/blame/{path}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "path",
          "in": "path",
          "required": true,
          "allowReserved": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "ref",
          "in": "query",
          "schema": {
            "type": "string"
          },
          "description": "Fully-qualified ref or full object id. Defaults to the repository's default branch."
        }
      ],
      "get": {
        "tags": [
          "repositories"
        ],
        "operationId": "blame",
        "summary": "Who last changed each line",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Every line with its commit, author and time.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "ref": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "line": {
                            "type": "integer"
                          },
                          "sha": {
                            "type": "string"
                          },
                          "author": {
                            "type": "string"
                          },
                          "authored_at": {
                            "type": "integer"
                          },
                          "content": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid path or ref."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "mcp"
        ],
        "operationId": "mcp",
        "summary": "MCP JSON-RPC endpoint",
        "description": "Model Context Protocol over JSON-RPC 2.0. Methods: `initialize`, `ping`, `tools/list`, `tools/call`. Tools cover administration (repositories, SSH keys) and discovery (refs, tree, file, search, log, compare, blame).\n\nThere is no tool that writes to a repository \u2014 an agent changes code by cloning with git and pushing. A tool failure caused by the caller is returned as a tool result with `isError: true` rather than as a JSON-RPC error, so the model sees it and adapts.\n\nA notification (no `id`) is answered with 202 and no body.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object"
                  }
                }
              },
              "examples": {
                "list tools": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list"
                  }
                },
                "create a repository": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "repo_create",
                      "arguments": {
                        "name": "shop"
                      }
                    }
                  }
                },
                "search": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "search",
                      "arguments": {
                        "repo": "shop",
                        "q": "TODO"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted; no body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/runs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        }
      ],
      "get": {
        "tags": [
          "ci"
        ],
        "operationId": "listRuns",
        "summary": "CI runs, newest first",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/RunStatus"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Runs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Run"
                      }
                    },
                    "truncated": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "ci"
        ],
        "operationId": "triggerRun",
        "summary": "Queue a run by hand",
        "description": "Queues a run for a ref that is already pushed. Requires CI to be enabled.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ref": {
                    "type": "string",
                    "description": "Defaults to the repository's default branch."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "CI is disabled, or the ref does not exist."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/runs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "ci"
        ],
        "operationId": "getRun",
        "summary": "One run",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "ci"
        ],
        "operationId": "cancelRun",
        "summary": "Cancel a run",
        "description": "The only supported transition is to `cancelled`. Not `DELETE`: the run still exists and stays readable afterwards.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "cancelled"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported transition."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The run already finished."
          }
        }
      }
    },
    "/v1/repos/{account}/{repo}/runs/{id}/logs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Account"
        },
        {
          "$ref": "#/components/parameters/Repo"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "ci"
        ],
        "operationId": "getRunLogs",
        "summary": "Captured output of a run",
        "description": "Plain text, capped at the configured size. Cacheable only once the run is finished.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Output so far.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API token. Scopes: `repo:read`, `repo:write`, `admin`, `ci`. Tokens expire; 90 days is the default."
      },
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "Git over HTTP. The username is ignored; the password is an API token."
      }
    },
    "parameters": {
      "Account": {
        "name": "account",
        "in": "path",
        "required": true,
        "description": "Case-folded account name.",
        "schema": {
          "$ref": "#/components/schemas/Name"
        }
      },
      "Repo": {
        "name": "repo",
        "in": "path",
        "required": true,
        "description": "Case-folded repository name.",
        "schema": {
          "$ref": "#/components/schemas/Name"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credential.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential is valid but lacks the scope.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource, or the caller may not read it.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. Carries `Retry-After`. The git transport is not rate limited.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "Name": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64,
        "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$",
        "description": "Case-folded on receipt. Must not contain `..`, end in `.lock`, or be a reserved word."
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "mode",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "standalone",
              "control",
              "tenant"
            ]
          },
          "version": {
            "type": "string"
          },
          "disk_free_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "disk_reserve_bytes": {
            "type": "integer"
          },
          "git_slots_available": {
            "type": "integer",
            "description": "Remaining concurrent git subprocess slots."
          },
          "rate_limit_per_minute": {
            "type": "integer"
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "account",
          "scopes",
          "repos"
        ],
        "properties": {
          "account": {
            "$ref": "#/components/schemas/Name"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "repo:read",
                "repo:write",
                "admin",
                "ci"
              ]
            }
          },
          "repos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Name"
            },
            "description": "Empty means every repository the account owns."
          },
          "usage": {
            "type": "object",
            "properties": {
              "repos": {
                "type": "integer"
              },
              "bytes": {
                "type": "integer",
                "description": "As git accounts for it: loose objects plus packs."
              }
            }
          },
          "limits": {
            "type": "object",
            "description": "Zero means the limit is disabled.",
            "properties": {
              "max_repos": {
                "type": "integer"
              },
              "max_repo_bytes": {
                "type": "integer"
              },
              "max_account_bytes": {
                "type": "integer"
              },
              "rate_per_minute": {
                "type": "integer"
              }
            }
          }
        }
      },
      "CreateRepo": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "$ref": "#/components/schemas/Name"
          },
          "default_branch": {
            "type": "string",
            "default": "refs/heads/main",
            "description": "Fully qualified and under `refs/heads/`."
          },
          "description": {
            "type": "string"
          },
          "protected_refs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "import_url": {
            "type": "string",
            "description": "Seed the repository from a public https/http remote. Refused for URLs embedding credentials or resolving to loopback, private or link-local addresses."
          }
        }
      },
      "Repository": {
        "type": "object",
        "required": [
          "account",
          "name",
          "default_branch",
          "created_at"
        ],
        "properties": {
          "account": {
            "$ref": "#/components/schemas/Name"
          },
          "name": {
            "$ref": "#/components/schemas/Name"
          },
          "display_name": {
            "type": "string",
            "description": "The name as typed. Never a path component."
          },
          "default_branch": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "created_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "visibility": {
            "type": "string",
            "enum": ["private", "public"],
            "description": "Public grants anonymous read and nothing else. Absent on create means private."
          },
          "clone_url_http": {
            "type": "string"
          },
          "clone_url_ssh": {
            "type": "string"
          },
          "web_url": {
            "type": "string",
            "description": "The repository's page in a browser. Present only when public — its presence is the answer to whether the link may be shared."
          },
          "protected_refs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Refs that may not be rewritten. Empty by default."
          }
        }
      },
      "Ref": {
        "type": "object",
        "required": [
          "name",
          "sha",
          "type"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Fully qualified, e.g. `refs/heads/main`."
          },
          "sha": {
            "type": "string",
            "pattern": "^[0-9a-f]{40,64}$"
          },
          "type": {
            "type": "string",
            "enum": [
              "branch",
              "tag",
              "note"
            ]
          },
          "updated_at": {
            "type": "integer"
          },
          "subject": {
            "type": "string"
          }
        }
      },
      "AddKey": {
        "type": "object",
        "required": [
          "title",
          "key"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "key": {
            "type": "string",
            "description": "One `authorized_keys` line. Option prefixes such as `command=` are refused. Supported: ssh-ed25519, ecdsa-sha2-nistp256/384/521, ssh-rsa."
          },
          "read_only": {
            "type": "boolean",
            "default": false,
            "description": "A read-only key may clone and fetch but not push."
          },
          "repos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Name"
            },
            "description": "Confines the key. Empty means every repository the account owns."
          }
        }
      },
      "Key": {
        "type": "object",
        "required": [
          "id",
          "title",
          "fingerprint",
          "read_only",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "fingerprint": {
            "type": "string",
            "description": "`SHA256:...`, matching `ssh-keygen -lf`."
          },
          "read_only": {
            "type": "boolean"
          },
          "repos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Name"
            }
          },
          "created_at": {
            "type": "integer"
          },
          "last_used_at": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457. Discriminate on `type`; `detail` is prose and may change.",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "`https://zuka.dev/problems/{slug}`.",
            "examples": [
              "https://zuka.dev/problems/invalid-name",
              "https://zuka.dev/problems/invalid-ref",
              "https://zuka.dev/problems/repo-exists",
              "https://zuka.dev/problems/non-fast-forward",
              "https://zuka.dev/problems/not-found",
              "https://zuka.dev/problems/storage-full",
              "https://zuka.dev/problems/quota-exceeded",
              "https://zuka.dev/problems/rate-limited"
            ]
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "current_sha": {
            "type": "string",
            "description": "On a ref conflict, the sha the ref actually points at."
          },
          "limit": {
            "type": "integer"
          },
          "used": {
            "type": "integer",
            "description": "On a quota problem, current usage against `limit`."
          },
          "retry_after": {
            "type": "integer",
            "description": "On a rate-limit problem, seconds until the next request is allowed."
          }
        }
      },
      "TreeEntry": {
        "type": "object",
        "required": [
          "path",
          "name",
          "type",
          "mode",
          "sha"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Full path from the repository root."
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "file",
              "dir",
              "symlink",
              "submodule"
            ]
          },
          "mode": {
            "type": "string",
            "examples": [
              "100644",
              "100755",
              "040000"
            ]
          },
          "sha": {
            "type": "string"
          },
          "size": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "Commit": {
        "type": "object",
        "required": [
          "sha",
          "message",
          "author_name",
          "authored_at",
          "parents"
        ],
        "properties": {
          "sha": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "author_name": {
            "type": "string"
          },
          "author_email": {
            "type": "string"
          },
          "authored_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "parents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Change": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "git name-status letter, e.g. M, A, D."
          },
          "path": {
            "type": "string"
          }
        }
      },
      "RunStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "succeeded",
          "failed",
          "cancelled",
          "timed_out"
        ]
      },
      "Run": {
        "type": "object",
        "required": [
          "id",
          "account",
          "repo",
          "git_ref",
          "sha",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "account": {
            "type": "string"
          },
          "repo": {
            "type": "string"
          },
          "git_ref": {
            "type": "string"
          },
          "sha": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/RunStatus"
          },
          "created_at": {
            "type": "integer"
          },
          "started_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "finished_at": {
            "type": [
              "integer",
              "null"
            ]
          },
          "exit_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    }
  }
}
