🚀 MAM 메시징 백플레인 전환 실행 로드맵 (implementation_plan.md)
- 문서 버전: v1.0.0 (
8c651798 / 28bb7340)
- 작성/관리 주체: Multi-Agent Orchestration Team (
claude, agy, cline)
- 기준 커밋:
a9934ad (276/276 baseline tests passing)
- 문서 목적: MAM의 메시징 인프라를 공개 HiveMQ 브로커에서
nats-server 전용 사설 브로커로 무중단 전환하기 위한 4개 트랙(Track 03)과 5단계 마일스톤(M0M4)의 구체적 실행 지침 및 진행 상황 추적.
- 연계 문서:
NATS_REPORT.md, PRIVATE_SERVER.md, IMPROVEMENTS.md
1. 개요 및 4개 트랙 구조
| 트랙 |
대상 과제 |
핵심 목표 |
코드 변경 지점 |
| Track 0 |
B-14, B-15 (P1) |
브로커 다운 시 65분 정지(Hang) 및 오판정 방지 (로컬 디스크 내결함성) |
publish_event.py, job_subscriber.py, multi-agent-mux-delegate-job |
| Track 1 |
O-5 (P2) |
nats-server MQTT 3.1.1 어댑터 호환성 및 Retained 메시지 실측 검증 |
격리 클론 ($SCRATCH/nats-spike) |
| Track 1R |
원격 프로덕션 (P1) |
VPS/홈랩 nats-server Docker 상시 가동 및 MAM 원격 백플레인 전환 |
PRIVATE_SERVER.md §9, 서버측 docker-compose.yml/nats.conf, .mam.env |
| Track 2 |
A-2, B-16 (P2) |
워크스페이스 지문 토픽 격리 및 auth_token 무조건 발급 강제 |
mqtt_common.py, registry.py, reconcile.sh |
| Track 3 |
문서/설정 동기화 |
공식 가이드, 배포 스크립트, 환경변수 템플릿 일원화 |
MESSAGING.md, IMPROVEMENTS.md, VERSIONS.md, deploy/* |
2. 단계별 마일스톤 (Milestones M0 ~ M4)
각 마일스톤은 완료 정의(DoD)와 엄격한 게이트(Gate)를 가지며, 게이트 조건을 충족하지 못하면 다음 마일스톤으로 진입할 수 없습니다.
| 마일스톤 |
이름 |
완료 정의 (Definition of Done) |
통과 게이트 (Gate Condition) |
| M0 |
문서 정합성 확보 |
PRIVATE_SERVER.md E-1~E-4 교정, 다능성 절 추가, 본 로드맵 작성 |
G-D1 ~ G-D4 가드 테스트 통과 (276 -> 280) |
| M1 |
내결함성 확보 (Track 0) |
B-14, B-15 코드 패치 완료 |
G-1 ~ G-10 가드 통과 + mutation 전건 FAIL 확인 (280 -> 290) |
| M2a |
로컬 스파이크 (Track 1) |
격리 클론에서 S-1 ~ S-9 스파이크 완수 |
S-3(Retained Terminal Event) 통과 (실패 시 mosquitto로 분기) |
| M2b |
원격 프로덕션 전환 (Track 1R) |
D-1~D-5 교정 + §9 원격 배포 + §9.5 전환 |
R-3(노출0) · R-5(retained) · R-6(신원) · R-9(계정격리) 동시 통과 |
| M3 |
보안 종결 (Track 2) |
A-2 지문 토픽 전환, G-11 무조건 토큰 발급 |
지문 토픽 동작 확인 후 legacy 구독 제거 |
| M4 |
동기화 완료 (Track 3) |
MESSAGING.md, IMPROVEMENTS.md, VERSIONS.md, deploy/* 정합 |
전체 테스트 스위트 100% Green |
3. Track 0: 가용성 및 로컬 내결함성 교정 (B-14, B-15)
핵심 원칙: 브로커 선택과 완전히 독립적인 선행 과제이며, Step 1 -> Step 2 -> Step 3의 엄격한 순서 의존성을 갖습니다. Step 2를 먼저 구현하면 디스크에 터미널 상태가 기록되지 않아 폴백 효과가 0이 됩니다.
3.1 Step 1 — publish_event.py 실패 처리 순서 재구성 (B-14 / F-1)
publish(...) 함수를 try-except로 감싸되, 네트워크 실패 시 즉시 return 2 하지 않고 publish_ok = False로 표시합니다.
mqtt_common.append_event 감사 로그 작성 및 mqtt_common.update_job_status(status=new_status) 레지스트리 상태 동기화를 발행 성공 여부와 무관하게 항상 수행합니다.
- 감사 로그 레코드에
"published": publish_ok 및 "publish_error": str(exc) 필드를 기록합니다.
- 모든 로컬 디스크 동기화가 완료된 후, 네트워크 발행이 실패했다면 기존 호출부 계약 유지를 위해
return 2를 반환합니다.
3.2 Step 2 — job_subscriber.py 로컬 디스크 폴백 도입 (B-15 / C1)
- 대기 루프의
queue.Empty 분기(job_subscriber.py:233)에서, 3초 간격 스로틀로 감시 중인 잡의 디스크 터미널 상태를 확인합니다 (registry.load_job -> mqtt_common.read_logged_status).
- 디스크에서 터미널 상태(
completed 또는 error)가 감지되면, source: disk-fallback 합성 이벤트를 표준 출력에 기록하고 즉시 정상 종료합니다.
- 종료 코드 매핑: 디스크 상태가
completed이면 return 0, error이면 return 1을 반환합니다.
3.3 Step 3 — multi-agent-mux-delegate-job 인프라 예외 분리 (B-15 / F-4)
job_subscriber.py의 미포착 브로커 접속 예외에 전용 종료 코드 rc=3을 부여합니다.
multi-agent-mux-delegate-job:331-341의 sub_rc 매핑에 rc=3 분기를 추가하여 job_status="broker_unavailable"로 분류하고, wait_for_job과 동일하게 디스크 상태를 재확인합니다.
3.4 Track 0 회귀 가드 매트릭스 (10종 신설 — G-1 ~ G-10)
| ID |
가드 내용 |
변이 검출 기준 (Mutation) |
| G-1 |
브로커 도달 불가 시 publish_event.py가 rc=2이면서 레지스트리 status=completed 기록 |
return 2를 상태 동기화 앞으로 이동 시 FAIL |
| G-2 |
동일 상황 감사 로그에 published: false 및 publish_error 레코드 존재 |
append_event를 성공 경로로만 한정 시 FAIL |
| G-3 |
브로커 정상 시 rc=0 + status=completed + published: true 무회귀 검증 |
— |
| G-4 |
발행 실패 후 last_seq가 1 증가하고 후속 발행이 더 큰 seq 사용 |
seq 롤백 도입 시 FAIL |
| G-5 |
디스크 status=completed 선작성 시 브로커 다운 상태에서도 job_subscriber.py가 3초 내 rc=0 종료 |
디스크 폴백 제거 시 FAIL |
| G-6 |
동일 조건에서 stdout 합성 라인에 disk-fallback 표기 확인 |
표기 누락 시 FAIL |
| G-7 |
디스크 status=error 시 폴백 rc=1 반환 확인 |
매핑 반전 시 FAIL |
| G-8 |
다중 잡 감시 시 전체 완료 전까지 조기 종료 방지 |
부분 종료 도입 시 FAIL |
| G-9 |
디스크 터미널 부재 + 브로커 실패 시 rc=3 반환 확인 |
rc=1로 되돌릴 시 FAIL |
| G-10 |
loop 위임 경로에서 rc=3 수신 시 job_status가 "error"로 오판되지 않음 확인 |
3분기 매핑 복원 시 FAIL |
4. Track 1: nats-server 스파이크 검증 (O-5)
실행 원칙: 메인 저장소 작업 트리를 오염시키지 않기 위해 격리 클론(git clone --local --no-hardlinks . "$SCRATCH/nats-spike")에서 수행하고 종료 후 삭제합니다.
| ID |
검증 항목 |
검증 방법 |
통과 기준 |
| S-1 |
nats-server MQTT 리스너 기본 수용 |
nats-server -c nats.conf 기동 후 started/progress/completed 3연속 발행 |
rc=0, 레지스트리 status=completed |
| S-2 |
paho 2.x CallbackAPIVersion.VERSION2 호환 |
on_connect CONNACK reason code 수신 확인 |
reason_code == 0 |
| S-3 |
Retained Terminal Event 전달 (핵심 관문) |
--event completed 발행 후 신규 job_subscriber.py 기동 |
즉시 최종 이벤트 수신 (실패 시 mosquitto로 회귀) |
| S-4 |
QoS 1 발행 ACK |
info.wait_for_publish() 대기 |
is_published() == True |
| S-5 |
와일드카드 토픽 라우팅 |
mam/<fp>/jobs/+/events 구독 후 이벤트 수신 |
SUBSCRIBED 출력 및 페이로드 수신 |
| S-6 |
인증 및 TLS 암호화 |
user/pass 및 TLS 구성 후 접속 테스트 |
자격증명 누락 시 거부, 유효 시 성공 |
| S-7 |
Subject 단위 권한 격리 |
Publisher write-only / Subscriber read-only 설정 |
비인가 작업 시 연결 거부 |
| S-8 |
전체 회귀 테스트 |
pytest tests/ -q |
전건 PASS (0 failed) |
| S-9 |
Track 0 내결함성 통합 검증 |
nats-server 강제 종료 상태에서 위임 잡 완주 테스트 |
wait_for_job 3초 내 반환 |
5. Track 1R: 원격 프로덕션 전환 로드맵 (M2b 상세)
6. Track 2: A-2 보안 결함 및 워크스페이스 격리 해소 (A-2, B-16)
auth_token 무조건 발급 (F-3 / G-11):
registry.register_job()에서 브로커 설정과 무관하게 항상 secrets.token_urlsafe(32) 기반 토큰을 발급하여 공개 브로커 환경에서도 HMAC 검증이 무력화되지 않도록 강제합니다.
- 워크스페이스 지문 토픽 3단계 전환 (
F-2):
- Step 1: 발행자 기본 토픽을
mam/<sha256[:12]>/jobs/<id>/events로 전환합니다.
- Step 2: 실환경 및 통합 테스트에서 이벤트 수신을 확인합니다.
- Step 3:
reconcile.sh:237의 레거시 전역 토픽(python/mqtt/jobs/...) 구독을 제거합니다.
7. Track 3: 문서 및 배포 설정 동기화
8. 진행 추적 체크리스트
M0: 문서 정합성 확보
M1: Track 0 내결함성 확보 (B-14, B-15)
M2a: Track 1 nats-server 로컬 실증 (O-5)
M2b: Track 1R 원격 프로덕션 전환
M3: Track 2 보안 및 토픽 격리 (A-2, B-16)
M4: Track 3 문서 및 배포 동기화