2026-09-21(월) — 아트조이 AI PC devplan 8080 상시운용 마감 + 포트 감시기 신설
[페페 지시] 「AI PC(100.122.89.28) devplan 8080 을 상시 운용으로 마감하라」. 전제(26.9.21 오전, 같은 날 앞 세션) =
tailscale serve→netsh portproxy 0.0.0.0:8080 → 127.0.0.1:8799교체 완료. 이 세션이 한 일 = ①재부팅 없이 부팅 지속 조건 검증 ②감시기 신설 ③유지보수 보호 ④공개범위 불변 확인.
1. 부팅 지속 — 실측한 것 (재부팅 안 함)
| 항목 | 실측값 | 재부팅 후 살아나나 |
|---|---|---|
| portproxy | HKLM\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp 에 0.0.0.0/8080 = 127.0.0.1/8799 |
✅ 레지스트리 영속 + iphlpsvc Automatic·Running |
| 방화벽 | Cody-Devplan-8080 PersistentStore · Enabled · Inbound · Allow · TCP 8080 · remote 100.64.0.0/255.192.0.0 |
✅ 정책 저장소라 부팅 시 ActiveStore 로 로드 |
| devplan 서버 | \Cody\DevplanServer = wscript C:\cody\devplan_server.vbs · SYSTEM · 부팅 트리거 + 5분 반복 · 배터리/네트워크/유휴 조건 없음 · StartWhenAvailable=True · MultipleInstances=IgnoreNew · 마지막 rc=0 |
✅ |
| 테일스케일 | 서비스 Automatic·Running · BackendState=Running |
✅ |
| 전원 | 구성표 고성능 · AC 대기모드 0(안 잠든다) · 가동시간 18시간 | ✅ |
| 실행 파일 | C:\Python312\pythonw.exe·C:\cody\serve_devplan.py 둘 다 존재 |
✅ |
재부팅 대신 한 실측 2건(둘 다 통과)
- ⓐ portproxy 재바인드 — 행을 delete 하니 8080 리스너가 사라지고(0), add 하니 svchost(iphlpsvc) pid 3928 이 0.0.0.0:8080 을 0.7초 만에 다시 잡았다. 로컬 http://127.0.0.1:8080/devplan/ 200.
→ 8080 을 선점하는 프로세스가 없고, 부팅 경로에서 바인드가 실패할 구조적 이유가 없다는 뜻.
- ⓑ 서버 되살리기 — 8799 pythonw(pid 8832)를 죽이고 schtasks /run "\Cody\DevplanServer" → 1초 만에 새 pid 17724 로 부활, HTTP 200 복귀.
⛒iphlpsvc 재시작(= 가장 완전한 부팅 시뮬레이션)은 일부러 안 했다.
Tailscale 서비스가 iphlpsvc 에 의존한다((Get-Service iphlpsvc).DependentServices = Tailscale, NcaSvc).
재시작하면 테일스케일이 내려가고 ssh·AnyDesk 가 전부 테일넷 위에 있어 원격 경로가 통째로 끊긴다.
다시 안 올라오면 아트조이 사무실에서 사람이 손대야 한다 — 얻을 정보보다 잃을 위험이 크다.
게다가 코덱스가 짚은 대표 실패조건(「listenaddress 를 특정 IP 로 주면 부팅 때 그 주소보다 iphlpsvc 가 먼저 떠서 바인드를 놓친다」)은
우리가 0.0.0.0 을 쓰므로 해당이 없다. IPv6 DisabledComponents=142 도 native IPv6(0x10) 는 안 끈 값이라 v4tov4 에 영향 없다(현재 정상 동작이 증거).
2. 🔴 덤으로 잡은 사실 — 8080 을 여는 건 우리 규칙이 아니다
방화벽 규칙 Cody-Devplan-8080 을 삭제했는데 접속이 그대로 200 이었다.
전수조사(인바운드·Enabled·Allow·TCP/Any·8080 또는 Any 포트·프로그램/패키지 미지정) 결과 남는 건 Tailscale-In 둘뿐:
Tailscale-In | remote=Any | local=100.122.89.28
Tailscale-In | remote=Any | local=fd7a:115c:a1e0::a73a:591d
Tailscale-In 은 포트가 Any 지만 LocalAddress 가 테일넷 주소로 묶여 있어 테일넷으로 들어온 트래픽만 매칭된다.
→ 공개 범위는 변함없이 테일넷 전용(LAN 192.168.1.x·공인 인바운드는 기본 차단 그대로). 우리 규칙은 이중잠금이었다.
⛒결론 = 「HTTP 200 이면 방화벽도 정상」은 거짓. 감시기는 방화벽을 가용성과 분리해서 매 주기 따로 본다.
3. 감시기 — \Cody\artjoy-devplan-watch (미니PC, 5분)
| 무엇 | 값 |
|---|---|
| 본체 | C:\dev\tools\artjoy-ai-setup\devplan_watch.py |
| 진입점 | C:\dev\tools\artjoy-ai-setup\_devplan_watch_hidden.vbs(wscript 숨김 · 창 0) |
| 원격 프로브 | C:\cody\devplan_probe.ps1(AI PC · 읽기 전용 · ASCII 전용) |
| 예약작업 | \Cody\artjoy-devplan-watch · delix Interactive · 5분 반복 · IgnoreNew · 실행시간 제한 4분 |
| 로그 | C:\_work\artjoy-dev\devplan_watch.log(1MB 회전) · 마지막 1회분 devplan_watch_task.log |
| 상태 | C:\_work\artjoy-dev\devplan_watch_state.json |
왜 미니PC에서 도나 — 검증해야 할 질문은 「조수은 PC 브라우저가 실제로 열리나」다. AI PC 안에서 자기 포트를 찌르면 테일넷을 타지 않아 그 질문의 답이 안 된다.
점검 5종 = ①http://100.122.89.28:8080/devplan 200 + 본문 마커(data-artjoy-page="devplan" — 200만 보면 엉뚱한 서버도 통과한다) ②8799 리슨 ③portproxy 행(레지스트리로 읽는다) ④방화벽 범위 ⑤테일스케일 BackendState.
복구 — 허용 6개뿐(사전 밖 명령은 실행 자체가 안 된다): server(=schtasks /run \Cody\DevplanServer) · portproxy(행 add) · rebind(행 delete+add) · fw_create · fw_enable · fw_narrow.
알림만 하고 손대지 않는 것 = ssh 불통(전원·테일스케일) · iphlpsvc 정지 · portproxy 행이 «다른 형태» · 방화벽 규칙 중복 · 테일넷 밖으로 열린 남의 규칙 · 8799 는 살아 있는데 HTTP 가 안 오는 hang.
🔴 절대 금지 — 재부팅·shutdown·강제 로그오프·프로세스 강제종료. selftest 가 소스 전체를 정규식(shutdown|Restart-Computer|Stop-Computer|logoff|Restart-Service|taskkill|Stop-Process)으로 훑어 한 줄이라도 있으면 실패한다.
시끄러움 제어 = 1회 실패는 무음(유예) → 2회 연속부터 복구 · 복구는 10분에 1번·사건당 3번까지(넘으면 gave_up = 자동복구 중단, 하루 1번만 상기) · 알림은 사건당 1회(6시간마다 리마인더) · 복귀는 정상 2회 연속이어야 인정(플래핑 방지) · 정상이면 완전 무음.
4. 유지보수 보호
python C:\dev\tools\artjoy-ai-setup\devplan_watch.py maint 30 "8799 서버 교체" # 최대 180분
python C:\dev\tools\artjoy-ai-setup\devplan_watch.py maint off # 풀면 10분 유예
표식 = AI PC C:\cody\MAINTENANCE.json(until·who·why). 살아 있는 동안 복구도 알림도 0.
- 고아(만료시각 지남·JSON 깨짐) → 재부팅 따위로 확대하지 않고 MAINTENANCE.expired.json 으로 이름만 바꾸고(지우지 않는다 — 무엇이 있었는지 남아야 사람이 판단한다) 알림 1회 + 10분 유예 후 감시 재개.
- ssh 불통이면 표식은 «없음»이 아니라 «불명» → 직전 주기가 유지보수였으면 그대로 유지보수로 본다(복구 금지).
- 표식을 걸면 진행 중이던 사건도 접는다(해제 후 0부터 다시 센다).
5. 시험 결과 — 전부 실측
| 시나리오 | 기대 | 결과 |
|---|---|---|
자체검사 selftest |
통과 | ✅ (금지명령 스캔 · 표식 5케이스 · portproxy 4케이스 · 방화벽 9케이스 · 상태기계 전이) |
| 정상 | 무음 | ✅ 로그만 정상. http=200 |
| 유지보수 중 portproxy 삭제 | 복구 0 · 알림 0 | ✅ 2주기 돌렸는데 행이 그대로 없었다 |
| 유지보수 해제 직후 | 10분 유예 | ✅ 유예/1회실패 — 조용히 넘긴다 |
| 1회 실패 | 무음 | ✅ |
| 2회 연속 실패(portproxy 없음) | 자동복구 + 알림 | ✅ 복구 portproxy rc=0 → 200 복귀 → 「🔧 자동복구」 |
| 방화벽 규칙 삭제(HTTP 는 정상) | 가용성과 별개로 잡아서 재생성 | ✅ 「⚠ 공개범위 드리프트」 + fw_create rc=0 · 범위 100.64.0.0/10 그대로 |
| 고아 표식(만료시각 과거) | 만료 처리 + 알림 1회 | ✅ .expired 로 rename |
| 예약작업 실전 | 숨김 실행 · rc=0 · 무음 | ✅ 창 0 · 39초 |
6. 운영 데이터·공개 범위
무변경. 8799 서버 프로세스는 시험 중 1회 재기동(kill→task)했고 DB·파일은 건드리지 않았다.
포트포워딩 추가 0 · Funnel 0 · tailscale serve 추가 0 · 방화벽은 삭제→동일 범위로 재생성(최종 상태 = 시작과 같다).