---
document_id: PLAI_SERVICE_FAILURE_DOMAIN_INVENTORY
schema_version: 1
canonical: true
snapshot_at: 2026-08-04T22:53:37+09:00
phase: PHASE_0_BASELINE_CAPTURED_GAPS_OPEN
production_changes_applied: false
secrets_collected: false
primary_server_preserved: true
external_transfers_locked: true
operator_signing_locked: true
public_rpc_exposed: false
validator_action_taken: false
evidence: docs/resilience/evidence/inventory-redacted.json
---

# PLAI 서비스 생존성 Phase 0 인벤토리와 failure-domain map

## 1. 판정

2026-08-04 스냅샷에서 `Primary A`는 웹 서비스, reverse proxy, MongoDB, DNS 도착점, 체인 RPC와 일부 체인 연결 기능이 한 Windows 호스트에 모인 공통 장애 도메인이다. 현재 상태만으로는 목표 RPO 5분/RTO 30분이나 메인 서버 소멸 후 복구 가능성을 입증할 수 없다.

기존 PC Sync 기반은 재사용 가능하다. 5개 PC의 최신 heartbeat가 확인됐고, gateway·chunk·signed manifest·restore-upload 기반도 활성 소스에 존재한다. 다만 실제 production 데이터의 복제·복원 증거는 없다. 등록된 backup index와 distributed chunk는 각각 1개이며 크기가 12 byte인 초기 검증 자료이므로 production backup으로 인정하지 않는다.

canonical QBFT는 chain ID `20260718`과 validator 4개를 유지한다. 로컬 PM2 validator 4개는 중지 상태이고, 네 대상 PC heartbeat는 `validator-active` 및 `direct-peers-applied`를 보고했다. loopback RPC에서 12초 동안 블록이 2개 증가했지만 RPC peer count는 1이었고, public bridge sync attestation은 유효하지 않았다. 따라서 체인이 현재 움직인다는 사실과 `Primary A` 상실 뒤 3/4 quorum이 유지된다는 보장은 구분한다.

이번 단계에서 production 서비스, DB 설정·데이터, Nginx, DNS, 공개 API, 외부 전송, 운영자 서명, validator 프로세스·키·set을 변경하거나 시작하지 않았다.

## 2. 조사 경계와 증거

다음 작업만 수행했다.

- 두 정본 handoff를 UTF-8 strict decoding으로 EOF까지 읽고 SHA-256을 기록했다.
- `pm2 list/describe`, listener, OS·CPU·RAM·disk, Nginx active config를 읽었다.
- DNS A record를 질의하고 local health endpoint를 GET으로 확인했다.
- MongoDB `hello`와 `listDatabases`만 실행했다. collection 문서와 credential은 읽지 않았다.
- 기존 `qbft-node-network-inventory.js`를 실행해 signed node heartbeat와 tunnel 상태를 읽고 TCP reachability만 probe했다.
- loopback QBFT RPC에서 chain ID, block height, validator 수, peer 수, sync 상태만 읽었다.
- `.env` 값, Firebase service account, private key, seed, 복구문구, API secret는 출력하거나 산출물에 넣지 않았다. URI는 host class, port, database name, credential 존재 여부로만 축약했다.

동적 결과는 이 문서의 스냅샷 시점에만 유효하다. heartbeat의 `active` 상태는 최근 300초 이내인 경우만 현재 상태로 해석했다.

## 3. Primary A 인벤토리

| 항목 | 관찰값 |
|---|---|
| 호스트 | `WIN-GP3TOEI61EO`, Windows Server 2022 Standard |
| CPU/RAM | AMD Ryzen Threadripper 3990X, logical CPU 128, RAM 255.9 GiB |
| 마지막 부팅 | 2026-07-28 23:26:29 KST |
| 로컬 disk | C 1.86 TiB, D 1.86 TiB, E 14.9 TiB, F 16.8 TiB; 모두 NTFS |
| public ingress | Nginx 한 인스턴스가 `0.0.0.0:80/443` 수신 |
| DB | MongoDB 8.2, `127.0.0.1:27017`, replica set 없음 |
| Redis | `6379` listener 없음 |
| canonical RPC | `127.0.0.1:8545`, public RPC 노출 없음 |
| QBFT bridge/P2P | local Java listener 30307/30308; public bridge sync attestation 미충족 |

