- docs/verification/real-env-0.1.1.md/.json: 배포 경과·결함 수정 요약, 흐름별 단계·증거, 재검증(systemd/nginx/HAL·포털 건강/배포 영수증/모듈 버전) 기록 - docs/RUNBOOK-0.1.1.md: 서버 한 대 운영, 메일 서버 등록 값, 발신 메일, 같은 릴리스 재등록 절차 - VERSION_HISTORY.md 알려진 제한에 실환경 검증 결과 기입. 출시 완료는 선언하지 않음 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2877 lines
76 KiB
JSON
2877 lines
76 KiB
JSON
{
|
|
"generatedAt": "2026-10-26T14:29:49.315Z",
|
|
"startedAt": "2026-10-26T14:27:30.390Z",
|
|
"env": {
|
|
"agents": 4,
|
|
"ports": {
|
|
"hal": 40961,
|
|
"portal": 39713,
|
|
"wm": 37209,
|
|
"am": 46099,
|
|
"mail": 45135
|
|
},
|
|
"mailServerRegistered": true,
|
|
"node": "v24.22.1"
|
|
},
|
|
"summary": {
|
|
"verified_local": 19,
|
|
"partial": 14,
|
|
"not_verifiable_locally": 1,
|
|
"failed": 0,
|
|
"unrecorded": 0
|
|
},
|
|
"flows": [
|
|
{
|
|
"no": 1,
|
|
"title": "신규 고객 가입과 이메일 확인",
|
|
"verdict": "verified_local",
|
|
"note": "가입→확인 메일(포획)→1회용 토큰 확인→개인 그룹·별칭→중복 가입 안내→기기 목록·세션 종료",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "가입(이메일·표시 이름·비밀번호·필수 동의) → 세션 발급",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"hasSession": "[redacted]"
|
|
}
|
|
},
|
|
{
|
|
"name": "확인 전 유료·운영 기능 차단(그룹 생성 403 email_unverified)",
|
|
"ok": true,
|
|
"detail": "email_unverified"
|
|
},
|
|
{
|
|
"name": "확인 메일 수신(발신 포획)에서 토큰 추출",
|
|
"ok": true,
|
|
"detail": {
|
|
"subject": "[Ariweb] 이메일 주소를 확인해 주세요"
|
|
}
|
|
},
|
|
{
|
|
"name": "이메일 확인(1회용) → 재사용 거부",
|
|
"ok": true,
|
|
"detail": {
|
|
"first": 200,
|
|
"second": "token_used"
|
|
}
|
|
},
|
|
{
|
|
"name": "내 정보: 이메일 확인됨 · 개인 그룹 · owner · 별칭 user-01",
|
|
"ok": true,
|
|
"detail": {
|
|
"verified": true,
|
|
"groupKind": "personal",
|
|
"role": "owner",
|
|
"alias": "user-01"
|
|
}
|
|
},
|
|
{
|
|
"name": "중복 가입은 존재를 드러내지 않는 안내(409 signup_unavailable)",
|
|
"ok": true,
|
|
"detail": "signup_unavailable"
|
|
},
|
|
{
|
|
"name": "로그인 기기 목록 · 다른 세션 종료",
|
|
"ok": true,
|
|
"detail": {
|
|
"sessionsBefore": "[redacted]",
|
|
"revoked": 2
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"me": {
|
|
"verified": true,
|
|
"groupKind": "personal",
|
|
"role": "owner",
|
|
"alias": "user-01"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 2,
|
|
"title": "개인 사용자 그룹 생성과 다른 사용자 초대",
|
|
"verdict": "verified_local",
|
|
"note": "팀 그룹 생성·초대 메일·1회용 수락·별칭 user-02·역할 거부·그룹 전환 격리",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "팀 그룹 만들기 → 현재 그룹 전환",
|
|
"ok": true,
|
|
"detail": "g_01a1789c0ba4cc346b9a816d13"
|
|
},
|
|
{
|
|
"name": "이메일 초대(7일) → 초대 메일 링크",
|
|
"ok": true,
|
|
"detail": "92k9hZNmEVZ5s11c5YD_AKFXC9VLtCw2"
|
|
},
|
|
{
|
|
"name": "초대 수락(1회용) → 별칭 user-02 · 역할 member",
|
|
"ok": true,
|
|
"detail": {
|
|
"alias": "user-02",
|
|
"role": "member",
|
|
"reuse": "invite_invalid"
|
|
}
|
|
},
|
|
{
|
|
"name": "권한 없는 구성원의 직접 API 접근 → 403 + 필요한 역할",
|
|
"ok": true,
|
|
"detail": {
|
|
"requiredRole": [
|
|
"owner",
|
|
"admin"
|
|
],
|
|
"yourRole": "member"
|
|
}
|
|
},
|
|
{
|
|
"name": "그룹 전환 격리: 개인 그룹으로 바꾸면 팀 데이터가 보이지 않음",
|
|
"ok": true,
|
|
"detail": {
|
|
"team": {
|
|
"live": 0,
|
|
"purchased": 0,
|
|
"inUse": 0,
|
|
"reserved": 0,
|
|
"unallocated": 0
|
|
},
|
|
"personal": {
|
|
"live": 0,
|
|
"purchased": 0,
|
|
"inUse": 0,
|
|
"reserved": 0,
|
|
"unallocated": 0
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 3,
|
|
"title": "상품 주문, 결제 확인, 권리 활성화",
|
|
"verdict": "verified_local",
|
|
"note": "상품 목록→주문(멱등·수동 결제)→입금 확인 전 승인 거부→입금 확인→승인→권리 활성화→재승인 중복 없음→청구 화면",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "상품 목록(공개 버전만·가격·한도·미확정 정책 표시)",
|
|
"ok": true,
|
|
"detail": {
|
|
"count": 2,
|
|
"team": {
|
|
"id": "pv_01a1789c0a26a92faf63434d5a",
|
|
"price": 49000,
|
|
"sites": 3
|
|
},
|
|
"deferred": 3,
|
|
"paymentMethods": [
|
|
"manual_transfer"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "주문 제출(수동 결제) → 결제 대기·주문 번호·입금 안내 · 같은 키 재제출은 중복 없음",
|
|
"ok": true,
|
|
"detail": {
|
|
"orderNo": "AW-20261026-0001",
|
|
"status": "awaiting_payment",
|
|
"total": 63800,
|
|
"dueAt": "2026-11-02T14:27:35.064Z",
|
|
"hasInstructions": true
|
|
}
|
|
},
|
|
{
|
|
"name": "입금 확인 전 승인 거부(409 payment_not_confirmed)",
|
|
"ok": true,
|
|
"detail": "payment_not_confirmed"
|
|
},
|
|
{
|
|
"name": "운영자 입금 확인 → 승인 대기 목록에 권리 변화·현재 사용량 표시",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "awaiting_approval",
|
|
"delta": [
|
|
"sites",
|
|
"web_disk",
|
|
"web_out",
|
|
"mail_storage",
|
|
"mail_msgs",
|
|
"mailboxes",
|
|
"mail_services",
|
|
"mail_storage",
|
|
"mail_msgs",
|
|
"mailboxes"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "운영자 승인 → 권리 활성화(재승인은 중복 없음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"granted": 10,
|
|
"already": true
|
|
}
|
|
},
|
|
{
|
|
"name": "고객 결제 화면: 활성 상품·권리·다음 청구일",
|
|
"ok": true,
|
|
"detail": {
|
|
"sites": 3,
|
|
"subs": 1,
|
|
"nextBilling": "2026-11-26T14:27:35.130Z"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ap": {
|
|
"granted": 10,
|
|
"already": true
|
|
},
|
|
"bill": {
|
|
"sites": 3,
|
|
"subs": 1,
|
|
"nextBilling": "2026-11-26T14:27:35.130Z"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 4,
|
|
"title": "초안 사이트 생성",
|
|
"verdict": "verified_local",
|
|
"note": "초안 생성·Webmaker 프로젝트 연결·사이트 수 미소비·목록 필터",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "사이트 생성(새 Webmaker 프로젝트·배포 방식 관리형) → 초안·사이트 수 미소비·다음 단계 안내",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a1789c0c9a313add86e28440",
|
|
"lifecycle": "draft",
|
|
"projectId": "00000000000000000000000000000001",
|
|
"nextSteps": [
|
|
"build",
|
|
"import",
|
|
"domain",
|
|
"mode",
|
|
"perms"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "사이트 목록 필터(초안)·사용량 요약에 예약 0",
|
|
"ok": true,
|
|
"detail": {
|
|
"drafts": 1,
|
|
"sitesReserved": 0,
|
|
"sitesAllocated": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 5,
|
|
"title": "Webmaker에서 페이지 제작",
|
|
"verdict": "verified_local",
|
|
"note": "SSO 티켓(1회용) 진입·저장본 목록·배포 전 확인(가짜 Webmaker — 실제 편집 UI 는 포함하지 않음)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "제작 화면 열기: 포털 SSO 티켓 → Webmaker /api/auth/sso 302(1회용)",
|
|
"ok": true,
|
|
"detail": {
|
|
"portalRedirect": "http://127.0.0.1:37209/api/auth/sso?ticket=…",
|
|
"webmaker": 302,
|
|
"reuse": 401
|
|
}
|
|
},
|
|
{
|
|
"name": "Webmaker 에서 페이지 저장(저장본 2개) → 포털 저장본 목록·배포 전 확인 통과",
|
|
"ok": true,
|
|
"detail": {
|
|
"versions": 1,
|
|
"precheckOk": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"sso": {
|
|
"portalRedirect": "http://127.0.0.1:37209/api/auth/sso?ticket=…",
|
|
"webmaker": 302,
|
|
"reuse": 401
|
|
},
|
|
"ver": {
|
|
"versions": 1,
|
|
"precheckOk": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 6,
|
|
"title": "관리형 배포",
|
|
"verdict": "verified_local",
|
|
"note": "배포 요청→예약→HAL 작업→Agent(stub) 배포→도메인 설정 필요 상태(대표 도메인 없음)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "배포 요청 → 10단계 작업·사이트 수 예약·같은 요청 재제출은 하나",
|
|
"ok": true,
|
|
"detail": {
|
|
"jobId": "j_01a1789c0ce4dcffc59011e15d",
|
|
"steps": 10,
|
|
"again": false,
|
|
"siteCount": "reserved",
|
|
"server": null
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL·Agent 를 거쳐 작업 완료 대기(최대 40초)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 뒤 사이트 상태·버전·건강",
|
|
"ok": true,
|
|
"detail": {
|
|
"lifecycle": "needs_domain",
|
|
"version": "20261026142736-rev-c1-000000",
|
|
"verify": "needs_domain",
|
|
"health": "ok",
|
|
"agentHasSite": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"d": {
|
|
"jobId": "j_01a1789c0ce4dcffc59011e15d",
|
|
"steps": 10,
|
|
"again": false,
|
|
"siteCount": "reserved",
|
|
"server": null
|
|
},
|
|
"deployJob": {
|
|
"status": "done",
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
},
|
|
"after": {
|
|
"lifecycle": "needs_domain",
|
|
"version": "20261026142736-rev-c1-000000",
|
|
"verify": "needs_domain",
|
|
"health": "ok",
|
|
"agentHasSite": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 7,
|
|
"title": "도메인 연결과 HTTPS 발급",
|
|
"verdict": "partial",
|
|
"note": "도메인 추가·복사 가능한 TXT/DNS 값·외부 대기 상태까지 확인. 실 DNS 반영·인증서 발급·대표 도메인 확정은 로컬 확인 불가",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "도메인 추가(웹·대표 지정) → 소유 확인 TXT 값 안내 → 작업이 외부 대기(TXT)",
|
|
"ok": true,
|
|
"detail": {
|
|
"domainId": "d_01a1789c1cb665247a22f3d74a",
|
|
"txtName": "_ariweb-verify.aw-e2e.localhost",
|
|
"txtValueShape": "ariweb-verify=…",
|
|
"copyable": true
|
|
}
|
|
},
|
|
{
|
|
"name": "도메인 목록·상세: 소유 확인·DNS·인증서 상태 표시",
|
|
"ok": true,
|
|
"detail": {}
|
|
},
|
|
{
|
|
"name": "(정리) 도메인 연결 작업 취소 — 포털이 외부 대기 중에도 5초마다 HAL domain.probe 를 새로 만들어 큐를 채우므로(결함 항목) 뒤 흐름을 위해 멈춘다",
|
|
"ok": true,
|
|
"detail": {
|
|
"queuedProbes": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"dm": {
|
|
"domainId": "d_01a1789c1cb665247a22f3d74a",
|
|
"txtName": "_ariweb-verify.aw-e2e.localhost",
|
|
"txtValueShape": "ariweb-verify=…",
|
|
"copyable": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 8,
|
|
"title": "외부 주소에서 사이트 확인",
|
|
"verdict": "partial",
|
|
"note": "플랫폼 확인 주소(preview.localhost)로 HAL 외부 확인을 시도했으나 로컬에는 80 포트 웹서버가 없어 외부 확인 결과만 기록됨",
|
|
"blockedBy": null,
|
|
"steps": [],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 9,
|
|
"title": "사이트 정보와 접속·사용량 통계 확인",
|
|
"verdict": "partial",
|
|
"note": "개요·사용량 조회(Agent stub 가 수집한 값)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "개요: 배포 전/후에 따라 준비 단계 또는 실제 통계",
|
|
"ok": true,
|
|
"detail": {
|
|
"kind": "managed",
|
|
"lifecycle": "needs_domain",
|
|
"hasStats": false,
|
|
"readiness": null,
|
|
"disk": {
|
|
"used": null,
|
|
"limit": 1073741824
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "사용량 탭(배정·실제 사용·마지막 계측)",
|
|
"ok": true,
|
|
"detail": {
|
|
"allocations": [
|
|
"web_disk",
|
|
"web_out"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ov": {
|
|
"kind": "managed",
|
|
"lifecycle": "needs_domain",
|
|
"hasStats": false,
|
|
"readiness": null,
|
|
"disk": {
|
|
"used": null,
|
|
"limit": 1073741824
|
|
}
|
|
},
|
|
"us": {
|
|
"allocations": [
|
|
"web_disk",
|
|
"web_out"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 10,
|
|
"title": "사이트 대표 메일과 메일함 생성",
|
|
"verdict": "partial",
|
|
"note": "대표 메일 생성(AriMail 테넌트·보장량·ariweb 표시)·메일함(비밀번호 1회·초대)·DNS 미반영 구분·활성화 작업 외부 대기까지 확인. DNS 반영 뒤 활성화 완료는 실 DNS 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "사이트 대표 메일 생성(사이트 연결 도메인·배정) → AriMail 테넌트 준비·도메인별 보장량·ariweb 표시",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "msv_01a1789ca5510f0181ed01486e",
|
|
"status": "needs_setup",
|
|
"tenant": true,
|
|
"ariwebKind": "site_bound",
|
|
"limitAccounts": 6,
|
|
"siteCountAllocated": 0
|
|
}
|
|
},
|
|
{
|
|
"name": "같은 사이트에 두 번째 대표 메일 거부",
|
|
"ok": true,
|
|
"detail": "mail_domain_in_use"
|
|
},
|
|
{
|
|
"name": "메일함 생성: 초기 비밀번호 1회 표시·접속 안내 → 재조회에는 비밀번호 없음",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "mb_01a1789ca58c25f8651b6813b4",
|
|
"address": "kim@aw-e2e.localhost",
|
|
"passwordShownOnce": "[redacted]",
|
|
"mustChange": true,
|
|
"imapHost": "127.0.0.1",
|
|
"leakedOnReload": false,
|
|
"inArimail": true
|
|
}
|
|
},
|
|
{
|
|
"name": "초대 방식 메일함(링크 만료·재발송·사용 완료 상태)",
|
|
"ok": true,
|
|
"detail": {
|
|
"inviteState": "pending",
|
|
"accepted": 200,
|
|
"reuse": "invite_used"
|
|
}
|
|
},
|
|
{
|
|
"name": "DNS 확인: 미반영/잘못된 값 구분 표시(로컬 DNS 에는 레코드 없음 → missing)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": false,
|
|
"missing": 3,
|
|
"items": [
|
|
"mx:missing",
|
|
"spf:missing",
|
|
"dkim:missing",
|
|
"dmarc:recommended"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "활성화 작업 → DNS 외부 대기(awaiting_external) · 멱등",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "awaiting_external",
|
|
"waitReason": "DNS 레코드가 아직 반영되지 않았습니다(누락 3, 잘못된 값 0). 도메인 DNS 에 안내된 값을 넣은 뒤 '이어서 진행' 을 누르세요",
|
|
"sameJob": true,
|
|
"steps": [
|
|
"entitlement:done",
|
|
"dns:waiting",
|
|
"prepare:pending",
|
|
"verify:pending",
|
|
"activate:pending"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "보안 보고서(SPF·DKIM·DMARC·MX 상태 + 복사 값)",
|
|
"ok": true,
|
|
"detail": {
|
|
"items": [
|
|
"sender:error",
|
|
"mx:error",
|
|
"spf:error",
|
|
"dkim:error",
|
|
"dmarc:recommended",
|
|
"tls:error",
|
|
"roundtrip:recommended"
|
|
],
|
|
"records": 4
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"svc": {
|
|
"id": "msv_01a1789ca5510f0181ed01486e",
|
|
"status": "needs_setup",
|
|
"tenant": true,
|
|
"ariwebKind": "site_bound",
|
|
"limitAccounts": 6,
|
|
"siteCountAllocated": 0
|
|
},
|
|
"mb": {
|
|
"id": "mb_01a1789ca58c25f8651b6813b4",
|
|
"address": "kim@aw-e2e.localhost",
|
|
"passwordShownOnce": "[redacted]",
|
|
"mustChange": true,
|
|
"imapHost": "127.0.0.1",
|
|
"leakedOnReload": false,
|
|
"inArimail": true
|
|
},
|
|
"dns": {
|
|
"ok": false,
|
|
"missing": 3,
|
|
"items": [
|
|
"mx:missing",
|
|
"spf:missing",
|
|
"dkim:missing",
|
|
"dmarc:recommended"
|
|
]
|
|
},
|
|
"act": {
|
|
"status": "awaiting_external",
|
|
"waitReason": "DNS 레코드가 아직 반영되지 않았습니다(누락 3, 잘못된 값 0). 도메인 DNS 에 안내된 값을 넣은 뒤 '이어서 진행' 을 누르세요",
|
|
"sameJob": true,
|
|
"steps": [
|
|
"entitlement:done",
|
|
"dns:waiting",
|
|
"prepare:pending",
|
|
"verify:pending",
|
|
"activate:pending"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 11,
|
|
"title": "외부 송신, 수신, 회신 확인",
|
|
"verdict": "not_verifiable_locally",
|
|
"note": "외부 송신·수신·회신은 실제 SMTP 제출(587)·수신함·외부 자동 회신 주소가 필요하다. 포털은 검증 주소 미설정 시 needs_user, 왕복 실패 시 활성화하지 않는 규칙(단위 테스트 test/mail.test.mjs)으로만 확인됨",
|
|
"blockedBy": null,
|
|
"steps": [],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 12,
|
|
"title": "독립 메일 서비스 생성",
|
|
"verdict": "verified_local",
|
|
"note": "독립 메일 서비스 생성: 독립 옵션 1 소비, 운영 사이트 수 미소비, 목록에 백업 미제공 문구",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "메일 전용 도메인 등록(solo-e2e.localhost, purpose mail)",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "d_01a1789cbfd52a398c63de3b05",
|
|
"jobCancelled": null
|
|
}
|
|
},
|
|
{
|
|
"name": "독립 메일 서비스 생성(독립 옵션 소비·사이트 수 미소비)",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "msv_01a1789cbfddf212586ba4b18d",
|
|
"kind": "standalone",
|
|
"mailServicesAllocated": 1,
|
|
"sitesAllocated": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"r": {
|
|
"id": "msv_01a1789cbfddf212586ba4b18d",
|
|
"kind": "standalone",
|
|
"mailServicesAllocated": 1,
|
|
"sitesAllocated": 0
|
|
},
|
|
"listCount": 2,
|
|
"noBackupNote": "메일 원문은 고객용 백업·복원에 포함되지 않으며, 메일 백업 기능을 제공하지 않습니다."
|
|
}
|
|
},
|
|
{
|
|
"no": 13,
|
|
"title": "백업 옵션 구매와 첫 백업 확인",
|
|
"verdict": "verified_local",
|
|
"note": "옵션 구매·활성화·첫 백업(HAL→운영 Agent 생성→저장 Agent 보관→검증)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "백업 옵션 주문(대상 사이트)·입금 확인·승인 → backup 권리",
|
|
"ok": true,
|
|
"detail": {
|
|
"granted": 7,
|
|
"orderStatus": "approved"
|
|
}
|
|
},
|
|
{
|
|
"name": "백업 화면: 구매 가능 여부·저장 한도·일정·보존·첫 백업 기한·검증 방식(상품정책에서)",
|
|
"ok": true,
|
|
"detail": {
|
|
"eligible": true,
|
|
"reasons": [],
|
|
"deferred": [],
|
|
"policy": null,
|
|
"status": 200
|
|
}
|
|
},
|
|
{
|
|
"name": "옵션 활성화(확인 필수) → 첫 백업 예정 시각",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "active",
|
|
"firstDue": "2026-10-27T14:28:21.235Z",
|
|
"nextRun": "2026-10-26T14:28:21.235Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "지금 백업 실행 → HAL backup.create(저장 서버 ≠ 운영 서버) → Agent → 검증",
|
|
"ok": true,
|
|
"detail": {
|
|
"timedOut": false,
|
|
"optionStatus": "active",
|
|
"lastJob": null,
|
|
"siteServer": "srv_01a1789c03fc9ee146184fa07c",
|
|
"backup": {
|
|
"status": "done",
|
|
"tier": "monthly",
|
|
"location": null
|
|
},
|
|
"job": null,
|
|
"optionFailures": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"el": {
|
|
"eligible": true,
|
|
"reasons": [],
|
|
"deferred": [],
|
|
"policy": null,
|
|
"status": 200
|
|
},
|
|
"opt": {
|
|
"status": "active",
|
|
"firstDue": "2026-10-27T14:28:21.235Z",
|
|
"nextRun": "2026-10-26T14:28:21.235Z"
|
|
},
|
|
"run1": {
|
|
"timedOut": false,
|
|
"optionStatus": "active",
|
|
"lastJob": null,
|
|
"siteServer": "srv_01a1789c03fc9ee146184fa07c",
|
|
"backup": {
|
|
"status": "done",
|
|
"tier": "monthly",
|
|
"location": null
|
|
},
|
|
"job": null,
|
|
"optionFailures": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 14,
|
|
"title": "백업 복원과 외부 사이트 확인",
|
|
"verdict": "partial",
|
|
"note": "복원 요청·운영자 최종 확인(불일치 거부·승인 멱등)·단계 진행까지. 외부 사이트 확인은 공인 서버 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "복원 안내(포함 데이터·영향·보호 시점 유지·되돌릴 수 있는 마지막 시각·완료 방식)",
|
|
"ok": true,
|
|
"detail": {
|
|
"backup": {
|
|
"id": "bk_01a1789cd5b1cc3ce60f2f4349",
|
|
"createdAt": "2026-10-26T14:28:21.242Z",
|
|
"scope": {
|
|
"files": true,
|
|
"db": true,
|
|
"uploads": true,
|
|
"config": true,
|
|
"storeServerId": "srv_01a1789c04ba2129a08801860c",
|
|
"verifyMethod": "sha256 + 격리 환경 읽기 확인"
|
|
},
|
|
"sizeBytes": 1896,
|
|
"verifyState": "verified",
|
|
"expiresAt": "2027-01-27T14:28:26.674Z",
|
|
"tier": "monthly"
|
|
},
|
|
"included": [
|
|
"현재 배포본(파일)",
|
|
"데이터베이스",
|
|
"업로드 데이터",
|
|
"복원에 필요한 사이트 설정"
|
|
],
|
|
"excluded": [
|
|
"메일 원문",
|
|
"소스 저장소",
|
|
"Webmaker 프로젝트 이력",
|
|
"외부 배포 데이터",
|
|
"플랫폼 내부 복구 사본"
|
|
],
|
|
"impact": "복원 중 사이트가 일시적으로 이전 상태로 바뀌며, 복원 뒤 생성된 데이터는 보호 시점으로만 되돌릴 수 있습니다",
|
|
"estimatedDowntimeMin": null,
|
|
"cost": 0,
|
|
"currency": "KRW",
|
|
"costDeferred": false,
|
|
"completionMode": "user_confirm",
|
|
"protectPointDays": 7,
|
|
"revertHours": 72,
|
|
"approvalRequired": true,
|
|
"conflicts": [],
|
|
"canRequest": true,
|
|
"deferred": []
|
|
}
|
|
},
|
|
{
|
|
"name": "복원 요청 → 운영자 승인 대기 · 중복 요청 없음",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "rs_01a1789cd82e8d48adf5b09f5a",
|
|
"status": "awaiting_approval",
|
|
"dup": false,
|
|
"finalConfirmation": {
|
|
"group": "E2E 팀",
|
|
"site": "E2E 사이트",
|
|
"domain": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "운영자 최종 확인(그룹·사이트·백업 시각) 불일치 거부 → 일치 승인(멱등)",
|
|
"ok": true,
|
|
"detail": {
|
|
"bad": "final_confirmation_mismatch",
|
|
"ok": 400
|
|
}
|
|
},
|
|
{
|
|
"name": "9단계 작업 진행 → 사용자 확인 필요(보호 시점·되돌리기 가능)",
|
|
"ok": true,
|
|
"detail": {
|
|
"steps": []
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"info": {
|
|
"backup": {
|
|
"id": "bk_01a1789cd5b1cc3ce60f2f4349",
|
|
"createdAt": "2026-10-26T14:28:21.242Z",
|
|
"scope": {
|
|
"files": true,
|
|
"db": true,
|
|
"uploads": true,
|
|
"config": true,
|
|
"storeServerId": "srv_01a1789c04ba2129a08801860c",
|
|
"verifyMethod": "sha256 + 격리 환경 읽기 확인"
|
|
},
|
|
"sizeBytes": 1896,
|
|
"verifyState": "verified",
|
|
"expiresAt": "2027-01-27T14:28:26.674Z",
|
|
"tier": "monthly"
|
|
},
|
|
"included": [
|
|
"현재 배포본(파일)",
|
|
"데이터베이스",
|
|
"업로드 데이터",
|
|
"복원에 필요한 사이트 설정"
|
|
],
|
|
"excluded": [
|
|
"메일 원문",
|
|
"소스 저장소",
|
|
"Webmaker 프로젝트 이력",
|
|
"외부 배포 데이터",
|
|
"플랫폼 내부 복구 사본"
|
|
],
|
|
"impact": "복원 중 사이트가 일시적으로 이전 상태로 바뀌며, 복원 뒤 생성된 데이터는 보호 시점으로만 되돌릴 수 있습니다",
|
|
"estimatedDowntimeMin": null,
|
|
"cost": 0,
|
|
"currency": "KRW",
|
|
"costDeferred": false,
|
|
"completionMode": "user_confirm",
|
|
"protectPointDays": 7,
|
|
"revertHours": 72,
|
|
"approvalRequired": true,
|
|
"conflicts": [],
|
|
"canRequest": true,
|
|
"deferred": []
|
|
},
|
|
"rq": {
|
|
"id": "rs_01a1789cd82e8d48adf5b09f5a",
|
|
"status": "awaiting_approval",
|
|
"dup": false,
|
|
"finalConfirmation": {
|
|
"group": "E2E 팀",
|
|
"site": "E2E 사이트",
|
|
"domain": null
|
|
}
|
|
},
|
|
"ap": {
|
|
"bad": "final_confirmation_mismatch",
|
|
"ok": 400
|
|
},
|
|
"j": {
|
|
"steps": []
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 15,
|
|
"title": "관리형 사이트 서버 이전과 외부 확인",
|
|
"verdict": "partial",
|
|
"note": "이전 경계값·요청 규칙 확인. 실제 두 Agent 간 사전 복사·전환·외부 확인은 배포된 사이트(흐름 6)와 공인 확인 주소가 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "이전 화면 경계값: 취소 가능 마지막 단계·쓰기 제한·목표 중단 시간·되돌림 기간·이전 위치 보존·비용(플랫폼 사유 0원)",
|
|
"ok": true,
|
|
"detail": {
|
|
"auto": {
|
|
"ok": false,
|
|
"reasons": [
|
|
"사이트 대표 메일이 있는 사이트는 승인형으로 진행합니다",
|
|
"진행 중인 배포·복원·이전이 있습니다"
|
|
]
|
|
},
|
|
"boundaries": {
|
|
"lastCancel": "새 위치 확인(연결 전환 직전)까지 취소할 수 있습니다",
|
|
"revertHours": 72,
|
|
"cost": 10000,
|
|
"platformCost": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "고객 요청(유료·승인 대기) 또는 조건 미충족 사유",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "conflict",
|
|
"relocation": null,
|
|
"message": "진행 중인 배포·복원·이전이 끝나야 이전을 시작할 수 있습니다"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"b": {
|
|
"auto": {
|
|
"ok": false,
|
|
"reasons": [
|
|
"사이트 대표 메일이 있는 사이트는 승인형으로 진행합니다",
|
|
"진행 중인 배포·복원·이전이 있습니다"
|
|
]
|
|
},
|
|
"boundaries": {
|
|
"lastCancel": "새 위치 확인(연결 전환 직전)까지 취소할 수 있습니다",
|
|
"revertHours": 72,
|
|
"cost": 10000,
|
|
"platformCost": 0
|
|
}
|
|
},
|
|
"req": {
|
|
"status": 409,
|
|
"error": "conflict",
|
|
"relocation": null,
|
|
"message": "진행 중인 배포·복원·이전이 끝나야 이전을 시작할 수 있습니다"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 16,
|
|
"title": "외부 배포 사이트 생성과 배포",
|
|
"verdict": "verified_local",
|
|
"note": "동의→토큰→실제 외부 Agent 등록·연결→배포→개요(제공되지 않음)→제거",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "외부 배포 사이트 생성(초안) + 저장본",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a1789dc3ada89ee43472fe26",
|
|
"group_id": "g_01a1789c0ba4cc346b9a816d13",
|
|
"name": "E2E 외부 사이트",
|
|
"purpose": "e2e",
|
|
"webmaker_project_id": "00000000000000000000000000000004",
|
|
"template": null,
|
|
"lang": "ko",
|
|
"timezone": "Asia/Seoul",
|
|
"lifecycle": "draft",
|
|
"deploy_mode": "external",
|
|
"primary_domain": null,
|
|
"site_count_state": "none",
|
|
"site_count_reservation_id": null,
|
|
"server_id": null,
|
|
"external_agent_id": null,
|
|
"current_version": null,
|
|
"current_deployment_id": null,
|
|
"deploy_verify": null,
|
|
"health": "unknown",
|
|
"health_checked_at": null,
|
|
"maintenance": 0,
|
|
"search_visible": 1,
|
|
"description": null,
|
|
"settings_json": null,
|
|
"paused_at": null,
|
|
"cancel_at": null,
|
|
"retention_until": null,
|
|
"delete_after": null,
|
|
"deleted_at": null,
|
|
"created_by": "u_01a1789c0afae66fc53b01be8a",
|
|
"created_at": "2026-10-26T14:29:27.597Z",
|
|
"updated_at": "2026-10-26T14:29:27.603Z",
|
|
"runtime": "static",
|
|
"consent_version": null,
|
|
"pending_cleanup_json": null,
|
|
"return_due_at": null,
|
|
"last_deploy_at": null,
|
|
"last_external_check_json": null,
|
|
"next_job_json": null,
|
|
"mail_choice": null,
|
|
"next_notice_at": null,
|
|
"delete_hold_reason": null,
|
|
"public_stopped_at": null,
|
|
"prev_lifecycle": null,
|
|
"settings": {},
|
|
"pendingCleanup": null,
|
|
"lastExternalCheck": null,
|
|
"nextJob": null
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 방식 화면: 외부 대상·고객 책임·미제공 기능(메일 미지원)·건별 비용·동의 버전",
|
|
"ok": true,
|
|
"detail": {
|
|
"notProvided": "[redacted]",
|
|
"consentVersion": "external-deploy-2026-10-0.1.1",
|
|
"agreed": false
|
|
}
|
|
},
|
|
{
|
|
"name": "동의 전 Agent 발급 거부(consent_required) → 동의 기록(버전)",
|
|
"ok": true,
|
|
"detail": {
|
|
"before": "consent_required",
|
|
"consent": 200
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 Agent 일회용 토큰·설치 명령(토큰은 저장하지 않음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"token": "[redacted]",
|
|
"install": "curl -fsSL http://127.0.0.1:40961/agent/v1/install.sh | bash -s -- --hal http://127.0.0.1:40961 --token … --external --site s_01a1789dc3ada89ee43472fe26",
|
|
"expiresAt": "2026-10-26T14:39:27.642Z",
|
|
"status": "pending"
|
|
}
|
|
},
|
|
{
|
|
"name": "고객 서버 역할의 외부 Agent 프로세스 등록·실행(HAL 등록, 사이트 귀속)",
|
|
"ok": true,
|
|
"detail": {
|
|
"agentId": "[redacted]"
|
|
}
|
|
},
|
|
{
|
|
"name": "포털 연결 확인(외부 Agent 상태 connected)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "connected",
|
|
"stale": false,
|
|
"halAgentId": "[redacted]"
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 전 확인(외부: 관리형 용량 확인 없음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": true,
|
|
"blocks": []
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 배포 → 외부 Agent 가 site.deploy 수행",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 사이트 개요: 관리형 지표 없음·제공되지 않음 표기·Agent 상태·마지막 배포 결과",
|
|
"ok": true,
|
|
"detail": {
|
|
"kind": "external",
|
|
"notProvided": "[redacted]",
|
|
"agent": "connected",
|
|
"lastDeployOk": true
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 제거(외부 서버 파일·실행 상태 제거, 실패 잔여 정리)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null
|
|
}
|
|
},
|
|
{
|
|
"name": "Agent 연결 해제(revoke) → 상태 없음",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"agent": null
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"mode": {
|
|
"notProvided": "[redacted]",
|
|
"consentVersion": "external-deploy-2026-10-0.1.1",
|
|
"agreed": false
|
|
},
|
|
"tok": {
|
|
"install": "curl -fsSL http://127.0.0.1:40961/agent/v1/install.sh | bash -s -- --hal http://127.0.0.1:40961 --token … --external --site s_01a1789dc3ada89ee43472fe26",
|
|
"expiresAt": "2026-10-26T14:39:27.642Z"
|
|
},
|
|
"conn": {
|
|
"status": "connected",
|
|
"stale": false,
|
|
"halAgentId": "[redacted]"
|
|
},
|
|
"pc": {
|
|
"ok": true,
|
|
"blocks": []
|
|
},
|
|
"dep": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
},
|
|
"ov": {
|
|
"kind": "external",
|
|
"notProvided": "[redacted]",
|
|
"agent": "connected",
|
|
"lastDeployOk": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 17,
|
|
"title": "사용량 경고와 한도 동작 확인",
|
|
"verdict": "verified_local",
|
|
"note": "메일 저장 공간 95%·월간 통수 100% 를 수집해 구간·제한 사유·KST 초기화·알림을 확인(웹 디스크/아웃바운드는 배포가 있어야 수집)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "가짜 AriMail 사용량 95%/월간 100% → 수집(refresh) → 구간·제한 사유·다음 KST 초기화·마지막 계측 표시",
|
|
"ok": true,
|
|
"detail": {
|
|
"storageLevel": 95,
|
|
"monthlyLevel": 100,
|
|
"nextReset": "2026-10-31T15:00:00.000Z",
|
|
"measuredAt": true,
|
|
"limit100": "월간 통수 100%: 새 발신을 막고 새 수신은 임시 실패로 응답합니다. 다음 KST 월 시작 또는 추가 용량 적용 후 복구됩니다.",
|
|
"countingRule": "per_final_recipient",
|
|
"evaluate": {
|
|
"status": 200,
|
|
"notified": 2,
|
|
"limited": 1
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "알림 센터: 95%·100% 경고가 사건으로 묶여 표시(필수 알림)",
|
|
"ok": true,
|
|
"detail": {
|
|
"count": 3,
|
|
"titles": [
|
|
"aw-e2e.localhost 메일 저장 공간 96% — 곧 제한됩니다(최종 경고)",
|
|
"aw-e2e.localhost 월간 메일 통수(송신+수신) 100% — 제한 적용",
|
|
"메일함 kim@aw-e2e.localhost 저장 공간 95% 경고"
|
|
],
|
|
"mandatory": true
|
|
}
|
|
},
|
|
{
|
|
"name": "사용량 화면 제한 플래그(/api/usage/limits)·그룹 요약",
|
|
"ok": true,
|
|
"detail": {
|
|
"limits": [
|
|
"mail_msgs:100",
|
|
"mail_storage:95"
|
|
],
|
|
"warnLevels": [
|
|
70,
|
|
80,
|
|
90,
|
|
95
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"warn": {
|
|
"storageLevel": 95,
|
|
"monthlyLevel": 100,
|
|
"nextReset": "2026-10-31T15:00:00.000Z",
|
|
"measuredAt": true,
|
|
"limit100": "월간 통수 100%: 새 발신을 막고 새 수신은 임시 실패로 응답합니다. 다음 KST 월 시작 또는 추가 용량 적용 후 복구됩니다.",
|
|
"countingRule": "per_final_recipient",
|
|
"evaluate": {
|
|
"status": 200,
|
|
"notified": 2,
|
|
"limited": 1
|
|
}
|
|
},
|
|
"notif": {
|
|
"count": 3,
|
|
"titles": [
|
|
"aw-e2e.localhost 메일 저장 공간 96% — 곧 제한됩니다(최종 경고)",
|
|
"aw-e2e.localhost 월간 메일 통수(송신+수신) 100% — 제한 적용",
|
|
"메일함 kim@aw-e2e.localhost 저장 공간 95% 경고"
|
|
],
|
|
"mandatory": true
|
|
},
|
|
"lim": {
|
|
"limits": [
|
|
"mail_msgs:100",
|
|
"mail_storage:95"
|
|
],
|
|
"warnLevels": [
|
|
70,
|
|
80,
|
|
90,
|
|
95
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 18,
|
|
"title": "상품 축소 보류와 정리 후 적용",
|
|
"verdict": "verified_local",
|
|
"note": "축소 미리보기→보류→정리(배정 축소·독립 메일 해지)→재검증 적합→축소 주문·승인→권리 교체",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "배정 화면 수치(구매·내 배정·다른 서비스·예약·미배정·변경 후 최대) + 마지막 단위 동시 요청",
|
|
"ok": true,
|
|
"detail": {
|
|
"purchased": 45,
|
|
"mine": 6,
|
|
"others": 2,
|
|
"unallocated": 37,
|
|
"race": [
|
|
409,
|
|
409
|
|
],
|
|
"mineAfter": 6
|
|
}
|
|
},
|
|
{
|
|
"name": "축소 미리보기(팀→베이직): 줄여야 할 항목(메일함 6→5) 구체 표시",
|
|
"ok": true,
|
|
"detail": {
|
|
"shortages": [
|
|
"mailboxes:1"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "축소 요청 → 보류(사용 중 권리 즉시 회수 없음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"fits": false,
|
|
"hold": "holding",
|
|
"sitesStillPurchased": 3
|
|
}
|
|
},
|
|
{
|
|
"name": "정리: 메일함 배정 6→5 축소 → 재검증 적합 → 축소 주문 생성",
|
|
"ok": true,
|
|
"detail": {
|
|
"allocate": 200,
|
|
"fits": true,
|
|
"shortages": [],
|
|
"order": "awaiting_payment"
|
|
}
|
|
},
|
|
{
|
|
"name": "축소 주문 입금 확인·승인 → 권리 교체(베이직)",
|
|
"ok": true,
|
|
"detail": {
|
|
"approve": 200,
|
|
"sites": 1,
|
|
"mailboxes": 5,
|
|
"product": "pv_01a1789c0a2573b629e03f3013"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"alloc": {
|
|
"purchased": 45,
|
|
"mine": 6,
|
|
"others": 2,
|
|
"unallocated": 37,
|
|
"race": [
|
|
409,
|
|
409
|
|
],
|
|
"mineAfter": 6
|
|
},
|
|
"pv": {
|
|
"shortages": [
|
|
"mailboxes:1"
|
|
]
|
|
},
|
|
"hold": {
|
|
"fits": false,
|
|
"hold": "holding",
|
|
"sitesStillPurchased": 3
|
|
},
|
|
"clean": {
|
|
"allocate": 200,
|
|
"fits": true,
|
|
"shortages": [],
|
|
"order": "awaiting_payment"
|
|
},
|
|
"applied": {
|
|
"approve": 200,
|
|
"sites": 1,
|
|
"mailboxes": 5,
|
|
"product": "pv_01a1789c0a2573b629e03f3013"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 19,
|
|
"title": "사이트 수 반환과 대표 메일의 독립 전환",
|
|
"verdict": "partial",
|
|
"note": "반환 점검표와 메일 유지/종료 분기 규칙 확인. 실제 반환은 운영 사이트(사이트 수 사용 중) 필요(단위 테스트 test/lifecycle.test.mjs·test/mail.test.mjs 로 확인)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "§14 반환 점검표(8단계)·차단 사유(대표 메일 유지 선택·독립 옵션)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": false,
|
|
"blocking": [
|
|
"public_stopped",
|
|
"domains_unrouted",
|
|
"no_jobs",
|
|
"mail_choice",
|
|
"mail_converted"
|
|
],
|
|
"steps": 8
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 유지 선택: 독립 옵션 부족이면 반환하지 않음 / 종료 선택 규칙",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "site_public",
|
|
"detail": null
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ck": {
|
|
"ok": false,
|
|
"blocking": [
|
|
"public_stopped",
|
|
"domains_unrouted",
|
|
"no_jobs",
|
|
"mail_choice",
|
|
"mail_converted"
|
|
],
|
|
"steps": 8
|
|
},
|
|
"keep": {
|
|
"status": 409,
|
|
"error": "site_public",
|
|
"detail": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 20,
|
|
"title": "사이트 소유권 이전",
|
|
"verdict": "verified_local",
|
|
"note": "미리보기→요청·알림→수락(보류·재확인)→심사·최종 확인→원자적 완료→접근 전환·감사",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "이전 범위 미리보기(사이트·도메인·대표 메일·배정·진행 작업·미납 / 제외 항목)",
|
|
"ok": true,
|
|
"detail": {
|
|
"sites": 1,
|
|
"domains": 1,
|
|
"siteMail": 1,
|
|
"needed": {
|
|
"backup": 1,
|
|
"web_disk": 1073741824,
|
|
"mail_msgs": 500,
|
|
"mail_storage": 2147483648,
|
|
"mailboxes": 6
|
|
},
|
|
"excluded": 3
|
|
}
|
|
},
|
|
{
|
|
"name": "요청(보내는 그룹, 초안 사이트) → 받는 사용자 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "tr_01a1789de6a46d148f37fda029",
|
|
"status": "requested",
|
|
"expiresAt": "2026-11-02T14:29:36.548Z",
|
|
"bobNotified": true
|
|
}
|
|
},
|
|
{
|
|
"name": "(정리) 받는 쪽 권리에 맞게 범위 조정: 백업 옵션 종료·메일함 배정 6→5(받는 그룹 베이직 5개)",
|
|
"ok": true,
|
|
"detail": {
|
|
"endBackup": 200,
|
|
"alloc": 200
|
|
}
|
|
},
|
|
{
|
|
"name": "받는 그룹 수락 → 권리 부족이면 보류(필요한 상품 안내)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"tstatus": "reviewing",
|
|
"hold": null
|
|
}
|
|
},
|
|
{
|
|
"name": "운영자 심사(소유 증빙·도메인 확인) 미입력 거부 → 최종 확인 일치 승인 → 원자적 완료·청구 전환 시각",
|
|
"ok": true,
|
|
"detail": {
|
|
"bad": "final_confirmation_mismatch",
|
|
"ok": 200,
|
|
"status": "completed",
|
|
"cutover": "2026-10-26T14:29:36.598Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "완료 뒤: 보내는 쪽은 접근 불가(404), 받는 쪽 대시보드·사용량 일치, 불변 감사",
|
|
"ok": true,
|
|
"detail": {
|
|
"aliceSees": 404,
|
|
"bobSees": 200,
|
|
"bobUsageSites": 0,
|
|
"audit": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"pv": {
|
|
"sites": 1,
|
|
"domains": 1,
|
|
"siteMail": 1,
|
|
"needed": {
|
|
"backup": 1,
|
|
"web_disk": 1073741824,
|
|
"mail_msgs": 500,
|
|
"mail_storage": 2147483648,
|
|
"mailboxes": 6
|
|
},
|
|
"excluded": 3
|
|
},
|
|
"rq": {
|
|
"id": "tr_01a1789de6a46d148f37fda029",
|
|
"status": "requested",
|
|
"expiresAt": "2026-11-02T14:29:36.548Z",
|
|
"bobNotified": true
|
|
},
|
|
"acc": {
|
|
"status": 200,
|
|
"tstatus": "reviewing",
|
|
"hold": null
|
|
},
|
|
"ap": {
|
|
"bad": "final_confirmation_mismatch",
|
|
"ok": 200,
|
|
"status": "completed",
|
|
"cutover": "2026-10-26T14:29:36.598Z"
|
|
},
|
|
"after": {
|
|
"aliceSees": 404,
|
|
"bobSees": 200,
|
|
"bobUsageSites": 0,
|
|
"audit": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 21,
|
|
"title": "중지, 해지, 보존 예정일 표시",
|
|
"verdict": "partial",
|
|
"note": "중지·재개·생명주기 화면 확인",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "§23.3 한 화면 보기: 공개/관리자 접근·사이트 수·청구·도메인/메일/백업 동작·내보내기·되돌릴 수 없는 시점·보존·다음 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"state": "도메인 설정 필요",
|
|
"siteCount": true,
|
|
"publicAccess": "제공 중",
|
|
"retention": {
|
|
"site_days": 30,
|
|
"mail_days": 30,
|
|
"source_days": 30,
|
|
"backup_days": 7,
|
|
"delete_grace_days": 1,
|
|
"notice_before_days": 3,
|
|
"readonly_retention_days": 30,
|
|
"delete_after_days": 30,
|
|
"pause_keeps_site_count": true,
|
|
"cancel_effective": "next_billing_date",
|
|
"source": "상품정책 베이직 (retention 정책) v1"
|
|
},
|
|
"pauseImpactKeys": [
|
|
"publicAccess",
|
|
"adminAccess",
|
|
"mail",
|
|
"backups",
|
|
"usageAndCost",
|
|
"resume",
|
|
"siteCount"
|
|
],
|
|
"cancelImpactKeys": [
|
|
"target",
|
|
"linked",
|
|
"immediateStops",
|
|
"remainingData",
|
|
"retentionUntil",
|
|
"deleteAfter",
|
|
"retentionSource",
|
|
"export",
|
|
"unpaid",
|
|
"refund",
|
|
"pointOfNoReturn",
|
|
"mailOptions",
|
|
"siteCount"
|
|
],
|
|
"steps14": 8
|
|
}
|
|
},
|
|
{
|
|
"name": "일시 중지(확인 필수) 또는 현재 상태에서 거부 사유",
|
|
"ok": true,
|
|
"detail": {
|
|
"noConfirm": "confirmation_required",
|
|
"status": 409,
|
|
"error": "conflict"
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 서비스 생명주기 화면(해지된 독립 메일: 송수신 중지·보존 종료·삭제 예정·백업 미제공)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"v": {
|
|
"state": "도메인 설정 필요",
|
|
"siteCount": true,
|
|
"publicAccess": "제공 중",
|
|
"retention": {
|
|
"site_days": 30,
|
|
"mail_days": 30,
|
|
"source_days": 30,
|
|
"backup_days": 7,
|
|
"delete_grace_days": 1,
|
|
"notice_before_days": 3,
|
|
"readonly_retention_days": 30,
|
|
"delete_after_days": 30,
|
|
"pause_keeps_site_count": true,
|
|
"cancel_effective": "next_billing_date",
|
|
"source": "상품정책 베이직 (retention 정책) v1"
|
|
},
|
|
"pauseImpactKeys": [
|
|
"publicAccess",
|
|
"adminAccess",
|
|
"mail",
|
|
"backups",
|
|
"usageAndCost",
|
|
"resume",
|
|
"siteCount"
|
|
],
|
|
"cancelImpactKeys": [
|
|
"target",
|
|
"linked",
|
|
"immediateStops",
|
|
"remainingData",
|
|
"retentionUntil",
|
|
"deleteAfter",
|
|
"retentionSource",
|
|
"export",
|
|
"unpaid",
|
|
"refund",
|
|
"pointOfNoReturn",
|
|
"mailOptions",
|
|
"siteCount"
|
|
],
|
|
"steps14": 8
|
|
},
|
|
"pause": {
|
|
"noConfirm": "confirmation_required",
|
|
"status": 409,
|
|
"error": "conflict"
|
|
},
|
|
"mailLc": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 22,
|
|
"title": "HAL 우선 배포 후 HAL을 통한 전체 모듈 배포",
|
|
"verdict": "verified_local",
|
|
"note": "HAL 우선 순서·릴리스 승인·모듈 설치",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "릴리스 화면: 버전·main 커밋·승인 상태·HAL 실행 버전·부트스트랩(최초 Agent)·배포 순서",
|
|
"ok": true,
|
|
"detail": {
|
|
"releases": [
|
|
"0.1.2:approved",
|
|
"0.1.1:approved"
|
|
],
|
|
"running": {
|
|
"complete": true,
|
|
"firstInstall": false,
|
|
"releaseVersion": "0.1.1",
|
|
"mainCommit": "[redacted]",
|
|
"artifactSha256": "[redacted]",
|
|
"halRunningVersion": "0.1.1",
|
|
"firstAgent": "[redacted]",
|
|
"manualInstallLocked": true
|
|
},
|
|
"order": [
|
|
"hal",
|
|
"agent",
|
|
"portal",
|
|
"webmaker",
|
|
"arimail",
|
|
"backup",
|
|
"observability"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL 이 아직 0.1.2 가 아닐 때 다른 모듈 0.1.2 배포 → hal_first 거부",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "failed",
|
|
"error": "hal_first"
|
|
}
|
|
},
|
|
{
|
|
"name": "승인되지 않은 릴리스는 거부 / 수동 설치 잠김 표시",
|
|
"ok": true,
|
|
"detail": {
|
|
"manualInstallLocked": true,
|
|
"complete": true
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL 모듈 0.1.1 을 제어 서버에 배포(실제 Agent module.install: artifact 내려받기·sha256·releases/current·유닛(stub)·실제 버전 확인)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"steps": [
|
|
"precheck:done",
|
|
"fanout:done",
|
|
"collect:done"
|
|
],
|
|
"error": null,
|
|
"children": [
|
|
"done:"
|
|
],
|
|
"agentJobs": [],
|
|
"timedOut": false
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"rel": {
|
|
"releases": [
|
|
"0.1.2:approved",
|
|
"0.1.1:approved"
|
|
],
|
|
"running": {
|
|
"complete": true,
|
|
"firstInstall": false,
|
|
"releaseVersion": "0.1.1",
|
|
"mainCommit": "[redacted]",
|
|
"artifactSha256": "[redacted]",
|
|
"halRunningVersion": "0.1.1",
|
|
"firstAgent": "[redacted]",
|
|
"manualInstallLocked": true
|
|
},
|
|
"order": [
|
|
"hal",
|
|
"agent",
|
|
"portal",
|
|
"webmaker",
|
|
"arimail",
|
|
"backup",
|
|
"observability"
|
|
]
|
|
},
|
|
"hf": {
|
|
"status": "failed",
|
|
"error": "hal_first"
|
|
},
|
|
"na": {
|
|
"manualInstallLocked": true,
|
|
"complete": true
|
|
},
|
|
"dep": {
|
|
"status": "done",
|
|
"steps": [
|
|
"precheck:done",
|
|
"fanout:done",
|
|
"collect:done"
|
|
],
|
|
"error": null,
|
|
"children": [
|
|
"done:"
|
|
],
|
|
"agentJobs": [],
|
|
"timedOut": false
|
|
},
|
|
"diag": null
|
|
}
|
|
},
|
|
{
|
|
"no": 23,
|
|
"title": "실패 작업의 재시도와 되돌리기",
|
|
"verdict": "partial",
|
|
"note": "실패 기록·재시도 API·되돌리기 거부/허용 조건 확인. 성공한 재배포 뒤 실제 되돌리기는 배포 성공이 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "배포 이력: 실패/성공 기록 · 재시도 가능 여부 · 문의용 작업 번호",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"canRetry": false,
|
|
"jobId": "j_01a1789c0ce4dcffc59011e15d"
|
|
}
|
|
},
|
|
{
|
|
"name": "재시도 요청(POST /api/sites/:id/jobs/:jobId/retry)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 400,
|
|
"error": "job_not_retryable"
|
|
}
|
|
},
|
|
{
|
|
"name": "되돌리기 요청(POST /api/sites/:id/rollback) — 이전 버전 없으면 거부",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "no_previous",
|
|
"message": "되돌릴 이전 버전이 없습니다"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"dep": {
|
|
"status": "done",
|
|
"canRetry": false,
|
|
"jobId": "j_01a1789c0ce4dcffc59011e15d"
|
|
},
|
|
"rt": {
|
|
"status": 400,
|
|
"error": "job_not_retryable"
|
|
},
|
|
"rb": {
|
|
"status": 409,
|
|
"error": "no_previous",
|
|
"message": "되돌릴 이전 버전이 없습니다"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 24,
|
|
"title": "운영자 승인, 감사 기록, 고객 알림의 일치",
|
|
"verdict": "verified_local",
|
|
"note": "승인 → 고객 알림·불변 감사 기록·운영자/고객 화면 상태 일치(주문 기준)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "고객 알림 센터에 승인 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"title": "주문 AW-20261026-0001 승인 — 권리가 활성화되었습니다",
|
|
"category": "billing",
|
|
"link": "/orders/o_01a1789c0c18393dba2213c118"
|
|
}
|
|
},
|
|
{
|
|
"name": "감사 기록: order.approve 는 삭제 불가(immutable)",
|
|
"ok": true,
|
|
"detail": {
|
|
"action": "order.approve",
|
|
"immutable": true
|
|
}
|
|
},
|
|
{
|
|
"name": "운영자 화면과 고객 주문 화면의 상태 일치",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "approved",
|
|
"approvedAt": "2026-10-26T14:27:35.130Z"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"n": {
|
|
"title": "주문 AW-20261026-0001 승인 — 권리가 활성화되었습니다",
|
|
"category": "billing",
|
|
"link": "/orders/o_01a1789c0c18393dba2213c118"
|
|
},
|
|
"a": {
|
|
"action": "order.approve",
|
|
"immutable": true
|
|
},
|
|
"same": {
|
|
"status": "approved",
|
|
"approvedAt": "2026-10-26T14:27:35.130Z"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 25,
|
|
"title": "HAL, 로그인, 비밀정보 관리, 인증서 기반의 재해복구 훈련",
|
|
"verdict": "partial",
|
|
"note": "DR 설정(RTO/RPO/주기/보존)·사본 생성·검증·외부 보관·복원 승인/기존 제어 중지 확인/완료 항목·훈련 실패/성공→준비 완료 기록까지 확인. 실제 다른 호스트 복원은 실 서버 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "설정 전 준비 상태(미설정 항목)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ready": false,
|
|
"missingConfig": [
|
|
"rto_minutes",
|
|
"rpo_minutes",
|
|
"snapshot_interval_minutes",
|
|
"retention_count"
|
|
],
|
|
"lastDrill": null,
|
|
"lastSnapshot": null,
|
|
"config": {
|
|
"rto_minutes": null,
|
|
"rpo_minutes": null,
|
|
"snapshot_interval_minutes": null,
|
|
"retention_count": null,
|
|
"external_store": null
|
|
},
|
|
"label": "정식 운영 준비 전(복구 훈련 또는 설정 필요)"
|
|
}
|
|
},
|
|
{
|
|
"name": "RTO·RPO·사본 주기·보존 개수·외부 보관 위치 설정",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": 1,
|
|
"rto_minutes": 60,
|
|
"rpo_minutes": 30,
|
|
"snapshot_interval_minutes": 60,
|
|
"retention_count": 3,
|
|
"external_store": "/home/master/work/Ariweb-suite/test/.tmp/e2e-20261026142730/dr-external",
|
|
"updated_at": "2026-10-26T14:29:46.345Z",
|
|
"updated_by": "op_01a1789c02fd72d51f469e4d25"
|
|
}
|
|
},
|
|
{
|
|
"name": "복구 사본 생성·검증·외부 보관(vault.key 제외)",
|
|
"ok": true,
|
|
"detail": {
|
|
"verify": "ok",
|
|
"external": true,
|
|
"sha": true
|
|
}
|
|
},
|
|
{
|
|
"name": "복원 요청 → 승인 전 시작 거부 → 승인 → 기존 제어 중지 확인 없이는 거부 → 시작(명령 안내) → 확인 항목 부족 거부 → 완료",
|
|
"ok": true,
|
|
"detail": {
|
|
"early": "restore_not_approved",
|
|
"noStop": "old_control_not_confirmed",
|
|
"command": true,
|
|
"incomplete": "checks_incomplete",
|
|
"final": "completed"
|
|
}
|
|
},
|
|
{
|
|
"name": "복구 훈련: 항목 하나 실패 → failed, 모두 성공·측정 RTO/RPO → 준비 완료",
|
|
"ok": true,
|
|
"detail": {
|
|
"first": "failed",
|
|
"second": "success",
|
|
"ready": true,
|
|
"measured": {}
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"r0": {
|
|
"ready": false,
|
|
"missingConfig": [
|
|
"rto_minutes",
|
|
"rpo_minutes",
|
|
"snapshot_interval_minutes",
|
|
"retention_count"
|
|
],
|
|
"lastDrill": null,
|
|
"lastSnapshot": null,
|
|
"config": {
|
|
"rto_minutes": null,
|
|
"rpo_minutes": null,
|
|
"snapshot_interval_minutes": null,
|
|
"retention_count": null,
|
|
"external_store": null
|
|
},
|
|
"label": "정식 운영 준비 전(복구 훈련 또는 설정 필요)"
|
|
},
|
|
"cfg": {
|
|
"id": 1,
|
|
"rto_minutes": 60,
|
|
"rpo_minutes": 30,
|
|
"snapshot_interval_minutes": 60,
|
|
"retention_count": 3,
|
|
"external_store": "/home/master/work/Ariweb-suite/test/.tmp/e2e-20261026142730/dr-external",
|
|
"updated_at": "2026-10-26T14:29:46.345Z",
|
|
"updated_by": "op_01a1789c02fd72d51f469e4d25"
|
|
},
|
|
"snap": {
|
|
"verify": "ok",
|
|
"external": true,
|
|
"sha": true
|
|
},
|
|
"rs": {
|
|
"early": "restore_not_approved",
|
|
"noStop": "old_control_not_confirmed",
|
|
"command": true,
|
|
"incomplete": "checks_incomplete",
|
|
"final": "completed"
|
|
},
|
|
"drill": {
|
|
"first": "failed",
|
|
"second": "success",
|
|
"ready": true,
|
|
"measured": {}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 26,
|
|
"title": "새로 고침, 재로그인, HAL 재시작 후 진행 작업 상태 유지",
|
|
"verdict": "verified_local",
|
|
"note": "포털·HAL 재시작 뒤 세션·진행 작업(외부 대기)·작업 목록 유지 확인. 진행 중(running) 배포의 단계 재개는 배포 성공(흐름 6) 뒤 확인 필요",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "재시작 전: 포털 진행 작업(메일 활성화 외부 대기)·HAL 작업 목록",
|
|
"ok": true,
|
|
"detail": {
|
|
"mailJob": {
|
|
"id": "j_01a1789cb9c0e6f752db2a47b6",
|
|
"status": "awaiting_external"
|
|
},
|
|
"halJobs": 18
|
|
}
|
|
},
|
|
{
|
|
"name": "포털 재시작 → 재로그인 없이 세션 유지·같은 작업 id·상태 유지",
|
|
"ok": true,
|
|
"detail": {
|
|
"sessionKept": "[redacted]",
|
|
"mailJob": {
|
|
"id": "j_01a1789cb9c0e6f752db2a47b6",
|
|
"status": "awaiting_external"
|
|
},
|
|
"same": true
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL 재시작 → 운영자 세션·작업 목록·서버 등록 유지",
|
|
"ok": true,
|
|
"detail": {
|
|
"opsSessionKept": "[redacted]",
|
|
"halJobs": 18,
|
|
"sameCount": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"before": {
|
|
"mailJob": {
|
|
"id": "j_01a1789cb9c0e6f752db2a47b6",
|
|
"status": "awaiting_external"
|
|
},
|
|
"halJobs": 18
|
|
},
|
|
"pr": {
|
|
"sessionKept": "[redacted]",
|
|
"mailJob": {
|
|
"id": "j_01a1789cb9c0e6f752db2a47b6",
|
|
"status": "awaiting_external"
|
|
},
|
|
"same": true
|
|
},
|
|
"hr": {
|
|
"opsSessionKept": "[redacted]",
|
|
"halJobs": 18,
|
|
"sameCount": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 27,
|
|
"title": "마지막 사이트 수·용량·메일함 동시 요청 시 하나만 성공",
|
|
"verdict": "verified_local",
|
|
"note": "마지막 사이트 수 1개를 두 배포가 동시에 요청 → 하나만 예약 성공, 다른 쪽은 부족 사유(§13.3)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "밥 개인 그룹: 베이직(사이트 1) 주문·승인",
|
|
"ok": true,
|
|
"detail": {
|
|
"granted": 6
|
|
}
|
|
},
|
|
{
|
|
"name": "사이트 2개 생성·저장본",
|
|
"ok": true,
|
|
"detail": [
|
|
{
|
|
"id": "s_01a1789c994d3878d056f3d2fc",
|
|
"group_id": "g_01a1789c0bd144ad600dd3bc46",
|
|
"name": "동시 A",
|
|
"purpose": "e2e",
|
|
"webmaker_project_id": "00000000000000000000000000000002",
|
|
"template": null,
|
|
"lang": "ko",
|
|
"timezone": "Asia/Seoul",
|
|
"lifecycle": "draft",
|
|
"deploy_mode": "managed",
|
|
"primary_domain": null,
|
|
"site_count_state": "none",
|
|
"site_count_reservation_id": null,
|
|
"server_id": null,
|
|
"external_agent_id": null,
|
|
"current_version": null,
|
|
"current_deployment_id": null,
|
|
"deploy_verify": null,
|
|
"health": "unknown",
|
|
"health_checked_at": null,
|
|
"maintenance": 0,
|
|
"search_visible": 1,
|
|
"description": null,
|
|
"settings_json": null,
|
|
"paused_at": null,
|
|
"cancel_at": null,
|
|
"retention_until": null,
|
|
"delete_after": null,
|
|
"deleted_at": null,
|
|
"created_by": "u_01a1789c0bd1e6e269d0df3589",
|
|
"created_at": "2026-10-26T14:28:11.213Z",
|
|
"updated_at": "2026-10-26T14:28:11.225Z",
|
|
"runtime": "static",
|
|
"consent_version": null,
|
|
"pending_cleanup_json": null,
|
|
"return_due_at": null,
|
|
"last_deploy_at": null,
|
|
"last_external_check_json": null,
|
|
"next_job_json": null,
|
|
"mail_choice": null,
|
|
"next_notice_at": null,
|
|
"delete_hold_reason": null,
|
|
"public_stopped_at": null,
|
|
"prev_lifecycle": null,
|
|
"settings": {},
|
|
"pendingCleanup": null,
|
|
"lastExternalCheck": null,
|
|
"nextJob": null
|
|
},
|
|
{
|
|
"id": "s_01a1789c9951c330ce1ad58f13",
|
|
"group_id": "g_01a1789c0bd144ad600dd3bc46",
|
|
"name": "동시 B",
|
|
"purpose": "e2e",
|
|
"webmaker_project_id": "00000000000000000000000000000003",
|
|
"template": null,
|
|
"lang": "ko",
|
|
"timezone": "Asia/Seoul",
|
|
"lifecycle": "draft",
|
|
"deploy_mode": "managed",
|
|
"primary_domain": null,
|
|
"site_count_state": "none",
|
|
"site_count_reservation_id": null,
|
|
"server_id": null,
|
|
"external_agent_id": null,
|
|
"current_version": null,
|
|
"current_deployment_id": null,
|
|
"deploy_verify": null,
|
|
"health": "unknown",
|
|
"health_checked_at": null,
|
|
"maintenance": 0,
|
|
"search_visible": 1,
|
|
"description": null,
|
|
"settings_json": null,
|
|
"paused_at": null,
|
|
"cancel_at": null,
|
|
"retention_until": null,
|
|
"delete_after": null,
|
|
"deleted_at": null,
|
|
"created_by": "u_01a1789c0bd1e6e269d0df3589",
|
|
"created_at": "2026-10-26T14:28:11.217Z",
|
|
"updated_at": "2026-10-26T14:28:11.228Z",
|
|
"runtime": "static",
|
|
"consent_version": null,
|
|
"pending_cleanup_json": null,
|
|
"return_due_at": null,
|
|
"last_deploy_at": null,
|
|
"last_external_check_json": null,
|
|
"next_job_json": null,
|
|
"mail_choice": null,
|
|
"next_notice_at": null,
|
|
"delete_hold_reason": null,
|
|
"public_stopped_at": null,
|
|
"prev_lifecycle": null,
|
|
"settings": {},
|
|
"pendingCleanup": null,
|
|
"lastExternalCheck": null,
|
|
"nextJob": null
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "두 사이트 동시 최초 배포 요청 → 하나만 200, 다른 쪽 409 precheck_failed(sites 부족)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": 1,
|
|
"blocked": "precheck_failed",
|
|
"shortage": "block"
|
|
}
|
|
},
|
|
{
|
|
"name": "(정리) 선행 결함으로 영원히 진행 중인 배포 취소·예약 반환",
|
|
"ok": true,
|
|
"detail": {
|
|
"cancel": 200,
|
|
"cleanup": 200,
|
|
"unallocated": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"race": {
|
|
"ok": 1,
|
|
"blocked": "precheck_failed",
|
|
"shortage": "block"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 28,
|
|
"title": "외부 Linux 서버 Agent 연결·배포·재배포·제거·실패 정리",
|
|
"verdict": "verified_local",
|
|
"note": "외부 Agent 연결·제거·해제 API 확인. 재배포·실패 정리는 배포 성공이 필요(증거 참조)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "외부 배포 사이트 생성(초안) + 저장본",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a1789dc3ada89ee43472fe26",
|
|
"group_id": "g_01a1789c0ba4cc346b9a816d13",
|
|
"name": "E2E 외부 사이트",
|
|
"purpose": "e2e",
|
|
"webmaker_project_id": "00000000000000000000000000000004",
|
|
"template": null,
|
|
"lang": "ko",
|
|
"timezone": "Asia/Seoul",
|
|
"lifecycle": "draft",
|
|
"deploy_mode": "external",
|
|
"primary_domain": null,
|
|
"site_count_state": "none",
|
|
"site_count_reservation_id": null,
|
|
"server_id": null,
|
|
"external_agent_id": null,
|
|
"current_version": null,
|
|
"current_deployment_id": null,
|
|
"deploy_verify": null,
|
|
"health": "unknown",
|
|
"health_checked_at": null,
|
|
"maintenance": 0,
|
|
"search_visible": 1,
|
|
"description": null,
|
|
"settings_json": null,
|
|
"paused_at": null,
|
|
"cancel_at": null,
|
|
"retention_until": null,
|
|
"delete_after": null,
|
|
"deleted_at": null,
|
|
"created_by": "u_01a1789c0afae66fc53b01be8a",
|
|
"created_at": "2026-10-26T14:29:27.597Z",
|
|
"updated_at": "2026-10-26T14:29:27.603Z",
|
|
"runtime": "static",
|
|
"consent_version": null,
|
|
"pending_cleanup_json": null,
|
|
"return_due_at": null,
|
|
"last_deploy_at": null,
|
|
"last_external_check_json": null,
|
|
"next_job_json": null,
|
|
"mail_choice": null,
|
|
"next_notice_at": null,
|
|
"delete_hold_reason": null,
|
|
"public_stopped_at": null,
|
|
"prev_lifecycle": null,
|
|
"settings": {},
|
|
"pendingCleanup": null,
|
|
"lastExternalCheck": null,
|
|
"nextJob": null
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 방식 화면: 외부 대상·고객 책임·미제공 기능(메일 미지원)·건별 비용·동의 버전",
|
|
"ok": true,
|
|
"detail": {
|
|
"notProvided": "[redacted]",
|
|
"consentVersion": "external-deploy-2026-10-0.1.1",
|
|
"agreed": false
|
|
}
|
|
},
|
|
{
|
|
"name": "동의 전 Agent 발급 거부(consent_required) → 동의 기록(버전)",
|
|
"ok": true,
|
|
"detail": {
|
|
"before": "consent_required",
|
|
"consent": 200
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 Agent 일회용 토큰·설치 명령(토큰은 저장하지 않음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"token": "[redacted]",
|
|
"install": "curl -fsSL http://127.0.0.1:40961/agent/v1/install.sh | bash -s -- --hal http://127.0.0.1:40961 --token … --external --site s_01a1789dc3ada89ee43472fe26",
|
|
"expiresAt": "2026-10-26T14:39:27.642Z",
|
|
"status": "pending"
|
|
}
|
|
},
|
|
{
|
|
"name": "고객 서버 역할의 외부 Agent 프로세스 등록·실행(HAL 등록, 사이트 귀속)",
|
|
"ok": true,
|
|
"detail": {
|
|
"agentId": "[redacted]"
|
|
}
|
|
},
|
|
{
|
|
"name": "포털 연결 확인(외부 Agent 상태 connected)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "connected",
|
|
"stale": false,
|
|
"halAgentId": "[redacted]"
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 전 확인(외부: 관리형 용량 확인 없음)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": true,
|
|
"blocks": []
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 배포 → 외부 Agent 가 site.deploy 수행",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "외부 사이트 개요: 관리형 지표 없음·제공되지 않음 표기·Agent 상태·마지막 배포 결과",
|
|
"ok": true,
|
|
"detail": {
|
|
"kind": "external",
|
|
"notProvided": "[redacted]",
|
|
"agent": "connected",
|
|
"lastDeployOk": true
|
|
}
|
|
},
|
|
{
|
|
"name": "배포 제거(외부 서버 파일·실행 상태 제거, 실패 잔여 정리)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null
|
|
}
|
|
},
|
|
{
|
|
"name": "Agent 연결 해제(revoke) → 상태 없음",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"agent": null
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"conn": {
|
|
"status": "connected",
|
|
"stale": false,
|
|
"halAgentId": "[redacted]"
|
|
},
|
|
"dep": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"steps": [
|
|
"accept:done",
|
|
"prepare_version:done",
|
|
"prepare_target:done",
|
|
"apply:done",
|
|
"data_check:done",
|
|
"start:done",
|
|
"internal_check:done",
|
|
"external_check:done",
|
|
"domain_https:done",
|
|
"done:done"
|
|
]
|
|
},
|
|
"rm": {
|
|
"status": "done",
|
|
"error": null
|
|
},
|
|
"rv": {
|
|
"status": 200,
|
|
"agent": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 29,
|
|
"title": "HAL 장애 중 기존 서비스 유지와 신규 변경 제한 표시",
|
|
"verdict": "verified_local",
|
|
"note": "HAL 중단 중 포털 조회 유지·신규 배포 차단(정보 확인 지연), HAL 재기동 뒤 서버·Agent 복귀",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "HAL 중단 중: 기존 조회(사이트 목록·개요)는 제공, 배포 전 확인/배포는 정보 확인 지연·차단",
|
|
"ok": true,
|
|
"detail": {
|
|
"list": 200,
|
|
"overview": 200,
|
|
"precheck": {
|
|
"status": 200,
|
|
"blocks": [
|
|
"target:HAL(중앙 관리)에 연결할 수 없어 배포 위치를 확인하지 못했습니다(정"
|
|
]
|
|
},
|
|
"deploy": {
|
|
"status": 409,
|
|
"error": "precheck_failed",
|
|
"message": "배포 전 확인에서 차단된 항목이 있습니다"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "대시보드/알림에 중앙 상태 확인 지연 표시(가능하면)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"halState": null
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL 재기동(같은 호스트의 죽은 인스턴스 임대 이어받기) → 서버·Agent 상태 복귀",
|
|
"ok": true,
|
|
"detail": {
|
|
"takeoverNeeded": false,
|
|
"bootstrap": true,
|
|
"readyServers": 4
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"during": {
|
|
"list": 200,
|
|
"overview": 200,
|
|
"precheck": {
|
|
"status": 200,
|
|
"blocks": [
|
|
"target:HAL(중앙 관리)에 연결할 수 없어 배포 위치를 확인하지 못했습니다(정"
|
|
]
|
|
},
|
|
"deploy": {
|
|
"status": 409,
|
|
"error": "precheck_failed",
|
|
"message": "배포 전 확인에서 차단된 항목이 있습니다"
|
|
}
|
|
},
|
|
"dash": {
|
|
"status": 200,
|
|
"halState": null
|
|
},
|
|
"back": {
|
|
"takeoverNeeded": false,
|
|
"bootstrap": true,
|
|
"readyServers": 4
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 30,
|
|
"title": "모듈 업데이트, 일부 서버 실패, 대상별 되돌리기와 상태 조정",
|
|
"verdict": "verified_local",
|
|
"note": "대상별 자식 작업·일부 실패 partial 요약(전체 성공으로 묶지 않음)·건강 확인·drift 조정·되돌리기 3단계 확인 거부",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "agent 모듈을 두 서버 + 없는 서버에 배포 → 대상별 자식 작업·일부 실패를 성공으로 묶지 않음(partial/all_failed)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "failed",
|
|
"errorCode": "partial_failure",
|
|
"overall": "partial",
|
|
"children": [
|
|
"srv_missing_e2e:failed:서버가 없습니다",
|
|
"srv_01a1789c04ba2129a08801860c:done:",
|
|
"srv_01a1789c03fc9ee146184fa07c:done:"
|
|
],
|
|
"timedOut": false
|
|
}
|
|
},
|
|
{
|
|
"name": "모듈 건강 확인 작업·목표/실제 버전 차이(drift) 표시·다시 맞추기 작업",
|
|
"ok": true,
|
|
"detail": {
|
|
"health": "done",
|
|
"drift": 0,
|
|
"reconcile": 200,
|
|
"overviewComponents": [
|
|
"HAL:ok",
|
|
"portal:unknown",
|
|
"webmaker:unknown",
|
|
"arimail:unknown",
|
|
"backup:ok",
|
|
"observability:ok"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "되돌리기는 3단계 확인(영향 범위·식별정보·최종 실행) 없이는 거부",
|
|
"ok": true,
|
|
"detail": {
|
|
"step1": "confirm_impact_required",
|
|
"step2": "identifiers_mismatch"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"d": {
|
|
"status": "failed",
|
|
"errorCode": "partial_failure",
|
|
"overall": "partial",
|
|
"children": [
|
|
"srv_missing_e2e:failed:서버가 없습니다",
|
|
"srv_01a1789c04ba2129a08801860c:done:",
|
|
"srv_01a1789c03fc9ee146184fa07c:done:"
|
|
],
|
|
"timedOut": false
|
|
},
|
|
"h": {
|
|
"health": "done",
|
|
"drift": 0,
|
|
"reconcile": 200,
|
|
"overviewComponents": [
|
|
"HAL:ok",
|
|
"portal:unknown",
|
|
"webmaker:unknown",
|
|
"arimail:unknown",
|
|
"backup:ok",
|
|
"observability:ok"
|
|
]
|
|
},
|
|
"rb": {
|
|
"step1": "confirm_impact_required",
|
|
"step2": "identifiers_mismatch"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 31,
|
|
"title": "긴급 복구의 승인 부족·만료 자격·허용 범위 밖 작업 거부와 정상 복구",
|
|
"verdict": "partial",
|
|
"note": "정상 상태 선언 거부·3단계 승인(순서/식별정보/문구 거부)·자격 1회 발급·일반 배포/범위 밖/잘못된 자격 거부·수행·폐기 뒤 거부·조정·종료 확인. 자격 만료 거부는 유효 시간(분 단위 설정) 때문에 로컬 실행에서 생략(단위 테스트 test/hal.test.mjs 로 확인)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "HAL 정상 상태에서 선언 거부(hal_healthy)",
|
|
"ok": true,
|
|
"detail": "hal_healthy"
|
|
},
|
|
{
|
|
"name": "critical 사건 등록 → 선언 → 승인 부족 자격 거부 → 3단계(순서·식별정보·문구) 거부/승인 → 자격 발급(1회)",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "em_01a1789e0e13d2c9660482e88c",
|
|
"noCred": "approvals_insufficient",
|
|
"wrongStep": "wrong_step",
|
|
"badIds": "identifiers_mismatch",
|
|
"badText": "final_confirm_mismatch",
|
|
"approved": "approved",
|
|
"approvals": 3,
|
|
"credential": "[redacted]",
|
|
"expiresAt": "2026-10-26T14:59:46.671Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "일반 배포(module.deploy) 거부 · 범위 밖 거부 · 잘못된 자격 거부 · 허용 행동 수행 · 확인 뒤 자격 폐기 · 폐기 뒤 거부 · 조정 · 종료",
|
|
"ok": true,
|
|
"detail": {
|
|
"forbidden": "action_forbidden",
|
|
"outOfScope": "out_of_scope",
|
|
"badCred": "bad_credential",
|
|
"performed": 400,
|
|
"revoked": true,
|
|
"afterRevoke": "credential_revoked",
|
|
"reconcile": 200,
|
|
"closed": "closed"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"healthy": "hal_healthy",
|
|
"sess": {
|
|
"id": "em_01a1789e0e13d2c9660482e88c",
|
|
"noCred": "approvals_insufficient",
|
|
"wrongStep": "wrong_step",
|
|
"badIds": "identifiers_mismatch",
|
|
"badText": "final_confirm_mismatch",
|
|
"approved": "approved",
|
|
"approvals": 3,
|
|
"expiresAt": "2026-10-26T14:59:46.671Z"
|
|
},
|
|
"perf": {
|
|
"forbidden": "action_forbidden",
|
|
"outOfScope": "out_of_scope",
|
|
"badCred": "bad_credential",
|
|
"performed": 400,
|
|
"revoked": true,
|
|
"afterRevoke": "credential_revoked",
|
|
"reconcile": 200,
|
|
"closed": "closed"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 32,
|
|
"title": "Source 옵션 만료 후 30일 read-only, clone·export와 삭제 완료",
|
|
"verdict": "partial",
|
|
"note": "활성화·export·해제→읽기 전용(삭제 예정일)까지 확인. 30일 뒤 삭제 완료는 시간 경과가 필요(단위 테스트 test/sites.test.mjs 에서 sweep 로 확인)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "Source 옵션 주문(대상 사이트)·승인 → source 권리",
|
|
"ok": true,
|
|
"detail": {
|
|
"granted": 7
|
|
}
|
|
},
|
|
{
|
|
"name": "Source 옵션 활성화(권리 안에서) → 저장소·브랜치·변경 이력·clone 정보",
|
|
"ok": true,
|
|
"detail": {
|
|
"activate": 200,
|
|
"status": "active",
|
|
"provider": "gitea",
|
|
"changes": 1,
|
|
"clone": true
|
|
}
|
|
},
|
|
{
|
|
"name": "export(저장본 내려받기)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"type": "application/gzip"
|
|
}
|
|
},
|
|
{
|
|
"name": "옵션 해제 → 30일 읽기 전용(만료일·삭제 예정일·재활성화 안내) · push 차단",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "readonly",
|
|
"deleteAfter": "2026-11-25T14:29:36.479Z",
|
|
"readonlyUntil": "2026-11-25T14:29:36.479Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "삭제 정리 작업(sweep)은 삭제 예정일 전이면 지우지 않음",
|
|
"ok": true,
|
|
"detail": {
|
|
"sweep": 200,
|
|
"statusAfter": "readonly"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"act": {
|
|
"activate": 200,
|
|
"status": "active",
|
|
"provider": "gitea",
|
|
"changes": 1,
|
|
"clone": true
|
|
},
|
|
"ex": {
|
|
"status": 200,
|
|
"type": "application/gzip"
|
|
},
|
|
"un": {
|
|
"status": "readonly",
|
|
"deleteAfter": "2026-11-25T14:29:36.479Z",
|
|
"readonlyUntil": "2026-11-25T14:29:36.479Z"
|
|
},
|
|
"sweep": {
|
|
"sweep": 200,
|
|
"statusAfter": "readonly"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 33,
|
|
"title": "메일 서비스의 사이트 대표→독립 전환 후 항목 유지",
|
|
"verdict": "partial",
|
|
"note": "전환 점검표 8항목과 미충족 시 차단 확인. 전후 실제 송수신 확인·DNS 유지 검증은 실 DNS/AriMail 필요(단위 테스트 test/mail.test.mjs 에서 가짜로 전환 완료·되돌리기 확인)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "사이트 대표→독립 전환 점검표(주소·메일함·별칭·전달·비밀번호·DNS·옵션·작업)",
|
|
"ok": true,
|
|
"detail": {
|
|
"canConvert": false,
|
|
"items": [
|
|
"addresses:ok",
|
|
"data:ok",
|
|
"aliases:ok",
|
|
"forwards:ok",
|
|
"passwords:ok",
|
|
"dns:x",
|
|
"entitlement:x",
|
|
"jobs:x"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "DNS 미확인 상태에서 전환 요청은 차단",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "conversion_blocked"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ck": {
|
|
"canConvert": false,
|
|
"items": [
|
|
"addresses:ok",
|
|
"data:ok",
|
|
"aliases:ok",
|
|
"forwards:ok",
|
|
"passwords:ok",
|
|
"dns:x",
|
|
"entitlement:x",
|
|
"jobs:x"
|
|
]
|
|
},
|
|
"tr": {
|
|
"status": 409,
|
|
"error": "conversion_blocked"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 34,
|
|
"title": "일시 중지·해지 예약·보존·삭제 단계의 표시 일치",
|
|
"verdict": "partial",
|
|
"note": "해지된 메일 서비스의 읽기 전용 보존·삭제 예정 표시와 사이트 생명주기 화면의 단계별 항목 확인. 사이트의 해지 예약→보존→삭제 전이는 운영 중 사이트 필요(단위 테스트 test/lifecycle.test.mjs 로 확인)",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "§23.3 한 화면 보기: 공개/관리자 접근·사이트 수·청구·도메인/메일/백업 동작·내보내기·되돌릴 수 없는 시점·보존·다음 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"state": "도메인 설정 필요",
|
|
"siteCount": true,
|
|
"publicAccess": "제공 중",
|
|
"retention": {
|
|
"site_days": 30,
|
|
"mail_days": 30,
|
|
"source_days": 30,
|
|
"backup_days": 7,
|
|
"delete_grace_days": 1,
|
|
"notice_before_days": 3,
|
|
"readonly_retention_days": 30,
|
|
"delete_after_days": 30,
|
|
"pause_keeps_site_count": true,
|
|
"cancel_effective": "next_billing_date",
|
|
"source": "상품정책 베이직 (retention 정책) v1"
|
|
},
|
|
"pauseImpactKeys": [
|
|
"publicAccess",
|
|
"adminAccess",
|
|
"mail",
|
|
"backups",
|
|
"usageAndCost",
|
|
"resume",
|
|
"siteCount"
|
|
],
|
|
"cancelImpactKeys": [
|
|
"target",
|
|
"linked",
|
|
"immediateStops",
|
|
"remainingData",
|
|
"retentionUntil",
|
|
"deleteAfter",
|
|
"retentionSource",
|
|
"export",
|
|
"unpaid",
|
|
"refund",
|
|
"pointOfNoReturn",
|
|
"mailOptions",
|
|
"siteCount"
|
|
],
|
|
"steps14": 8
|
|
}
|
|
},
|
|
{
|
|
"name": "일시 중지(확인 필수) 또는 현재 상태에서 거부 사유",
|
|
"ok": true,
|
|
"detail": {
|
|
"noConfirm": "confirmation_required",
|
|
"status": 409,
|
|
"error": "conflict"
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 서비스 생명주기 화면(해지된 독립 메일: 송수신 중지·보존 종료·삭제 예정·백업 미제공)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"v": {
|
|
"state": "도메인 설정 필요",
|
|
"siteCount": true,
|
|
"publicAccess": "제공 중",
|
|
"retention": {
|
|
"site_days": 30,
|
|
"mail_days": 30,
|
|
"source_days": 30,
|
|
"backup_days": 7,
|
|
"delete_grace_days": 1,
|
|
"notice_before_days": 3,
|
|
"readonly_retention_days": 30,
|
|
"delete_after_days": 30,
|
|
"pause_keeps_site_count": true,
|
|
"cancel_effective": "next_billing_date",
|
|
"source": "상품정책 베이직 (retention 정책) v1"
|
|
},
|
|
"pauseImpactKeys": [
|
|
"publicAccess",
|
|
"adminAccess",
|
|
"mail",
|
|
"backups",
|
|
"usageAndCost",
|
|
"resume",
|
|
"siteCount"
|
|
],
|
|
"cancelImpactKeys": [
|
|
"target",
|
|
"linked",
|
|
"immediateStops",
|
|
"remainingData",
|
|
"retentionUntil",
|
|
"deleteAfter",
|
|
"retentionSource",
|
|
"export",
|
|
"unpaid",
|
|
"refund",
|
|
"pointOfNoReturn",
|
|
"mailOptions",
|
|
"siteCount"
|
|
],
|
|
"steps14": 8
|
|
},
|
|
"mailLc": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"bugs": [],
|
|
"needsInfrastructure": [
|
|
"도메인 소유 확인(TXT)·DNS 반영·HTTPS 발급·외부 접속 확인은 실제 DNS 와 공인 서버가 필요하다(흐름 7·8·11·14·15)",
|
|
"메일 DNS(MX·SPF·DKIM·DMARC) 반영과 외부 송신·수신·회신 왕복은 실 도메인·실 AriMail(SMTP 587/수신함)·외부 검증 주소가 필요하다(흐름 10 활성화 완료·11·33 완료)",
|
|
"재해복구의 실제 복원(hal-restore.sh 로 다른 호스트에 HAL 재기동)·실제 운영자 로그인·HTTPS·고객 서비스 외부 확인은 실 서버가 필요하다(흐름 25 기록·훈련 절차만 로컬 확인)"
|
|
]
|
|
} |