3P by GN⁺ | ★ favorite | 댓글 1개
  • 작고 빠른 HTTPS 서버인 zeroserve는 웹사이트 tarball을 받아 HTTP/2와 TLS 1.3으로 제공하고, tarball 안의 eBPF 프로그램을 사용자 공간 샌드박스 미들웨어로 요청마다 실행함
  • 구성 파일 없이 eBPF 프로그램이 요청별 라우팅, 헤더, 인증, 속도 제한, 프록시를 결정해 nginx·Caddy의 선언형 설정과 별도 스크립팅 계층을 하나로 합침
  • 사이트는 단일 tar 파일로 인덱싱되고 디스크에 풀리지 않으며, tarball 교체와 SIGHUP으로 사이트·스크립트·TLS 자료를 연결 손실 없이 원자적으로 교체함
  • 단일 코어 HTTPS 벤치마크에서 zeroserve는 소형 정적 파일 36,681 req/s, 10ms eBPF 동적 JSON 46,945 req/s, 소형 프록시 26,486 req/s를 기록했지만, 100KB 프록시는 nginx가 5,882 req/s로 우위임
  • zeroserve는 nginx와 Caddy의 대안을 목표로 단일 tarball 배포, 프로그램형 설정, 사용자 공간 eBPF, 현대적 TLS를 결합하지만, 큰 프록시 응답에는 nginx가 더 적합함

개요

  • zeroserve는 웹사이트 tarball 하나를 HTTP/2와 TLS 1.3으로 제공하는 작고 빠른 무설정 HTTPS 서버임
  • tarball 안에 넣은 eBPF 프로그램은 모든 요청에서 사용자 공간 샌드박스 미들웨어로 실행되며, 요청 재작성, 인증, 속도 제한, 백엔드 리버스 프록시 처리 가능
  • 단일 코어 기준으로 소형·대형 정적 파일, 스크립트 미들웨어, 소형 응답 프록시 대부분의 워크로드에서 nginx보다 높은 성능을 보이는 목표의 서버임
  • eBPF 스크립트는 네이티브 코드로 JIT 컴파일되고 사용자 공간에서 샌드박싱되며, 요청마다 실행할 만큼 낮은 비용을 목표로 함
  • 네트워크와 디스크 작업은 monoio 런타임을 통해 io_uring으로 제출됨
  • TLS 1.3, HTTP/2, Encrypted Client Hello, SNI 인증서 선택, JA4 핑거프린팅 지원
  • 전체 사이트와 TLS 자료는 tarball 하나에서 제공되며, SIGHUP으로 핫 리로드 가능

구성 모델: 프로그램이 곧 설정

  • zeroserve는 nginx와 Caddy의 대안을 목표로 하며, 핵심 설계 선택은 구성 방식임
  • nginx와 Caddy는 location 블록, rewrite 규칙, map 지시어, try_files 같은 선언형 설정 언어를 제공하고, 한계에 도달하면 Lua나 Caddy 플러그인 같은 선택적 스크립팅 런타임을 옆에 붙이는 구조임
  • 그 구조에서는 동작이 자체 제어 흐름을 가진 지시어 계층과 요청 생명주기의 특정 지점에서 실행되는 스크립트 계층으로 나뉨
  • zeroserve에는 구성 파일이 없으며, eBPF 프로그램 하나가 모든 요청을 보고 라우팅, 헤더, 인증, 속도 제한, 프록시를 결정함

단일 tarball을 그대로 제공

  • 전체 사이트는 하나의 tar 파일이며, zeroserve는 로드 시 path -> byte-range 맵을 만들고 tarball 자체에 바이트 범위 읽기를 수행해 파일 제공
  • 어떤 파일도 디스크에 풀리지 않기 때문에 사이트는 단일 파일 안에만 존재하며, 잘못된 location 규칙이 노출할 문서 루트가 없음
  • 배포는 단일 파일의 원자적 교체 방식이며, 새 버전 배포는 tarball 교체 뒤 SIGHUP 전송
  • 디렉터리 패키징과 실행 명령은 다음 형식임
zeroserve --pack ./public > site.tar  
zeroserve --addr 0.0.0.0:8080 site.tar  
  • 핫 리로드 명령은 다음 형식임
