본문으로 건너뛰기
피드

SMTP를 HTTP 위에서 다시 만든다면? 기존 부품만 조립한 현대식 이메일 실험

backend 약 10분
vote
0
댓글
북마크

글쓴이는 SMTP를 대체하려는 게 아니라, HTTP·WebFinger·Ed25519·HPKE·JMAP 같은 이미 검증된 기술로 이메일의 문제를 다시 설계해보는 HMTP 실험을 소개해. 핵심은 주소 형태만 user@domain으로 유지하고, 전송·발견·서명·암호화·스팸 방어·읽기 모델을 전부 현대 웹 인프라 위에 얹는 거야.

  • 1

    HMTP는 새 기술을 발명하기보다 HTTP, TLS, WebFinger, ActivityPub, HPKE, JMAP 같은 기존 표준을 조립하는 설계임

  • 2

    MX 레코드 대신 .well-known 문서로 사용자별 inbox와 키를 발견하고 위임할 수 있음

  • 3

    메시지는 콘텐츠 해시 기반 ID를 가져 재시도 중복 문제를 구조적으로 줄임

  • 4

    서명과 엔드투엔드 암호화를 기본 모델에 넣고, 낯선 발신자는 요청함이나 402 Payment Required로 제어함

  • 5

    글쓴이는 Python 단일 파일 프로토타입까지 구현했지만, 암호화는 감사를 받지 않았으니 실제 비밀 통신에 쓰지 말라고 못박음

  • 이 글은 “이메일을 HTTP 위에서 다시 설계하면 어떨까?”라는 꽤 개발자다운 장난에서 출발함

    • 목표는 Gmail이나 기존 SMTP 세계와 호환되는 새 메일 시스템을 만드는 게 아님
    • 주소 모양만 user@domain으로 남기고, 전송·수신·키·위임·스팸 방어를 전부 다시 조립해보자는 실험임
  • 이름은 HMTP, Hypertext Mail Transfer Protocol임

    • SMTP의 Simple 대신 HTTP의 H가 들어간다는 농담 섞인 작명임
    • 하지만 내용은 꽤 진지함. HTTP, TLS, WebFinger, ActivityPub, Webmention, Ed25519, HPKE, sigchain, JMAP 같은 이미 배포된 기술만 사용하자는 설계임
  • HTTP를 고른 이유는 단순히 익숙해서가 아니라, 새 프로토콜이 언젠가 만들어야 할 걸 이미 갖고 있기 때문임

    • TLS, 가상 호스팅, SNI는 그냥 따라옴
    • 202 Accepted는 큐에 들어감, 429 Too Many RequestsRetry-After는 속도 제한, 404/410은 메일함 없음, 413은 너무 큼, 심지어 402 Payment Required는 유료 안티스팸에 딱 맞음
    • Nginx, 프록시, 로드밸런서, 언어별 HTTP 라이브러리도 그대로 쓸 수 있음

MX 대신 .well-known

  • HMTP의 사용자 발견은 DNS MX 대신 정적 문서로 처리함

    • 예를 들어 https://example.com/.well-known/hmtp/ana를 GET하면 Ana의 inbox URL, 키, 기기 목록을 JSON으로 받는 식임
    • inbox는 mail.migadu.example 같은 다른 호스트에 있어도 되고, 정적 블로그가 JSON 하나로 메일 제공자에게 위임하는 것도 가능함
  • 이 방식은 MX가 못 하던 사용자별 위임도 가능하게 함

    • 같은 도메인의 각 메일함이 서로 다른 제공자에 붙을 수 있음
    • 굳이 새 라우트를 만들 필요도 없음. WebFinger(RFC 7033)가 이미 비슷한 발견 문제를 풀고 있고, Mastodon이 대규모에서 돌아간다는 걸 보여줬음

정체성은 키 하나에 걸면 안 됨

  • 글쓴이는 정체성을 암호키 자체로 보면 안 된다고 봄

    • 키는 잃어버릴 수 있고, 만료될 수 있고, 교체될 수 있음
    • 그래서 현재 키와 키 교체 체인을 discovery 문서에 공개하고, 새 키는 이전 키가 서명하는 sigchain 구조를 제안함
  • 그래도 키를 완전히 잃어버리면 도메인 통제권이 fallback이 됨

    • 예를 들어 노트북을 도난당해 서명된 회전을 못 하면, 도메인이 새 키를 선언하되 30일 같은 공지 기간을 둠
    • 이때 서버는 “서명이 아니라 도메인으로 정체성이 다시 고정됐다”는 경고를 보여줄 수 있음

⚠️주의

