repos
repos 카테고리의 API 작업 16개를 다룹니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos | 프로젝트의 저장소 목록(runlot repo list) |
| POST | /v1/orgs/{orgSlug}/projects/{projectName}/repos | 저장소를 생성합니다(runlot repo create) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName} | 단일 저장소 |
| PATCH | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName} | 이름, 공개 범위, 기본 브랜치를 변경합니다(runlot repo rename|visibility) |
| DELETE | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName} | 저장소를 삭제합니다(runlot repo delete) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/ref-updates | 참조 이력(runlot repo history) |
| GET | /v1/me/ssh-keys | 내 SSH 공개 키(runlot key list) |
| POST | /v1/me/ssh-keys | SSH 공개 키를 등록합니다(runlot key add) |
| DELETE | /v1/me/ssh-keys/{keyId} | SSH 공개 키를 제거합니다(runlot key rm) |
| GET | /v1/me/tokens | 내 액세스 토큰(runlot token list) |
| POST | /v1/me/tokens | 액세스 토큰을 발급합니다(runlot token create) |
| DELETE | /v1/me/tokens/{tokenId} | 액세스 토큰을 폐기합니다(runlot token revoke) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/refs | 브랜치와 태그(대시보드 탐색) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/tree | 단일 디렉터리(대시보드 탐색) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/blob | 단일 파일(대시보드 탐색) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/commits | 커밋 로그(대시보드 탐색) |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos
viewer 이상이 필요합니다.
저장소는 프로젝트에 속합니다 (docs/git-hosting.md §19). 인가는 조직 역할에서 오며 — 프로젝트 자체에는 역할이 없습니다 — 이름의 고유성과 목록 조회는 프로젝트 범위로 한정됩니다. 하나의 프로젝트는 여러 저장소(앱, API, 인프라)를 가질 수 있습니다.
operationId listRepos
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 저장소 목록 | object |
| 403 | — | — |
| 404 | — | — |
POST /v1/orgs/{orgSlug}/projects/{projectName}/repos
member 이상 권한이 필요합니다. repo.create로 감사 기록됩니다.
디스크에 아무것도 만들지 않습니다 (docs/git-hosting.md §10). 행 하나만 삽입하고, node-git이 첫 접근 시 저장소 디렉터리를 생성합니다 — 이 방식 덕분에 git 노드가 다운되어 있어도 이 호출은 성공합니다.
operationId createRepo
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 201 | 생성된 저장소 | Repo |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 409 | 이 프로젝트에 같은 이름의 저장소가 있습니다 (repo_exists) | Error |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}
단일 저장소
operationId getRepo
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 저장소 | Repo |
| 403 | — | — |
| 404 | — | — |
PATCH /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}
관리자 권한이 필요합니다. repo.update로 감사 기록됩니다.
이름 변경은 디스크를 건드리지 않습니다 (§7.1 — 경로에 이름이 포함되지 않습니다). 푸시가 진행 중일 때 이름이 바뀌어도 해당 푸시는 중단되지 않습니다.
operationId updateRepo
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 204 | 변경됨 | — |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 409 | 같은 이름의 저장소가 이미 존재합니다 (repo_exists) | Error |
DELETE /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}
관리자 권한이 필요합니다. repo.delete로 감사 기록됩니다.
이것은 소프트 삭제입니다 (§7.4). URL에서는 즉시 사라지고, 디스크 사본은 보존 기간이 지난 뒤 삭제됩니다 — 되돌릴 수 없는 파기는 항상 마지막에 오며, 취소 가능한 창이 반드시 있어야 합니다.
operationId deleteRepo
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 204 | 삭제됨 | — |
| 403 | — | — |
| 404 | — | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/ref-updates
뷰어 이상의 권한이 필요합니다. 가장 최근 200개 항목입니다.
이것은 감사 로그의 사본이 아니라 저장소의 이력입니다 (§6.1). old는 강제 푸시로 유실된 커밋을 복구할 수 있는 유일한 단서입니다.
operationId listRefUpdates
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | Ref 이력 | object |
| 403 | — | — |
| 404 | — | — |
GET /v1/me/ssh-keys
내 SSH 공개 키(runlot key list)
operationId listSSHKeys
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 키 목록 | object |
POST /v1/me/ssh-keys
지문(fingerprint)은 전역적으로 고유합니다 (§5.3). 키 자체가 신원이므로, 중복되면 신원이 모호해집니다 — 등록 순서에 신원이 좌우되는 인증은 인증이 아닙니다. 이미 등록된 키는 409 ssh_key_exists를 반환합니다.
허용: ed25519, ecdsa-sha2-nistp256/384/521, rsa (2048비트 이상). DSA는 거부됩니다.
operationId addSSHKey
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 201 | 등록된 키 | SSHKey |
| 400 | — | — |
| 409 | 공개 키가 이미 등록되어 있습니다 (ssh_key_exists) | Error |
DELETE /v1/me/ssh-keys/{keyId}
인가 캐시를 우회합니다 (§5.4). 키 삭제는 바로 지금 그 키를 차단하고 싶기 때문에 존재하는 기능이므로, TTL만큼 기다리게 만든다면 이 기능은 거짓이 됩니다.
operationId deleteSSHKey
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 204 | 삭제됨 | — |
| 404 | — | — |
GET /v1/me/tokens
평문은 어디에도 존재하지 않습니다. 해시만 저장됩니다 (§5.2).
operationId listAccessTokens
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 토큰 목록 | object |
POST /v1/me/tokens
평문은 이 응답에서 단 한 번만 전달됩니다. 다시 볼 수 없습니다.
git은 HTTPS를 통해 Basic 인증을 보내며, 사용자 이름은 무시하고 비밀번호 필드의 토큰만 확인합니다 (§5.2). 접두사 runlot_pat_는 고정되어 있습니다 — 유출 스캔이 동작하려면 저장소, 로그, 붙여넣기에서 기계적으로 탐지할 수 있어야 합니다.
operationId createAccessToken
요청 본문: application/json · object
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 201 | 발급된 토큰(평문 포함) | AccessTokenCreated |
| 400 | — | — |
DELETE /v1/me/tokens/{tokenId}
행을 삭제하는 대신 revokedAt을 설정합니다 — 삭제하면 "이 토큰이 얼마나 오래 살아 있었는가"에 답할 방법이 없어집니다. 인가 캐시를 우회합니다 (§5.4).
operationId revokeAccessToken
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 204 | 폐기됨 | — |
| 404 | — | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/refs
viewer 이상이 필요합니다.
브라우징은 대시보드 세션으로 인증하고, git 전송은 PAT 또는 SSH로 인증합니다 (docs/git-hosting.md §12.2). 두 인증이 하나의 표면에 있었다면, 이 프로세스가 세션 쿠키도 처리해야 했을 것이고 CSRF 표면이 저장소 쓰기까지 확장되었을 것입니다. 그래서 대시보드는 이 경로를 거치고, cp-public이 node-git의 내부 읽기 표면으로 중계합니다 — 인가는 여기서 끝납니다.
operationId getRepoRefs
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | ref 목록 | RepoRefs |
| 403 | — | — |
| 404 | — | — |
| 503 | git 노드를 찾을 수 없거나 접근할 수 없습니다 (git_unavailable, git_unreachable) | Error |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/tree
뷰어 이상의 권한이 필요합니다. path가 비어 있으면 루트입니다.
1000개 항목에서 잘립니다 — 무제한 브라우징은 저장소 하나가 대시보드를 죽이는 수단이 됩니다. 잘렸다면 truncated가 true입니다.
트리를 벗어나는 경로(a/../..)는 404를 반환합니다. ..를 조용히 무시하면 요청한 것과 다른 경로가 반환됩니다.
operationId getRepoTree
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 디렉터리 | RepoTree |
| 403 | — | — |
| 404 | 저장소, ref, 또는 경로가 존재하지 않습니다 (no_ref·no_path) | Error |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/blob
viewer 이상이 필요합니다.
1 MiB를 초과하는 콘텐츠는 포함되지 않습니다 (tooLarge). 읽은 뒤 자르는 것이 아니라 아예 읽지 않습니다 — 한 번 읽으면 이미 메모리가 소모된 뒤이기 때문입니다. 바이너리 파일도 콘텐츠를 담지 않습니다 (binary).
operationId getRepoBlob
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 파일 | RepoBlob |
| 403 | — | — |
| 404 | 저장소, ref, 또는 경로가 존재하지 않습니다 (no_ref·no_path) | Error |
GET /v1/orgs/{orgSlug}/projects/{projectName}/repos/{repoName}/commits
뷰어 이상의 권한이 필요합니다. 기본값 200, 최대 200.
operationId getRepoCommits
| 매개변수 | 위치 | 필수 | 타입 | 설명 |
|---|---|---|---|---|
limit | query | 아니오 | 정수 | — |
| 상태 코드 | 설명 | 응답 본문 |
|---|---|---|
| 200 | 커밋 목록 | object |
| 403 | — | — |
| 404 | — | — |