killall -SIGHUP zeroserve  
  • 리로드는 같은 프로세스 안에서 사이트, 스크립트, TLS 자료를 원자적으로 교체하고 연결 손실 없이 동작함
  • 각 인스턴스는 단일 스레드 이벤트 루프이며, 프로세스 하나 기준으로는 제한이지만 확장 단위가 “더 많은 프로세스”일 때 맞는 형태로 제시됨

사용자 공간 eBPF 스크립팅

  • .zeroserve/scripts/ 아래에 둔 모든 .c 파일은 패키징 시점에 clangllc로 eBPF 오브젝트로 컴파일되고 모든 요청에서 실행됨
  • eBPF는 커널 BPF 서브시스템이나 CAP_BPF 없이 일반 비권한 프로세스 안의 async-ebpf 런타임에서 사용자 공간으로 실행됨
  • async-ebpf는 uBPF를 내장해 바이트코드를 네이티브 x86-64 머신 코드로 JIT 컴파일함
  • 포인터 케이지(pointer cage)는 JIT 컴파일된 코드의 모든 메모리 접근을 프로그램 전용 아레나로 마스킹해 잘못된 접근을 스크립트 메모리 안에 가둠
  • 스크립트는 zeroserve의 단일 이벤트 루프에서 직접 실행되며, 느린 스크립트가 다른 연결을 멈추지 않도록 타이머가 JIT 컴파일된 네이티브 코드를 실행 중간에 인터럽트하고 제어를 이벤트 루프로 돌려줄 수 있음
  • 프로그래밍 모델은 파일명 정렬 순서로 실행되는 스크립트 체인이며, 스크립트들은 요청별 메타데이터 맵을 공유함
  • 스크립트가 zs_respondzs_reverse_proxy를 호출하면 체인은 단락 종료됨
  • zs.response.header.* 아래 키는 모든 응답의 헤더가 되며, 다른 키는 HTML 파일의 <zs-meta>visitor</zs-meta> 같은 플레이스홀더를 출력 시점에 치환하는 작은 템플릿 패스에 사용됨
  • 헬퍼 표면은 요청 메서드·경로·쿼리·헤더·피어 주소 읽기, URI 재작성, 헤더 설정·삭제를 지원함
  • 암호화와 인코딩 헬퍼는 SHA-256, HMAC-SHA256, base64, hex, getrandom 제공
  • JSON 헬퍼는 요청 본문 파싱, 문서 트리 생성·수정, zs_json_respond 응답 지원
  • 속도 제한은 피어 IP나 API 키 같은 임의 키 기반의 토큰 버킷을 지원하며, 상태는 핫 리로드 뒤에도 유지됨
  • AWS SigV4 헬퍼는 S3와 기타 AWS 서비스 통신용 서명 Authorization 헤더와 presigned URL 지원
  • OIDC 로그인은 Authorization Code + PKCE 기반 relying-party 흐름을 제공하며, 전체 로그인 세션을 sealed XChaCha20-Poly1305 쿠키에 담아 서버를 상태 비저장으로 유지한 채 정적 사이트를 “Google로 로그인” 뒤에 둘 수 있음
  • 동적 엔드포인트는 특정 경로에서 스크립트가 직접 응답하는 방식이며, 예시에서는 /health 요청에 application/json 헤더와 {"status":"ok"} 본문을 반환함
  • 각 스크립트는 기본 256KB 메모리 상한 아래 실행되며, 런타임은 오래 실행되는 스크립트를 실행기에서 시간 분할하고 폭주 스크립트를 스로틀링함
  • 스크립트는 zs_call로 서로 호출할 수 있으며, 호출 깊이는 제한됨
  • 무한 루프에 빠진 스크립트는 자기 요청만 지연시키며, 선점 타이머가 이를 인터럽트해 서버가 다른 요청을 계속 처리함
  • TLS 계층은 TLS 1.3 전용이며 BoringSSL로 종료됨
  • Encrypted Client Hello는 실제 SNI가 평문으로 나타나지 않게 하며, 디렉터리 기반 SNI 인증서 선택과 스크립트에 노출되는 JA4 클라이언트 핑거프린팅 제공
  • 투명 ECH 릴레이 모드는 복호화할 수 없는 핸드셰이크를 실제 업스트림으로 바이트 단위 그대로 전달해 보호된 이름이 공개 이름 뒤에 섞일 수 있게 함