> 도메인 기반 정체성의 약점은 도메인이 소유물이 아니라 임대물이라는 점임. 도메인을 놓치면 새 등록자가 .well-known에 자기 키를 올리고 메일을 받거나 서명할 수 있는데, 이 문제는 ATProto가 DID로 풀려는 바로 그 지점임.

배달은 POST, 책임은 발신자 서버

  • HMTP 배달은 수신자 inbox로 HTTP POST를 보내는 구조임

    • 다만 클라이언트가 직접 상대 서버로 보내는 게 아니라, 자기 서버의 outbox에 인증된 POST를 넣음
    • 그다음 발신자 서버가 큐에 넣고, 실패하면 지수 백오프(exponential backoff)로 재시도하고, Retry-After도 존중함
  • 여기서는 SMTP의 MUA/MSA/MTA 분리가 옳았다고 인정함

    • 목적지 서버가 다운돼도 내 서버가 며칠 동안 재시도하고, 사용자는 잊고 살아도 되는 구조가 이메일의 조용한 장점이라는 거임
    • HMTP는 그 장점을 유지하되, 메시지 ID를 콘텐츠 해시로 만들어 재시도 중복 문제를 줄임
sequenceDiagram
    autonumber
    participant 발신자클라이언트 as 발신자 클라이언트
    participant 발신자서버 as 발신자 서버
    participant 수신자서버 as 수신자 서버
    발신자클라이언트->>발신자서버: outbox에 서명된 메시지 POST
    발신자서버-->>발신자클라이언트: 202 큐 등록
    발신자서버->>수신자서버: .well-known으로 inbox와 키 조회
    발신자서버->>수신자서버: inbox에 봉인된 본문 POST
    Note over 수신자서버: 첫 시도 실패
    발신자서버->>수신자서버: 같은 ID로 재시도
    수신자서버->>발신자서버: 발신자 키 조회 후 서명 검증
    수신자서버-->>발신자서버: 201 배달 완료

서명, 암호화, 스팸 방어도 프로토콜 안으로

  • 메시지는 그냥 텍스트가 아니라 서명된 객체임

    • id, from, to, date, in_reply_to, subject, 암호화된 body, signature를 가진 JSON 객체에 가까움
    • 저장된 메일 자체가 암호학적 증명을 갖고 있어서, 전달된 메일도 원래 서명을 보존할 수 있음
  • 발신자 검증은 DKIM 대신 .well-known에서 키를 가져와 확인함

    • 수신 서버가 from 도메인의 키 문서를 GET하고, 그 키로 서명이 맞는지 검증함
    • 본문은 X25519 키와 HPKE로 봉인하고, envelope만 라우팅과 필터링을 위해 보이게 둠
  • 첨부파일은 메시지 안에 base64로 우겨넣지 않음

    • {hash, url, size} 형태로 발신자 서버의 파일을 가리키고, 수신자는 필요할 때 다운로드함
    • 수신자 서버가 원하면 미러링할 수 있어서, 메일함이 첨부파일 때문에 무거워지는 문제도 줄어듦
  • 스팸 방어는 여러 층으로 쌓음

    • 도메인에 정체성을 묶어 독립 신원 생성 비용을 올림
    • 모르는 발신자는 바로 inbox가 아니라 Signal의 메시지 요청처럼 requests 박스에 들어감
    • 원하면 첫 연락에 402 Payment Required를 돌려서 콜드 스팸 비용을 0이 아니게 만들 수 있음

중요

> 글쓴이의 결론은 “검증 가능한 속성은 평판에 기대지 않아도 된다”는 것임. SPF, DKIM, DMARC, PTR, 블록리스트, IP 워밍업을 쌓는 대신, 메시지마다 서명 하나와 .well-known GET 하나로 발신자 증명을 하자는 주장임.

읽기는 JMAP에 맡김

  • HMTP는 배달을 정의하고, 읽기·동기화·검색은 JMAP을 쓰자는 입장임

    • SMTP와 IMAP/POP처럼 세계가 갈라지는 대신, 전체 사이클을 JSON/HTTP 위에 올림
    • 클라이언트 푸시는 SSE나 WebPush를 붙이면 됨
  • 글쓴이는 실제 Python 프로토타입도 만들었음

    • 단일 파일 구현이고, 서명된 배달, 원본 키 검증, 엔드투엔드 암호화, 첫 연락 consent, 중복 제거, 스레드, 지수 백오프 큐, 키 회전을 포함함
    • 빠진 건 서명된 키 회전 체인, 402 postage, 참조형 첨부파일, JMAP 읽기 쪽임
    • 암호화는 감사를 받지 않았으니 실제 중요한 비밀에는 쓰지 말라고 선을 그음

