감사 체인 v2 · 증적
마이그레이션 023이 독립된 두 번째 감사 체인을 추가해요. v1 audit_events 체인은 그대로 유지돼요 — GET /api/v1/audit/verify로 계속 검증 가능해요.
audit_events_v2 (체인)
행 구성: sequence(UNIQUE, counter 행 CAS로 원자 발급), audit_version=2, hash_algorithm='HMAC-SHA256', key_id, prev_sequence, prev_hash, integrity_hash, event_type, actor_id, correlation_id, operation_id, payload_json(canonical JSON), occurred_at.
integrity_hash= RFC 2104 HMAC-SHA256(key, canonical fields) — v1의 비표준 구조와 plain-SHA-256 폴백 문제를 해소해요.- 링크 모델: writer는 잠시
sequence-1을 기다린 뒤, 없으면 자신 아래 최신 커밋 행에 링크하고prev_sequence에 기록해요. 순번을 태우고 죽은 writer가 체인을 쐐기로 막지 않아요. 순번 공백은sequence_gaps로 보고해요. - 키 부재 정책:
NOUGAT_AUDIT_HMAC_KEY미설정 → prod-config 환경은AUDIT_KEY_MISSING으로 fail closed(금전 쓰기 전 차단). 비prod는 경고 로그와 함께 방출 skip — 약해진 행은 체인에 절대 들어가지 않아요. 키 로테이션은 행별key_id로 추적해요(NOUGAT_AUDIT_HMAC_KEY_ID, 기본v1). - Append-only — UPDATE/DELETE 경로 없음.
GET /api/v1/admin/audit/v2/events
GET /api/v1/admin/audit/v2/verify → {ok,checked,first_mismatch,link_breaks,sequence_gaps}2
verify는 설정 키가 필요해요 — 부재 시 500 AUDIT_KEY_MISSING, 다른 key_id 행은 AUDIT_KEY_ID_MISMATCH.
audit_evidence (검증 가능 증적)
도메인 이벤트당 1레코드 — 로그가 아니에요: evidence_id, event_type, refs_json(id 참조), payload_json(redact), payload_hash(canonical sha256), audit_event_id+audit_sequence(봉인 체인 링크).
방출 지점: payment.capture, refund.attempt, refund.apply, settlement.commit, fee_rate.change, license.revoke, recovery.execute, recovery.verify, migration.apply(cron이 schema_version으로 관측), alert:recon_failures.
Redact: email|card|secret|token|password|authorization|api_key|payment_key|license_key|name|phone|address를 포함하는 키의 값은 저장 전 [REDACTED]로 치환해요 — PII·카드·비밀 값 없음.
GET /api/v1/admin/audit/evidence[?event_type=]audit_checkpoints
일일 구간 봉인(scope='daily', scope_key='YYYY-MM-DD'): first/last_sequence, last_event_id, event_count, root_hash — 구간 integrity_hash의 순차 fold sha256. 같은 구간은 항상 같은 해시예요. append-only.
- cron이 어제 구간을 자동 생성 —
POST /api/v1/admin/audit/checkpoints {scope_key?}로 수동 생성도 가능해요. storage_reference='NOT_CONFIGURED'— R2 내보내기는 PLANNED/NOT_VERIFIED(승인 대기, 업로드 코드 없음).
cron 보강
5분 tick마다: 연속 실패/미완주 recon run ≥3 → alert:recon_failures 증적(외부 알림 PLANNED), migration.apply 관측 증적, 어제 체크포인트.
NOT_VERIFIED / PLANNED
- 외부 알림 발송(email/Slack/webhook) — PLANNED, 현재는 감사 레코드만.
- 체크포인트 객체 저장소 내보내기 — PLANNED(
NOT_CONFIGURED). - 로테이션된 과거
key_id행의 재검증 — 현재 키만 재계산 가능해요. yp integrityCLI는 recon summary/report/v1-verify만 커버해요 — v2/증적 서피스는 admin API 전용.