### PM2 핵심 서비스

| 서비스 | source/entry | bind port | mode | snapshot 상태 | restart 누계 | health |
|---|---|---:|---|---|---:|---|
| AISFOR | `E:\aisfor\server.js` | 5108 | fork 1개 | online | 249 | `/health` 404 |
| AISTALK | `F:\aistalk\server.js` | 5110 | fork 1개 | online | 4 | `/health` 200, process-only |
| AISGROUP | `E:\aisfor-group\app.js` | 5118 loopback | fork 1개 | online | 0 | `/health` 404 |
| AISMALL | `E:\aismall\app.js` | 3700 | fork 1개 | online | 0 | `/health` 404 |
| PLAI Sharing | `F:\plai-sharing\server.js` | 4100 | fork 1개 | online | 37 | `/healthz` 200, DB 상태 포함 |
| PLAIS Chain web | `D:\aisfor-chain\server.js` | 4317 loopback | fork 1개 | online | 378 | `/health` 200, process-only |
| QBFT RPC | `start-besu-node.js` | 8545 loopback | fork 1개 | online | 1 | JSON-RPC read 확인 |
| QBFT public bridge | `start-qbft-public-bridge.js` | RPC 없음 | fork 1개 | online | 1 | listening true, synced false |
| local validator 1~4 | `start-besu-node.js` | - | fork | stopped | 0/1/3/1 | 시작하지 않음 |

`aisfor`와 `aisfor-chain`의 높은 restart 누계 원인은 이번 읽기 전용 조사로 확정하지 않았다. 자동 failover 후보를 만들기 전에 별도 로그 기반 원인 분석이 필요하다.

## 4. DNS와 ingress map

관찰한 다음 A record는 모두 같은 `Primary A` public address를 가리켰다.

- `aisfor.kr`, `talk.aisfor.co.kr`, `group.aisfor.kr`, `mall.aisfor.kr`, `sharing.aisfor.kr`, `chain.aisfor.kr`
- `for.plais.kr`, `talk.plais.kr`, `group.plais.kr`, `mall.plais.kr`, `sharing.plais.kr`, `chain.plais.kr`

Nginx upstream도 모두 같은 호스트의 loopback/로컬 port다.

| 역할 | AISFOR domain upstream | PLAIS alias upstream |
|---|---:|---:|
| identity/service | 5108 | `for.plais.kr` → 5108 |
| talk | 5110 | `talk.plais.kr` → 5110 |
| group | 5118 | `group.plais.kr` → 5118 |
| mall | 3700 | `mall.plais.kr` → 3700 |
| sharing | 4100 | `sharing.plais.kr` → 4100 |
| chain | 4317 | `chain.plais.kr` → 4317 |

따라서 DNS, TLS private key, Nginx process, Windows host, NIC, 회선 또는 전원 중 하나가 실패하면 모든 public entry가 동시에 손실된다. DNS registrar/provider와 인증서 갱신 계정의 소유자·복구 절차는 source에서 확인할 수 없어 미확인으로 남겼다.

## 5. 데이터·세션·실시간 상태

### MongoDB

`mongod.cfg`에는 `replication` 설정이 없고 `hello.setName`도 null이다. MongoDB는 loopback 단일 writable primary이며 다음 application DB가 같은 physical disk/process failure domain에 있다.

| DB | snapshot size | 소비 서비스 |
|---|---:|---|
| `aisfor` | 358,993,920 byte | AISFOR, custom Mongo session store |
| `aistalk` | 3,993,600 byte | AISTALK, `connect-mongo` session |
| `aisfor-group` | 1,880,064 byte | AISGROUP, `connect-mongo` session |
| `mall` | 2,322,432 byte | AISMALL |
| `plai-sharing` | 1,224,704 byte | PLAI Sharing |
| `aisfor_chain_ledger` | 51,675,136 byte | chain DB adapter/observation |