기술 맥락

  • HMTP의 가장 큰 기술적 선택은 새 서버 생태계를 만들지 않고 HTTP 규약 위의 관습으로 남겠다는 거예요. 그래서 TLS, 상태 코드, 프록시, 로드밸런서, 기존 운영 도구를 그대로 먹을 수 있고, 프로토콜 채택 비용을 낮추는 게 핵심이에요.

  • MX 레코드 대신 .well-known이나 WebFinger를 쓰는 이유는 DNS가 도메인 단위 위임에는 강하지만 사용자별 위임에는 둔하기 때문이에요. 정적 JSON 문서로 inbox와 키를 발견하면, 정적 사이트를 쓰는 사람도 메일 처리를 외부 제공자에게 넘길 수 있어요.

  • 메시지 ID를 콘텐츠 해시로 만드는 건 재시도 설계에서 꽤 중요한 포인트예요. 네트워크에서 ACK가 사라지면 같은 메일이 두 번 들어가는 고전 문제가 생기는데, 같은 본문은 같은 ID를 갖게 하면 수신 서버가 구조적으로 dedupe할 수 있거든요.

  • 스팸 방어를 필터링 이후가 아니라 첫 접촉 단계로 당기는 것도 중요한 차이예요. 모르는 사람의 메일을 일단 inbox에 넣고 나중에 분류하는 대신, 요청함과 선택적 비용을 두면 낯선 발신자가 문은 두드릴 수 있지만 방 안을 채우지는 못해요.

이 글의 재미는 ‘이메일은 망했으니 새로 만들자’가 아니라, 이메일이 이미 잘한 store-and-forward 구조는 살리고 나머지를 현대 웹 표준으로 갈아끼우자는 점이야. 실제 배포 가능성은 별개로, 프로토콜 설계할 때 기존 인프라를 얼마나 많이 빌려올 수 있는지 보여주는 꽤 좋은 사고 실험임.

댓글

댓글

댓글을 불러오는 중...

backend

데카트론 독일, 웹 결제에 유럽식 계좌이체 ‘웨로’ 붙였다

데카트론 독일이 자사 웹사이트 결제 수단에 유럽 결제 이니셔티브의 웨로를 도입했다. 카드 네트워크를 거치지 않는 실시간 계좌 간 결제로 수수료를 낮추고, 멤버십과 결제를 묶어 고객 유지까지 노리는 움직임이다.

backend

버셀 실험실의 Scriptc, TypeScript를 Node 없이 네이티브 바이너리로 컴파일

Vercel Labs가 공개한 Scriptc는 일반 TypeScript 코드를 Node, V8, JavaScript 엔진 없이 작은 네이티브 실행 파일로 컴파일하는 실험적 컴파일러다. 정적으로 컴파일 가능한 코드와 동적 실행이 필요한 코드를 명확히 나누고, Node와 결과가 바이트 단위로 맞는지 800개 이상 테스트로 검증한다.

backend

고의 새 가비지 컬렉터 ‘그린 티’, 힙을 훑는 방식이 어떻게 달라졌나

고 1.25에서 도입되고 1.26에서 기본값이 된 새 가비지 컬렉터 ‘그린 티’를 실제 힙 배치와 성능 측정으로 뜯어본 글이다. 핵심은 포인터를 무작정 따라가는 대신 메모리 span 단위로 훑어 캐시 친화성을 높였다는 점이고, 동시에 고의 비이동식 컬렉터가 여전히 sparse page 회수에 약하다는 한계도 같이 보여준다.

backend

SIMD는 고수 전용 흑마법이 아니라, 모든 개발자가 알아야 할 반복문 최적화임

미첼 하시모토가 SIMD를 ‘복잡한 고성능 영역’으로만 볼 필요가 없다고 설명한 글이다. Ghostty의 실제 Zig 코드를 예로 들며, 큰 배열이나 문자열을 훑는 뜨거운 반복문은 벡터 너비만큼 한 번에 처리하는 5단계 패턴으로 생각할 수 있다고 풀어낸다.

backend

스타트업이 Postgres로 버티려면 알아야 할 생존 팁

Hatchet이 2년 동안 프로덕션 Postgres를 굴리며 겪은 문제를 스키마, 쿼리, 마이그레이션, 커넥션, 쿼리 플래너, autovacuum, 파티셔닝까지 한 번에 정리한 글이다. 핵심은 “인덱스만 붙이면 끝”이 아니라, 락·통계·배치·부풀어 오른 테이블까지 운영 관점에서 같이 봐야 한다는 점이다.