컨테이너에서 아무 패키지나 npm install 하면 lock 에서 @emnapi/core·@emnapi/runtime 이 빠지고 npm ci 가 실패했다.
원인은 npm 11.6.1~11.6.2 의 버그다 — npm install 이 의존 표시를 다시 계산하면서, 들어오는 peer 간선이 있는 패키지를
일반 의존성이어도 peer 로 남기고 peer 이면서 optional 인 패키지를 지운다. jest 가 쓰는 unrs-resolver 1.12.x 의 wasm32 바인딩이
그 모양이다(@napi-rs/wasm-runtime 1.x 가 emnapi 를 필수 peer 로 둔다).
- unrs-resolver 와 바인딩을 1.11.1(2025-07), @napi-rs/wasm-runtime 을 0.2.12 로 내렸다 — emnapi 를 일반 의존성으로 쓴다.
모두 개발 전용이고 package.json·운영 의존성·npm audit 결과는 그대로다
- 깨끗한 리눅스 컨테이너(npm 11.6.2)에서 확인: npm ci, 인자 없는 npm install 뒤 lock 바이트 동일,
다른 패키지를 설치한 뒤 npm ci, npm ci --omit=dev, 운영 이미지 빌드
- npm 을 11.6.3 이상으로 올리면 원인이 사라진다 (docker 스킬 "의존성은 컨테이너 안에서")
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
메일 쓰기에 서식 편집기가 들어오면서 서버가 HTML 본문을 받아 보낸다. 편집기의 출력은 믿지 않고 서버가 다시 정화한다.
- 서식 본문(bodyHtml)은 보내는 메일 전용 허용 목록으로 다시 정화해 텍스트 대체 본문과 multipart/alternative 로 보낸다
(style 은 속성마다 값 형식을 처음부터 끝까지 맞추고, 링크는 http·https·mailto 만). 정화해서 비면 텍스트 메일로 보낸다
- 받은 메일 정화가 편집기 서식을 모두 남기도록 넓혔다 — 보낸편지함·받는 사람 화면에서 서식이 사라지지 않게
- 발송 POST /api/messages 만 JSON 4MB (서식 본문·긴 인용). 중복 방지 지문에 정화한 서식 본문을 넣는다
- htmlparser2 는 중첩 깊이의 제곱으로 느려져, 깊게 중첩한 2MB 메일 한 통에 3분 가까이 서버가 멈췄다.
정화 전에 선형 시간으로 파서 스택 비용을 재고, 넘으면 받은 메일은 htmlBody 없이(텍스트로), 서식 본문은 VAL_001
- 정화한 서식 본문이 2,000,000바이트(본문 읽기 한도)를 넘으면 VAL_001 — 다시 읽을 때 표시 없이 잘리지 않게
- 받은 메일 정화의 다른 제곱 시간 경로 2개(style 의 url() 정규식, 주소 없는 img 를 지우는 필터)도 고쳤다
- htmlparser2 를 직접 의존성으로 선언한다 — sanitize-html 이 쓰는 12.0.0 과 같은 판이라 lock 은 루트 한 줄만 바뀐다
계약: docs/mail/api-contract.md 2절 "서식 본문"·"읽기 한도"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
첫 사내 모듈인 웹메일의 백엔드다. 메일 엔진은 외부(Stalwart)에 두고 백엔드가 JMAP 으로 중계한다.
- 직원 로그인 = 메일 계정: 엔진으로 비밀번호를 확인한 뒤 HttpOnly 쿠키 JWT 를 발급한다.
리프레시 세션은 DB 에 해시로 저장하고 1회용으로 회전한다 (이미 쓴 토큰이 오면 세션 폐기)
- 로그인 실패 한도는 엔진에 묻기 전에 원자적으로 차지하고, IPv6 는 /64 로 묶어 센다
- 상태를 바꾸는 요청은 출처(Sec-Fetch-Site·Origin)를 검사하고, 클라이언트 IP 는 TRUST_PROXY 로만 해석한다
- 메일함·목록·읽기·발송·첨부 API: 받은 HTML 은 정화해서 내보내고, 발송은 Idempotency-Key 로 중복을 막는다
- 메일 엔진 관리자 계정은 로그인·대리 접근 대상에서 막는다 (게이트웨이가 관리자까지 대리할 수 있다)
- 에러 코드 AUTH_004·MAIL_001~006 을 추가하고 클라이언트 에러 사전을 다시 생성했다
계약: docs/mail/api-contract.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>