Mongo URI들은 현재 모두 loopback이고 credential이 URI에 없었다. `mongod.cfg`에도 authorization 활성화가 관찰되지 않았으나 loopback bind로 외부 노출은 제한돼 있다. replica set 전환은 Phase 3 계획정지·rollback gate 전에는 수행하지 않는다.

### Session과 Socket.IO

| 서비스 | session/runtime 상태 | failover 영향 |
|---|---|---|
| AISFOR | custom Mongo session store; `SESSION_SECRET` env는 snapshot에서 없음 | 코드 fallback 경로 사용 중. 값은 기록하지 않음. replica DB와 동일 secret 배포 필요 |
| AISTALK | Mongo `sessions`; HTTP와 Socket.IO가 같은 session middleware 사용 | DB 생존 시 session 재사용 가능하나 Socket.IO room/presence는 process-local |
| AISGROUP | Mongo session | DB 생존 시 session 재사용 가능 |
| AISMALL | Redis session을 시도하지만 Redis listener가 없어 MemoryStore fallback | 프로세스 재시작/standby 전환 시 session 소실 |
| PLAI Sharing | 별도 Express session 없이 AISFOR SSO 해석; post/live memory cache 존재 | Primary AISFOR SSO와 local cache에 의존 |

AISFOR, AISTALK, AISMALL, PLAI Sharing 어디에도 Socket.IO Redis adapter가 없다. active-active 전에 shared adapter, sticky/reconnect 정책과 event idempotency가 필요하다.

### Chain persistence

PLAIS Chain의 현재 기본 backend는 `json-file`이다. DB write adapter와 Mongo observation은 켜져 있지만 Mongo read switch와 Mongo live-write switch는 꺼져 있다. `data`, `runtime`, Mongo observation DB가 모두 Primary A에 있어 웹서버·RPC·복구 자료의 물리적 독립성이 없다.

## 6. local file inventory

| 경로 | files | bytes | 성격 |
|---|---:|---:|---|
| `E:\aisfor\uploads` | 141 | 866,723,129 | PII·cloud·memo·profile 등 |
| `E:\aisfor\downloads` | 159 | 2,206,936,376 | client/release artifact |
| `F:\aistalk\uploads` | 315 | 369,002,065 | private chat·sticker·profile |
| `F:\aistalk\public\downloads` | 17 | 1,227,780,837 | Android package |
| `E:\aisfor-group\uploads` | 40 | 155,367,224 | group file·notice·event |
| `E:\aismall\uploads` | 18 | 142,032,486 | inquiry 등 |
| `E:\aismall\public\uploads` | 176 | 316,585,887 | product·review·live media |
| `F:\plai-sharing\uploads` | 538 | 8,706,281,302 | user media·live·moderation |
| `D:\aisfor-chain\data` | 61 | 41,880,173 | JSON ledger/control evidence |
| `D:\aisfor-chain\runtime` | 1,399 | 5,754,832,813 | Besu/runtime state |
| MongoDB data dir | 1,289 | 2,599,268,188 | 모든 local Mongo DB |

공개 IPFS/public chain에는 이 원문 데이터를 복제하지 않는다. Phase 1에서는 암호화, manifest hash, retention, owner, restore proof를 갖춘 별도 failure domain backup만 인정한다.

## 7. 중복 실행을 막아야 할 background job

| 서비스 | 현재 자동 작업 | standby fence 필요성 |
|---|---|---|
| AISFOR | network worker 60초, point settlement simulation 10분, blockchain proof worker 1시간/auto-submit, push reminder 30초, distributed orphan cleanup/rebalance/repair/server-original prune | 필수. snapshot에서 모두 disabled=false이고 prune도 disabled=false |
| AISTALK | request/socket 기반 push·FCM, missed-call timeout, attachment registration | push/event idempotency와 presence fencing 필요 |
| AISGROUP | 별도 주기 worker 미발견 | request write fence 필요 |
| AISMALL | boot 직후와 매 30초 order auto-confirm | 필수. 현재 코드에 standby/leader fence 없음 |
| PLAI Sharing | point revocation retry 5분, storage status refresh 60초, subtitle/live persistence queue | 필수. memory cache와 queue ownership 분리 필요 |
| PLAIS Chain | canonical ledger monitor, callback/reward/ledger writes | signing·external transfer lock 유지, writer 단일성 필요 |

