jikim
요약. jikim: 자율 개선 회차 12회, 릴리즈 8건. 최근 릴리즈 v0.2.11 (자산 2개). 건강 C
- 12회차
- 1프로젝트
- 8배포 준비 완료
- 0릴리즈 진행 중
- 0병합 완료
- 3검토 대기
- 0검증 실패
- 0변경 없음
- 1실행 오류
- $71.91비용
- 2시간 30분에이전트 시간
현황
- 저장소
- https://github.com/hkjang/jikim
- 마지막 회차
- 2026-09-12 18:14 KST — 🚀 릴리즈 merged PR #26, released v0.2.11
- 최근 릴리즈
- v0.2.11 — released · 자산 2개 (이전 v0.2.10: 2개) 전체 릴리즈 →
회차 이력
| 일시 | 프로젝트 | 결과 |
|---|---|---|
| 2026-09-12 18:14 | jikim | 배포 준비 완료 merged PR #26, released v0.2.11 |
| 2026-09-11 03:12 | jikim | 검토 대기 guarded files, PR open PR #25 |
| 2026-09-10 20:10 | jikim | 배포 준비 완료 merged PR #24, released v0.2.9 |
| 2026-09-10 12:31 | jikim | 배포 준비 완료 merged PR #23, released v0.2.8 |
| 2026-09-09 15:47 | jikim | 검토 대기 review held, PR open PR #22 |
| 2026-09-09 15:41 | jikim | 실행 오류 hold: budget |
| 2026-09-09 08:38 | jikim | 배포 준비 완료 merged PR #21, released v0.2.6 |
| 2026-09-09 01:29 | jikim | 배포 준비 완료 merged PR #18, released v0.2.5 |
| 2026-09-08 20:41 | jikim | 배포 준비 완료 merged PR #17, released v0.2.4 |
| 2026-09-07 01:41 | jikim | 검토 대기 guarded files, PR open PR #16 |
| 2026-09-06 15:47 | jikim | 배포 준비 완료 merged PR #15, released v0.2.3 |
| 2026-09-05 03:52 | jikim | 배포 준비 완료 merged PR #14, released v0.2.2 |
비용·사용량
| 시각 | 프로젝트 | 단계 | 시간 | 턴 | 비용 | 토큰 입력/출력 | 종료 |
|---|---|---|---|---|---|---|---|
| 18:07 | jikim | 릴리즈 | 4분 | 27 | $1.87 | 1.7M / 13K | success |
| 18:03 | jikim | review | 4분 | 18 | $1.60 | 1.0M / 16K | success |
| 17:59 | jikim | 개선 | 21분 | 97 | $11.67 | 14.8M / 85K | success |
| 16:49 | jikim | 릴리즈 | 9분 | 43 | $2.92 | 3.1M / 21K | success |
| 03:12 | jikim | 개선 | 23분 | 127 | $14.53 | 20.1M / 77K | success |
| 20:04 | jikim | 릴리즈 | 2분 | 23 | $1.17 | 944K / 7K | success |
| 20:01 | jikim | review | 3분 | 14 | $0.95 | 529K / 13K | success |
| 19:58 | jikim | 개선 | 7분 | 54 | $3.92 | 4.3M / 29K | success |
| 12:24 | jikim | 릴리즈 | 2분 | 22 | $1.05 | 843K / 6K | success |
| 12:21 | jikim | review | 4분 | 20 | $1.17 | 803K / 14K | success |
| 12:18 | jikim | 개선 | 7분 | 63 | $3.84 | 4.3M / 29K | success |
| 10:56 | jikim | 릴리즈 | 6분 | 36 | $1.74 | 1.8M / 10K | success |
| 15:46 | jikim | 개선 | 6분 | 49 | $2.89 | 2.8M / 25K | success |
| 08:31 | jikim | 릴리즈 | 2분 | 19 | $0.85 | 639K / 6K | success |
| 08:28 | jikim | review | 3분 | 21 | $0.95 | 509K / 11K | success |
| 08:25 | jikim | 개선 | 5분 | 40 | $2.00 | 1.7M / 20K | success |
| 01:20 | jikim | 릴리즈 | 2분 | 21 | $1.01 | 771K / 6K | success |
| 01:18 | jikim | review | 3분 | 21 | $1.08 | 738K / 11K | success |
| 01:15 | jikim | 개선 | 5분 | 33 | $1.89 | 1.6M / 19K | success |
| 20:34 | jikim | 릴리즈 | 2분 | 21 | $1.08 | 810K / 7K | success |
| 20:31 | jikim | review | 4분 | 16 | $1.18 | 714K / 15K | success |
| 20:27 | jikim | 개선 | 7분 | 38 | $2.84 | 2.2M / 31K | success |
| 01:41 | jikim | 개선 | 12분 | 74 | $5.93 | 6.8M / 43K | success |
| 15:40 | jikim | 릴리즈 | 2분 | 23 | $1.07 | 841K / 7K | success |
| 15:37 | jikim | review | 2분 | 19 | $0.75 | 432K / 8K | success |
| 15:35 | jikim | 개선 | 5분 | 38 | $1.97 | 1.7M / 18K | success |
아이디어 백로그 — 대기 13 / 전체 15
| 아이디어 | 가치/위험/크기 | 상태 | 메모 | 갱신 |
|---|---|---|---|---|
| 감사 로그 보존(audit_retention_days) 자동 정리 구현 | 3/3/M | 대기 | 설정은 저장·검증되지만 실제 삭제 작업이 없다. UI가 프리뷰라고 명시하고 있어 정직성 문제는 아니지만 기능 공백이다. 관리자 가이드에도 '자동으로 지워지지 않으므로 매월 증가량을 확인하라'로 적었다. 삭제는 되돌릴 수 없으므로 배치 주기·드라이런·삭제 자체의 감사 기록 설계가 필요하다. | 2026-09-12 |
| 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 | 2/1/S | 대기 | auth_handlers.go login과 openbao.go baoUserpassLogin 모두 Authenticate 직후 succeeded()를 부르고 그 뒤에 AllowLocalLogin을 확인한다. 로컬 로그인이 꺼진 계정에 올바른 비밀번호를 넣으면 실패 카운터가 초기화된다. succeeded() 호출을 CreateSession 직전으로 옮기면 된다. 2026-09-12 재확인: 그대로다(이번 회차는 캠페인 우선). | 2026-09-12 |
| settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 | 2/1/S | 대기 | resource_handlers.go settings()가 approval에 four_eyes·required_approvals·supported_targets를 주입하는데 updateSettings()는 *_configured와 supported_events만 삭제한다. 나머지는 workflow 설정 JSON에 적재되고 validateSetting이 미지의 키를 허용하므로 400 없이 DB에만 쓰레기 필드가 쌓인다. GET이 매번 덮어쓰므로 화면 증상은 없다. 2026-09-12 재확인: 그대로다. | 2026-09-12 |
| 관리 화면 전반의 'v0.2.0' 프리뷰 문구를 현재 버전 표기로 바꾸기 | 2/1/S | 대기 | FeaturePage.tsx의 거의 모든 프리뷰 notice, SecretEditorPage.tsx 회전 정책 안내, SecretDetailPage.tsx, SettingsPage.tsx(일반 설정 설명·감사 보존·허용 네트워크·최초 비밀번호 변경)에 'v0.2.0은 …'이 남아 있다. 이 문구가 가이드에 실리는 캡처(secret-new.png, guide.png)에 그대로 보인다. APP_VERSION 참조나 '현재 버전' 표현으로 바꾸고 캡처를 다시 찍으면 된다. 2026-09-12 재확인: 그대로다. | 2026-09-12 |
| 모바일 fullPage 캡처가 너무 길어 갤러리·문서에서 읽을 수 없는 문제 정리 | 2/1/S | 대기 | dashboard-mobile.png는 1024x7875(가로 대비 7.7배), admin-settings-mobile.png는 3785px이다. A4 한 쪽에 들어가는 최대 비율이 약 1.45라 두 문서 어디에도 실을 수 없어 가이드는 데스크톱 캡처만 썼다. 감사 로그 화면처럼 mobile.spec.ts도 viewport 높이 또는 clip으로 찍으면 가이드의 모바일 절을 만들 수 있다. 2026-09-12: 방문 추적 탭 캡처는 이 교훈대로 viewport 높이로 찍었다. | 2026-09-12 |
| 캡처 파이프라인(all-pages.spec.ts)에 방문 추적 탭 캡처를 정식 편입하고 admin-settings.png를 7개 탭 기준으로 다시 찍기 | 2/1/S | 대기 | 2026-09-12 신규. admin-settings-tracking.png는 이번에 일회성 Playwright 스크립트로 개발 빌드(0.2.10-dev)에서 찍었고 manifest에 required:false·fixture:true로 넣었다. 기존 admin-settings.png는 v0.2.9 기준 6개 탭이라 새 탭이 없다. E2E 전용 컨테이너에서 tracking 설정을 켜고 csp-report를 하나 보내 표가 보이는 상태로 찍은 뒤 원래 값(꺼짐)으로 되돌리는 케이스를 spec에 추가하면 릴리스마다 자동으로 갱신된다. | 2026-09-12 |
| authorizeTransit이 없는 transit key의 decrypt를 permission denied로 보고하는 문제 정리 | 2/2/S | 대기 | openbao.go authorizeTransit은 encrypt일 때만 TransitPermission의 ErrNotFound를 create capability 확인으로 흡수하고 decrypt는 (false, ErrNotFound)를 그대로 돌려준다. baoAllow → baoAccessFailure에서 403 permission denied가 되어 같은 파일 baoTransitFailure가 ErrNotFound를 400 encryption key not found로 매핑하는 것과 어긋난다. 호출자는 이미 경로 decrypt capability를 통과한 뒤라 key 부재를 알려도 노출 문제는 없다. | 2026-09-12 |
| baoKVWrite가 SecretExistsByPath로 create·update capability를 고르면서 생기는 TOCTOU 정리 | 2/2/S | 대기 | openbao.go baoKVWrite는 먼저 SecretExistsByPath로 존재 여부를 보고 capability를 create/update 중 하나로 고른 뒤 PutOpenBaoSecret을 부른다. 두 호출 사이에 다른 요청이 같은 path를 만들면 create만 가진 caller가 기존 key를 덮어쓸 수 있다. 두 capability 중 하나라도 있으면 통과시키고 실제 결과 버전으로 판정하거나 쓰기를 한 트랜잭션으로 합치는 편이 정확하다. | 2026-09-12 |
| requestedOpenBaoVersion이 음수 version 쿼리를 오류 대신 latest로 처리하는 동작 정리 | 2/2/S | 대기 | openbao.go requestedOpenBaoVersion은 version<1이면 0(latest)을 돌려준다. OpenBao는 잘못된 버전에 오류를 낸다. 현재 동작이 docs/guides/compatibility.md에 문서화돼 있어 문서와 코드를 함께 바꿔야 하고 클라이언트 호환 영향이 있으므로 differential suite와 함께 다루는 편이 낫다. | 2026-09-12 |
| 승인 워크플로가 켜진 상태의 검토·승인 화면 캡처 추가 | 2/2/M | 대기 | approvals.png는 워크플로가 꺼진 기본 상태의 안내 화면이라, 가이드가 정작 검토자가 보는 대기 목록을 보여 주지 못한다. E2E 전용 컨테이너에서 PATCH /api/v1/settings로 워크플로를 켜고 일반 사용자 계정으로 요청을 만든 뒤 캡처하고 원래 값으로 되돌리면 된다. 기존 값을 먼저 읽어 복원해야 한다. | 2026-09-12 |
| SPA 내부 이동 시 include_admin 제외가 처음 연 주소 기준으로만 적용되는 한계 보완 | 2/2/M | 대기 | 2026-09-12 신규. 스니펫은 SPA 셸에 한 번 실리므로 /dashboard로 들어온 뒤 화면 안에서 /admin/settings로 이동하면 계속 동작한다(가이드 3.7절에 명시). 서버가 셸에 data-tracking-exclude 경로 목록을 실어 주고 클라이언트 라우터 훅이 관리 경로 진입 시 tracker의 pageview 호출을 막는 식으로 보완할 수 있으나 제공자마다 API가 달라 momento만 우선 지원하는 편이 현실적이다. | 2026-09-12 |
| Transit rewrap/datakey 등 후속 엔드포인트 확장 | 2/3/M | 대기 | /v1/transit/rewrap/{key}는 회전 후 재암호화를 위해 실사용 빈도가 높고 기존 저장 계층(transit_key_versions)만으로 구현 가능하다. /v1/transit/keys/* 관리 API는 jikim 관리 API와 권한 모델이 겹치므로 신중히. | 2026-09-12 |
| 방문 추적 설정 조회를 짧은 TTL 캐시로 묶어 SPA 셸 요청마다 생기는 settings 조회 줄이기 | 1/1/S | 대기 | 2026-09-12 신규. decorateIndex가 셸 요청마다 store.TrackingConfig를 호출한다(정적 자산·API에는 없음). 페이지 로드당 한 번이라 부하는 작지만, 다른 설정 조회와 같이 수 초 TTL 캐시를 두면 DB 왕복을 없앨 수 있다. 캐시 때문에 저장 직후 반영이 늦어지는 점을 가이드에 적어야 한다. | 2026-09-12 |
| 캠페인 tracking-2026-09 — 관리자가 화면에서 방문 추적 스크립트를 붙이는 체계(nonce 기반 CSP·Momento 같은 오리진 프록시·차단 출처 기록) | 4/2/M | 완료 | 2026-09-12 구현. internal/tracking, settings의 tracking 키, SPA 셸 nonce·정책, csp-report와 admin 위반 API, /momento/* 프록시, 관리 화면 방문 추적 탭, 가이드 3.7절과 PDF. 기본 꺼짐, 'unsafe-inline' 미사용. 실제 PostgreSQL·stub 수집기·Chromium으로 로드 확인. 커밋 67f0934. | 2026-09-12 |
| settings.go의 데드 코드 var _ = pgx.ErrNoRows 제거 | 1/1/S | 완료 | 2026-09-12 방문 추적 캠페인에서 settings.go를 건드리며 함께 제거했다(pgx import도 정리). 커밋 67f0934. | 2026-09-12 |
교훈 (깨졌던 변경)
- 2026-09-07 rejected-by-human — 사람이 PR 을 반려함. 같은 접근은 피할 것. (링크)
원장 (에이전트가 남긴 기록)
2026-09-05
- 선택: SQL LIKE 와일드카드 이스케이프로 OpenBao LIST prefix 범위 이탈 수정 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
ListSecretChildren이 caller prefix를 그대로LIKE패턴에 넣어_,%가 와일드카드로 해석됐고,kvlike_xprefix LIST가kvlikeXx하위 key 이름까지 반환했습니다(감사 로그·Secret 검색어도 동일 문제).internal/store/store.go에escapeLike,secrets.go에secretChildrenPattern을 추가해 세 호출지점 모두 문자 그대로 대조하도록 고쳤습니다. 검증은logic_test.go단위 테스트 추가 + 일회용 postgres:17-alpine 컨테이너에JIKIM_TEST_POSTGRES_DSN을 걸어 통합 테스트를 실행해 수정 전 FAIL / 수정 후 PASS를 확인했고,./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)가 통과했습니다. 커밋6f66501. - 보류 아이디어:
security설정의 잘못된 타입 값(예:allow_local_login: "false")이 오류 없이 저장되고 조용히 무시되는 문제를 검증으로 막기 (가치 3 / 위험 2 / S)- 신뢰 프록시 목록 기반
X-Forwarded-For처리로 감사 로그·로그인 rate limit의 IP가 리버스 프록시 주소로 고정되는 문제 해결 (가치 4 / 위험 3 / M) - Transit
batch_input/batch_results지원 추가로 OpenBao 호환 범위 확대 (가치 3 / 위험 3 / M) baoUserpassLogin에서AllowLocalLogin확인 전에 rate limiter를succeeded로 초기화하는 순서 정리 (가치 2 / 위험 1 / S)server.go의var _ = fmt.Sprintf,auth_handlers.go의var _ = store.ErrUnauthorized같은 데드 코드 제거 (가치 1 / 위험 1 / S)
- 릴리즈: v0.2.2 (2026-09-05)
2026-09-06
- 선택: 잘못된 타입의 security·service 설정 값을 fail-closed로 거부 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
validateSetting이security·service설정의 타입을 확인하지 않아allow_local_login: "false"나session_timeout_minutes: "60"같은 값이 200으로 저장되고,SecurityConfig가 읽을 때 타입 단언에 실패해 조용히 버려졌습니다. 관리자에게는 로컬 로그인이 꺼진 것처럼 보이지만 실제로는 기본값(허용)이 유지되는 무증상 보안 오설정이라optionalString·optionalIntInRange헬퍼를 추가해 security의 boolean·정수·문자열 필드, service의 문자열 필드, AI의max_tokens·timeout_seconds를 타입까지 검증하고 어긋나면 400으로 거부하게 했습니다. 검증은settings_validation_test.go에 거부/수용 케이스 단위 테스트를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켜 확인했습니다. 커밋dbf7b03. - 보류 아이디어:
- 신뢰 프록시 목록 기반
X-Forwarded-For처리로 감사 로그·로그인 rate limit의 IP가 리버스 프록시 주소로 고정되는 문제 해결 (가치 4 / 위험 3 / M) - Transit
batch_input/batch_results지원 추가로 OpenBao 호환 범위 확대 (가치 3 / 위험 3 / M) - 로그인 성공 판정 전에 rate limiter를
succeeded로 초기화하는 순서 정리 (login,baoUserpassLogin둘 다 해당) (가치 2 / 위험 1 / S) settingsGET이 주입하는four_eyes·required_approvals·supported_targets가 PUT 왕복 시workflow설정에 그대로 저장되는 문제 정리 (가치 2 / 위험 1 / S)server.go·auth_handlers.go·settings.go의var _ = ...데드 코드 제거와 관리 화면의 “v0.2.0 프리뷰” 문구 최신화 (가치 1 / 위험 1 / S)
- 신뢰 프록시 목록 기반
- 릴리즈: v0.2.3 (2026-09-06, run 2026-09-06-153047-jikim-improve)
2026-09-07
- 선택: 신뢰 Reverse Proxy 설정 기반 X-Forwarded-For 처리로 감사 로그·로그인 rate limit IP 고정 문제 해결 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
remoteIP가RemoteAddr만 사용해 운영 권장 구성인 TLS Reverse Proxy 뒤에서는 감사 로그의 IP가 항상 Proxy 주소로 기록되고 로그인 실패 제한 키의 IP 성분도 모든 클라이언트에서 동일해졌습니다.X-Forwarded-For를 무조건 신뢰하면 누구나 IP를 위조할 수 있어 환경변수 대신security설정에trusted_proxies(CIDR·IP 목록)를 추가하고, 접속 주소가 등록 대역일 때만 전달 체인을 오른쪽에서 왼쪽으로 훑어 신뢰 대역 밖 첫 주소를 클라이언트로 판정하도록clientIP를 새로 만들었습니다(목록이 비면 기존 동작 유지, 30초 TTL 캐시). 저장소의 “환경변수 네 개” 제품 계약을 깨지 않으려고 설정 값으로 두었고 관리 화면 보안 탭에 입력 필드와 안내를 추가했습니다. 검증은clientIP판별 10개 케이스·rate key 분리·trusted_proxies검증/파싱 단위 테스트를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켜 확인했습니다. 커밋aa0eccf, 릴리스 커밋a20a367(v0.2.4). - 보류 아이디어: Transit batch_input/batch_results 지원으로 OpenBao 호환 범위 확대 (3/3/M) / 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / requestIsHTTPS가 신뢰 Proxy 여부와 무관하게 X-Forwarded-Proto를 신뢰하는 부분을 trusted_proxies와 일관되게 정리 (2/3/S)
2026-09-08
- 선택: OpenBao Transit batch_input/batch_results 지원 추가 (가치 3 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
/v1/transit/{encrypt,decrypt}/{key}가 단건만 처리해baoCLI와 OpenBao SDK가 흔히 쓰는 batch 호출을 사용할 수 없었습니다.batch_input이 있을 때만 동작하는 추가 경로로 구현해 기존 단건 계약은 그대로 두고, 입력 순서를 유지한batch_results·reference반향·항목별error·전부 실패 시에만 400이라는 OpenBao 의미를 맞췄으며, 미구현 파라미터(context,nonce,associated_data,key_version)는 조용히 무시하지 않고 항목 오류로 처리했습니다. batch decrypt는 감사 기록에 실패하면 평문을 전부 withhold하는 기존 보안 불변식을 유지합니다. 검증은 순서·항목 오류·빈 배열·전부 실패·감사 실패·권한 거부를 덮는 단위 테스트 5개(hook 기반, DB 불필요)를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켜 확인했습니다. 커밋29788a6. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / requestedOpenBaoVersion이 음수 version 쿼리를 오류 대신 latest로 처리하는 동작 정리 (2/2/S) / 데드 코드(var _ = …) 제거와 관리 화면의 ‘v0.2.0 프리뷰’ 문구 최신화 (1/1/S)
- 릴리즈: v0.2.4 (2026-09-08, run 2026-09-08-202134-jikim-improve)
2026-09-09
- 선택: Transit 핸들러의 store 오류를 종류별 상태 코드로 분리 (가치 2 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
/v1/transit/{encrypt,decrypt}/{key}가 store 오류를 종류와 무관하게400 + err.Error()로 반환해 DB 장애가 “잘못된 요청”으로 보고되고 pgx·crypto 내부 오류 문자열(SQLSTATE, DSN host 등)이 클라이언트에 노출됐습니다. KV 핸들러와 같은 방식으로baoTransitFailure헬퍼를 추가해ErrInvalid는 400+이유,ErrNotFound는 OpenBao와 동일한 400encryption key not found, 나머지는 500+일반 메시지로 나누고 batch 경로도 같은 판정을 항목별error에 적용했으며(전부 실패인데 서버 장애가 섞이면 400 대신 500), AEAD 인증 실패는 잘못된 입력이므로TransitDecrypt에서ErrInvalid로 감싸 400을 유지했습니다. 검증은 단건 4케이스·batch 서버 장애·부분 성공을 덮는 hook 기반 단위 테스트 3개를 추가하고 기존 batch 테스트의 hook 오류를 실제 store 오류 타입으로 바로잡은 뒤./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켜 확인했습니다. 커밋ab3f6b1. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / MCP tool 오류가 모든 store 오류를 err.Error() 그대로 반환하는 문제를 Transit과 같은 방식으로 정리 (2/1/S) / Transit rewrap 엔드포인트 추가 (2/3/M)
- 릴리즈: v0.2.5 (2026-09-09, run 2026-09-09-011104-jikim-improve)
2026-09-09
- 선택: MCP 도구 오류에서 store 내부 정보 노출 차단 (가치 2 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
mcpToolCall이 모든 도구 실패를err.Error()그대로isError본문에 실어 보내, DB 장애 시 pgx 오류의 DSN host·SQLSTATE가 MCP 클라이언트에 노출되고transit.decrypt는 서버 장애까지 감사 기록에 400으로 남겼습니다. Transit 핸들러의baoTransitFailure와 같은 방식으로mcpToolFailure를 추가해 store sentinel(ErrInvalid/ErrNotFound/ErrForbidden/ErrUnauthorized/ErrConflict)과 디스패치가 직접 만든 오류만 사유를 전달하고 나머지는 “도구를 실행할 수 없습니다”로 접었으며, 디스패치 sentinel이 자기 상태 코드를 들고 다니게 해transit.decrypt감사 StatusCode가 실제 실패 종류를 따르도록 했습니다. 검증은 누출 차단·감사 상태 500·ErrInvalid 사유 보존·sentinel 매핑을 덮는 hook 기반 단위 테스트 4개를 추가하고(전용mcp_tool_errors_test.go)./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켜 확인했습니다. 곁들여transit.encrypt가 테스트 seam을 우회해s.store를 직접 부르던 것을s.encryptTransit로 맞추고 데드 코드var _ model.User를 제거했으며, api-guide에 도구 실패 텍스트 계약을 문서화했습니다. 커밋c5926b5. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / MCP secrets.metadata가 SecretVersions 실패 시에도 result를 채워 부분 결과와 오류가 섞이는 경로 정리 (2/1/S) / Transit rewrap 엔드포인트 추가 (2/3/M)
- 릴리즈: v0.2.6 (2026-09-09, run 2026-09-09-082103-jikim-improve)
2026-09-09
- 선택: 실패한 capability 확인을 permission denied가 아닌 서버 장애로 보고 (가치 3 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약: OpenBao KV·Transit 핸들러 9곳과 MCP 도구 3곳이
allowed, err := CanAccess(...)결과를err != nil || !allowed로 접어 DB 장애로 policy 조회가 실패해도 403permission denied를 돌려줬습니다. 호출자는 정책 오설정을 쫓게 되고 재시도 가능한 서버 장애가 종결 오류로 보이므로, 앞선 v0.2.5·v0.2.6의 오류 매핑 정리와 같은 방식으로baoAllow·baoAccessFailure와mcpAccessFailure를 추가해 판정 불가와 거부를 나눴습니다(sentinel은 기존대로 403, ErrInvalid는 400+이유, 나머지는 500failed to check permissions이며 driver 문자열 미노출, MCP transit.decrypt 감사 StatusCode도 실제 실패 종류를 따름). 곁들여 userpass login이 SecurityConfig 조회 실패를 “local login is disabled”로 보고하던 것을 500으로 분리하고 secrets.metadata의 부분 결과 경로를 막았으며, compatibility.md에 판정 계약을 문서화했습니다. 검증은 hook 기반 단위 테스트 6개(openbao_access_errors_test.go) 추가 후./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose) 통과로 확인했습니다. 커밋c143d70. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / SessionByToken이 DB 장애까지 ErrUnauthorized로 접어 인증 미들웨어가 장애를 403으로 보고하는 문제 정리 (3/2/M) / Transit rewrap 엔드포인트 추가 (2/3/M)
- 릴리즈: v0.2.7 (2026-09-10, run 2026-09-10-105020-jikim-approve)
2026-09-10
- 선택: SessionByToken·Authenticate가 DB 장애를 인증 실패로 접는 문제 수정 (가치 3 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
users.go의Authenticate와SessionByToken이QueryRow실패를 원인과 무관하게ErrUnauthorized로 바꿔, DB 장애가 “자격증명 거부”·”세션 만료”로 보고되고 인증 미들웨어는 401/403을, 로그인 rate limiter는 장애를 실패 시도로 계산해 5분 창 동안 계정을 잠갔습니다. v0.2.5~v0.2.7의 오류 매핑 정리와 같은 방식으로lookupFailed로pgx.ErrNoRows와 그 밖의 오류를 나눠 행 없음만 sentinel로 남기고,withAuth·sessionStatus는storeError로 500을,withBaoAuth는 새baoSessionFailure로 만료 토큰(403permission denied)과 조회 불가(500failed to look up token)를 갈랐으며,login·baoUserpassLogin은 장애를 실패 시도로 계산하지 않고 500을 반환하고 driver 문자열을 응답에 담지 않습니다. 검증은 hook 기반 단위 테스트 7개(auth_outage_test.go6개 + storelookupFailed1개)를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose) 통과로 확인했습니다. 커밋d79383b. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / baoKVWrite가 SecretExistsByPath로 create·update capability를 고르면서 생기는 TOCTOU 정리 (2/2/S) / Transit rewrap 엔드포인트 추가 (2/3/M)
- 릴리즈: v0.2.8 (2026-09-10, run 2026-09-10-121123-jikim-improve)
2026-09-10
- 선택:
/v1/sys/health가 PostgreSQL 장애를 정상 active로 보고하는 문제 수정 (가치 3 / 위험 2 / 작업량 M) - 결과: 성공
- 요약:
baoHealth가 저장소 상태를 전혀 보지 않고 항상200과"sealed": false를 반환해, OpenBao 클라이언트와 Load Balancer가 표준 probe로 쓰는 경로가 DB에 닿지 못하는 노드도 “정상”이라 답했습니다. jikim의 Secret과 Transit key는 전부 PostgreSQL에 있으므로 저장소 미도달은 OpenBao의 seal과 같은 운영 상태로 보아/readyz와 동일한 2초 제한 ping 후"sealed": true와503을 반환하도록 바꾸고(원인 driver 문자열·DSN·SQLSTATE 미노출), probe 설정 이식을 위해activecode·sealedcode를 지원하되 100~599 정수가 아니면 조용히 무시하지 않고400으로 거부합니다. 저장소 확인은ready와 공유하는pingStorageseam으로 모았고, 곁들여/v1/sys/health를 감사 대상에서 제외했습니다(이미 제외된/healthz·/readyz와 같은 비인증 probe인데 poll마다 감사 row가 쌓였고 이제는 장애 중에도 기록을 시도하게 되므로). 검증은 hook 기반 단위 테스트 5개(openbao_health_test.go)를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose) 통과로 확인했습니다. 커밋311c4d3. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / authorizeTransit이 존재하지 않는 transit key의 decrypt를 400 encryption key not found 대신 403 permission denied로 보고하는 문제 정리 (2/2/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / baoKVWrite가 SecretExistsByPath로 create·update capability를 고르면서 생기는 TOCTOU 정리 (2/2/S)
- 릴리즈: v0.2.9 (2026-09-10, run 2026-09-10-195116-jikim-improve)
2026-09-11
-
(원장 항목에 비밀/내부 정보 의심 문자열이 있어 비공개 기록으로 옮김 — run 2026-09-11-025110-jikim-improve)
- 릴리즈: v0.2.10 (2026-09-11, run 2026-09-11-164027-jikim-approve)
2026-09-12
- 선택: 캠페인 tracking-2026-09 — 관리자가 화면에서 방문 추적 스크립트를 붙이는 체계(nonce 기반 CSP·Momento 같은 오리진 프록시·차단 출처 기록) (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: jikim은 React 관리 화면을 제공하므로 TRACKING-STANDARD.md를 그대로 따라
internal/tracking(설정·스니펫·정책 출처·위반 기록),settings의tracking키(타입·제공자·8KB·URL 검증, 기본 꺼짐), SPA 셸 전용decorateIndex(요청마다 nonce를 만들어 모든<script>에 붙이고 같은 nonce·스니펫 출처·report-uri를script-src·connect-src·img-src에 더함,'unsafe-inline'미사용, 설정 저장소 장애 시 추적 없이 이전 정책으로 응답),/api/v1/tracking/csp-report(무인증 204, 감사 제외, 메모리 100개 고리)와 admin 전용 목록·삭제·한 번 눌러 허용 API,/momento/*같은 오리진 프록시(Momento가 켜졌을 때만 열리고 Cookie·Authorization·X-Vault-Token 제거, Set-Cookie 미전달, 64KB·10초), 관리 화면 방문 추적 탭(Momento 첫 자리·프록시 기본 ON·차단 출처 표와 허용 버튼)을 추가했고 브라우저가 그리지 않는/api/*·/v1/*·/mcp·probe·/momento/*응답의 정책은default-src 'none'으로 더 좁혔습니다. 곁들여 settings.go의 데드 코드var _ = pgx.ErrNoRows를 제거했습니다(보류 아이디어 [1/1/S] 해결). 검증은 tracking 12개·store 1개·httpapi 9개 단위 테스트(hook 기반, DB 불필요)와 설정 탭 vitest 4개를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose)를 통과시켰으며, 실제 PostgreSQL 16 컨테이너와 stub 수집기로 서버를 띄워 curl로 꺼짐 기본값·8KB 거부·nonce와 스니펫 일치·관리 화면 제외·custom 스니펫 출처 반영·신고 기록/허용/감사 미기록·끄면 정책 원복을 확인하고 Playwright Chromium으로/momento/tracker.js가 정책 위반·콘솔 오류 없이 프록시를 거쳐 로드되고 수집기가 Cookie 없는 요청을 받는 것을 확인했습니다. 그 화면을docs/screenshots/admin-settings-tracking.png로 캡처해 관리자 가이드 3.7절(설정 방법·CSP·nonce·프록시·API 표)에 실었고 공용 md2pdf로 PDF를 다시 구웠습니다. 커밋67f0934. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / 캡처 파이프라인(all-pages.spec.ts)에 방문 추적 탭 캡처를 정식 편입하고 admin-settings.png를 7개 탭 기준으로 다시 찍기 (2/1/S) / SPA 내부 이동 시 include_admin 제외가 처음 연 주소 기준으로만 적용되는 한계를 클라이언트 라우팅 훅으로 보완 (2/2/M)
- 릴리즈: v0.2.11 (2026-09-12, run 2026-09-12-174012-jikim-improve)