성능

  • 벤치마크 조건

    • zeroserve, nginx 1.26, Caddy 2.11을 8코어 Ryzen 7 3700X에서 같은 콘텐츠와 같은 자체 서명 인증서로 HTTPS 제공 비교
    • zeroserve 인스턴스가 설계상 단일 스레드이므로 비교 기준은 코어당 성능임
    • 모든 서버는 taskset으로 CPU 하나에 고정됐고, nginx는 worker_processes 1, Caddy는 GOMAXPROCS=1, zeroserve는 기존 단일 스레드 구조 사용
    • 부하는 다른 코어에서 wrk -t4 -c100으로 생성했고, 10초 실행 3회의 중앙값 사용
    • wrk는 HTTP/1.1을 사용하므로 수치는 TLS 1.3 위의 HTTP/1.1이며, 긴 keep-alive 연결로 핸드셰이크 비용을 분산한 이미 열린 HTTPS 연결의 정상 상태 비용임
  • 소형 정적 파일 174B

    서버 req/s p99
    zeroserve 36,681 5.4 ms
    nginx 31,226 7.8 ms
    Caddy 12,830 22 ms
    • zeroserve는 단일 코어에서 nginx보다 약 17% 빠르게 소형 파일을 제공했고, 꼬리 지연도 더 낮음
    • HTML 페이지, 소형 JSON, CSS 같은 정적 사이트 기본 사례가 zeroserve 튜닝 대상임
  • 대형 정적 파일 100KB

    서버 req/s 처리량 p99
    zeroserve 8,000 782 MB/s 22 ms
    nginx 7,600 773 MB/s 28 ms
    Caddy 6,084 590 MB/s 44 ms
    • 세 서버의 결과는 가까웠고, zeroserve가 단일 코어에서 약 780 MB/s로 약간 앞섬
    • nginx의 대형 파일 강점인 sendfile()은 TLS 아래에서 사용되지 않으며, 바이트를 사용자 공간에서 암호화해야 하므로 세 서버 모두 암호화와 쓰기 루프에 묶임
    • kernel TLS를 세 서버 모두 끈 상태에서 zeroserve의 io_uring 읽기·쓰기 경로가 약간 더 빠른 결과임

eBPF vs Lua

  • 스크립팅 비교 대상은 웹 서버 안에서 빠른 코드를 실행하는 일반적 방식인 nginx + LuaJIT ngx_http_lua_module
  • zeroserve는 기본값으로 스크립트 선점 타이머를 2ms마다 설정하며, 세밀한 간격은 문제 스크립트를 빠르게 스로틀링하지만 정상 스크립트에도 비용을 줌
  • 기본 2ms에서는 완전 동적 응답 기준 eBPF가 약 32k req/s로 nginx Lua의 41k req/s보다 낮음
  • --preempt-timer-interval-ms를 10으로 올리면 스크립팅 처리량이 약 40% 회복되고 결과가 뒤집힘
  • 요청별 헤더 주입 미들웨어

    엔진 req/s p99
    zeroserve eBPF 10ms 43,709 5.1 ms
    zeroserve eBPF 2ms 기본값 31,334 6.7 ms
    nginx Lua header_filter 28,653 8.4 ms
    • 스크립트가 실행되지만 정적 파일은 계속 제공되는 미들웨어 사례에서 10ms eBPF가 nginx Lua보다 약 50% 높고 꼬리 지연도 더 낮음
  • 완전 동적 JSON 응답

    엔진 req/s p99
    zeroserve eBPF 10ms 46,945 4.5 ms
    nginx Lua content_by_lua 41,231 6.4 ms
    zeroserve eBPF 2ms 기본값 32,393 6.7 ms
    • 10ms 간격의 조정된 eBPF는 완전 합성 응답에서도 nginx의 content_by_lua보다 높은 처리량을 기록함
    • 두 엔진 모두 네이티브 코드로 컴파일되며, LuaJIT는 트레이싱 JIT이고 async-ebpf는 uBPF를 통해 eBPF를 JIT 컴파일함
    • TLS 암호화가 공통 요청 비용인 조건에서 조정된 eBPF 경로가 처리량에서 앞섬
    • 2ms 기본값에서는 eBPF가 미들웨어 우위는 유지하지만 합성 응답 선두는 내주므로, 운영 스크립트에는 10ms 사용 권장