Passive service package는 기본값을 그대로 복사하면 안 된다. worker fence가 기본 `closed`이고 명시적 leader lease가 없으면 write job을 시작하지 않는 형태여야 한다.

## 8. PC node inventory

최근 300초 heartbeat 기준 5개 PC가 active였다. IP는 evidence에서 raw 값을 제거하고 shared egress group과 private range만 남겼다.

| PC | owner label | OS | storage quota/used | gateway | observed 역할 | failure domain |
|---|---|---|---:|---|---|---|
| Ellia-Main-01 | ellia | Windows 10.0.19045 | 2048/2.65 GiB | 5277, Primary A에서 직접 도달 실패 | storage + remote validator runtime report | `PC_EGRESS_A` |
| Ellia-test-02 | ellia | Windows 10.0.19045 | 1536/1.63 GiB | 5277, 직접 도달 실패 | storage + remote validator runtime report | `PC_EGRESS_A` |
| Ellia-test-03 | ellia | Windows 10.0.26100 | 1024/2.51 GiB | 5277, 직접 도달 실패 | storage + remote validator runtime report | `PC_EGRESS_A` |
| WIN-I3J2KPSOOLB | ellis | Windows 10.0.26200 | 1024/4.52 GiB | 미보고 | storage + remote validator runtime report | `PC_EGRESS_B` |
| ellia05 | ellis | Windows 10.0.26100 | 1536/0.56 GiB | 미보고 | storage heartbeat only | `PC_EGRESS_C` |

세 Ellia PC는 같은 관찰 public egress를 공유한다. 이것만으로 같은 전원·건물이라고 단정할 수는 없지만, 독립성이 입증되기 전에는 하나의 WAN/site failure domain으로 계산한다. `WIN-I3J2KPSOOLB`와 `ellia05`는 서로 다른 egress를 보고했지만 전원, ISP, NAT, 물리 위치의 독립성은 미확인이다.

remote CPU, RAM, physical disk health, host uptime, UPS, 자동부팅, Windows service account와 방화벽 상태는 현재 heartbeat schema에 없어서 미확인이다. storage quota는 실제 free disk 전체가 아니라 node에 할당된 quota로 해석한다.

## 9. Failure-domain map

```text
Public users / Android wrappers
              |
              | all observed DNS A records
              v
+------------------------- FD-PRIMARY-A --------------------------+
| one public address -> one Nginx -> six local service processes |
| one standalone MongoDB / no Redis / local uploads & releases   |
| chain web + loopback RPC + bridge + JSON/runtime state          |
+-----------------------------------------------------------------+
              |
              | PC Sync control/heartbeat + validator tunnel evidence
              |
       +------+----------------------+------------------+
       v                             v                  v
+-- PC_EGRESS_A --------+    +-- PC_EGRESS_B --+  +-- PC_EGRESS_C --+
| Ellia-Main-01         |    | WIN-I3J2KPSOOLB |  | ellia05          |
| Ellia-test-02         |    | storage + val.  |  | storage only    |
| Ellia-test-03         |    +-----------------+  +------------------+
| shared observed WAN  |
+-----------------------+
```

