[PonsWarp Grid] coordinator를 "안전하게" 노출한다는 것
제어면이 바이트를 안 다루기로 했으니, coordinator가 노출하는 라우트는 메타만 다뤄야 한다. 이 원칙은 S1에서 문장으로 정리됐고, 이 편은 그 문장이 ponswarp-grid에서 실제 라우트 목록으로 구현된 과정을 본다. 공개 서비스로 나가는 coordinator의 라우트를 만들면서 무엇을 넣고 무엇을 금지했는지 — 그리고 "safely"라는 단어가 커밋 메시지에서 실제로 의미한 것이 무엇이었는지다.
배경을 한 문장으로 정리하면 이렇다. ponswarp-grid의 엔진은 앞 편에서 짜였고, 그것을 세상에 공개하려면 coordinator라는 접점을 인터넷에 내놓아야 한다. 내부 데모의 coordinator는 신경 쓸 것이 없다 — 쓰는 사람이 자기 자신뿐이니까. 공개 coordinator는 다르다. 첫 요청부터 모든 요청이 적대적일 수 있다고 가정해야 한다. 이 편의 라우트 표는 그 가정 위에서 만들어졌다.
"SAFELY"라는 수식이 커밋 제목에 붙는 일은 드물다. 보통 라우트 노출 커밋은 "add coordinator routes" 정도로 쓰인다. "safely"를 제목에 명시한 것은 안전이 이 커밋의 속성이 아니라 주제였다는 뜻이다 — 라우트를 무엇을 넣을지보다 무엇을 안 넣을지로 정의한 커밋이라는 선언이다.
허용/거부 원칙
| 면 | 허용 | 금지 |
|---|---|---|
| 방/멤버 | 생성·조회·입장 | 불특정 멤버 열거 |
| 조각 지도 | 조각 보유(Have) 정보 | 파일 바이트 |
| 연산 | mesh ops (CLI) | 무인가 파일 쓰기 |
이 표를 읽는 핵심은 허용 열보다 금지 열이다. 허용은 여러 버전이 가능하지만 금지는 제품의 정체성이다. 특히 이 표가 만들어진 맥락 — 공개 서비스로의 첫 노출 — 을 감안하면, 금지 열은 "가능성 있는 기능을 막았다"가 아니라 "적대적 가정 위에서 반드시 막아야 할 것"이다.
이 표의 세 행은 전부 같은 질문의 답이다 — "coordinator가 알아도 되는 것과 해도 되는 것의 경계는 어디인가." 방/멤버 행에서 금지된 "불특정 멤버 열거"는 눈에 띄기 쉽지 않은 항목인데, 공개 서비스에서는 치명적이다. 멤버 목록을 열거할 수 있으면 그리드의 존재와 구성이 외부에 노출되고, 조각 지도와 결합하면 "누가 무엇을 받고 있는가"의 사회적 정보까지 유출된다. 라우트를 만들 때 열거 기능을 안 넣는 것은 기능 부족이 아니라 프라이버시 설계다.
조각 지도 행의 "파일 바이트 금지"는 S1-02의 원칙 그 자체다. 세 번째 행의 "무인가 파일 쓰기" 금지는 coordinator가 파일 시스템과 아무 관계도 없어야 한다는 것을 의미한다 — 쓰기 경로가 없어야 침해됐을 때 털릴 것도 없다.
허용 열을 좁게 유지하는 또 하나의 이유는 진화다. 라우트는 한번 공개되면 부담이 된다 — 외부 클라이언트(브라우저, 스크립트)가 의존하기 시작하면 빼기 어렵다. 그래서 처음부터 "생성·조회·입장"이라는 최소 동사만 공개하고, 나머지는 CLI와 같은 내부 표면으로 돌린 것이다. 라우트를 늘리는 것은 언제든 가능하지만 줄이는 것은 이동 비용이 든다. 공개 API 설계의 비대칭 — 넣기 쉽고 빼기 어려움 — 을 처음부터 감안한 표다.
구현 커밋
| 커밋 | 내용 |
|---|---|
785ed4a | Expose grid coordinator routes safely |
466c3f3 | Wire mesh operations into the CLI |
a7c0d7b | Harden grid piece transfer under load |
세 커밋을 묶어 보면 하나의 배포 시퀀스다. 안전하게 열고, 운영할 수 있게 하고, 버티게 만든다. 이 순서가 뒤집힌 배포 — 먼저 열고 나중에 경화 — 는 공개 서비스 사고의 전형적인 서사인데, 여기서는 커밋 단위로 그 순서를 강제했다. 커밋 히스토리가 우선순위의 기록이 되는 사례다.
785ed4a가 이 편의 심장이다. "safely"는 라우트를 줄이는 것으로 구현됐다. coordinator가 제공하는 표면이 작을수록 공격 면도 작고, 제어면과 데이터면의 경계가 흐려질 틈도 없다. 라우트 표면을 줄이는 방식의 보안은 "막을 것을 다 막았나"를 검증할 필요가 없다는 점에서 정적 필터링보다 강하다 — 허용 목록에 없으면 전부 없는 것이다.
이 커밋의 설계 결정을 더 좁혀 보면, "safely"의 반대말이 "위험"이 아니라 "편함"이라는 게 보인다. 편하게 만들자면 멤버 열거(디버깅에 유용), 상태 덤프(운영에 유용), 테스트용 파일 업로드(개발에 유용) 같은 라우트가 자연스럽게 따라붙는다. 하나하나는 무해해 보이는 편의 라우트들이 합쳐지면 coordinator의 전체 모습이 드러나는 관찰 도구가 된다. 785ed4a가 거부한 것은 위협 모델의 특정 공격이 아니라 이 편의 라우트의 축적 자체다.
466c3f3의 mesh operations CLI 연결은 이 편에서 두 가지로 읽힌다. 첫째, 운영 가능성 — coordinator의 동작을 텍스트 계약으로 다룰 수 있어야 스크립트와 에이전트가 그리드를 운영할 수 있다. 둘째, 표면 통제 — mesh 연산을 라우트가 아니라 CLI로 노출한 것은 "공개 웹으로 나가는 표면"과 "운영자가 쓰는 표면"을 분리하겠다는 선택이다. 같은 기능이라도 어디로 노출되는가가 보안의 절반이다.
부하 아래의 경화
a7c0d7b는 piece 전송을 부하 아래에서 다뤘다. 순서는 이랬다.
- 라우트를 안전하게 노출 (표면 축소)
- mesh 연산을 CLI로 묶음 (운영 가능)
- piece 전송을 부하 아래에서 경화 (안정성)
이 순서는 우연이 아니라 우선순위다. 안전 → 운영 가능 → 안정의 순서로, 안정성이 세 번째인 이유는 "안정적으로 잘못된 것"보다 "불안정하지만 경계가 옳은 것"이 나으기 때문이다. 반대로 안정성을 먼저 만들면 경계 문제가 안정성 뒤에 숨는다. 경계가 옳다는 게 확인된 뒤에야 부하 테스트의 실패가 "경계의 결함"이 아니라 "자원 관리의 결함"으로 읽힐 수 있다. 분류가 빠르면 수정도 빠르다.
부하 경화가 브라우저 환경에서 특별히 조심할 것은 메모리다. 데스크톱은 부하가 메모리를 치솟아도 OS의 스왑과 OOM 킬러가 최후의 방어선이 되지만, 브라우저 탭은 그런 완충이 없다 — 자기 탭의 메모리 상한을 넘으면 브라우저가 탭을 죽인다. 그래서 브라우저 그리드의 부하 경화는 "처리량을 버티는가"보다 "동시에 열려 있는 조각 버퍼의 총량이 상한 안에 머무는가"가 핵심 지표가 된다. 같은 "경화"라도 환경에 따라 감시해야 할 계기판이 다르다.
부하 경화의 또 하나의 산출물은 실패 모드 목록이다. 부하를 걸어보면 실패가 나오는데, 실패의 종류를 정리해두면 다음 설계의 입력이 된다. 조각 버퍼 상한 도달, WebSocket 끊김, Have 정보 지연 — 부하 경화에서 수집된 실패 양식들은 이후 resume 설계와 연결 해제 재접속 정책의 요구사항이 된다. 부하 테스트를 "깨지는지 보는 테스트"로만 쓰면 절반이고, 실패 양식을 수집하는 관측 장비로 쓰면 전부다.
"safely"의 현재 한계
coordinator의 "safely"는 현재 미인증 상태의 공개 라우트 수준이다. 라우트가 적다는 것과 라우트가 인증됐다는 것은 다르다. 지금의 안전은 "표면이 작아서 우연히 안전한" 상태에 가깝고, 인증·정책(토큰, mTLS)이 붙으면 라우트 표의 허용 열이 다시 좁아진다 — 입장 자격을 검증하게 되니까. 이는 S4 백로그의 Control MVP로 이어진다.
이 한계를 인정하는 것이 중요한 이유는, "safely"라는 커밋 메시지가 시간이 지나면 과장으로 읽힐 수 있기 때문이다. 정직한 기록은 커밋이 달성한 것(표면 축소)과 아직 아닌 것(인증)을 구분한다. 인증이 붙을 때 허용 열이 좁아지는 방향도 미리 적어뒀다 — 입장 토큰이 생기면 "조회"는 자격자만으로 제한되고, mesh ops는 인증된 운영자만으로 제한된다. 지금의 라우트 표가 나중의 인증 설계를 미리 제약하는 모양새라서, 나중에 Control MVP를 설계할 때 이 표가 그대로 요구사항이 된다. 오늘의 표면 축소가 내일의 설계 자료가 되는 셈이다.
보안의 단계적 접근도 기록해 둔다. "인증 없이 공개"는 최종 상태가 아니라 시작점이다. 표면이 작은 상태에서 인증을 붙이는 것은 표면이 넓은 상태에서 인증을 붙이는 것보다 검증할 것이 적다. 공개화를 먼저 하고(표면 축소와 함께) 인증을 백로그로 남긴 순서는, 보안 완성을 기다리며 제품이 세상에 나가지 못하는 상황과 세상에 나가되 표면이 넓은 상황 사이에서 택한 제3의 길이었다.
마지막으로 coordinator 표면 축소가 준 대가도 공정하게 적는다. 라우트가 적으면 외부에서 시스템 상태를 볼 수 있는 창도 적다. 운영자 입장에서 coordinator의 현재 상태 — 방 개수, 활성 전송, 부하 — 를 보고 싶어지는 순간이 오는데, 그런 관측 라우트는 전부 금지 열에 가까운 성격이라 함부로 못 만든다. 관측은 인증된 채널(CLI, metrics 엔드포인트의 로컬 제한 등)로 따로 설계해야 한다. 표면 축소는 관측의 비용을 유발하지만, 그 비용을 내는 것이 공개 표면의 안전을 사는 방법이었다.
다음 편은 이 coordinator 위에서 실제 전송이 어떻게 분기되는지 — direct와 grid — 를 다룬다.