리버스 프록시로 사용

  • zeroserve는 zs_reverse_proxy("http://127.0.0.1:9000";)를 스크립트에서 호출해 백엔드로 프록시함
  • 업스트림 연결 풀은 백엔드당 최대 128개 연결과 30초 유휴 재사용을 지원함
  • 공정 비교를 위해 nginx는 기본적으로 요청마다 업스트림 연결을 닫는 특성을 고려해 keepalive 128, proxy_http_version 1.1, 비운 Connection 헤더를 명시 사용함
  • Caddy는 기본 동작대로 연결 재사용
  • 각 프록시는 단일 코어에서 TLS를 종료하고 공유 평문 백엔드로 전달했으며, 백엔드는 별도 2코어 서버로 자체 100k req/s를 유지해 프록시 오버헤드만 측정함
  • 소형 174B 응답 프록시

    프록시 req/s p50 p99
    zeroserve 26,486 3.3 ms 8 ms
    nginx 21,761 4.2 ms 10.5 ms
    Caddy 7,683 10.3 ms 33 ms
    • zeroserve의 풀링된 io_uring 프록시는 nginx보다 약 22% 앞섰고 Caddy 대비 약 3.4배 처리량을 기록함
    • API 호출, 소형 JSON, 앱 서버 HTML 같은 일반적 프록시 워크로드에서 zeroserve가 TLS 종료와 백엔드 전달을 더 빠르게 수행함
  • 100KB 응답 프록시

    프록시 req/s 처리량
    nginx 5,882 585 MB/s
    Caddy 4,285 406 MB/s
    zeroserve 3,631 359 MB/s
    • 프록시 본문이 커지면 nginx의 버퍼링이 바이트를 더 효율적으로 이동해 앞서고, Caddy가 중간, zeroserve가 뒤처짐
    • 프록시 응답이 크면 nginx가 더 나은 도구이며, 작고 많은 응답이면 zeroserve가 더 빠름

메모리

  • 유휴 상태의 단일 zeroserve 인스턴스는 약 15MB PSS를 사용하며, nginx의 약 6MB보다 많고 Caddy의 약 60MB보다 적음
  • 실행 단위가 전체 프로세스라는 점이 중요하며, 코어마다 복사본을 실행할 때 같은 바이너리를 매핑해 코드 페이지를 공유함
  • 추가 프로세스는 자체 워킹셋 외에는 적은 메모리를 더함

공개

  • zeroserve는 GitHub에서 오픈소스로 공개된 프로젝트임

댓글과 토론