| failure domain | 포함 자원 | 단일 실패 영향 | 현재 완화 | 판정 |
|---|---|---|---|---|
| `FD-PRIMARY-A-HOST` | Nginx, PM2, Mongo, local files, chain web/RPC/runtime | 전체 public service와 DB 동시 손실 | PC chunks/validator runtime 일부 | CRITICAL |
| `FD-DNS-ACCOUNT` | 12개 core domain control | 모든 endpoint discovery 손실 | built-in fallback 없음 | CRITICAL/UNKNOWN OWNER |
| `FD-TLS-ACCOUNT` | Nginx certificate/key와 갱신 | HTTPS 접속 실패 | 다수 cert file만 확인 | HIGH/UNKNOWN RECOVERY |
| `FD-MONGO-A` | 6개 app DB·3개 session store | login·write·read 손실 | replica 없음 | CRITICAL |
| `FD-REDIS-ABSENT` | AISMALL session, 향후 Socket.IO bus | session process-local | 없음 | HIGH |
| `FD-LOCAL-MEDIA` | 모든 upload/download/runtime path | media·release·restore 손실 | production restore proof 없음 | CRITICAL |
| `FD-PC-EGRESS-A` | canonical target PC 3대 | 3 validator/storage nodes의 WAN 동시 손실 가능 | direct tunnel 보고 | CRITICAL until independence proven |
| `FD-OPERATOR-ELLIA` | target PC 3대 | 운영자/계정 장애가 3대에 동시 영향 | 두 번째 owner 존재 | HIGH |
| `FD-OPERATOR-ELLIS` | target PC 2대 | 운영자/계정 장애가 2대에 동시 영향 | 첫 번째 owner 존재 | HIGH |
| `FD-EXTERNAL-PROVIDERS` | OpenAI, Web Push/VAPID, Firebase/FCM, Google STUN, TRONGrid, app stores | 일부 기능 또는 push/call/chain 조회 저하 | fallback 미입증 | MEDIUM~HIGH |

## 10. 기존 기반의 재사용 경계

### PC Sync에서 그대로 재사용

- gateway default port 5277와 `/gateway/health`, `/gateway/manifest`, `/gateway/decentralized-manifest`, chunk 목록/제공
- node register/heartbeat와 storage quota 상태
- encrypted chunk stored/verify/restore-upload task
- signed sync client update manifest의 hash, expiry, rollback 방지 검증
- supervisor heartbeat와 device binding

서비스 replica에는 이 control plane의 node identity, heartbeat, signed manifest, health 표준을 확장한다. user media의 plaintext를 gateway manifest에 넣지 않고 encrypted object/chunk reference와 hash만 사용한다.

### Canonical validator migration에서 그대로 재사용

- chain ID `20260718`, canonical genesis와 block history 보존
- identity별 단일 private key 실행, local/remote 동시 실행 금지
- pre-sync와 activation approval 분리
- validator는 P2P only, RPC node는 별도 loopback 유지
- 3/4 quorum과 one-at-a-time rollback 원칙

일반 service/storage PC 역할 배치는 validator set 변경이나 validator 자동 승격을 의미하지 않는다. 이번 Phase 0에서는 validator key, approval, process, topology를 변경하지 않았다.

## 11. RPO/RTO와 데이터 등급

| 서비스 | 데이터 등급 | 목표 RPO | 목표 RTO | 현재 입증 상태 |
|---|---|---:|---:|---|
| AISFOR identity/cloud/memo | HIGH PII/PRIVATE | ≤5분 | ≤30분 | 미입증 |
| AISTALK chat/attachment/profile | HIGH PRIVATE/COMMUNICATION | ≤5분 | ≤30분 | 미입증 |
| AISGROUP membership/dues/files | HIGH PRIVATE/FINANCIAL-LIKE | ≤5분 | ≤30분 | 미입증 |
| AISMALL order/seller/media | HIGH PII/COMMERCE | ≤5분 | ≤30분 | 미입증 |
| PLAI Sharing media/moderation/live | HIGH USER CONTENT | ≤5분 | ≤30분 | 미입증 |
| PLAIS Chain canonical ledger/RPC | INTEGRITY CRITICAL/PUBLIC | canonical history loss 0 | ≤30분 | 현재 liveness만 관찰, host-loss 미입증 |
| DNS/endpoint manifest | CONTROL PLANE | 변경 손실 0 | ≤30분 | 단일 DNS path |

## 12. Backup과 복원 판정

- Mongo logical backup, filesystem snapshot, upload archive, release archive의 최근 성공 evidence를 찾지 못했다.
- 실제 production DB나 media를 격리 PC에 복원한 기록을 찾지 못했다.
- chain ledger의 backup index 1개와 verified chunk 1개는 12 byte이며 2026-06-29 생성된 초기 검증 자료다.
- source release manifest/ZIP은 존재하지만 DB·uploads·runtime·secret 복구를 포함하지 않는다.
- 따라서 `restore_proven=false`, `rpo_measured=false`, `rto_measured=false`다.

