Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2870 lines
81 KiB
JSON
2870 lines
81 KiB
JSON
{
|
|
"generatedAt": "2026-10-26T13:02:10.786Z",
|
|
"startedAt": "2026-10-26T12:57:29.622Z",
|
|
"env": {
|
|
"agents": 4,
|
|
"ports": {
|
|
"hal": 35361,
|
|
"portal": 35287,
|
|
"wm": 43619,
|
|
"am": 33137,
|
|
"mail": 40085
|
|
},
|
|
"mailServerRegistered": true,
|
|
"node": "v24.22.1"
|
|
},
|
|
"summary": {
|
|
"verified_local": 13,
|
|
"partial": 15,
|
|
"not_verifiable_locally": 2,
|
|
"failed": 4,
|
|
"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_01a17849a2c2dec27ee9f14777"
|
|
},
|
|
{
|
|
"name": "이메일 초대(7일) → 초대 메일 링크",
|
|
"ok": true,
|
|
"detail": "RAXi6X0pnmr-nCEKiIX63pk26vTbIB5d"
|
|
},
|
|
{
|
|
"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_01a17849a127747d9c2bbbb24c",
|
|
"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-02T12:57:34.263Z",
|
|
"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-26T12:57:34.327Z"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ap": {
|
|
"granted": 10,
|
|
"already": true
|
|
},
|
|
"bill": {
|
|
"sites": 3,
|
|
"subs": 1,
|
|
"nextBilling": "2026-11-26T12:57:34.327Z"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 4,
|
|
"title": "초안 사이트 생성",
|
|
"verdict": "verified_local",
|
|
"note": "초안 생성·Webmaker 프로젝트 연결·사이트 수 미소비·목록 필터",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "사이트 생성(새 Webmaker 프로젝트·배포 방식 관리형) → 초안·사이트 수 미소비·다음 단계 안내",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a17849a3be6f07d129dbdb60",
|
|
"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:43619/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:43619/api/auth/sso?ticket=…",
|
|
"webmaker": 302,
|
|
"reuse": 401
|
|
},
|
|
"ver": {
|
|
"versions": 1,
|
|
"precheckOk": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 6,
|
|
"title": "관리형 배포",
|
|
"verdict": "failed",
|
|
"note": "시간 초과 — 포털 작업 queued(attempt 8, 배포 서버 작업 진행 중) · HAL site.deploy 작업 8개(최근: [\"failed\",\"Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni\"])",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "배포 요청 → 10단계 작업·사이트 수 예약·같은 요청 재제출은 하나",
|
|
"ok": true,
|
|
"detail": {
|
|
"jobId": "j_01a17849a4096e33f476b17d1b",
|
|
"steps": 10,
|
|
"again": false,
|
|
"siteCount": "reserved",
|
|
"server": null
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL·Agent 를 거쳐 작업 완료 대기(최대 40초)",
|
|
"ok": false,
|
|
"detail": "시간 초과 — 포털 작업 queued(attempt 8, 배포 서버 작업 진행 중) · HAL site.deploy 작업 8개(최근: [\"failed\",\"Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni\"])"
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 같은 Agent 에 로컬 저장본으로 site.deploy → 성공하면 결함은 포털→Agent 번들 전달 구간",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"layout": true,
|
|
"externalCheck": {
|
|
"ok": false,
|
|
"status": 404,
|
|
"ms": 19,
|
|
"url": "http://direct_e2e.preview.localhost/",
|
|
"markerFound": false
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "사용자 취소(진행 중 작업) → 중간 결과 정리·예약 반환",
|
|
"ok": true,
|
|
"detail": {
|
|
"cancel": 200,
|
|
"cleanup": 200,
|
|
"siteCount": "reserved",
|
|
"lifecycle": "building"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"halLog": [
|
|
"2026-10-26T12:58:01.231Z [jobs] site.deploy j_01a1784a0781bd4cb2d160762e 실패: Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni",
|
|
"2026-10-26T12:58:06.242Z [jobs] site.deploy j_01a1784a1b1b6362b7ba3a604c 실패: Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni",
|
|
"2026-10-26T12:58:11.248Z [jobs] site.deploy j_01a1784a2eb913f75494650303 실패: Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"no": 7,
|
|
"title": "도메인 연결과 HTTPS 발급",
|
|
"verdict": "partial",
|
|
"note": "도메인 추가·복사 가능한 TXT/DNS 값·외부 대기 상태까지 확인. 실 DNS 반영·인증서 발급·대표 도메인 확정은 로컬 확인 불가",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "도메인 추가(웹·대표 지정) → 소유 확인 TXT 값 안내 → 작업이 외부 대기(TXT)",
|
|
"ok": true,
|
|
"detail": {
|
|
"domainId": "d_01a1784a491476e7f378e8217c",
|
|
"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": 1
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"dm": {
|
|
"domainId": "d_01a1784a491476e7f378e8217c",
|
|
"txtName": "_ariweb-verify.aw-e2e.localhost",
|
|
"txtValueShape": "ariweb-verify=…",
|
|
"copyable": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 8,
|
|
"title": "외부 주소에서 사이트 확인",
|
|
"verdict": "not_verifiable_locally",
|
|
"note": "외부 주소 확인은 공인 서버·DNS 필요",
|
|
"blockedBy": 6,
|
|
"steps": [],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 9,
|
|
"title": "사이트 정보와 접속·사용량 통계 확인",
|
|
"verdict": "partial",
|
|
"note": "미배포 사이트는 통계 대신 준비 단계 표시(§8.1) 확인. 실제 접속·네트워크 통계는 배포 뒤 수집",
|
|
"blockedBy": 6,
|
|
"steps": [
|
|
{
|
|
"name": "개요: 배포 전/후에 따라 준비 단계 또는 실제 통계",
|
|
"ok": true,
|
|
"detail": {
|
|
"kind": "not_deployed",
|
|
"lifecycle": "building",
|
|
"hasStats": false,
|
|
"readiness": [
|
|
{
|
|
"id": "build",
|
|
"label": "Webmaker 에서 사이트 제작",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=build",
|
|
"done": true
|
|
},
|
|
{
|
|
"id": "import",
|
|
"label": "콘텐츠 가져오기",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=build",
|
|
"done": false,
|
|
"unsupported": "이번 버전에서 지원하지 않음"
|
|
},
|
|
{
|
|
"id": "domain",
|
|
"label": "도메인 연결",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=domains",
|
|
"done": false
|
|
},
|
|
{
|
|
"id": "mode",
|
|
"label": "배포 방식 선택",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60/deploy/mode",
|
|
"done": true
|
|
},
|
|
{
|
|
"id": "perms",
|
|
"label": "구성원 권한 설정",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=settings",
|
|
"done": false
|
|
}
|
|
],
|
|
"disk": null
|
|
}
|
|
},
|
|
{
|
|
"name": "사용량 탭(배정·실제 사용·마지막 계측)",
|
|
"ok": true,
|
|
"detail": {
|
|
"allocations": [
|
|
"web_disk",
|
|
"web_out"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ov": {
|
|
"kind": "not_deployed",
|
|
"lifecycle": "building",
|
|
"hasStats": false,
|
|
"readiness": [
|
|
{
|
|
"id": "build",
|
|
"label": "Webmaker 에서 사이트 제작",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=build",
|
|
"done": true
|
|
},
|
|
{
|
|
"id": "import",
|
|
"label": "콘텐츠 가져오기",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=build",
|
|
"done": false,
|
|
"unsupported": "이번 버전에서 지원하지 않음"
|
|
},
|
|
{
|
|
"id": "domain",
|
|
"label": "도메인 연결",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=domains",
|
|
"done": false
|
|
},
|
|
{
|
|
"id": "mode",
|
|
"label": "배포 방식 선택",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60/deploy/mode",
|
|
"done": true
|
|
},
|
|
{
|
|
"id": "perms",
|
|
"label": "구성원 권한 설정",
|
|
"href": "/sites/s_01a17849a3be6f07d129dbdb60?tab=settings",
|
|
"done": false
|
|
}
|
|
],
|
|
"disk": null
|
|
},
|
|
"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_01a1784ad1a3c31d4ea6cb7447",
|
|
"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_01a1784ad1df7ea80529a8cf8a",
|
|
"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_01a1784ad1a3c31d4ea6cb7447",
|
|
"status": "needs_setup",
|
|
"tenant": true,
|
|
"ariwebKind": "site_bound",
|
|
"limitAccounts": 6,
|
|
"siteCountAllocated": 0
|
|
},
|
|
"mb": {
|
|
"id": "mb_01a1784ad1df7ea80529a8cf8a",
|
|
"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_01a1784af801ae5cf98ef55e1f",
|
|
"jobCancelled": null
|
|
}
|
|
},
|
|
{
|
|
"name": "독립 메일 서비스 생성(독립 옵션 소비·사이트 수 미소비)",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "msv_01a1784af809f14b89bc606f73",
|
|
"kind": "standalone",
|
|
"mailServicesAllocated": 1,
|
|
"sitesAllocated": 0
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"r": {
|
|
"id": "msv_01a1784af809f14b89bc606f73",
|
|
"kind": "standalone",
|
|
"mailServicesAllocated": 1,
|
|
"sitesAllocated": 0
|
|
},
|
|
"listCount": 2,
|
|
"noBackupNote": "메일 원문은 고객용 백업·복원에 포함되지 않으며, 메일 백업 기능을 제공하지 않습니다."
|
|
}
|
|
},
|
|
{
|
|
"no": 13,
|
|
"title": "백업 옵션 구매와 첫 백업 확인",
|
|
"verdict": "partial",
|
|
"note": "옵션 구매·활성화까지 확인. 실제 백업 생성은 배포된 사이트 파일이 있어야 함(흐름 6 결함에 막힘)",
|
|
"blockedBy": 6,
|
|
"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-27T12:59:01.665Z",
|
|
"nextRun": "2026-10-26T12:59:01.665Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "지금 백업 실행 → HAL backup.create(저장 서버 ≠ 운영 서버) → Agent → 검증",
|
|
"ok": true,
|
|
"detail": {
|
|
"timedOut": true,
|
|
"optionStatus": "active",
|
|
"lastJob": null,
|
|
"siteServer": null,
|
|
"backup": null,
|
|
"job": null
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"el": {
|
|
"eligible": true,
|
|
"reasons": [],
|
|
"deferred": [],
|
|
"policy": null,
|
|
"status": 200
|
|
},
|
|
"opt": {
|
|
"status": "active",
|
|
"firstDue": "2026-10-27T12:59:01.665Z",
|
|
"nextRun": "2026-10-26T12:59:01.665Z"
|
|
},
|
|
"run1": {
|
|
"timedOut": true,
|
|
"optionStatus": "active",
|
|
"lastJob": null,
|
|
"siteServer": null,
|
|
"backup": null,
|
|
"job": null
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 14,
|
|
"title": "백업 복원과 외부 사이트 확인",
|
|
"verdict": "partial",
|
|
"note": "완료된 백업이 없어 복원을 요청할 수 없음",
|
|
"blockedBy": 13,
|
|
"steps": [],
|
|
"evidence": {}
|
|
},
|
|
{
|
|
"no": 15,
|
|
"title": "관리형 사이트 서버 이전과 외부 확인",
|
|
"verdict": "partial",
|
|
"note": "이전 경계값·요청 규칙 확인. 실제 두 Agent 간 사전 복사·전환·외부 확인은 배포된 사이트(흐름 6)와 공인 확인 주소가 필요",
|
|
"blockedBy": 6,
|
|
"steps": [
|
|
{
|
|
"name": "이전 화면 경계값: 취소 가능 마지막 단계·쓰기 제한·목표 중단 시간·되돌림 기간·이전 위치 보존·비용(플랫폼 사유 0원)",
|
|
"ok": true,
|
|
"detail": {
|
|
"auto": {
|
|
"ok": false,
|
|
"reasons": [
|
|
"사이트 대표 메일이 있는 사이트는 승인형으로 진행합니다"
|
|
]
|
|
},
|
|
"boundaries": {
|
|
"lastCancel": "새 위치 확인(연결 전환 직전)까지 취소할 수 있습니다",
|
|
"revertHours": 72,
|
|
"cost": 10000,
|
|
"platformCost": 0
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "고객 요청(유료·승인 대기) 또는 조건 미충족 사유",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"relocation": {
|
|
"status": "awaiting_approval",
|
|
"kind": "customer_paid",
|
|
"cost": 10000
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "전환 전 고객 취소 가능(요청 취소 → 소유권 이전 등 다른 작업 차단 해제)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"rstatus": "cancelled"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"b": {
|
|
"auto": {
|
|
"ok": false,
|
|
"reasons": [
|
|
"사이트 대표 메일이 있는 사이트는 승인형으로 진행합니다"
|
|
]
|
|
},
|
|
"boundaries": {
|
|
"lastCancel": "새 위치 확인(연결 전환 직전)까지 취소할 수 있습니다",
|
|
"revertHours": 72,
|
|
"cost": 10000,
|
|
"platformCost": 0
|
|
}
|
|
},
|
|
"req": {
|
|
"status": 200,
|
|
"relocation": {
|
|
"status": "awaiting_approval",
|
|
"kind": "customer_paid",
|
|
"cost": 10000
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 16,
|
|
"title": "외부 배포 사이트 생성과 배포",
|
|
"verdict": "failed",
|
|
"note": "동의(버전)·토큰 발급 거부/허용 규칙까지 확인했으나 토큰 발급 API 가 DB 제약으로 500 — 결함 항목 참조",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "외부 배포 사이트 생성(초안) + 저장본",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a1784b5d03e229abe92a46a1",
|
|
"group_id": "g_01a17849a2c2dec27ee9f14777",
|
|
"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_01a17849a21df78a8debb70c18",
|
|
"created_at": "2026-10-26T12:59:27.363Z",
|
|
"updated_at": "2026-10-26T12:59:27.368Z",
|
|
"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": false,
|
|
"detail": "500 {\"ok\":false,\"error\":\"ERR_SQLITE_ERROR\",\"message\":\"일시적인 오류가 발생했습니다. 잠시 후 다시 시도하세요\"} | portal: 2026-10-26T12:59:27.404Z [http] POST /api/sites/s_01a1784b5d03e229abe92a46a1/external 500: Error: NOT NULL constraint failed: external_agents.hal_agent_id | at Object.run (file:///home/master/work/Ar"
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 토큰 발급(포털 서비스 키)",
|
|
"ok": true,
|
|
"detail": {
|
|
"token": "[redacted]",
|
|
"expiresAt": "2026-10-26T13:09:27.408Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 등록·실행(사이트 귀속) → by-site 상태 connected",
|
|
"ok": true,
|
|
"detail": {
|
|
"agentId": "[redacted]",
|
|
"status": "connected",
|
|
"siteId": "s_01a1784b5d03e229abe92a46a1"
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 배포(site.deploy, 로컬 저장본) → releases/current 생성",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125937-v1",
|
|
"leftovers": []
|
|
},
|
|
"layout": true
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 재배포(v2) → 이전 버전 기록",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125950-v2",
|
|
"leftovers": []
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 실패 배포(표식 불일치) → 이전 버전 유지·남은 파일(leftovers) 보고",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "failed",
|
|
"error": "내부 확인 실패(404) — 이전 버전을 유지합니다",
|
|
"errorCode": "internal_check_failed",
|
|
"result": null
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 다른 사이트 작업 거부(site_mismatch) · 허용되지 않는 종류 거부",
|
|
"ok": true,
|
|
"detail": {}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 배포 제거(site.remove) → 파일·vhost 제거",
|
|
"ok": true,
|
|
"detail": {
|
|
"layoutAfter": false
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"mode": {
|
|
"notProvided": "[redacted]",
|
|
"consentVersion": "external-deploy-2026-10-0.1.1",
|
|
"agreed": false
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"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_01a17849a126e838aa8bb17065"
|
|
}
|
|
}
|
|
],
|
|
"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_01a17849a126e838aa8bb17065"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 19,
|
|
"title": "사이트 수 반환과 대표 메일의 독립 전환",
|
|
"verdict": "partial",
|
|
"note": "반환 점검표와 메일 유지/종료 분기 규칙 확인. 실제 반환은 운영 사이트(사이트 수 사용 중) 필요(단위 테스트 test/lifecycle.test.mjs·test/mail.test.mjs 로 확인)",
|
|
"blockedBy": 6,
|
|
"steps": [
|
|
{
|
|
"name": "§14 반환 점검표(8단계)·차단 사유(대표 메일 유지 선택·독립 옵션)",
|
|
"ok": true,
|
|
"detail": {
|
|
"ok": false,
|
|
"blocking": [
|
|
"domains_unrouted",
|
|
"mail_choice",
|
|
"mail_converted"
|
|
],
|
|
"steps": 8
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 유지 선택: 독립 옵션 부족이면 반환하지 않음 / 종료 선택 규칙",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "quota_insufficient",
|
|
"detail": [
|
|
"resource",
|
|
"needed",
|
|
"available",
|
|
"link"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"ck": {
|
|
"ok": false,
|
|
"blocking": [
|
|
"domains_unrouted",
|
|
"mail_choice",
|
|
"mail_converted"
|
|
],
|
|
"steps": 8
|
|
},
|
|
"keep": {
|
|
"status": 409,
|
|
"error": "quota_insufficient",
|
|
"detail": [
|
|
"resource",
|
|
"needed",
|
|
"available",
|
|
"link"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"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_01a1784c7a9e2007160a39f536",
|
|
"status": "requested",
|
|
"expiresAt": "2026-11-02T13:00:40.478Z",
|
|
"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-26T13:00:40.527Z"
|
|
}
|
|
},
|
|
{
|
|
"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_01a1784c7a9e2007160a39f536",
|
|
"status": "requested",
|
|
"expiresAt": "2026-11-02T13:00:40.478Z",
|
|
"bobNotified": true
|
|
},
|
|
"acc": {
|
|
"status": 200,
|
|
"tstatus": "reviewing",
|
|
"hold": null
|
|
},
|
|
"ap": {
|
|
"bad": "final_confirmation_mismatch",
|
|
"ok": 200,
|
|
"status": "completed",
|
|
"cutover": "2026-10-26T13:00:40.527Z"
|
|
},
|
|
"after": {
|
|
"aliceSees": 404,
|
|
"bobSees": 200,
|
|
"bobUsageSites": 0,
|
|
"audit": true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 21,
|
|
"title": "중지, 해지, 보존 예정일 표시",
|
|
"verdict": "partial",
|
|
"note": "생명주기 화면·중지/해지 영향 안내·보존 정책 표시 확인. 운영 중 사이트의 실제 중지·해지는 배포 성공(흐름 6) 필요",
|
|
"blockedBy": 6,
|
|
"steps": [
|
|
{
|
|
"name": "§23.3 한 화면 보기: 공개/관리자 접근·사이트 수·청구·도메인/메일/백업 동작·내보내기·되돌릴 수 없는 시점·보존·다음 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"state": "제작 중",
|
|
"siteCount": false,
|
|
"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": "lifecycle_transition"
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 서비스 생명주기 화면(해지된 독립 메일: 송수신 중지·보존 종료·삭제 예정·백업 미제공)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"v": {
|
|
"state": "제작 중",
|
|
"siteCount": false,
|
|
"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": "lifecycle_transition"
|
|
},
|
|
"mailLc": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 22,
|
|
"title": "HAL 우선 배포 후 HAL을 통한 전체 모듈 배포",
|
|
"verdict": "failed",
|
|
"note": "HAL 우선 순서(hal_first 거부)·릴리스 승인·부트스트랩 잠김은 확인. 모듈 배포 작업은 완료되지 않음(진단 증거 참조)",
|
|
"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": null,
|
|
"mainCommit": null,
|
|
"artifactSha256": null,
|
|
"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": "queued",
|
|
"steps": [
|
|
"precheck:pending",
|
|
"fanout:pending",
|
|
"collect:pending"
|
|
],
|
|
"error": null,
|
|
"children": [],
|
|
"agentJobs": [],
|
|
"timedOut": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"rel": {
|
|
"releases": [
|
|
"0.1.2:approved",
|
|
"0.1.1:approved"
|
|
],
|
|
"running": {
|
|
"complete": true,
|
|
"firstInstall": false,
|
|
"releaseVersion": null,
|
|
"mainCommit": null,
|
|
"artifactSha256": null,
|
|
"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": "queued",
|
|
"steps": [
|
|
"precheck:pending",
|
|
"fanout:pending",
|
|
"collect:pending"
|
|
],
|
|
"error": null,
|
|
"children": [],
|
|
"agentJobs": [],
|
|
"timedOut": true
|
|
},
|
|
"diag": {
|
|
"stuckNow": [
|
|
{
|
|
"id": "j_01a1784d7f02143ed47d1fcaf9",
|
|
"kind": "domain.probe",
|
|
"status": "queued",
|
|
"attempt": 0,
|
|
"step": "dispatch",
|
|
"detail": "",
|
|
"updatedAt": "2026-10-26T13:01:47.138Z"
|
|
},
|
|
{
|
|
"id": "j_01a1784d6b6a20949d03d2dee8",
|
|
"kind": "domain.probe",
|
|
"status": "queued",
|
|
"attempt": 0,
|
|
"step": "dispatch",
|
|
"detail": "",
|
|
"updatedAt": "2026-10-26T13:01:42.122Z"
|
|
},
|
|
{
|
|
"id": "j_01a1784d671c8f951049094a53",
|
|
"kind": "module.deploy",
|
|
"status": "queued",
|
|
"attempt": 0,
|
|
"step": "precheck",
|
|
"detail": "",
|
|
"updatedAt": "2026-10-26T13:01:41.020Z"
|
|
},
|
|
{
|
|
"id": "j_01a1784d57cf28f7528a1a8ccb",
|
|
"kind": "domain.probe",
|
|
"status": "queued",
|
|
"attempt": 0,
|
|
"step": "dispatch",
|
|
"detail": "",
|
|
"updatedAt": "2026-10-26T13:01:37.103Z"
|
|
},
|
|
{
|
|
"id": "j_01a1784d44338bf61b6691d5a8",
|
|
"kind": "domain.probe",
|
|
"status": "running",
|
|
"attempt": 1,
|
|
"step": "await",
|
|
"detail": "",
|
|
"updatedAt": "2026-10-26T13:01:46.885Z"
|
|
}
|
|
],
|
|
"halLog": [
|
|
"2026-10-26T12:58:56.345Z [jobs] site.deploy j_01a1784ac7d0815275cc3e88aa 실패: Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/lIw5d3l2igVGOIlJa1BCaKwXYo7r2bJJ",
|
|
"2026-10-26T12:59:11.375Z [jobs] backup.create j_01a1784afb5dd2de92b7a63b4f 실패: 대상 서버 또는 Agent 가 필요합니다",
|
|
"2026-10-26T13:00:04.476Z [jobs] site.deploy j_01a1784bbc086436dc87f4b997 실패: 내부 확인 실패(404) — 이전 버전을 유지합니다",
|
|
"2026-10-26T13:00:18.498Z [jobs] site.deploy j_01a1784befea6c15eb6d7782eb 실패: 이 외부 Agent 는 다른 사이트에 귀속되어 있습니다",
|
|
"2026-10-26T13:00:31.517Z [jobs] backup.create j_01a1784c1721827ff3a7fd81e1 실패: 외부 Agent 는 backup.create 작업을 받을 수 없습니다",
|
|
"2026-10-26T13:01:40.630Z [jobs] module.deploy j_01a1784d2b6df1c41a7815b555 실패: HAL 이 아직 0.1.2 으로 실행되고 있지 않습니다(현재 0.1.1). HAL 을 먼저 배포하세요"
|
|
],
|
|
"ctlAgentLog": [
|
|
"2026-10-26T12:57:32.329Z [agent] Ariweb Node Agent v0.1.1 ag_01a178499afa793eb4b22d9b01 (managed) → http://127.0.0.1:35361"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"no": 23,
|
|
"title": "실패 작업의 재시도와 되돌리기",
|
|
"verdict": "partial",
|
|
"note": "실패 기록·재시도 API·되돌리기 거부/허용 조건 확인. 성공한 재배포 뒤 실제 되돌리기는 배포 성공이 필요",
|
|
"blockedBy": 6,
|
|
"steps": [
|
|
{
|
|
"name": "배포 이력: 실패/성공 기록 · 재시도 가능 여부 · 문의용 작업 번호",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "cancelled",
|
|
"failedStep": null,
|
|
"canRetry": false,
|
|
"jobId": "j_01a17849a4096e33f476b17d1b"
|
|
}
|
|
},
|
|
{
|
|
"name": "재시도 요청(POST /api/sites/:id/jobs/:jobId/retry)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 500,
|
|
"error": "job_not_retryable"
|
|
}
|
|
},
|
|
{
|
|
"name": "되돌리기 요청(POST /api/sites/:id/rollback) — 이전 버전 없으면 거부",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 409,
|
|
"error": "no_previous",
|
|
"message": "되돌릴 이전 버전이 없습니다"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"dep": {
|
|
"status": "cancelled",
|
|
"failedStep": null,
|
|
"canRetry": false,
|
|
"jobId": "j_01a17849a4096e33f476b17d1b"
|
|
},
|
|
"rt": {
|
|
"status": 500,
|
|
"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_01a17849a33601ca26016fa94a"
|
|
}
|
|
},
|
|
{
|
|
"name": "감사 기록: order.approve 는 삭제 불가(immutable)",
|
|
"ok": true,
|
|
"detail": {
|
|
"action": "order.approve",
|
|
"immutable": true
|
|
}
|
|
},
|
|
{
|
|
"name": "운영자 화면과 고객 주문 화면의 상태 일치",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "approved",
|
|
"approvedAt": "2026-10-26T12:57:34.327Z"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"n": {
|
|
"title": "주문 AW-20261026-0001 승인 — 권리가 활성화되었습니다",
|
|
"category": "billing",
|
|
"link": "/orders/o_01a17849a33601ca26016fa94a"
|
|
},
|
|
"a": {
|
|
"action": "order.approve",
|
|
"immutable": true
|
|
},
|
|
"same": {
|
|
"status": "approved",
|
|
"approvedAt": "2026-10-26T12:57:34.327Z"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"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-20261026125729/dr-external",
|
|
"updated_at": "2026-10-26T13:02:07.823Z",
|
|
"updated_by": "op_01a178499a27f1ba88344e1145"
|
|
}
|
|
},
|
|
{
|
|
"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_with_findings"
|
|
}
|
|
},
|
|
{
|
|
"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-20261026125729/dr-external",
|
|
"updated_at": "2026-10-26T13:02:07.823Z",
|
|
"updated_by": "op_01a178499a27f1ba88344e1145"
|
|
},
|
|
"snap": {
|
|
"verify": "ok",
|
|
"external": true,
|
|
"sha": true
|
|
},
|
|
"rs": {
|
|
"early": "restore_not_approved",
|
|
"noStop": "old_control_not_confirmed",
|
|
"command": true,
|
|
"incomplete": "checks_incomplete",
|
|
"final": "completed_with_findings"
|
|
},
|
|
"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_01a1784ae61b14370f923139a1",
|
|
"status": "awaiting_external"
|
|
},
|
|
"halJobs": 50
|
|
}
|
|
},
|
|
{
|
|
"name": "포털 재시작 → 재로그인 없이 세션 유지·같은 작업 id·상태 유지",
|
|
"ok": true,
|
|
"detail": {
|
|
"sessionKept": "[redacted]",
|
|
"mailJob": {
|
|
"id": "j_01a1784ae61b14370f923139a1",
|
|
"status": "awaiting_external"
|
|
},
|
|
"same": true
|
|
}
|
|
},
|
|
{
|
|
"name": "HAL 재시작 → 운영자 세션·작업 목록·서버 등록 유지",
|
|
"ok": true,
|
|
"detail": {
|
|
"opsSessionKept": "[redacted]",
|
|
"halJobs": 50,
|
|
"sameCount": true
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"before": {
|
|
"mailJob": {
|
|
"id": "j_01a1784ae61b14370f923139a1",
|
|
"status": "awaiting_external"
|
|
},
|
|
"halJobs": 50
|
|
},
|
|
"pr": {
|
|
"sessionKept": "[redacted]",
|
|
"mailJob": {
|
|
"id": "j_01a1784ae61b14370f923139a1",
|
|
"status": "awaiting_external"
|
|
},
|
|
"same": true
|
|
},
|
|
"hr": {
|
|
"opsSessionKept": "[redacted]",
|
|
"halJobs": 50,
|
|
"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_01a1784ac5a389af03b57b32a3",
|
|
"group_id": "g_01a17849a2ee454ab48b65c13d",
|
|
"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_01a17849a2eef6c9267cb0d204",
|
|
"created_at": "2026-10-26T12:58:48.611Z",
|
|
"updated_at": "2026-10-26T12:58:48.619Z",
|
|
"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_01a1784ac5a7fa23018c39de03",
|
|
"group_id": "g_01a17849a2ee454ab48b65c13d",
|
|
"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_01a17849a2eef6c9267cb0d204",
|
|
"created_at": "2026-10-26T12:58:48.615Z",
|
|
"updated_at": "2026-10-26T12:58:48.622Z",
|
|
"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": "partial",
|
|
"note": "HAL·외부 Agent 경로(등록·연결·배포·재배포·실패 시 이전 버전 유지와 남은 파일·다른 사이트/허용되지 않는 종류 거부·제거)는 확인. 포털 화면 경로는 토큰 발급 결함으로 막힘",
|
|
"blockedBy": null,
|
|
"steps": [
|
|
{
|
|
"name": "외부 배포 사이트 생성(초안) + 저장본",
|
|
"ok": true,
|
|
"detail": {
|
|
"id": "s_01a1784b5d03e229abe92a46a1",
|
|
"group_id": "g_01a17849a2c2dec27ee9f14777",
|
|
"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_01a17849a21df78a8debb70c18",
|
|
"created_at": "2026-10-26T12:59:27.363Z",
|
|
"updated_at": "2026-10-26T12:59:27.368Z",
|
|
"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": false,
|
|
"detail": "500 {\"ok\":false,\"error\":\"ERR_SQLITE_ERROR\",\"message\":\"일시적인 오류가 발생했습니다. 잠시 후 다시 시도하세요\"} | portal: 2026-10-26T12:59:27.404Z [http] POST /api/sites/s_01a1784b5d03e229abe92a46a1/external 500: Error: NOT NULL constraint failed: external_agents.hal_agent_id | at Object.run (file:///home/master/work/Ar"
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 토큰 발급(포털 서비스 키)",
|
|
"ok": true,
|
|
"detail": {
|
|
"token": "[redacted]",
|
|
"expiresAt": "2026-10-26T13:09:27.408Z"
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 등록·실행(사이트 귀속) → by-site 상태 connected",
|
|
"ok": true,
|
|
"detail": {
|
|
"agentId": "[redacted]",
|
|
"status": "connected",
|
|
"siteId": "s_01a1784b5d03e229abe92a46a1"
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 외부 Agent 배포(site.deploy, 로컬 저장본) → releases/current 생성",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125937-v1",
|
|
"leftovers": []
|
|
},
|
|
"layout": true
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 재배포(v2) → 이전 버전 기록",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125950-v2",
|
|
"leftovers": []
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 실패 배포(표식 불일치) → 이전 버전 유지·남은 파일(leftovers) 보고",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "failed",
|
|
"error": "내부 확인 실패(404) — 이전 버전을 유지합니다",
|
|
"errorCode": "internal_check_failed",
|
|
"result": null
|
|
}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 다른 사이트 작업 거부(site_mismatch) · 허용되지 않는 종류 거부",
|
|
"ok": true,
|
|
"detail": {}
|
|
},
|
|
{
|
|
"name": "[HAL 직접] 배포 제거(site.remove) → 파일·vhost 제거",
|
|
"ok": true,
|
|
"detail": {
|
|
"layoutAfter": false
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"halTok": {
|
|
"expiresAt": "2026-10-26T13:09:27.408Z"
|
|
},
|
|
"d1": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125937-v1",
|
|
"leftovers": []
|
|
},
|
|
"layout": true
|
|
},
|
|
"d2": {
|
|
"status": "done",
|
|
"error": null,
|
|
"errorCode": null,
|
|
"result": {
|
|
"version": "20261026125950-v2",
|
|
"leftovers": []
|
|
}
|
|
},
|
|
"d3": {
|
|
"status": "failed",
|
|
"error": "내부 확인 실패(404) — 이전 버전을 유지합니다",
|
|
"errorCode": "internal_check_failed",
|
|
"result": null
|
|
},
|
|
"d4": {},
|
|
"d5": {
|
|
"layoutAfter": false
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"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": "failed",
|
|
"note": "대상별 자식 작업 생성·되돌리기 3단계 확인 거부는 확인. 일부 실패 요약·건강 확인·drift 조정은 작업 엔진 교착(흐름 22 결함)으로 완료되지 않음",
|
|
"blockedBy": 22,
|
|
"steps": [
|
|
{
|
|
"name": "agent 모듈을 두 서버 + 없는 서버에 배포 → 대상별 자식 작업·일부 실패를 성공으로 묶지 않음(partial/all_failed)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": "queued",
|
|
"errorCode": null,
|
|
"overall": "none",
|
|
"children": [],
|
|
"timedOut": true
|
|
}
|
|
},
|
|
{
|
|
"name": "모듈 건강 확인 작업·목표/실제 버전 차이(drift) 표시·다시 맞추기 작업",
|
|
"ok": true,
|
|
"detail": {
|
|
"health": "timeout",
|
|
"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": "queued",
|
|
"errorCode": null,
|
|
"overall": "none",
|
|
"children": [],
|
|
"timedOut": true
|
|
},
|
|
"h": {
|
|
"health": "timeout",
|
|
"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_01a1784dd0ec3f08f6ddf4281a",
|
|
"noCred": "approvals_insufficient",
|
|
"wrongStep": "wrong_step",
|
|
"badIds": "identifiers_mismatch",
|
|
"badText": "final_confirm_mismatch",
|
|
"approved": "approved",
|
|
"approvals": 3,
|
|
"credential": "[redacted]",
|
|
"expiresAt": "2026-10-26T13:32:08.135Z"
|
|
}
|
|
},
|
|
{
|
|
"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_01a1784dd0ec3f08f6ddf4281a",
|
|
"noCred": "approvals_insufficient",
|
|
"wrongStep": "wrong_step",
|
|
"badIds": "identifiers_mismatch",
|
|
"badText": "final_confirm_mismatch",
|
|
"approved": "approved",
|
|
"approvals": 3,
|
|
"expiresAt": "2026-10-26T13:32:08.135Z"
|
|
},
|
|
"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-25T13:00:40.412Z",
|
|
"readonlyUntil": "2026-11-25T13:00:40.412Z"
|
|
}
|
|
},
|
|
{
|
|
"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-25T13:00:40.412Z",
|
|
"readonlyUntil": "2026-11-25T13:00:40.412Z"
|
|
},
|
|
"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": 6,
|
|
"steps": [
|
|
{
|
|
"name": "§23.3 한 화면 보기: 공개/관리자 접근·사이트 수·청구·도메인/메일/백업 동작·내보내기·되돌릴 수 없는 시점·보존·다음 알림",
|
|
"ok": true,
|
|
"detail": {
|
|
"state": "제작 중",
|
|
"siteCount": false,
|
|
"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": "lifecycle_transition"
|
|
}
|
|
},
|
|
{
|
|
"name": "메일 서비스 생명주기 화면(해지된 독립 메일: 송수신 중지·보존 종료·삭제 예정·백업 미제공)",
|
|
"ok": true,
|
|
"detail": {
|
|
"status": 200,
|
|
"sendReceive": "송수신 가능",
|
|
"retentionUntil": false,
|
|
"deleteAfter": false,
|
|
"backupNote": "고객용 메일 백업·복원은 제공하지 않습니다(§17.4)"
|
|
}
|
|
}
|
|
],
|
|
"evidence": {
|
|
"v": {
|
|
"state": "제작 중",
|
|
"siteCount": false,
|
|
"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": [
|
|
{
|
|
"title": "Agent 가 포털 저장본(bundleUrl, 절대 URL)을 내려받지 못함(HAL 주소를 앞에 덧붙임)",
|
|
"file": "agent/client.mjs (download: fetchImpl(base + path)) / agent/jobs/site.mjs (fetchBundle)",
|
|
"route": "HAL site.deploy → Agent fetch 단계",
|
|
"expected": "bundleUrl 이 절대 URL 이면 그대로 내려받는다(서명은 HAL 전용이므로 포털 번들 URL 에는 붙이지 않음)",
|
|
"actual": "Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni",
|
|
"repro": "포털 배포 → HAL site.deploy → Agent: \"Failed to parse URL from http://<hal>http://<portal>/internal/bundles/…\"",
|
|
"flows": [
|
|
6,
|
|
8,
|
|
9,
|
|
13,
|
|
14,
|
|
15,
|
|
23
|
|
]
|
|
},
|
|
{
|
|
"title": "포털 배포 작업이 실패한 HAL 작업을 실패로 보지 않고 5초마다 새 HAL 작업을 무한 재제출(배포가 영원히 \"진행 중\")",
|
|
"file": "portal/sites/service.mjs (halJobFor: prev HAL 작업이 failed/cancelled 면 fresh 가 아니어도 새 작업을 만든다 → awaitHal 이 실패를 보지 못함)",
|
|
"route": "POST /api/sites/:id/deploy",
|
|
"expected": "HAL 작업이 failed 면 포털 작업도 failed(이전 버전 유지·재시도 버튼), 새 HAL 작업은 사용자 재시도(fresh) 때만",
|
|
"actual": "portal job queued attempt 9 (배포 서버 작업 진행 중); HAL 에 같은 사이트의 site.deploy 실패 작업이 attempt 수만큼 쌓임",
|
|
"repro": "Agent 가 실패하는 배포를 요청하고 /api/sites/:id/jobs/:id 를 지켜보면 attempt 가 5초마다 증가",
|
|
"flows": [
|
|
6,
|
|
23,
|
|
13,
|
|
20
|
|
]
|
|
},
|
|
{
|
|
"title": "관리형 배포 종단 흐름 실패(포털→HAL→Agent 저장본 전달)",
|
|
"file": "agent/client.mjs (download: fetchImpl(base + path)) / agent/jobs/site.mjs (fetchBundle: client.download(p.bundleUrl))",
|
|
"route": "site.deploy → Agent fetch 단계",
|
|
"expected": "절대 URL(bundleUrl=http://<portal>/internal/bundles/<token>)은 HAL base 를 붙이지 않고 그대로 내려받아야 한다",
|
|
"actual": "시간 초과 — 포털 작업 queued(attempt 8, 배포 서버 작업 진행 중) · HAL site.deploy 작업 8개(최근: [\"failed\",\"Failed to parse URL from http://127.0.0.1:35361http://127.0.0.1:35287/internal/bundles/v74iR3dd-nVRU4N1LEVDmEe0ZuK300Ni\"])",
|
|
"repro": "사이트 생성→저장본→POST /api/sites/:id/deploy→HAL site.deploy→Agent site.deploy fetch 단계에서 \"HAL 내려받기 실패\" 또는 잘못된 URL 오류",
|
|
"flows": [
|
|
6,
|
|
8,
|
|
9,
|
|
13,
|
|
14,
|
|
15,
|
|
23
|
|
]
|
|
},
|
|
{
|
|
"title": "취소된 배포 작업의 재시도 요청이 500(서버 오류)으로 응답(작업 상태 오류는 4xx 여야 함)",
|
|
"file": "portal/sites/api.mjs (jobs/:jobId/:action → lib/jobs JobError 를 HttpError 로 바꾸지 않음)",
|
|
"route": "POST /api/sites/:id/jobs/:jobId/retry",
|
|
"expected": "409 job_not_retryable + 안내",
|
|
"actual": "500 job_not_retryable",
|
|
"repro": "취소된 작업에 retry",
|
|
"flows": [
|
|
23
|
|
]
|
|
},
|
|
{
|
|
"title": "외부 Agent 토큰 발급이 DB 제약(external_agents.hal_agent_id NOT NULL)으로 500",
|
|
"file": "portal/sites/service.mjs (createExternalAgent) / portal/sites/model.mjs (external_agents.hal_agent_id)",
|
|
"route": "POST /api/sites/:id/external",
|
|
"expected": "HAL 토큰 발급 응답의 agentId 가 null(등록 전)이어도 토큰·설치 명령을 돌려주고, Agent 등록 뒤 by-site 조회로 hal_agent_id 를 채운다",
|
|
"actual": "2026-10-26T12:59:27.404Z [http] POST /api/sites/s_01a1784b5d03e229abe92a46a1/external 500: Error: NOT NULL constraint failed: external_agents.hal_agent_id | at Object.run (file:///home/master/work/Ariweb-suite/lib/db.mjs:52:41) | at Object.createExternalAgent (file:///home/master/work/Ariweb",
|
|
"repro": "외부 배포 사이트 → 동의 → POST /api/sites/:id/external → 500 \"NOT NULL constraint failed: external_agents.hal_agent_id\"",
|
|
"flows": [
|
|
16,
|
|
28
|
|
]
|
|
},
|
|
{
|
|
"title": "HAL 작업 엔진이 작업을 한 번에 하나씩만 실행(tick 이 execute 를 await)해 느린 작업이 큐 전체를 막고, module.deploy 부모가 자식 완료를 기다리면 자식이 실행될 수 없어 교착",
|
|
"file": "lib/jobs.mjs (createJobEngine.start/tick) / hal/jobs.mjs (fanout 이 자식 완료를 폴링)",
|
|
"route": "POST /ops/api/modules/:name/deploy",
|
|
"expected": "엔진은 running 수가 concurrency 미만이면 실행을 분리(detach)해 여러 작업을 동시에 진행해야 한다(부모가 자식을 기다려도 자식이 실행됨)",
|
|
"actual": {
|
|
"status": "queued",
|
|
"steps": [
|
|
"precheck:pending",
|
|
"fanout:pending",
|
|
"collect:pending"
|
|
],
|
|
"error": null,
|
|
"children": [],
|
|
"agentJobs": [],
|
|
"timedOut": true
|
|
},
|
|
"repro": "(1) Agent 가 수십 초 걸리는 domain.probe 가 queued 로 쌓이면 다른 모든 HAL 작업이 기다린다 (2) 큐가 비어도 모듈 배포는 부모 collect:running + 자식 queued 로 영원히 멈춘다(test/hal.test.mjs 는 pump() 로 tick 을 동시에 돌려 통과)",
|
|
"flows": [
|
|
22,
|
|
30
|
|
]
|
|
}
|
|
],
|
|
"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 기록·훈련 절차만 로컬 확인)"
|
|
]
|
|
} |