runlot
ReferenceAPI

repos

Covers 16 API operations in the repos category.

MethodPathDescription
GET/v1/orgs/{orgSlug}/projects/{projectName}/reposList of repositories for the project (runlot repo list)
POST/v1/orgs/{orgSlug}/projects/{projectName}/reposCreates a repository (runlot repo create)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}A single repository
PATCH/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}Changes the name, visibility, or default branch (runlot repo rename|visibility)
DELETE/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}Deletes a repository (runlot repo delete)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/ref-updatesRef history (runlot repo history)
GET/v1/me/ssh-keysMy SSH public keys (runlot key list)
POST/v1/me/ssh-keysRegisters an SSH public key (runlot key add)
DELETE/v1/me/ssh-keys/{keyId}Removes an SSH public key (runlot key rm)
GET/v1/me/tokensMy access tokens (runlot token list)
POST/v1/me/tokensIssue an access token (runlot token create)
DELETE/v1/me/tokens/{tokenId}Revoke an access token (runlot token revoke)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/refsBranches and tags (dashboard browsing)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/treeA single directory (dashboard browsing)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/blobA single file (dashboard browse)
GET/v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/commitsCommit log (dashboard browse)

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos

Requires viewer or higher.

A repository belongs to a project (docs/git-hosting.md §19). Authorization comes from the org role — a project has no role of its own — but name uniqueness and listing are scoped to the project. One project can have several repositories (app, API, infra).

operationId listRepos

Status codeDescriptionResponse body
200List of repositoriesobject
403
404

POST /v1/orgs/{orgSlug}/projects/{projectName}/repos

Requires member or higher. Audited as repo.create.

Does not create anything on disk (docs/git-hosting.md §10). It only inserts a row, and node-git creates the repository directory on first access — that way this call succeeds even while the git node is down.

operationId createRepo

Request body: application/json · object

Status codeDescriptionResponse body
201The repository that was createdRepo
400
403
404
409This project has a repository with the same name (repo_exists)Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}

A single repository

operationId getRepo

Status codeDescriptionResponse body
200RepositoryRepo
403
404

PATCH /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}

Requires admin. Audited as repo.update.

Renaming does not touch the disk (§7.1 — the path does not contain the name). If the name changes while a push is in progress, that push is not interrupted.

operationId updateRepo

Request body: application/json · object

Status codeDescriptionResponse body
204changed
400
403
404
409A repository with that name already exists (repo_exists)Error

DELETE /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}

Requires admin. Audited as repo.delete.

This is a soft delete (§7.4). It disappears from the URL immediately, and the disk copy is deleted after a retention period — irreversible destruction always comes last, and there must be a cancellation window.

operationId deleteRepo

Status codeDescriptionResponse body
204deleted
403
404

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/ref-updates

Requires viewer or higher. The most recent 200 entries.

This is the repository's history, not a copy of the audit log (§6.1). old is the only clue for recovering a commit lost to a force-push.

operationId listRefUpdates

Status codeDescriptionResponse body
200Ref historyobject
403
404

GET /v1/me/ssh-keys

My SSH public keys (runlot key list)

operationId listSSHKeys

Status codeDescriptionResponse body
200Key listobject

POST /v1/me/ssh-keys

The fingerprint is globally UNIQUE (§5.3). The key is the identity itself, so a duplicate makes the identity ambiguous — authentication where identity depends on registration order is not authentication. A key that is already registered returns 409 ssh_key_exists.

Allowed: ed25519, ecdsa-sha2-nistp256/384/521, rsa (2048 bits or more). DSA is rejected.

operationId addSSHKey

Request body: application/json · object

Status codeDescriptionResponse body
201Registered keySSHKey
400
409Public key already registered (ssh_key_exists)Error

DELETE /v1/me/ssh-keys/{keyId}

Bypasses the authorization cache (§5.4). Deleting a key exists precisely because you want to block it right now, so making it wait for the TTL would make the feature a lie.

operationId deleteSSHKey

Status codeDescriptionResponse body
204deleted
404

GET /v1/me/tokens

The plaintext exists nowhere. Only the hash is stored (§5.2).

operationId listAccessTokens

Status codeDescriptionResponse body
200Token listobject

POST /v1/me/tokens

The plaintext is carried in this response only once. You cannot see it again.

git sends Basic auth over HTTPS, and we ignore the username and only look at the token in the password field (§5.2). The prefix runlot_pat_ is fixed — it needs to be mechanically detectable in repositories, logs, and pastes for leak scanning to work.

operationId createAccessToken

Request body: application/json · object

Status codeDescriptionResponse body
201The issued token (including the plaintext)AccessTokenCreated
400

DELETE /v1/me/tokens/{tokenId}

Sets revokedAt instead of deleting the row — deleting it would leave no way to answer "how long was this token alive". Bypasses the authorization cache (§5.4).

operationId revokeAccessToken

Status codeDescriptionResponse body
204discarded
404

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/refs

Requires viewer or higher.

Browsing authenticates with a dashboard session, while git transport authenticates with a PAT or SSH (docs/git-hosting.md §12.2). If both authentications lived on one surface, that process would have to handle session cookies too, extending the CSRF surface to repository writes. So the dashboard goes through this path, and cp-public relays to node-git's internal read surface — authorization ends here.

operationId getRepoRefs

Status codeDescriptionResponse body
200List of refsRepoRefs
403
404
503The git node is missing or unreachable (git_unavailable, git_unreachable)Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/tree

Requires viewer or higher. path is the root when empty.

Truncates at 1000 entries — unbounded browsing is a way for a single repository to kill the dashboard. If truncated, truncated is true.

A path that escapes the tree (a/../..) returns 404. Silently dropping .. would return a different path than the one requested.

operationId getRepoTree

Status codeDescriptionResponse body
200DirectoryRepoTree
403
404The repository, ref, or path does not exist (no_ref·no_path)Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/blob

Requires viewer or higher.

Content over 1 MiB is not included (tooLarge). It is not read and then truncated; it is never read at all — once it is read, the memory is already spent. Binary files also carry no content (binary).

operationId getRepoBlob

Status codeDescriptionResponse body
200FileRepoBlob
403
404The repository, ref, or path does not exist (no_ref·no_path)Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/commits

Requires viewer or higher. Default 200, maximum 200.

operationId getRepoCommits

ParameterLocationRequiredTypeDescription
limitqueryNointeger
Status codeDescriptionResponse body
200Commit listobject
403
404

On this page