Hacker News 의견들
  • TechEmpower 웹 서버 벤치마크가 사라지면서, 이런 새 프로젝트들이 스스로를 입증할 기회가 줄어든 것 같음
    수정: 내가 뒤처진 것 같고, 요즘 뜨는 건 https://www.http-arena.com/leaderboard/인 듯함. 행운을 빔

    • 죽었다는 게 무슨 뜻인지 모르겠음. https://www.techempower.com/benchmarks/#section=data-r23에 아직 있고, 마지막 벤치마크도 2025년 2월임
      다만 원래도 자주 돌리는 편은 아니고, 라운드 기록을 보면 1년에 한 번 이하로 실행됨
    • LLM UI/UX가 너무 별로임. 이런 것에 주말 하루이틀만 써도 사용자 경험을 꽤 개선할 수 있을 텐데 왜 안 하는지 모르겠음
  • 이런 시도가 LLM 덕분에 비교적 싸고 빠르게 탐색 가능해져서 나오는 걸 보는 게 좋음
    다만 여기서 느낀 점은 nginx 자체가 꽤 인상적이라는 것임. 또 눈에 띈 대목은, 이 프로젝트가 nginx와 Caddy의 대안이며 설정 방식에 베팅한다는 설명이었음
    nginx와 Caddy는 선언형 설정 언어를 제공하고, 그 한계에 닿으면 Lua나 Caddy 플러그인 같은 스크립트 런타임을 옆에 붙이는 구조라 동작이 두 계층으로 나뉨
    하지만 그 베팅은 잘못된 것 같음. 사람들은 오래전부터 코드보다 설정을 선호했고, 내장 기능만으로도 충분한 경우가 많아서 C 코드를 쓸 필요가 없음

    • 그렇게 확신하긴 어려움
      모든 설정 파일 형식은 처음엔 단순하게 시작하는 것 같음. YAML도 기본은 꽤 합리적이었는데, 사람들이 앵커와 별칭으로 더 복잡한 걸 원하기 시작했음
      GitLab조차 조건문과 변수 비슷한 자체 형식을 갖고 있고, 특정 위치에서만 동작하는 해킹에 가까움. Apache도 XML 기반 설정 형식에서 비슷한 길을 갔음
      결국 설정 관리를 위한 수많은 맞춤형 프로그래밍 언어가 생김. 기업 환경에서는 직접 편집하지 않고 Ansible 워크플로를 스크립트로 짜서 원격 수술을 함
      차라리 서버에 Lua나 Python 같은 인터프리터를 내장해 설정 관리를 하게 했으면 이 과정을 건너뛸 수 있었고, 맞춤 설정 파일을 프로그램으로 고치는 것보다 단순했을 것임
      물론 맞춤형 시도들이 일반 언어보다 특정 용도에 최적화됐다고 말할 수는 있지만, 그런 주장은 애초에 그 장치가 필요 없었을 장난감 예제의 좁은 범위에만 맞음
      Windows INI 파일을 기억하는지. 코드가 코드이고 데이터가 데이터였던 좋은 시절이었음
    • 앞으로 96시간 안에 누군가 LLM으로 nginx나 Caddy 설정 파일을 zeroserve가 쓸 수 있는 코드로 변환해 포장하는 도구를 만들 수 있을 것 같음
      더 단순하게는 Kubernetes 클러스터의 Ingress 매니페스트를 모두 읽어 pack을 다시 만들 수도 있음
      요점은 도구와 설정 사이의 인터페이스도 또 하나의 API일 뿐이고, 시스템 운영자는 이미 더 높은 수준의 구성물로 시스템 상태를 기술하고 있으며, 설정을 이루는 구체적인 바이트는 그 결과물이라는 점임
    • 복잡성을 추상화하고 매크로로 “설정 파일”식 구성을 달성하는 건 어떨까 싶음
    • AI가 점점 더 사람의 말 → 기계의 효과를 가능하게 하면서 이 선호가 바뀔지 살펴볼 가치는 있어 보임
      AI 입장에서는 그 방식이 더 다루기 쉬울 수 있음. AI는 양쪽 모두 처리할 수 있으니, 그런 전환이 명확히 좋은 아이디어로 자리 잡기까지는 시간이 오래 걸릴 수 있음
    • 왜 그렇게 LLM에 공을 돌리고 싶어 하는지 모르겠음. 글 작성에 LLM 도움을 받았다고 해서, 실험까지 LLM이 대신 했다는 뜻은 아님
  • 아이디어는 마음에 듦
    다만 eBPF 디렉터리에 .c 파일 대신 .rs 파일을 넣을 수 있으면 더 안심될 것 같음. 이미 Rust 프로젝트이기도 하니
    그리고 왠지 커널 가속 웹 서버를 기대했음. eBPF로 안전하게 할 수 있다면 정말 대단할 듯함
    또 단일 스레드라니? Linux에서 포크하고 들어오는 연결 큐를 공유하는 건 거의 사소한 일이고, Rust로도 몇 줄이면 될 것임. SO_REUSEPORT를 쓰면 나머지는 커널이 처리함
    참고로 io_uring을 밀 생각이라면 kTLS도 같이 밀어야 한다고 봄. 핸드셰이크 이후 사용자 공간 SSL 처리를 피할 수 있으면 설계가 크게 단순해짐

    • 고마움. fork + SO_REUSEPORT를 구현할 예정임
      지금까지는 이런 용도로 nftables를 써와서 직접 필요하지 않았음
  • 아주 멋짐. 이것을 XDP 프로그램이나 소켓 맵에 붙는 프로그램 같은 다른 BPF 프로그램 타입과 결합해서, L7 HTTP 기능을 더 아래 계층으로 통합할 수 있을지 궁금함

  • 아이디어는 좋지만 정적 파일에 집중해야 할지는 모르겠음. 요즘은 그 목적으로 서버를 새로 띄우는 경우가 드묾

    • 지난주에 Ghost를 정적으로 변환하면서 딱 그렇게 했고, 자체 포함 바이너리 하나가 더 빠르지 않을까 반쯤 생각하고 있었음
      그래서 이건 나를 위해 만들어진 것처럼 느껴지지만, 내가 일반적인 사용자가 아니라는 건 인정함
    • 도메인에 따라 다름. 여러 과학 분야에서는 큰 데이터셋을 정적 파일 형식으로 효율적으로 제공함. 예를 들면 https://zarr.dev/https://parquet.apache.org/ 같은 것들임
  • 좋아 보이고 기능도 괜찮음. 하지만 어딘가 너무 인위적으로 느껴져서 마음이 확 붙지는 않음
    지표가 가짜인지, 편의 함수들이 실제로 동작하는지, 제대로 된 강화 작업이 있었는지 알 수 없음
    바이브 코딩으로 만들고 README가 자동 생성된 것까지는 받아들일 수 있음. 그런데 발표 블로그 글까지 AI가 만든 것이고, 소프트웨어 품질에 대한 이해가 나와 같은지 판단할 근거가 전혀 없음
    이상한 세상임. 몇 년 전 AI 고지 없이 발표됐다면 의심 없이 받아들였을 텐데, 지금은 멋진 README와 그럴듯한 명령줄 매개변수가 보이면 README가 환각한 것이고 실제로는 옵션이 없을지도 모른다고 바로 의심하게 됨

    • 작성자임. 이 프로젝트의 몇몇 핵심 부분, 예를 들면 async-ebpf는 그런 코딩 에이전트들이 나오기 훨씬 전에 작성됐음
      zeroserve 자체를 만들 때는 AI 도움을 많이 쓰지만, AI 출력은 직접 확인하고 책임도 내가 짐
    • 벤치마크를 보면, 174B짜리 작은 정적 파일에서 zeroserve는 36,681 req/s와 p99 5.4ms, nginx는 31,226 req/s와 p99 7.8ms, Caddy는 12,830 req/s와 p99 22ms임
      zeroserve는 단일 코어에서 작은 파일을 nginx보다 약 17% 빠르게 제공하고 꼬리 지연도 더 좁음. HTML 페이지, 작은 JSON, CSS가 zeroserve가 맞춘 경우임
      100KB 큰 정적 파일에서는 zeroserve가 8,000 req/s, 782 MB/s, p99 22ms이고 nginx는 7,600 req/s, 773 MB/s, p99 28ms, Caddy는 6,084 req/s, 590 MB/s, p99 44ms임
      그래도 나는 이런 신생 프로젝트보다 감사받고, 실전에서 검증되고, 강화된 오래된 프로젝트를 택하겠음. 위험을 감수할 만큼 개선 폭이 크지 않음
    • 정말 안타까운 상황임. 최근에 ffmpeg-wasm 프로젝트가 있었고 테스트해보니 동작은 했음. 하지만 바이브 코딩 AI였고, 나는 AI를 견딜 수 없음. 동작해도 마찬가지임
      가능한 한 구식 시대에 남기로 했음. 똑똑한 사람들이 소프트웨어를 공개하고, 똑똑한 사람들이 유지보수함. 그들에게 AI는 필요 없음. 그게 내 틈새임
      우리가 사라질 수도 있지만 그래도 그쪽이 더 좋음. 다만 그런 똑똑한 사람들이 문서를 쓴다는 전제가 붙음. 문서 쓰기를 싫어하는 똑똑한 사람도 많음
      오래전에 문서가 없는 소프트웨어는 아무리 훌륭해도 내 시간을 들일 가치가 없다고 정했음. 주로 애플리케이션 쪽 이야기이고, Linux 문서는 거의 보지 않았지만 남들은 아주 나쁘지는 않다고도 하니 알 수 없음
  • 흥미로운 새 개념이고 마음에 듦
    진짜 질문은 개발자 헌신도와 커뮤니티임. Caddy와 Nginx 쪽 사람들은 제품 지원을 꾸준히 해왔고, 이 프로젝트도 많은 집중과 관심이 필요할 것임

  • tarball인가?

    • 바이트 범위로 리소스에 접근하기 쉬운 단순한 형식이고, 모두가 도구를 갖고 있으며, 무엇보다 압축하지 않기 때문임
    • “One tarball, served in place” 섹션 첫 문단에 따르면, 전체 사이트가 하나의 tar 파일이고 zeroserve는 로드 시 이를 색인해 경로에서 바이트 범위 맵을 만든 뒤 tarball 자체에 대해 바이트 범위 읽기를 수행해 파일을 제공함
      디스크에는 아무것도 풀지 않음. 사이트가 그 파일 하나에 완전히 들어 있으므로, 잘못된 location 규칙이 노출할 문서 루트가 없고 배포도 단일 원자적 파일 교체가 됨
      다만 그 설명도 LLM식 정당화일 수 있음. 글 곳곳에 “the right shape”나 “the surface is broad” 같은 표현이 흩어져 있음