## 13. Secret·계정 인벤토리 원칙과 미확인 사항

값은 수집하지 않고 존재 여부와 책임 항목만 기록했다.

| secret/account class | 존재 관찰 | owner | recovery procedure |
|---|---|---|---|
| Mongo URI | 각 서비스 config ref 존재 | 미확인 | 미확인 |
| session signing secret | AISTALK/AISGROUP/AISMALL 존재; AISFOR env 부재 | 미확인 | 미확인 |
| Web Push VAPID | AISFOR/AISTALK 존재 | 미확인 | 미확인 |
| Firebase service account/FCM | AISTALK source 지원, 실제 custody는 미확인 | 미확인 | 미확인 |
| OpenAI API key | AISFOR/AISTALK source ref | 미확인 | 미확인 |
| TRONGrid API key | PLAIS Chain source ref | 미확인 | 미확인 |
| DNS registrar/provider | domain 동작만 확인 | 미확인 | 미확인 |
| TLS issuance/renewal | local cert file만 확인 | 미확인 | 미확인 |
| Windows/PM2 admin | 단일 host runtime account 관찰 | 미확인 | 미확인 |
| Play Console/app signing | Android artifact 링크만 확인 | 미확인 | 미확인 |
| validator/operator keys | encrypted custody 기반만 확인, 값 미열람 | 승인 gate 유지 | 기존 runbook |

## 14. Phase 0 남은 gate

다음 항목이 채워지기 전에는 Phase 0을 완료로 표시하거나 Phase 2 service standby를 production DB에 연결하지 않는다.

1. 각 PC의 CPU, RAM, disk model/health/free, uptime, UPS, auto-start, physical site, ISP/NAT/VPN, maintenance owner를 signed inventory로 수집
2. `PC_EGRESS_A` 3대의 실제 전원·건물·router 독립성 확인
3. DNS, TLS, domain registrar, Windows, PM2, Firebase, Play Console, backup 계정의 owner와 복구 담당자 지정
4. 모든 service의 secret reference owner와 새 node 전달/rotation 절차 작성
5. Mongo·uploads·downloads·chain data/runtime의 현재 backup job과 retention 확인
6. production traffic/DB와 격리된 PC에서 실제 restore proof 생성
7. AISFOR/AISGROUP/AISMALL의 dependency-aware readiness endpoint 설계
8. AISFOR, AISMALL, PLAI Sharing worker를 기본 fence-closed로 만드는 standby profile 설계
9. Primary A 차단 시 direct validator tunnel과 3/4 quorum이 유지되는지 별도 승인된 rehearsal로 검증

다음 안전 작업은 Phase 1의 실제 backup 실행이 아니라, 먼저 위 미확인 ownership·hardware 항목을 signed/redacted inventory schema로 보완하고 격리 restore rehearsal 계획과 rollback 기준을 작성하는 것이다.

## 15. Phase 0 후속 preflight 결과

읽기 전용 후속 조사와 준비물은 `02-PHASE1_BACKUP_RESTORE_REHEARSAL_KO.md`에 기록했다.

- MongoDB backup/restore tools와 공개키 암호화 도구가 현재 설치돼 있지 않다.
- production backup scheduled task를 찾지 못했다. AISTALK의 빈 `backup` directory와 AISMALL의 작은 source성 `backup` directory는 production restore evidence로 인정하지 않는다.
- elevated `vssadmin list shadows`는 shadow entry를 반환하지 않았다.
- PM2 자동 시작 task는 최근 성공했지만 Nginx 자동 시작 task의 최근 result는 1이었다.
- win-acme renewal task의 최근 result는 `0xFFFFFFFF`였다.
- 핵심 TLS certificate는 2026-09-19~2026-10-22 사이에 만료된다.
- remote hardware 공백을 채우기 위한 local-only collector와 Phase 1 fail-closed preflight를 추가했으며 어느 것도 PC에 배포하거나 production backup을 실행하지 않았다.
