이 노트에 대하여
incidentcli는 장애를 빨리 끄는 도구가 아니다. 알람 발화 → 자동 분류 → 가설 + 확률 → 수문장 검증 → 사람 결정 → 자동 액션 → 종결을 같은 record 한 객체에 박아 누가 언제 왜/무엇을 했는지를 5원칙(재현성·무결성·순수성·추적가능성·불변성)이 통과한 형태로 남긴다. abductcli의 anomaly → signal → memo → evaluation 흐름을 운영 인시던트 도메인에 고정한 자리이고, voscli의 read-only 회수 + record 운영 패턴을 호출 쪽으로 한 단 더 올렸다. 이 노트는 그 문제의식과 현재 골격, 다음 자리를 공개용으로 정리한다.
히스토리
- 생성 — v0.2-1 close 직후, 공개용 첫 소개 노트로 골격 정리.
관련메타
- † #힣 보안사고 대응 다섯가지 원칙 — 포렌식 증류 — 5원칙의 원천 자리. 이 도구는 그 원칙을 운영 인시던트 도메인으로 옮긴 첫 사례.
- † #논리 — 가설 + 확률 명시는 추론 정밀도 자리와 닿아 있다.
- † #워크플로우 #자동화 — 자동 액션을 사전 확정 runbook 안에서만 발화하게 묶는 자리.
관련노트
- §logickocli 한국어 자연어 추론 표준 논리 좌표계 — 같은 “자연어 → 정규화된 좌표계” 패턴. 한쪽은 논증, 한쪽은 인시던트.
- §butlercli 집사 — 생활 운영 루프 — 같은 “요청을 record로 묶어 추적”의 가정용 자매.
- §denotecli day-query 통합 타임라인 — incidentcli record는 Denote ID를 식별자로 받아 노트 생태계와 cross-link.
한 줄
운영 인시던트를 한 record 객체 에 묶고, 자동 액션이 외부로 나가기 직전에 수문장 preflight 게이트를 두는 CLI 도구다.
왜 만드는가
운영 인시던트는 보통 다음 순으로 무너진다.
- 알람이 발화한다 — 어디서 무엇이 왜 떨어졌는지 채널마다 흩어져 박힌다.
- 사람이 깬다 — 누가 먼저 봤는지, 왜 그렇게 판단했는지 일부만 남는다.
- 자동 액션이 들어간다 — 어떤 기준으로 들어갔는지, 그게 안전한 액션이었는지 근거가 record로 남지 않는다.
- 종결한다 — 비슷한 사건이 다시 와도 같은 결정을 같은 정밀도로 다시 내릴 보장이 없다.
이 흐름의 문제는 “장애가 길어졌다”가 아니라 다음번에 같은 결정을 다시 내릴 수 있다는 신뢰가 안 쌓인다 는 자리다. 한 번 잘 끈 것보다, 다음에도 같은 정밀도로 끌 수 있는 구조가 쌓이지 않는다. 그래서 일이 끝나도 조직의 기억 이 생기지 않는다.
incidentcli는 이걸 바꾸기 위해 record 한 객체 를 1차 단위로 박는다.
- 어떤 알람이 어디서 어떻게 발화했는지 (`evidence[]`)
- 무엇이 확정이고 무엇이 추정인지 (`hypotheses[].probability`)
- 누가 언제 왜 결정했는지 (`decisions[]`)
- 어떤 자동 액션이 어떤 사전 승인된 runbook 안에서 발화했는지 (`actions[]`)
- 외부 호출 전에 어떤 검증을 통과했는지 (`preflight[]`)
이 record가 append-only 로 누적되면 같은 사건의 결정 정밀도가 비교 가능해진다. 한 번의 사건이 단발 일화로 끝나는 게 아니라 “보편 원칙”으로 한 단계 증류된다. 이게 가능해지는 게 1차 목표다.
다섯 원칙이 운영 인시던트에서 어떻게 작동하는가
2025-11-21에 박은 다섯 원칙 — 재현성, 무결성, 순수성, 추적가능성, 불변성 — 은 원래 보안사고/포렌식 자리의 원천 명제다. 이 원칙을 운영 인시던트 도메인으로 옮기면 두 자리에 동시에 박힌다.
| 원칙 | record 측 (외부 가드) | 담당자 측 (내부 가드) |
|---|---|---|
| 재현성 | 사전 확정 runbook 안에서만, 같은 트리거 = 같은 액션 | 같은 격리 환경, 같은 image-version, 같은 입력으로 같은 결정 |
| 무결성 | record는 append-only. evidence는 SHA-256 봉인. 시간 역전 거부 | 호스트와 분리. 자격증명 직접 노출 금지, 주입 모델 |
| 순수성 | 가설은 확률을 명시. 확정과 추정을 표면화해 섞지 않는다 | 정의되지 않은 입력 채널 거부. 쓰레기 신호에 휘둘리지 않음 |
| 추적가능성 | 모든 결정에 `{who, when, why}`. 식별자는 Denote ID로 노트 생태계와 cross-link | 결정 trace가 런타임 로그(JSONL)로 박혀 record와 연결 |
| 불변성 | `schema_version`을 둬, 도구가 변해도 과거 record가 호환된다 | 런타임 `image_version`. 런타임이 바뀌어도 결정 재현 가능 |
이 표가 핵심이다. 한 면만 닫으면 다른 면으로 사고가 새어 나간다. 외부 가드 만 두면 오염된 담당자가 잘못 결정한 게 자동 액션으로 외부에 나간다. 내부 가드 만 두면 깨끗한 담당자가 검증 미달인 코드를 그대로 통과시킨다. 그래서 두 면을 같이 닫는다.
수문장의 두 면
| 면 | 책임 | 위치 |
|---|---|---|
| 외부 가드 (수문장의 손) | preflight 검증 게이트 → 자동 액션 거부 → 미통과 시 개선 요구 발송 | incidentcli 본체 |
| 내부 가드 (수문장의 결) | 담당자 자체가 오염원이 되지 않도록 격리. 정의된 외부 호출 인터페이스만 뚫림 | 격리된 담당자 런타임 |
외부 가드의 preflight 항목 (자동 액션이 외부로 나가기 직전 통과해야 하는 검증):
- 안정 릴리즈 후보가 존재하는가
- 그 릴리즈의 테스트가 통과한 기록이 있는가
- 영향 받은 인터페이스가 테스트로 검증 되었는가 — 미통과면 자동 액션 거부 + 개선 요구 발송
- 외부 종속 시스템 측 응답 정합성
- 해당 runbook이 사전 리더 회의 승인 상태인가
여기서 중요한 자리: incidentcli는 백엔드 테스트 코드 자체를 담지 않는다. 백엔드 팀이 가진 검증 테스트를 호출 만 하고, 그 결과가 기준치 미달이면 액션을 안 발화하고 대신 “테스트 강화하라” 메시지를 보낸다. 이게 진짜 수문장 자리다. 코드의 책임을 옮기는 게 아니라 호출 결과를 정직하게 record에 박는다.
분기 — origin
같은 알람이 와도 원인이 어디냐에 따라 워크플로가 갈린다.
| origin (canonical snake_case) | 워크플로 |
|---|---|
internal_backend | 안정 릴리즈 검증 + 백엔드 테스트 통과 → 승인 → 롤백 |
external_vendor_a | 외부 시스템 측 정보 조회 → 내부 승인 → 해당 측 채널로 리포트 |
external_vendor_b | 외부 OEM 측 응답 확인 → 우회 / 대기 |
unknown | 사람 결정 자리. 자동 분기 액션 금지 |
분기 판정 자체는 incidentcli classify --origin 명령이 hypothesis 기반으로 박는다. `certainty.level = confirmed` 가 아니면 `unknown` 으로 후퇴하고 사람 결정을 호출한다. 추측을 확정처럼 박는 순간 신뢰를 잃기 때문에, origin = unknown 인 record는 close 거부 한다 — classify 먼저.
record schema의 일반 모양
- id:
YYYYMMDDTHHMMSS(Denote ID). 사후 식별자. - opened_at: 사건 시각 KST ISO8601
+09:00. id ≠ opened_at — 분리. - timeline.{first_down_at, first_alert_at, trigger_commit_at, trigger_apply_at, revert_commit_at, source_tz, note}: id와 opened_at 사이의 인과 봉합 자리. commit ≠ apply 같은 자리는 별도로 봉합.
- origin: canonical snake_case 4 값.
- certainty.{level, probability, evidence_ref}: confirmed / estimated / rejected.
- hypotheses[]: 텍스트 + 확률 + source. 확률 명시 필수.
- preflight[]: 수문장 검증 항목 결과.
- actions[]: 자동 또는 사람 액션. v0.4 이후 `{runbook_id, approved_at, preflight_ref, executed_at, result, who}` 필수.
- decisions[]: 사람 결정 `{who, when, why}`.
- evidence[]: 경로 +
sha256(full 64 hex만) +source+provenance.{source_tz, fetched_at}. - runtime.{image_version, host_isolated, note}: 담당자 런타임 봉인 (v0.3 자리).
- schema_version:
"1.0". 도구가 변해도 record 호환.
형식은 JSON. 누적이 무거워지면 PostgreSQL JSONB 컬럼으로 lossless 직행. 사람이 읽는 자리는 jq / yq 로 충분하다.
시간축 — KST 단일 정착
incidentcli의 모든 입력 저장 출력은 Asia/Seoul (UTC+9) 단일 시간축. 외부 시스템 시간대(UTC / CST / 외부 OEM 측 다양)는 collector가 KST로 정규화하고, 원본 시간대(`source_tz`)는 evidence provenance에 박는다. 추정 시간대 그대로 저장 금지 — silent 1시간 / 9시간 shift가 가장 흔한 무결성 사고 자리.
CLI 입력은 Denote 형식 YYYYMMDDTHHMMSS (한 토큰, 정렬 가능, 파일명 호환). record 저장은 ISO8601 +09:00. 두 표현은 1:1 변환 가능.
자매 도구
incidentcli는 다음 도구 가족의 막내다. 코드를 차용하지 않고 원칙 만 차용한다.
- abductcli — anomaly → signal → memo → evaluation. 가설/ 확률 명시 자리. 상위 패턴.
- voscli (회사 도메인 친자) — read-only 회수 + record 운영. incidentcli는 voscli가 읽음 으로 끝내는 자리에서 한 단 더 가서 호출 까지 간다. 그래서 수문장 가드가 한 단 더 깊다.
- denotecli — Denote 노트 CLI. record가 Denote ID를 식별자로 받아 노트 생태계와 cross-link.
- gitcli — git 타임라인. 직전 배포 후보 추출 자리 (preflight에서 활용).
- gogcli — Google Workspace 통합. 결정 알림 라인 자리.
원칙 차용 — append-only, fast-fail, Denote ID cross-link. 친자 관계는 코드가 아니라 원칙에서 성립한다.
fast-fail — 면피 코드 금지
이 도구의 1차 사용자는 사람이 아니라 담당자 에이전트 다. 그래서 에러 처리 정책이 다르다.
에러처리는 ‘경고’가 아니라 그냥 박살나야 한다. 스킬은 면피 코드가 있으면 안 된다. 에이전트가 쓰는 거라서, 대충 경고는 에이전트가 우회해 버린다. 박살나야 한다.
박살나야 하는 자리:
- 스키마 무결성 — 필수 필드 누락,
schema_version미지정 - runbook 무결성 — 등록되지 않은 runbook 호출 시도
- append-only 위반 — 이미 박힌 필드 수정 시도
- 시간축 무결성 —
closed_at < opened_at, KST가 아닌 offset, Denote ID 길이 위반 - evidence 무결성 — SHA-256 mismatch,
source_tz누락 - preflight 무결성 — 미통과인데 자동 액션 시도
- origin 무결성 —
unknown인데 자동 분기 액션 시도
CLI는 exit non-zero. record 자체에 “일단 박고 나중에 정리하지”라는 자리가 없다.
격리 런타임 — 본인 하네스 비종속
담당자 에이전트 자체가 오염원이 되지 않도록 격리된 런타임 위에서 돈다. 채택한 자리는 pi-chat (Gondolin 마이크로 VM) — 아르민 / 마리오 측 메인라인 추종.
격리 런타임이 보장하는 자리:
- 호스트 분리 —
/workspace(인시던트 별) +/shared(계정 공용) 밖 쓰기 차단 - 명시적 outbound — 정의된 외부 호출 인터페이스 외 임의 HTTP 차단
- 자격증명 주입 모델 — 담당자에게 secret 값을 직접 노출하지 않고 작업환경에 주입만
- 런타임
image_version— 결정 재현 가능 (5원칙 불변성 자리) - 결정 trace JSONL — 담당자 결정이 런타임 로그로 박혀 record와 cross-link
여기서 의도적으로 내 (GLG) 개인 스킬셋, 분신 호출 경로, 개인 자격증명에 종속시키지 않는다. incidentcli는 완성되면 독립 에이전트 로 서야 한다. 본인 개인 도구체인이 깊숙이 관여하면 도구가 사람에게 묶인다. pi 런타임 + pi-chat surface 자체는 채택하되, 그 위에 회사 도메인 skill + 회사 자격증명만 주입하는 모델로 간다.
현재 상태 — v0.2-1 진입
- v0.0 close (2026-05-19) — repo skeleton, 정체성 정렬 (수문장 / 분기 / 격리 / 본인 하네스 비종속), 정보채널 정찰, 핵심 결정 5건 (언어 = Go, record = JSON, 시간축 = KST 단일, id ≠ opened_at 분리, sha256 vs sha256_prefix 분리) 박힘.
- v0.1 close (2026-05-19) — 운영 사이클 5 명령 (
init/attach/classify/decide/close) +validate+ingest+tz parse/format박힘.internal/tz/(KST 단일 + Denote ID),internal/record/(load + Validate + AppendDecision + AppendEvidence + Save, append-only), 첫 read-only collector (5 테이블 summary + redaction). 인터페이스 16 시나리오 PASS. - v0.2-1 done (2026-05-19) — 두 번째 collector 박힘. 정의된 외부 채널에서 회수한 raw JSON을 받아 PII 마스킹된 sanitized summary로 변환. 입력 형식 3종 자동 감지, 시간창 필터, LIMIT 5 timeline sample, body truncate 200 chars. 사람 이름은
[REDACTED], bot/role 식별자는 보존 (origin signal 유지). 라이브 외부 API 호출 없음 — 외부 채널은 collector 밖.
ROADMAP 한 줄씩
- v0.2-2 ~ v0.2-5 — 정보채널 자동화 (다른 collector + 외부 attach 어댑터 + 수문장 preflight 본체 + 개선 요구 draft + SHA-256 manifest 자동 봉인). 한 텀에 다 박지 않고 쪼개 간다.
- v0.3 — 담당자 자동 운영 + 격리 런타임 통합. alert webhook → 격리 런타임 담당자 호출 → record 80% 자동 채움. 사람은 decide + action 만.
- v0.4 — 자동 액션 (수문장 preflight 통과 후 사전 확정 runbook 발화). “사람을 안 깨우는 골든타임”의 첫 실증.
- v0.5 — 누적 / 비교 / postmortem 정착.
report --weekly --quarterly,compare <id1> <id2>. 사례 → 보편 원칙 증류 한 바퀴 더.
비목표
- 실시간 모니터링 대시보드 — 알람 발송·전파 시스템 측 책임.
- 알람 발송·전파 — 운영 모니터링 인프라 측 책임. incidentcli는 발화 신호를 받아 record만 운영.
- 추상 incident management 플랫폼 — PagerDuty 대체가 아니다.
- 사람 호출 자동화 — 전화·SMS는 별도 라인.
- 자동 RCA / AI 자동 해결 — 자동 액션은 사전 확정 runbook 범위 안에서만.
- 백엔드 테스트 코드 / 롤백 스크립트 본체 / 외부 시스템 검증 로직 — 각 측 책임. incidentcli는 호출만 한다.
- 본인 (GLG) 개인 스킬셋 / 익스텐션 / 분신 군대에 종속 — 완성 후 독립 에이전트로 서야 한다.
다음 한 걸음
다음 텀은 두 자리 중 하나로 결정.
- v0.2-2 — 세 번째 collector. git timeline 측 회수 (
git_trace) + 배포 trace 측 회수 (cicd). 한 텀에 collector 둘 또는 분리 한 텀씩. - report MVP —
incidentcli report <id>명령. record 한 건 + evidence summary 한 묶음을 사람이 읽는 markdown으로 자동 렌더링. ROADMAP 상 v0.5 자리이지만 단일 record 측은 v0.2 안에 흡수 가능. 기존 retrofit record로 회귀 검증 가능.
분신 호출 정책은 유지 — “힣이 명시적으로 요청한 경우만”. 다음 텀은 GLG 결정 받은 뒤 시작.
Comments