From 21f7e7de77246e1e3840a595b00d24fee5ebdd64 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 10 Aug 2026 12:12:10 +0900 Subject: [PATCH] docs(proofline): approve GHCR image publication spec --- .proofline/criteria/ac-0004.md | 16 +++++++ .proofline/criteria/ac-0005.md | 16 +++++++ .proofline/criteria/ac-0006.md | 16 +++++++ .proofline/criteria/ac-0007.md | 16 +++++++ .proofline/criteria/ac-0008.md | 16 +++++++ .proofline/identities.json | 4 +- .proofline/lines/line-0002/dcy-0002.md | 57 +++++++++++++++++++++++++ .proofline/lines/line-0002/line-0002.md | 3 ++ .proofline/lines/line-0002/req-0002.md | 50 ++++++++++++++++++++++ 9 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 .proofline/criteria/ac-0004.md create mode 100644 .proofline/criteria/ac-0005.md create mode 100644 .proofline/criteria/ac-0006.md create mode 100644 .proofline/criteria/ac-0007.md create mode 100644 .proofline/criteria/ac-0008.md create mode 100644 .proofline/lines/line-0002/dcy-0002.md create mode 100644 .proofline/lines/line-0002/line-0002.md create mode 100644 .proofline/lines/line-0002/req-0002.md diff --git a/.proofline/criteria/ac-0004.md b/.proofline/criteria/ac-0004.md new file mode 100644 index 0000000..1ed714f --- /dev/null +++ b/.proofline/criteria/ac-0004.md @@ -0,0 +1,16 @@ +--- +id: "ac-0004" +status: active +--- + +# 검증 성공 이미지에 한정된 GHCR 게시 + +## Criterion + +`main` 또는 `v*` tag push의 정확한 source commit에 대해 repository가 정의한 검증 gate, image build, built-container smoke와 tested-image artifact 생성·전달·checksum·load 검증이 모두 성공한 경우에만 GitHub Actions가 registry login/push 단계에 진입해야 한다. 이 push 전 gate 중 하나라도 실패하면 registry write 없이 fail-closed 해야 한다. push 이후에만 반환되는 digest가 누락되거나 형식이 잘못되면 job은 nonzero로 종료하고 성공 output·summary를 남기지 않아야 하며, 이미 발생한 registry write를 발생하지 않은 것으로 보고해서는 안 된다. + +## Verification + +- 성공 조건: synthetic workflow fixture에서 모든 push 전 gate가 성공한 경우에만 publish job의 login/push가 실행 가능하고, push가 반환한 유효한 digest가 확인된 경우에만 성공 output과 summary가 생성된다. +- 실패·경계 조건: repository 검증, build, smoke, artifact 생성·upload·download, checksum 비교 또는 load 실패는 login/push 전 nonzero로 종료된다. push 후 digest 누락·형식 오류는 job nonzero와 성공 output·summary 부재로 이어진다. +- 검사 방법: workflow parser 기반 구조 테스트와 publication helper의 recording-runner 단위 테스트로 push 전/후 실패 시점을 분리해 검증하고, `publish.needs` 및 `packages: write`의 publish-job 한정을 정적 검사한다. 실제 최초 GHCR 실행은 별도 승인된 `main` 반영 이후 integration/release evidence로 확인한다. diff --git a/.proofline/criteria/ac-0005.md b/.proofline/criteria/ac-0005.md new file mode 100644 index 0000000..8fedef3 --- /dev/null +++ b/.proofline/criteria/ac-0005.md @@ -0,0 +1,16 @@ +--- +id: "ac-0005" +status: active +--- + +# 검증 이미지와 게시 이미지의 동일성 + +## Criterion + +verify job은 한 번 빌드하여 built-container smoke를 통과한 `trex-core` 이미지를 archive로 export하고 archive bytes의 SHA-256을 기록한 뒤 image archive와 checksum을 immutable workflow artifact로 전달해야 한다. publish job은 download한 archive의 SHA-256을 load 전에 비교하고 일치하는 archive만 load·tag·push해야 한다. publish job에서 source를 다시 checkout하거나 `docker build` 또는 동등한 재빌드를 수행해서는 안 된다. + +## Verification + +- 성공 조건: smoke가 성공한 local image를 export한 archive의 SHA-256과 publish job이 download한 archive의 SHA-256이 일치하고, checksum 확인 뒤 load된 image가 GHCR tag의 유일한 원본이 된다. +- 실패·경계 조건: archive 또는 checksum 누락, archive byte 변조, checksum 불일치, load 실패, publish job의 checkout·build 또는 별도 source-based image 생성은 registry login/push 전 nonzero로 종료된다. +- 검사 방법: workflow 구조 테스트로 smoke→save→checksum→upload 및 download→checksum compare→load→tag→push 순서를 검사하고, recording-runner 단위 테스트에서 원본·변조 archive fixture를 사용해 mismatch 시 load/login/push가 호출되지 않음을 검증한다. diff --git a/.proofline/criteria/ac-0006.md b/.proofline/criteria/ac-0006.md new file mode 100644 index 0000000..ee611b7 --- /dev/null +++ b/.proofline/criteria/ac-0006.md @@ -0,0 +1,16 @@ +--- +id: "ac-0006" +status: active +--- + +# stable SHA tag와 immutable digest 추적성 + +## Criterion + +모든 성공 게시 이미지는 전체 40자리 lowercase hexadecimal source commit에서 결정적으로 파생된 앞 12자리 lowercase SHA tag와 전체 commit 값을 담은 `org.opencontainers.image.revision` label을 가져야 한다. SHA tag는 source를 찾기 위한 stable selector이며 배포·rollback의 불변 정본은 registry가 반환한 `sha256` digest다. `v*` tag event에서는 같은 loaded image에 해당 Git tag를 추가하지만 이동 가능한 `latest` tag는 게시하지 않아야 하며, workflow output 또는 summary는 GHCR image reference, 64-hex digest와 전체 source commit을 함께 기록해야 한다. + +## Verification + +- 성공 조건: `main` event는 source commit 앞 12자리 SHA tag를, `v*` event는 같은 loaded image의 SHA tag와 release tag를 생성한다. OCI revision과 summary/output의 image reference·`sha256` digest·전체 source commit이 해당 실행 입력 및 push 결과와 일치한다. +- 실패·경계 조건: source commit 형식 오류, SHA tag 파생 불일치, 빈/형식 오류 digest, source commit 불일치, release tag용 별도 build, `latest` tag 생성 또는 OCI revision 누락은 성공 output·summary를 만들 수 없다. digest 오류가 push 이후 발견되면 job은 nonzero로 종료한다. +- 검사 방법: synthetic `main`/`v*` event와 유효·무효 commit/digest를 publication helper에 입력하는 단위 테스트, Docker image label inspection, workflow의 tag/summary 정적 검사 및 `latest` 생성 금지 회귀 검사를 실행한다. diff --git a/.proofline/criteria/ac-0007.md b/.proofline/criteria/ac-0007.md new file mode 100644 index 0000000..680259c --- /dev/null +++ b/.proofline/criteria/ac-0007.md @@ -0,0 +1,16 @@ +--- +id: "ac-0007" +status: active +--- + +# built-container package 및 entrypoint smoke + +## Criterion + +GHCR 게시 후보는 source tree가 아니라 실제로 빌드된 image 안에서 `python -c "import trex_core"`와 `trex-core-daemon --help`, `trex-core --help`, `trex-core-reset --help`를 각각 실행하여 package와 세 console entrypoint가 설치되어 있음을 입증해야 한다. 각 probe는 exit code `0`이어야 하며 daemon setup, migration, DB·RabbitMQ·Redis·portfolio-hub·KIS 연결, 실제 주문 또는 credential 해석을 수행해서는 안 된다. + +## Verification + +- 성공 조건: network가 차단되고 운영 config·credential이 없는 최소권한 container에서 네 probe가 각각 정해진 timeout 안에 exit code `0`으로 끝나며 container가 정리된다. +- 실패·경계 조건: package import 실패, entrypoint 누락, nonzero exit, timeout, 외부 연결 시도 또는 cleanup 실패는 smoke nonzero로 이어지고 image publish를 차단한다. `trex-core-reset` probe에는 `--yes` 또는 config argument를 전달하지 않는다. +- 검사 방법: built image를 대상으로 하는 smoke harness와 recording-runner 단위 테스트로 정확한 네 command, per-command timeout, network 차단, no-config/no-secret 환경, failure propagation 및 cleanup을 검증하고 CI에서 실제 local image smoke를 실행한다. diff --git a/.proofline/criteria/ac-0008.md b/.proofline/criteria/ac-0008.md new file mode 100644 index 0000000..fc28f50 --- /dev/null +++ b/.proofline/criteria/ac-0008.md @@ -0,0 +1,16 @@ +--- +id: "ac-0008" +status: active +--- + +# 운영 설정과 자격증명의 이미지 비포함 + +## Criterion + +`trex-core` Dockerfile은 package 설치에 필요한 `pyproject.toml`과 `trex_core/`만 application source로 copy하고 repository 전체를 copy하지 않아야 한다. `.dockerignore`는 최소한 `.git`, `.env`, `.env.*`, `config/`와 `.proofline/`을 build context에서 제외해야 한다. image filesystem·history·inspect metadata와 export archive에는 운영 per-instance TOML, `.env`, credential, token, private key, 실제 connection string 또는 synthetic canary marker가 없어야 한다. daemon의 필수 config와 secret은 runtime에 read-only mount, secret 또는 environment로만 주입하고, 문서는 digest pin과 이전 digest rollback을 안내하며 `trex-core-reset`을 자동 rollback 절차에 포함하지 않아야 한다. + +## Verification + +- 성공 조건: 임시 build context의 제외 대상 `.env.ci-canary`에 고정 marker `TREX_SECRET_CANARY_DO_NOT_SHIP`을 둔 상태로 이미지를 빌드해도 marker가 image filesystem, `docker history --no-trunc`, `docker image inspect` 결과와 exported image archive 어디에서도 발견되지 않는다. Dockerfile에는 `COPY .`가 없고 runtime 문서는 외부 주입과 digest rollback 경계를 설명한다. +- 실패·경계 조건: 필수 ignore path 누락, repository 전체 copy, synthetic canary 또는 금지 파일·값이 검사 surface에 존재하면 FAIL이다. helper의 locally captured stdout/stderr에 canary가 노출되거나 rollback 안내가 DB reset·자동 data mutation을 실행하도록 제시해도 FAIL이다. +- 검사 방법: Dockerfile·`.dockerignore` contract test, synthetic canary가 있는 임시 context의 실제 image build, filesystem/history/inspect/export archive scan, helper의 local output 검사와 runtime documentation contract test를 실행한다. 실제 credential이나 실제 운영 connection string은 fixture·명령·evidence에 사용하지 않는다. diff --git a/.proofline/identities.json b/.proofline/identities.json index d8e2967..55489e2 100644 --- a/.proofline/identities.json +++ b/.proofline/identities.json @@ -1,5 +1,5 @@ { "schema_version": 1, - "next_line_number": 2, - "next_ac_number": 4 + "next_line_number": 3, + "next_ac_number": 9 } diff --git a/.proofline/lines/line-0002/dcy-0002.md b/.proofline/lines/line-0002/dcy-0002.md new file mode 100644 index 0000000..93ad2af --- /dev/null +++ b/.proofline/lines/line-0002/dcy-0002.md @@ -0,0 +1,57 @@ +--- +id: "dcy-0002" +status: confirmed +--- + +# trex-core 검증 컨테이너 이미지의 GHCR 게시 + +## Problem + +`trex-core`는 Python 패키지와 daemon entrypoint를 제공하지만 저장소 자체에는 운영자가 가져다 실행할 수 있는 컨테이너 이미지 정의와 GitHub 게시 자동화가 없다. 따라서 특정 source commit으로부터 어떤 이미지가 만들어졌는지, 그 이미지가 repository 검증과 컨테이너 검사를 통과했는지, 운영자가 어떤 불변 식별자로 같은 이미지를 가져와야 하는지에 대한 공통 배포 계약이 없다. + +`kis-data-hub`는 GitHub Actions에서 source 검증, 이미지 빌드, built-container 검사, 검증 이미지 artifact 전달, GHCR 게시를 분리하고 source commit·tag·digest를 기록한다. 요청자는 `trex-core`도 같은 방식을 채택하고 그 배포 방식을 일회성 구현 메모가 아니라 장기 active Acceptance Criteria로 보존하기로 결정했다. + +기대 상태는 `trex-core`의 정확한 source commit에서 만든 컨테이너가 repository 및 built-image 검증을 모두 통과한 경우에만 `ghcr.io/fourblocks2020/trex-core`에 게시되고, 운영자가 source commit과 digest를 역추적하며 불변 이미지로 배포·롤백할 수 있는 것이다. + +## Evidence + +- 현재 기준 `trex-core` source commit은 `c0f36946a3a06ec0d2bde2e9f40686d602754f50`이고 `main`은 `origin/main`과 동기화되어 있다. `git status --short --branch`는 변경 전 `## main...origin/main`을 반환했다. +- `pyproject.toml:1-12,20-23`은 배포명 `trex-core`, Python `>=3.12`, runtime dependencies와 `trex-core-daemon`·`trex-core`·`trex-core-reset` entrypoint를 선언한다. +- `trex_core/runtime/daemon.py:562-585`에서 daemon은 `--config`를 필수로 받고 `--config-path` 또는 explicit path로 per-instance TOML을 읽는다. 따라서 운영 config와 credential을 이미지에 고정하지 않고 런타임에 주입하는 계약이 필요하다. +- 현재 저장소 검색과 파일 존재 검사에서 repository-root `Dockerfile`과 `.github/workflows/publish-image.yml`은 모두 존재하지 않았다. `.github/` workflow도 없다. +- `trex-sim/compose/dockerfiles/Dockerfile.trex-core:1-29`에는 simulation 전용 이미지 정의가 있으나 workspace-root build context, sim config, `/start.sh`에 결합되어 있고 `trex-sim` 격리 스택만을 위한 것이므로 `trex-core`의 독립 게시 이미지 계약을 대신하지 않는다. +- 비교 기준인 `kis-data-hub/.github/workflows/publish-image.yml:3-8,14-78`은 `main`과 `v*` push에서 test→build→built-container liveness→tested-image artifact 업로드를 수행한다. 같은 파일 `80-141`은 publish job이 artifact를 로드하고 GHCR에 commit SHA tag를 게시하며 tag event에서는 release tag를 추가하고 digest와 source commit을 기록한다. +- `kis-data-hub/Dockerfile:1-16`은 서비스의 Python version에 맞는 slim base에서 package를 설치하고 runtime command를 선언한다. 현재 `kis-data-hub` checkout은 `c38b975b37a44d93f3728389d47efcfe696bd895`이며 관련 게시 구현의 마지막 수정은 `77a3bcc1cc9d4304e82abdd9ca277786bd30c4ad`다. +- `HOME=/home/genichin gh run list --repo fourblocks2020/kis-data-hub --workflow publish-image.yml --limit 5`에서 최근 `main` push의 workflow 성공 실행 `30742432705`, `30742417884`, `30742101305`가 확인되어 비교 대상 방식이 GitHub에서 실제 실행됨을 확인했다. + +## Scope + +- AC admission 분류는 새로운 장기 active 동작을 정의하는 `create`다. 기존 `ac-0001`~`ac-0003`은 KIS 잔고 snapshot 완전성과 reconciliation 회귀를 소유하므로 수정하거나 이 배포 계약을 결합하지 않는다. +- repository-root에서 독립적으로 빌드되는 Python 3.12 기반 `trex-core` 컨테이너 이미지 계약을 정의한다. 이미지에는 package와 세 console entrypoint를 포함하되 운영 TOML, `.env`, credential, token, 실제 connection string은 포함하지 않는다. +- GitHub Actions는 정확한 source commit을 checkout하고 repository 검증을 통과한 뒤 이미지를 한 번 빌드한다. built-container 검사까지 통과한 동일 이미지를 artifact로 publish job에 전달하며 publish job은 이미지를 다시 빌드하지 않는다. +- 게시 대상은 `ghcr.io/fourblocks2020/trex-core`다. `main` push에는 source commit에서 파생된 불변 SHA tag를 게시하고 `v*` tag push에는 같은 이미지에 해당 release tag를 추가한다. 이동 가능한 `latest` tag는 게시하지 않는다. +- 게시 이미지에는 최소한 `org.opencontainers.image.revision`으로 전체 source commit을 기록하고, GitHub Actions summary 또는 output에 게시 tag, digest, source commit을 남긴다. +- repository 검증, image build, built-container 검사, artifact 전달 또는 digest 확인 중 하나라도 실패하면 publish 단계는 실패하거나 실행되지 않아야 하며 검증되지 않은 이미지를 정상 게시 결과로 보고하지 않는다. +- built-container 검사는 `trex-core`의 daemon 특성에 맞춰 package import와 세 console entrypoint의 실행 가능성을 확인하고, 운영 서비스·운영계좌·실제 credential을 사용하지 않는 deterministic synthetic 경계에서 수행한다. HTTP `/health`가 있는 `kis-data-hub`의 probe를 그대로 복제하지 않는다. +- runtime 사용 문서는 per-instance config와 secret을 외부에서 주입하는 방법, SHA tag 또는 digest pin, 이전 digest로의 rollback 원칙을 기록한다. 파괴적인 `trex-core-reset`은 자동 rollback 절차에 포함하지 않는다. +- 구현 검증은 repository-local test, workflow 정적/단위 검증, local Docker build와 built-image smoke까지 포함한다. GitHub `main` 반영과 최초 GHCR 게시 같은 외부 쓰기는 별도 승인 이후의 integration/release evidence로 구분한다. + +## Out of Scope + +- 운영 서버 또는 production Compose에서 이미지 pull·교체·재시작 수행 +- 실제 KIS 계좌, 운영 credential, 운영 PostgreSQL·RabbitMQ·Redis·portfolio-hub를 이용한 smoke test +- DB schema 변경, migration 실행, `trex-core-reset`, 운영 데이터 rollback +- `kis-data-hub`, `portfolio-hub`, 전략 daemon 또는 `trex-sim` 저장소 변경 +- multi-architecture image, public package visibility 변경, image signing·SBOM·provenance attestation 도입 +- 새로운 애플리케이션 health HTTP endpoint 추가 +- 특정 release version 결정, release tag 생성, 운영 배포 완료 또는 production 검증 주장 + +## Risks and Unknowns + +### Risks + +- publish job이 이미지를 다시 빌드하면 검증한 bytes와 게시한 bytes가 달라질 수 있다. 완화책은 verify job의 tested image를 artifact로 넘겨 publish job이 그대로 load·tag·push하도록 강제하는 것이다. +- 모든 `main` push가 게시 trigger이면 문서-only commit도 새 SHA image를 만든다. 이는 현재 `kis-data-hub`와 동일한 단순·일관된 정책으로 수용하고, 향후 비용이나 보존 문제가 관찰되면 path filter 또는 retention을 별도 변경으로 다룬다. +- daemon startup은 외부 서비스에 의존하므로 운영 의존성을 CI에 연결하면 credential 노출이나 비결정적 실패가 생길 수 있다. 완화책은 synthetic config와 local disposable dependencies 또는 외부 연결 없는 entrypoint smoke를 사용하고 실제 운영 연결을 금지하는 것이다. +- mutable `latest`나 version tag만 사용하면 source와 rollback 지점이 불명확해질 수 있다. 완화책은 모든 게시에서 commit SHA tag와 digest를 남기고 운영 배포에는 digest pin을 우선하는 것이다. +- 이미지에 운영 config나 secret이 복사되면 registry 접근자가 이를 회수할 수 있다. 완화책은 좁은 Docker build context, `.dockerignore`, 금지 파일 검사와 runtime-only 주입 계약이다. diff --git a/.proofline/lines/line-0002/line-0002.md b/.proofline/lines/line-0002/line-0002.md new file mode 100644 index 0000000..1408721 --- /dev/null +++ b/.proofline/lines/line-0002/line-0002.md @@ -0,0 +1,3 @@ +--- +id: "line-0002" +--- diff --git a/.proofline/lines/line-0002/req-0002.md b/.proofline/lines/line-0002/req-0002.md new file mode 100644 index 0000000..1559a3b --- /dev/null +++ b/.proofline/lines/line-0002/req-0002.md @@ -0,0 +1,50 @@ +--- +id: "req-0002" +status: approved +discovery: "dcy-0002" +criteria: + create: + - "ac-0004" + - "ac-0005" + - "ac-0006" + - "ac-0007" + - "ac-0008" + update: [] + retire: [] + satisfy: [] +--- + +# trex-core 검증 컨테이너 이미지의 GHCR 게시 + +## Objective + +정확한 `trex-core` source commit으로부터 만든 컨테이너가 repository 검증과 built-container 검사를 모두 통과한 경우에만 `ghcr.io/fourblocks2020/trex-core`에 게시되도록 하여, 운영자가 source commit·stable SHA tag·immutable digest를 역추적하고 같은 digest로 배포하거나 이전 digest로 롤백할 수 있게 한다. + +## Scope + +- repository-root `Dockerfile`과 제한된 build context로 Python 3.12 기반 이미지를 만들고 `trex-core-daemon`, `trex-core`, `trex-core-reset` console entrypoint를 설치한다. +- GitHub Actions는 `main` 및 `v*` tag push의 정확한 commit을 checkout하고 repository가 정의한 검증 gate, image build와 built-container smoke를 순서대로 수행한다. +- verify job에서 검사를 마친 이미지 archive와 그 SHA-256을 artifact로 전달하고, 별도 publish job은 checksum이 일치하는 artifact를 load하여 재빌드 없이 GHCR에 게시한다. +- 모든 성공 게시에는 전체 source commit에서 결정적으로 파생된 stable SHA tag와 OCI revision을 부여하고 registry가 반환한 immutable digest를 배포·rollback의 정본으로 기록한다. `v*` tag event는 같은 loaded image에 해당 release tag를 추가하며 `latest`는 만들지 않는다. +- workflow output 또는 summary에 게시 image reference, digest와 전체 source commit을 남긴다. +- Docker build와 built-container 검사는 운영 TOML, `.env`, credential, token, 실제 connection string 또는 운영 서비스를 사용하지 않는다. +- runtime 문서는 per-instance config·secret 외부 주입, SHA tag 탐색과 digest pin, 이전 digest rollback을 설명하되 파괴적인 reset을 자동 rollback으로 안내하지 않는다. + +## Constraints + +- repository 검증, image build, built-container smoke, tested-image artifact 생성·전달·checksum·load 검증 중 하나라도 실패하면 registry login/push 전에 중단한다. +- registry push 이후에만 얻을 수 있는 digest가 누락되거나 형식이 잘못되면 job을 nonzero로 종료하고 성공 output·summary를 남기지 않는다. 이미 발생한 registry write를 발생하지 않은 것으로 주장하지 않는다. +- publish job은 검증된 artifact를 다시 빌드할 수 없으며 `packages: write` 권한은 GHCR 게시가 필요한 최소 job 범위로 제한한다. +- runtime image와 검증은 `pyproject.toml`의 Python `>=3.12` 및 현재 console entrypoint 계약을 보존한다. +- built-container smoke는 package import와 무부작용 entrypoint probe로 한정하고 실제 주문, migration, daemon setup, 실제 KIS 계좌, 운영 credential 또는 production service에 연결하지 않는다. +- `--config`가 필수인 daemon 계약을 보존하고 운영 config를 image layer에 고정하지 않는다. +- GitHub `main` 반영과 최초 GHCR package write는 구현 검증과 분리된 외부 쓰기이며 별도 승인을 요구한다. + +## Non-Goals + +- 운영 서버·production Compose의 image 교체, 재시작 또는 배포 완료 +- release version 선택, release tag 생성, production 검증 또는 release 완료 주장 +- DB migration, `trex-core-reset`, 운영 데이터 rollback 자동화 +- 다른 TREX 저장소나 `trex-sim` 격리 stack 변경 +- 새로운 HTTP health endpoint, multi-architecture image, image signing, SBOM 또는 provenance attestation +- GHCR package visibility나 조직 보안정책 변경