REST API
이 페이지는 change(데이터 이해하기)를 프로그램에서 직접 조회하는
읽기 전용 공개 API /v1의 사용법을 설명합니다.
사전 준비
- 필요한 것은
curl하나뿐입니다. 인증도 API 키도 가입도 필요 없습니다. - 제한: IP당 분당 1200회. 초과하면
429가 돌아오고, 몇 초 뒤에 다시 시도하면 되는지Retry-After헤더가 알려 줍니다. - 모든 시간은 UTC, 응답은 JSON입니다.
- 전체 명세는 API가 스스로 제공합니다. https://ratatosk.io/v1을 열면 엔드포인트·파라미터·예시가 한 화면에 나옵니다.
change의 생김새
여기서 돌아오는 것은 전부 change입니다. change 하나는 릴리스가 한 일 하나이고, 걸러 내고 판단할 수 있는 축 세 개를 함께 답니다.
| 축 | 값 | 무엇에 답하나 |
|---|---|---|
family |
security breaking deprecated |
어떤 종류인가 — 사람이 구독할 때 고르는 축 |
bucket |
action check plan other |
지금 어떻게 행동할 것인가 |
applies_if |
불리언 식 | 나에게 해당하는가 |
라우팅의 기준은 bucket입니다. action은 그 버전을 쓰는 모든 설치에 해당합니다.
check는 applies_if가 내 설정과 맞을 때만 해당하므로, 무언가를 권하기 전에
그 조건부터 확인해야 합니다. plan은 앞으로 예고된 것이고, other는 전수 기록
— 봇 의존성 범프와 일상적인 줄이라 기본적으로 빠집니다.
bucket은 웹 화면·주간 메일과 같은 규칙으로 서버가 계산해 실어 보냅니다.
그래서 세 표면이 같은 수를 답합니다. 다른 필드로 다시 계산하지 마세요.
엔드포인트
| 경로 | 용도 |
|---|---|
GET /v1 |
자기서술 인덱스 — 전체 엔드포인트·파라미터·예시 |
GET /v1/changes |
증분 동기화 피드. project family bucket actionability evaluable since limit 필터. limit은 1~200 (기본 50) |
GET /v1/changes/by-entity |
역인덱스: 식별자 하나(CVE·CRD·플래그 등)를 건드린 모든 change. 파라미터: name(필수)·kind |
GET /v1/matters/{key} |
한 사안이 등장한 릴리스 전부, 오래된 것부터. ?include=all이면 note급 기록까지 |
GET /v1/projects |
추적 프로젝트 전목록. 필드는 아래 "프로젝트 메타데이터" 참조 |
GET /v1/releases/{project}/{version} |
릴리스 하나: 봉투 + 그 릴리스의 change 전부. include=raw로 원문(raw_notes) 포함, 잘린 경우 raw_notes_truncated: true. 원문에는 출처 귀속 고지 raw_notes_notice가 함께 옵니다 — 원문을 재게시한다면 그대로 표기하세요 |
GET /v1/releases/{project} |
그 프로젝트의 최신 분석 릴리스 (버전 생략) |
GET /v1/releases/{project}?limit=N |
최신 릴리스 N개 요약 — 최신순 조회 경로 (1~20, 범위 밖은 조용히 보정) |
GET /v1/upgrade/{project}?from=&to= |
업그레이드로 떠안는 것 전체. 릴리스 갈래를 가로질러 matter_key 단위로 접습니다. to는 from보다 위여야 함(같거나 아래면 빈 구간 → 400), to 생략 시 최신까지 전부. 버전이 서버로 전송됨 — 아래 "버전 정보 노출 범위 선택" 참조 |
예시
이 페이지의 응답 예시는 모두 2026-08-10에 실제로 받은 응답을 줄인
것입니다(생략한 자리는 …). 데이터는 계속 늘어나므로 지금 실행하면 건수와
값이 다를 수 있습니다.
모두에게 해당하는 보안 change:
curl "https://ratatosk.io/v1/changes?family=security&bucket=action&limit=2"
{
"changes": [
{
"change_id": "argo:v3.5.0:c140f331",
"matter_key": "argo/dependency/formidable#value_changed",
"project": "argo",
"version": "v3.5.0",
"version_rank": [3, 5, 0],
"family": "security",
"actionability": "act",
"bucket": "action",
"kind": "value_changed",
"applies_if": { "evaluable": false, "mode": "universal", "clauses": [], "raw": null },
"advisories": [],
"subjects": [ { "kind": "dependency", "name": "formidable", … } ],
"quote": "chore(deps): update dependency formidable to v2.1.3 [security]",
"source_url": "https://github.com/argoproj/argo-cd/releases/tag/v3.5.0",
"seq": 1415
},
… 1건 더 …
],
"next_since": 1421
}
특정 CVE를 다룬 change 찾기 — 한 번의 호출로 전 프로젝트를 가로지릅니다:
curl "https://ratatosk.io/v1/changes/by-entity?name=CVE-2026-41178"
{
"changes": [
{
"change_id": "buildpacks:v0.40.9:83576398",
"matter_key": "buildpacks/advisory:cve-2026-41178",
"project": "buildpacks",
"version": "v0.40.9",
"family": "security",
"bucket": "action",
"advisories": [
{ "id": "CVE-2026-41178", "severity": "medium" },
{ "id": "GO-2026-5158", "severity": "medium" }
],
"subjects": [
{ "kind": "dependency", "name": "go.opentelemetry.io/otel", … },
{ "kind": "cve", "name": "cve-2026-41178", "name_full": "CVE-2026-41178", … }
],
…
}
]
}
이름이 모호하면 kind로 좁힙니다. 값과 뜻은 데이터 이해하기에
있습니다:
curl "https://ratatosk.io/v1/changes/by-entity?name=challenges.acme.cert-manager.io&kind=crd"
최신 릴리스 요약 — 버전마다 층별·가족별 건수가 함께 옵니다:
curl "https://ratatosk.io/v1/releases/istio?limit=3"
{
"project": "istio",
"count": 3,
"releases": [
{
"version": "1.29.6",
"version_rank": [1, 29, 6],
"released_at": "2026-07-16T16:51:06.000Z",
"reviewed_at": "2026-08-09T04:33:14.694Z",
"changes_total": 0,
"by_bucket": {},
"by_family": {},
"max_severity": null,
"notes_total": 5,
"api_url": "https://ratatosk.io/v1/releases/istio/1.29.6"
},
… 2건 더 …
]
}
changes_total: 0인데 notes_total: 5인 것은 감사 가능한 침묵입니다.
릴리스를 읽었고, 다섯 줄을 기록했고, 그중 당신이 할 일은 없다는 뜻입니다.
빈칸이 아니라 답입니다.
업그레이드로 떠안는 것 전부:
curl "https://ratatosk.io/v1/upgrade/istio?from=1.27.0&to=1.27.7"
{
"project": "istio",
"from": "1.27.0",
"to": "1.27.7",
"releases_covered": ["1.27.6", "1.27.7"],
"changes_total_before_dedupe": 5,
"changes": [
{
"change_id": "istio:1.27.7:8a66e585",
"matter_key": "istio/advisory:cve-2025-61732",
"family": "security",
"bucket": "action",
"kind": "defect_corrected",
"advisories": [ { "id": "CVE-2025-61732", "severity": "high" } ],
"quote": "- CVE-2025-61732 (CVSS score 8.6, High): A discrepancy between how Go and C/C++ …",
…
},
… action → check → plan 순으로 더 …
],
"privacy": "The versions sent in from/to reach this server (they are not written to access logs — …",
…
}
한 사안을 릴리스 너머로 추적하기
matter_key는 그 밑에 깔린 사안의 정체성이고, 릴리스와 갈래를 가로질러
유지됩니다. /v1/matters/{key}는 최신 하나가 아니라 등장한 모든 릴리스를
오래된 것부터 돌려줍니다:
curl "https://ratatosk.io/v1/matters/containerd%2Fadvisory%3Acve-2026-47262"
키는 change에서 그대로 복사해 URL 인코딩하세요 — 대소문자를 구분하고 /와
:를 포함합니다.
왜 전부를 싣는가: 같은 containerd 보안 롤업이 다섯 갈래에 각각 권고 2건·4건· 10건을 달고 착지했습니다. 최신 하나만 보여 주면, 2건짜리 갈래를 쓰는 사람은 자기가 다 덮인 줄 알게 됩니다.
이 change가 나에게 해당하는지 판단하기
applies_if는 문장이 아니라 불리언 식입니다:
"applies_if": {
"evaluable": true,
"mode": "any_of",
"clauses": [
{ "kind": "api", "name": "ScheduleJobAlpha1", "verb": "uses", "polarity": "present" }
],
"raw": null
}
evaluable이 true면 문장을 해석하는 대신 절(clause)을 내 매니페스트와
대조하면 됩니다. mode는 all_of·any_of·universal(전원 해당)이고, 각 절이
찾아볼 대상을 지목합니다. evaluable이 false면 raw에 원문 문장이 담깁니다 —
없는 구조를 지어내는 대신 없다고 밝힙니다.
기계로 판정 가능한 것만 보려면 ?evaluable=true로 거르세요.
심각도 판단
심각도는 change가 아니라 인용된 권고에 붙습니다. advisories의 각 항목은
분석 시점에 굳은 값이 아니라 공식 출처에서 다시 읽은 현재 값을 답니다.
릴리스 요약은 그중 최고치를 max_severity로 내보냅니다.
advisories: []는 "등급 없음"이 아니라 릴리스 노트가 권고 식별자를 대지 않았다는
뜻입니다.
증분 조회
/v1/changes는 seq 오름차순 — 분석이 오래된 것부터입니다. 그래서 첫 페이지는
최신 데이터가 아닙니다. 응답의 next_since를 다음 요청의 since로 넘기면 새로
생긴 것만 받습니다:
# 1회차: 처음부터
curl "https://ratatosk.io/v1/changes?limit=100"
# → { "changes": [ ... ], "next_since": 295 }
# 2회차: 받은 next_since를 since로
curl "https://ratatosk.io/v1/changes?limit=100&since=295"
# → { "changes": [ ... ], "next_since": 398 }
next_since는 그 페이지 마지막 change의 seq입니다 — 개수도 offset도
아닙니다. 시퀀스 번호는 연속하지 않으므로 직접 계산하지 말고 받은 값을 그대로
돌려주세요. next_since가 null로 오면 더 받을 것이 없다는 뜻, 즉 로컬 사본이
최신이라는 뜻입니다. since=null은 보내지 마세요 — 400입니다.
bucket=other 행은 요청하지 않으면 빠집니다. 기록의 대부분이고 대개 의존성
범프입니다.
이 커서는 로컬 사본을 맞춰 두기 위한 것입니다. "X의 최신 릴리스"를 알고 싶다면
/v1/releases/{project}를 쓰세요.
프로젝트 메타데이터
GET /v1/projects의 각 항목은 slug·name·tier·category와
analyzed_releases(분석된 릴리스 수)를 담고, 여기에 다음 필드가 더해집니다.
image_aliases: 이 프로젝트의 컨테이너 이미지가 클러스터에서 쓰는 다른 이름들. 돌고 있는 이미지 이름을 슬러그로 되짚을 때 씁니다.cluster_core: true: 클러스터 자체가 딛고 선 구성요소(컨트롤 플레인· 데이터스토어·DNS·런타임·CNI/데이터플레인) 표시. 내 스택에 없더라도 확인할 가치가 있습니다.visibility: 일부 항목에만 있는 관측 힌트. 그 구성요소가 평범한 조회로는 안 보일 수 있다고 알려 줍니다 — 예컨대 etcd는 쿠버네티스 API 밖에서 돌 수 있습니다.
응답에서는 이렇게 보입니다:
{
"projects": [
…
{ "slug": "envoy", "name": "Envoy", "tier": "graduated",
"category": "networking", "analyzed_releases": 22,
"image_aliases": ["cilium-envoy"], "cluster_core": true },
…
],
"count": 76
}
버전 정보 노출 범위 선택
"업그레이드하면 무엇을 떠안는가"에 답하는 길은 하나가 아니고, 내 설정이 서버에 얼마나 닿는지가 다릅니다. 전체 비교는 MCP 서버에 있고, 이 API 안에는 두 가지 선택지가 있습니다.
- 로컬 구간 비교: change를 받아
version_rank로 직접 비교합니다. 버전이 내 기기를 떠나지 않습니다.version_rank는 표기법에 무관한 정렬용 숫자 배열로,v0.42.0은[0, 42, 0]이 됩니다. 규칙은 이렇습니다 — 그 순위가 현재 버전보다 크고 목표 버전 이하이면 그 change는 구간에 듭니다. /v1/upgrade: 서버가 구간을 계산해 줍니다.from/to버전이 서버에 닿지만 처리 중 메모리만 거치고 접근 로그에는 쓰이지 않습니다 — 쿼리는 로깅 전에 제거되고, 경로는 접두사로 정규화되며, IP는 마스킹됩니다 (개인정보 처리방침). 응답의privacy필드가 이 처리를 명시합니다.
손으로 해 보면 이렇습니다:
{ "change_id": "istio:1.27.7:8a66e585", "project": "istio",
"version": "1.27.7", "version_rank": [1, 27, 7], … }
내 버전이 [1, 27, 0]이고 목표가 [1, 27, 7]이면
[1, 27, 0] < [1, 27, 7] ≤ [1, 27, 7] — 이 change는 구간 안입니다.
프로젝트의 태그를 숫자로 정렬할 수 없으면 version_rank는 null입니다
(모노레포 태그 flagd/v0.16.1, 채널 태그 lts-4081.3.8 등). 짐작하는 대신
구간 비교에서 제외합니다.
그 밖의 기계 판독 엔드포인트
/md(마크다운 협상): 홈·/releases·릴리스 상세를Accept: text/markdown헤더로 요청하면 HTML 대신 마크다운이 옵니다. 에이전트가 파싱하기 쉽습니다./.well-known/api-catalog: 이 사이트가 제공하는 API의 기계 판독 카탈로그 (RFC 9727)./.well-known/mcp/server-card.json: MCP 서버 카드 — 에이전트가 MCP 서버를 기계적으로 발견할 때 쓰는 표준 문서. MCP 서버 참조.
문제 해결
- 404 (버전 태그): 프로젝트마다
v접두사가 다릅니다(envoy는v1.39.0, istio는1.28.9)./v1/releases/{project}/{version}은 두 표기를 모두 받지만, 그래도 태그가 틀리면 404 응답 본문에 그 프로젝트의 최근 분석 태그가 담겨 옵니다. 그중 하나를 그대로 다시 쓰세요. - 404 (사안 키):
matter_key는/와:를 포함하고 대소문자를 구분합니다. URL 인코딩하고, change에서 그대로 복사하세요. - 429: 분당 1200회 제한을 넘겼습니다.
Retry-After헤더의 초만큼 기다렸다가 다시 시도하세요. 요청 수를 줄이려면next_since커서로 증분만 받으면 됩니다 — 매번 전체를 다시 받는 것보다 훨씬 적습니다. - 모르는 프로젝트 슬러그: 먼저
GET /v1/projects를 확인하세요. 돌고 있는 이미지 이름과 슬러그가 다르면image_aliases에서 그 이미지 이름을 찾으세요. - 결과가 비어 이상해 보일 때:
bucket=other는 기본적으로 빠집니다. 전수 기록이 필요하면actionability=note또는bucket=other를 붙이세요.
이용 조건
분석은 AI가 생성하며 무보증으로 제공됩니다. 모든 분석은 근거가 된 출처로 연결됩니다. 이용약관을 참고하세요.