Repository navigation
chore(deps): move the kpubdata pin to the 0.9 line - #1050
Conversation
kpubdata 0.9.0 is the first release with Client(env_keys=False). On 0.8.0 the multi-user mode could not keep the operator's keys out of a request, so serve refused to start there (#990); on the 0.9 line it starts. Seven imports from kpubdata's private modules now use the public names the release exports, and their allowlist entries are removed (#1002). Closes #1002 Refs #990 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yeongseon
left a comment
There was a problem hiding this comment.
diff 를 읽었고 CLEAN 이라 승인합니다. 이 핀이 들어가야 다중 사용자(OIDC) 모드가 기동합니다.
핀과 public import 전환을 한 PR 로 묶은 것: 따로 올려 달라고 했지만 받아들입니다. 0.9.0 을 설치하면 check_kpubdata_imports.py 의 게이트가 allowlist 의 그 항목들을 스스로 거부한다는 설명이 맞고, 핀만 올린 PR 은 통과할 수 없었습니다.
확인한 것:
- 잠금 파일에서 바뀐 것은 kpubdata 항목뿐입니다(0.8.0 → 0.9.0 의 sdist·wheel 해시와 specifier).
env_keys실동작 테스트가 이제 돌고 skip 이 9 → 8 로 줄었다는 것. CI 의kpubdata 0.9.0잡과kpubdata main ↔ Builder잡이 모두 통과했습니다 — 본문이 걱정한 "kpubdata 0.8.0잡 이름이 박혀 있다" 는 아니었습니다. 잡 이름이 지원 릴리스 목록에서 만들어집니다.- baseline 의 16개가 실제 0.9.0 카탈로그로 모두
unknown이라는 것 — #1036 리뷰가 "핀을 올리는 PR 에서 봐야 한다" 고 남긴 확인입니다.
datago.air_quality 가 forbidden 이 되는 것 — 의도된 결과입니다. kpubdata 0.9.0 의 spec 이 그렇게 선언합니다(type: 공공누리_3유형, modification_allowed: false, redistribution: forbidden, kpubdata#720). #759 의 owner 결정(D6, 2026-09-30)이 air_quality 게시를 멈췄고 scheduled-air-quality.yml 은 비활성 상태입니다(마지막 실행 2026-09-29). 예약 게시 경로는 config 의 license_name: kogl-type-3 로 판정하므로 이 핀과 무관하게 이미 forbidden 입니다.
달라지는 것은 Builder 서비스 쪽입니다: air_quality 로 만든 빌드는 게시뿐 아니라 /query·/preview·다운로드도 막힙니다(#688 의 설계). 영향 범위를 확인했습니다 —
- Builder 저장소의
specs·tests/e2e·docs·README.md에서 air_quality 를 쓰는 것은scripts/configs/air_quality.yaml하나입니다. - Studio 의 실 Builder e2e(
e2e/real-builder.spec.ts)는 replay 소스로datago.air_station을 씁니다. 0.9.0 에서unknown이라 읽기는 되고 공개 게시만 막힙니다. Studio 의 다른 e2e 일곱 파일이 air_quality 를 쓰지만 CI 에서는 mock 으로 돕니다.
남는 것: Studio 의 데모·문서·mock 이 air_quality 를 예제로 쓰는데 실제 Builder 에서는 그 데이터셋으로 아무것도 읽을 수 없게 됩니다. 예제를 다른 데이터셋으로 옮기는 이슈를 Studio 에 올려 주세요. 그리고 #990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다 — Refs 로 둔 것이 맞습니다.
…dential mode (#1051) Closes #990 #1050 리뷰가 남긴 것: "#990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다". 그 테스트다. 코드 변경은 없다. ## 이슈의 세 조건과 근거 1. **키 없는 빌드가 거부된다** — `test_require_own_credential.py::test_build_is_refused_before_any_client_exists` (#1014). 이 PR 의 두 번째 테스트도 실제 client 로 같은 것을 본다. 2. **`env_keys` 가 없는 kpubdata 로는 이 모드로 기동하지 않는다** — `test_serve_refuses_to_start_when_it_cannot_keep_the_promise`, `test_a_kpubdata_without_env_keys_is_refused_not_silently_used` (#1014). 3. **누출 테스트: 운영자 키 문자열이 요청·로그·매니페스트 어디에도 없다** — 이 PR. 지금까지의 테스트는 client 가 **무엇으로 만들어지는지**(`env_keys=False`, 키 조회가 `None`)를 봤고, 실제로 **무엇이 나가는지**는 보지 않았다. 0.8.0 에서는 볼 수 없었다. ## 테스트 (`tests/unit/test_operator_key_never_leaves.py`) 진짜 kpubdata `Client`(`cli._create_client`)와 진짜 `HttpTransport` 로 `BuilderService.build` 를 돌리고, 소켓(`httpx.Client.send`)만 바꿨다. 운영자 키는 kpubdata 가 읽는 이름 셋(`KPUBDATA_DATAGO_API_KEY`, `DATAGO_API_KEY`, `KPUBDATA_API_KEY`) 모두에 canary 값으로 넣었다. - **자기 키가 있는 요청자의 빌드:** 요청이 실제로 나갔고, 나간 요청마다 요청자의 키가 있고 운영자 키는 없다. 응답 본문, DEBUG 로그(본문과 레코드의 `extra`), run 디렉터리의 모든 파일(매니페스트·스냅샷·이벤트·산출물)에 운영자 키가 없다. 요청자의 키도 로그와 디스크에 없다. - **키가 없는 요청자:** 403 `provider_credential_required` 이고 **요청이 하나도 나가지 않는다**(나가면 테스트가 그 자리에서 실패한다). - **측정이 실제로 보는지:** 스위치를 끄면 같은 키 없는 빌드가 운영자 키로 나가고, 위 캡처에 그 키가 잡힌다. 이것이 없으면 "없다"는 단언들이 아무것도 안 나가도 통과한다. 데이터셋은 `datago.apt_trade` 를 썼다 — 0.9.0 에서 약관이 `allowed` 라 키 말고는 빌드를 막는 것이 없다(`air_quality` 는 이제 `forbidden`). ## 검증 - `pytest tests/unit/test_operator_key_never_leaves.py tests/unit/test_require_own_credential.py` → `14 passed`. - `ruff check`, `ruff format` 통과. `# type: ignore` 없음. - kpubdata 가 `env_keys` 를 받지 않으면 파일 전체가 skip 된다(핀이 0.9 라인이라 CI 에서는 돈다). - 전체 스위트는 CI 에 맡긴다. 실제 provider 호출은 아니다 — 응답은 표준 envelope 한 건을 흉내 낸 것이다. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1057) ## 무엇을 #992 의 남은 항목 가운데 "OIDC 설정으로 `serve` 기동 → `/healthz` 200" 스모크를 더합니다. 테스트 파일 하나와 CHANGELOG 뿐이고, 서비스 코드는 바꾸지 않았습니다. ## 어떻게 `tests/unit/test_oidc_start_smoke.py` 가 실제 `serve` 명령을 별도 프로세스로 띄웁니다. 환경은 다중 사용자 프로필입니다. ``` OIDC_ISSUER, OIDC_AUDIENCE, KPUBDATA_BUILDER_ADMIN_SUBJECTS=<issuer>|<sub>, KPUBDATA_BUILDER_REQUIRE_OWN_PROVIDER_CREDENTIAL=true ``` `DEV_MODE`, `API_KEY`, `CREDENTIAL_MASTER_KEY` 는 테스트 실행 환경에 있더라도 빼고 띄웁니다. - 프로세스가 뜨고 `GET /healthz` → 200 `{"status": "ok"}` - 토큰 없이 `/providers`, `/builds`, `/version` → 401 `unauthorized` - 검증할 수 없는 토큰 → 401 또는 503 `auth_unavailable` (통과시키지 않음) - 부정: `OIDC_AUDIENCE` 가 없거나 `ADMIN_SUBJECTS` 가 없으면 "refusing to start" 로 종료 kpubdata 0.9.0 pin(#1050) 덕에 가능해졌습니다 — `REQUIRE_OWN_PROVIDER_CREDENTIAL` 이 켜져 있으면 `serve` 는 `env_keys` 를 모르는 kpubdata 에서 기동을 거부합니다(#990). ## 한계 - IdP 에 닿지 않습니다(`idp.invalid`). **서비스가 뜨고 닫혀 있다**는 것만 보이고, 로그인이 된다는 것은 보이지 않습니다. 수용 기준의 "실 Keycloak 토큰으로 `GET /providers` 200" 은 실제 realm 이 정해진 뒤의 일입니다. - 컨테이너나 compose 를 띄우지 않습니다. 이미지·compose 의 변수 전달은 `test_prod_oidc_plumbing.py` 가 파일로 확인합니다. - 손으로 띄웠을 때 본 것: 토큰이 없을 때의 401 문구가 OIDC 배포에서도 `api key not configured` 입니다. 동작은 맞고 문구만 어색합니다. 이 PR 에서는 건드리지 않았습니다. ## #992 수용 기준 대조 - [x] 배포 이미지에서 `import jwt` — #1020, #1033 - [x] OIDC 변수가 있는 프로필이 CI 에서 기동 스모크를 통과 — **이 PR** (프로세스 기동 기준. 컨테이너 기동은 아님) - [ ] 실 Keycloak 토큰으로 `GET /providers` 200 — 배포 대상·realm 결정 필요 ## 검증 - 새 파일 5건 통과 (약 5초), `ruff`, `mypy` 통과 - 전체 단위 스위트는 로컬에서 돌리지 않았습니다(테스트 추가만). CI 에 맡깁니다. Refs #992 (닫지 않습니다) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1056) ## 무엇을 #802 — `POST /providers/{provider}/probe`. 요청의 `X-Provider-Key` 헤더에 실린 키로, 그 provider 의 데이터셋마다 kpubdata `PROBE_STATUSES`(available / application_required / params_invalid …)와 소속 서비스(`service_id`)를 돌려줍니다. 계약 1.87.0 (additive). kpubdata 0.9.0 이 `Client.probe` 와 `env_keys=False` 를 공개 API 로 내면서(#1050 에서 pin) 막혀 있던 것이 풀렸습니다. ## 동작 - **요청의 키만 씁니다.** 클라이언트를 `Client(provider_keys={provider: key}, cache=False, env_keys=False)` 로 만듭니다. 저장된 credential 이나 서버 환경변수의 키는 배포 모드와 무관하게 쓰지 않습니다. 헤더에 그 provider 의 키가 없으면 400 `provider_key_required`. - **남기지 않습니다.** 요청이 끝나면 클라이언트를 닫고, 결과를 저장하지 않습니다. 응답에는 키에서 유도한 값이 없습니다. - **데이터셋별.** kpubdata 가 spec 을 가진 데이터셋(오늘 datago 22, localdata 3)을 하나씩 호출합니다(15초, 재시도·캐시 없음 — kpubdata 의 probe 전송). body 의 `datasets` 로 좁힐 수 있습니다(최대 50). - **시간 한도.** 45초가 지나면 새 호출을 시작하지 않고, 닿지 못한 데이터셋을 `not_probed` 에 적고 `complete: false` 로 답합니다. 관찰하지 않은 것에 상태를 붙이지 않습니다. ## 범위 대조 (이슈 체크리스트) - [x] `POST /providers/{provider}/probe`, 키는 헤더로만, 요청이 끝나면 버림 - [x] 응답: 데이터셋별 `PROBE_STATUSES` 어휘 그대로 + `service_id` - [ ] 결과를 계정에 저장 — **이 PR 은 서버에 저장하지 않습니다.** 응답에 키에서 유도한 값이 없어 호출자가 계정 기준으로 보관할 수는 있습니다. Builder 가 계정별로 보관해야 한다면(예: `GET /providers` 의 `last_test` 처럼) 후속으로 하겠습니다 — 결정이 필요합니다. - [x] 키가 로그·에러·응답에 남지 않음 — 아래 테스트. 다만 #686 의 canary gate(`test_canary_leak_gate.py`) 본체에 이 경로를 넣지는 않았고, 같은 방식(실제 kpubdata `Client` + `httpx.MockTransport`)의 테스트를 이 파일에 뒀습니다. ## 테스트 (`tests/unit/test_provider_key_probe.py`, 19건) - 헤더 없음 / 다른 provider 의 키만 있음 → 400, probe 를 열지 않음 (운영자 환경변수 키가 있어도) - 실제 `Client`: 업스트림에 간 요청에 헤더의 키가 있고 운영자 키는 없음 - 실제 `Client`: 업스트림이 401/403/500 으로 요청 URL(키 포함)을 되돌려 줘도 응답과 DEBUG 로그에 키 없음 - 실제 `Client`: 다른 provider 로 연 probe 는 환경변수 키로 내려가지 않고 `auth_unknown`, 호출 0건 - probe 가 예외를 던지면 502 `probe unavailable` (예외 문구 미노출) - body 검증 6가지, 모르는 provider 404, 시간 한도, spec 없는 데이터셋 - 200 응답을 계약 스키마로 검증 ## 머지 순서 kpubdata-studio#764 를 먼저 머지해야 했고, **머지됐습니다**(스키마만 추가). 이 PR 은 200 응답 예시를 더하는데, Studio 의 drift 검사는 Builder `main` 의 모든 예시에 Studio 스키마를 요구합니다(스키마 없이 돌리면 1건 실패하는 것을 확인했습니다). 새 named error 예시는 넣지 않았습니다. ## 정해 주셨으면 하는 것 - 경로 이름(`/probe`, 이슈의 제안 그대로)과 응답 모양. - 45초 한도·순차 호출. 병렬로 돌리면 빨라지지만 사용자 키로 provider 에 동시 호출을 내게 됩니다. - `status` 를 계약에서 enum 으로 두지 않았습니다(kpubdata 가 상태를 더할 수 있어서). ## 검증 - `ruff check`, `ruff format`, `mypy src` 통과 - `scripts/check_contract_compat.py --base origin/main` → `compatible: 1.86.0 -> 1.87.0` - 단위 스위트: `pytest tests/unit -x` 종료 코드 0, 실패 없음 (스킵 8건은 기존 것) Closes #802 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Closes #1002
Refs #990, kpubdata-lab/kpubdata#780
kpubdata 0.9.0 이 PyPI 에 올라왔다(
https://pypi.org/pypi/kpubdata/json→0.9.0). kpubdata#812 가 정한 대로 핀을 옮긴다:kpubdata>=0.9.0,<0.10.변경
pyproject.toml,uv.lock:kpubdata>=0.8.0,<0.9→>=0.9.0,<0.10. 잠금 파일에서 바뀐 것은 kpubdata 항목뿐이다(uv lock --no-sources --upgrade-package kpubdata,--check통과).find_spec(agent/monitor.py, agent/pipeline.py, cli.py),discover_specs(cli.py),KPubDataConfig(service/providers.py, verify/runner.py) — 이슈가 적은 6개.SENSITIVE_PARAM_KEYS(logging_redaction.py) — 0.9.0 이 public 으로 내보낸다(kpubdata#782). 이슈에는 "남는 항목"으로 적혀 있었지만 이제 필요 없다.test_the_repository_passes_against_the_installed_kpubdata가 실패했고, 메시지가 위 6개를 하나씩 짚었다.verify/runner.py의 7개다:SpecExecutor,check_payload_error,extract_items,extract_total_count,ExampleSpec,SpecDefinition,HttpTransport.tests/unit/test_verify_cli.py:find_spec을 패치하는 대상을kpubdata.core.spec→kpubdata로. import 경로가 바뀌어 예전 대상은 더 이상 닿지 않는다(테스트 하나가 실패했다).pyproject.toml주석,CONTRIBUTING.md의 핀 설명,cli.py의 안내 문구를 0.9 기준으로.#990 에 대해
#1014 가 "
env_keys없는 kpubdata 로는 이 모드로 기동하지 않는다"를 넣었고, 0.8.0 에서는 그 거부가 항상 걸렸다. 0.9 라인에서는Client가env_keys를 받으므로 다중 사용자 모드가 기동한다. 0.8.0 이라 건너뛰던 테스트(test_require_own_credential.py의env_keys실동작 테스트)가 이제 돌고 통과한다 — skip 수가 9 → 8 로 줄었다.이슈의 세 조건이 이 핀으로 모두 충족되는지는 판단을 맡긴다. 그래서
Closes가 아니라Refs다.client_keeps_environment_keys_out()확인은 남겨 두었다 — 누군가 더 낮은 버전을 설치한 환경을 위한 것이다.0.9.0 카탈로그가 말하는 약관 — 배포에서 달라지는 것
설치된 0.9.0 카탈로그를 읽으면(
Client(...).datasets.list()): 약관 선언 없음 134,unknown18,forbidden1,allowed3.datago.air_quality가forbidden이다. 0.8.0 에서는redistribution선언이 없어unknown이었다. Builder 는forbidden에서 게시(공개·비공개 모두)와/query·/preview·stage sample·warehouse 읽기·export·artifact 다운로드를 모두 막는다(feat: publish gate — enforce the redistribution verdict, legacy path included #688). 이 핀이 들어가면 air_quality 로 만든 빌드는 Builder 밖으로 한 행도 나가지 않는다. fix(publish): reconcile existing air-quality publications with source license restrictions #759("air_quality 는 KOGL 3유형인데 변형해 게시된다")가 다루던 문제의 답이 이것이라면 의도된 결과다 — 다만 데모·문서·e2e 가 air_quality 를 예제로 많이 쓴다는 점은 확인이 필요하다. 단위 테스트는 약관 조회를 conftest 에서 고정하므로 이 변화를 보지 못한다.unknown이다 — kpubdata 가 직접 그렇게 보고하고(kpubdata#814), Builder 의 fix(publish): read allowed without a confirmed attribution as unknown #1036 도 같은 판정을 한다. 16개 전부를 실제 카탈로그로 확인했다:source_verdict(...)→ 16개 모두unknown. fix(publish): read allowed without a confirmed attribution as unknown #1036 리뷰가 남긴 "핀을 올리는 PR 에서 봐야 한다"는 확인이 이것이다.allowed로 남는 것은apt_trade,apt_rent,village_fcst셋이다.검증
pytest tests/unit(kpubdata 0.9.0 설치) →4409 passed, 8 skipped(8분 12초).scripts/check_kpubdata_imports.py→checked 246 files: no unlisted kpubdata private imports.ruff check,ruff format --check,mypy src통과.AuthError, 건수 0, NODATA, 자격 증명 요청의 리다이렉트 미추적)이 Builder 의 실제 provider 호출 경로에 주는 영향은 단위 테스트가 통과한다는 것까지만 확인했다.kpubdata 0.8.0잡은 이름이 워크플로에 박혀 있다 — 핀이 바뀌면 그 잡이 무엇을 설치하는지 확인이 필요하고, 워크플로 파일은 제가 고칠 수 없다.🤖 Generated with Claude Code