원문 캡처 · github.com
원문 캡처 · github.com

Xet Server 저장소는 Hugging Face의 실제 Xet 저장 프로토콜과 호환되는 Go 서버다. 실제 hf CLI를 연결해 모델 파일을 올리고 내려받을 수 있으며, 별도 프록시 xet-proxyd는 Hugging Face에서 한 번 가져온 데이터를 오프라인에서도 제공한다.

Xet은 대형 모델 파일을 저장할 때 Git LFS를 대체하는 콘텐츠 정의 청킹과 중복 제거 중심의 저장 계층이다. Xet Server는 BLAKE3 해싱, xorb·shard 바이너리 형식, LZ4·ByteGrouping 압축을 구현하고, 실제 hf_xet 클라이언트와의 종단 간 검증을 거쳤다고 프로젝트 측은 설명한다. 자세한 프로토콜 구현 내용은 프로토콜 문서에 있다.

서버와 프록시의 쓰임새

xetd는 Xet CAS 서버를 실행하며 -hub-addr를 지정하면 Hub API 호환 계층도 별도 HTTP 포트에서 제공한다. 이 계층을 함께 실행해야 실제 hf upload와 hf download를 사용할 수 있다. xet CLI에는 프로토콜 호환 경로가 아닌 간단한 자체 데이터 API가 들어 있다.

xet-proxyd는 실제 huggingface.co 앞에서 요청을 중계하고 데이터를 캐시한다. 목표는 속도 향상뿐 아니라 오프라인 복원력이다. 프록시가 성공적으로 중계한 저장소 메타데이터와 파일 데이터는 Hugging Face에 연결할 수 없게 된 뒤에도 제공할 수 있다. 프록시의 데이터 디렉터리는 일반 xetd가 그대로 이어받을 수 있어, 프록시 프로세스 없이도 캐시한 파일을 쓸 수 있다. 단, 프록시의 CAS 포트는 Hub 포트가 실제 Hub 요청을 한 번 이상 중계해 저장소별 CAS 주소를 알아낸 뒤에야 동작한다.

저장소에는 파일 시스템과 S3 호환 저장소 백엔드, 선택형 인증·저장 공간 정리·요청 제한, 재시작 후 메타데이터 복원 기능도 포함된다. 대용량 객체는 스트리밍 방식으로 처리하도록 설계됐으며, 프로젝트는 실제 GGUF 모델 파일 최대 27.6GB로 종단 간 검증했다고 밝힌다.

설치와 실행

빌드에는 Go 1.27 이상과 GNU Make가 필요하다. 통합 테스트에는 Bash가 필요하고, 실제 hf CLI 왕복 테스트에는 선택적으로 pipenv가 필요하다. 다이어그램 렌더링에는 merman-cli 또는 mmdc를 사용할 수 있다.

text
make pre-check # verify Go/bash are present
make build # -> bin/xetd, bin/xet-proxyd, bin/xet

Go로 설치하는 명령은 다음과 같다.

text
go install github.com/guilt/xet-server/cmd/xetd@latest
go install github.com/guilt/xet-server/cmd/xet-proxyd@latest
go install github.com/guilt/xet-server/cmd/xet@latest

태그가 붙은 릴리스는 플랫폼별 ZIP 파일을 GitHub Releases에 배포한다. 각 ZIP에는 xetd, xet-proxyd, xet 실행 파일과 README, 변경 기록, 라이선스, 문서 및 버전 파일이 들어간다. 함께 공개하는 checksums.txt에는 ZIP별 SHA-256이 기록된다.

CAS 서버만 실행하거나 Hub API 계층도 함께 실행할 수 있다.

text
# CAS server only, on :8420
./bin/xetd -addr :8420 -data ./xet-data

# CAS server + Hub API shim (needed for the real hf CLI), on :8420 / :8421
./bin/xetd -addr :8420 -hub-addr :8421 -data ./xet-data

-max-storage-bytes와 -eviction-interval로 저장 공간 자동 정리를, -rate-limit-rps와 -rate-limit-burst로 업로드 요청 제한을 켤 수 있다. 둘 다 기본값은 꺼짐이다. 중복 제거 적중 때 저장된 데이터와 새 업로드의 바이트를 대조하는 -verify-dedup과, 메타데이터 저장 주기를 바꾸는 -snapshot-interval도 선택 사항이다.

text
# With storage auto-pruning (evict least-recently-used xorbs over 10GB,
# checked every 5 minutes) and per-source-IP upload rate limiting (5
# requests/sec sustained, burst of 20) - both optional, both off by default:
./bin/xetd -addr :8420 -data ./xet-data \
 -max-storage-bytes 10737418240 -eviction-interval 5m \
 -rate-limit-rps 5 -rate-limit-burst 20

# With dedup-hit content verification (see "Storage backends" below) and
# a faster persistence checkpoint interval than the 1-minute default:
./bin/xetd -addr :8420 -data ./xet-data -verify-dedup -snapshot-interval 15s

각 포트의 루트 주소를 브라우저로 열면 해당 포트의 엔드포인트 목록이 표시된다. CAS 서버 주소에서는 오프라인 Swagger UI를 /api-docs/에서 볼 수 있다.

기본 설정에서는 xetd에 인증이 없다. 따라서 인증을 켜지 않으면 모든 클라이언트가 읽고 쓸 수 있다. -auth-token <secret> 또는 $XETD_AUTH_TOKEN을 지정하면 요청에 Authorization: Bearer <secret>을 요구하며, 엔드포인트별로 읽기와 쓰기 권한을 나눈다. 업로드와 커밋에는 쓰기 권한이, 다운로드·재구성·resolve에는 읽기 권한이 필요하다. 기본값인 -auth-token None 또는 미설정 상태에서는 검사가 꺼진다.

Hub 계층이 발급하는 단기 토큰은 저장소별로 제한된다. alice/one에 발급한 토큰은 bob/two에서 사용할 수 없고, 저장소 범위 쓰기 토큰으로 새 저장소를 만들 때도 해당 소유자 네임스페이스 안에서만 허용된다. 공유 비밀은 admin 권한을 갖는다. 공유 기기나 명령행 기록이 남는 환경에서는 명령 인수 대신 환경 변수를 쓰는 편이 낫다. -auth-token 인수는 다른 로컬 사용자가 ps -ef나 /proc/<pid>/cmdline에서 볼 수 있고 셸 기록에도 남을 수 있다. 명령행 옵션과 환경 변수가 모두 설정되면 옵션이 우선한다.

text
export XETD_AUTH_TOKEN="a-long-random-secret"
./bin/xetd -addr :8420 -hub-addr :8421 -data ./xet-data

./bin/xet push -server http://localhost:8420 -auth-token "a-long-random-secret" ./model.safetensors
# or, equivalently, export XET_AUTH_TOKEN (or HF_TOKEN, the same variable
# the real `hf` CLI reads) instead of passing -auth-token:
export XET_AUTH_TOKEN="a-long-random-secret"
./bin/xet push -server http://localhost:8420 ./model.safetensors

실제 hf CLI를 연결하려면 Hub 포트를 HF_ENDPOINT로 지정한다.

text
export HF_ENDPOINT="http://localhost:8421" # the Hub shim's address
export HF_TOKEN="anything" # ignored unless xetd was started with -auth-token/$XETD_AUTH_TOKEN - see Authentication above

hf upload myuser/my-model ./model.safetensors model.safetensors
hf download myuser/my-model model.safetensors --local-dir ./downloaded
cmp ./model.safetensors ./downloaded/model.safetensors # byte-identical

프록시로 캐시하고 오프라인에서 이어 쓰기

프록시는 CAS와 Hub용 포트를 각각 지정해 실행한다. HF_ENDPOINT는 Hub 포트를 가리킨다.

text
./bin/xet-proxyd -addr :8420 -hub-addr :8421 -data ./xet-proxy-data

export HF_ENDPOINT="http://localhost:8421"
export HF_TOKEN="anything" # forwarded upstream unchanged - see below
hf download someuser/some-model --local-dir ./downloaded

프록시는 호출자의 Authorization 헤더를 변경하지 않고 upstream에 전달하며 자체 자격 증명을 보관하거나 사용하지 않는다. 프록시 자체 접근 제한은 별도 설정인 -auth-token 또는 $XET_PROXYD_AUTH_TOKEN으로 관리한다. 업스트림 오류가 나면 캐시된 데이터를 사용하며, 기본 -cache-ttl -1은 메타데이터를 제공하기 전에 우선 upstream을 확인한다. -cache-ttl은 Hub 메타데이터에만 영향을 주고 콘텐츠 주소 기반 CAS 바이트 캐시에는 적용되지 않는다. 메타데이터 호출 제한 시간은 기본 30초다. -no-cache를 쓰면 로컬 캐시를 읽거나 쓰지 않는 순수 중계 모드가 된다.

Xet 토큰이 다운로드 중 만료돼 CAS가 401을 반환하면 프록시는 클라이언트가 제공한 자격 증명으로 같은 저장소·참조·읽기 또는 쓰기 범위의 토큰을 새로 발급받아 한 번 재시도한다. 업스트림에서 토큰을 갱신하지 못하면 만료된 토큰을 대신 제공하지 않고 오류를 반환한다. 프록시의 요청 제한은 기본적으로 꺼져 있으며, 켜면 두 포트의 모든 경로에 적용된다. 두 포트가 제한기를 공유하므로 포트를 나눠 호출해 허용량을 늘릴 수 없다.

오프라인으로 넘기는 흐름은 다음과 같다. 프록시를 통해 데이터를 받은 뒤 프록시를 종료하고, 같은 데이터 디렉터리를 일반 xetd에 지정하면 된다.

text
# 1. Cache something while online:
./bin/xet-proxyd -addr :8420 -hub-addr :8421 -data ./xet-proxy-data &
HF_ENDPOINT=http://localhost:8421 hf download someuser/some-model --local-dir ./downloaded

# 2. Stop the proxy (a final snapshot is taken automatically):
kill %1

# 3. huggingface.co is gone / unreachable / you're fully offline now.
# Point a PLAIN xetd at the SAME data directory - no -upstream-hub-url,
# no network, nothing but what step 1 already cached:
./bin/xetd -addr :8420 -hub-addr :8421 -data ./xet-proxy-data

# 4. The exact same download still works, byte-identical, forever:
HF_ENDPOINT=http://localhost:8421 hf download someuser/some-model --local-dir ./downloaded-again
cmp -r ./downloaded ./downloaded-again

프로젝트의 통합 테스트는 로컬 모의 Hub를 상대로 실제 hf CLI의 업로드·다운로드, 프록시 종료, upstream 제거, 일반 xetd로의 데이터 디렉터리 이전, 재다운로드를 확인한다. 중복 제거된 파일의 일부 xorb 범위만 내려받는 경우 프록시는 해당 청크를 새 xorb로 묶고 파일 참조를 고쳐 캐시에 저장한다. upstream 응답의 범위가 불완전하거나 잘못돼 파일을 완전히 캐시할 수 없으면 다운로드는 실시간 중계로 계속되지만 해당 파일은 캐시되지 않는다. 비-Xet 파일도 캐시되지 않는다. 이 한계와 대안은 미러링 안내서에 설명돼 있다.

프록시를 처음 시작했을 때 CAS 포트는 Hub 포트가 저장소별 Xet 토큰 요청을 실제 Hub에 중계하기 전까지 upstream CAS 주소를 모른다. 그 전에 CAS 포트로 직접 요청하면 503 upstream CAS URL not yet known을 반환한다. 먼저 HF_ENDPOINT를 Hub 포트로 지정해 hf download, hf upload 또는 토큰 왕복 요청을 해야 한다. 외부에서 접속 가능한 CAS 주소를 쓰려면 -addr에 호스트를 넣는 대신 -cas-url http://<host>:<port>를 지정한다.

저장소와 데이터 보존 조건

기본 파일 시스템 저장소는 fsstore다. s3store는 AWS S3나 MinIO 같은 S3 호환 서비스에 저장하며, AWS SDK 대신 자체 SigV4 서명 구현을 사용한다. 실제 MinIO 테스트는 XET_S3STORE_LIVE_TEST=1과 XET_TEST_S3_* 환경 변수를 지정해 실행한다.

-max-storage-bytes N -eviction-interval 5m을 설정하면 용량이 N바이트를 넘을 때 최근 사용량이 적은 xorb부터 지운다. 업로드와 가져오기는 모두 접근으로 계산하며, 가져오는 중인 xorb는 건너뛴다. 이 기능은 프록시 캐시에서는 다시 받을 수 있는 데이터를 정리하는 데 적합하다. 단독 xetd는 업로드된 파일의 유일한 사본일 수 있으므로 이 방식으로 xorb를 지우면 파일이 손상될 수 있다. 프로젝트는 단독 서버에서 보존 대상을 지정하는 xetd gc 사용을 안내한다. Git LFS 파일은 Git 참조와 대조해 보존하고, Hub 계층으로 올린 파일은 Hub 등록 정보가 보존 대상 기록으로 쓰인다. xetd gc는 기본 22일의 유예 기간 동안 업로드·가져오기·중복 제거 응답에 등장한 데이터를 지우지 않는다. 클라이언트는 자체 shard 캐시와 서버의 중복 제거 응답을 3주간 보유할 수 있기 때문이다. -sync-folder와는 함께 사용할 수 없다.

업로드 제한은 -rate-limit-rps N -rate-limit-burst N으로 설정한다. 단독 xetd에서는 출발지 IP별 업로드만 제한하며, 내려받기·재구성·HEAD 요청은 제한하지 않는다. 제한을 넘으면 429와 Retry-After를 반환한다. -verify-dedup은 중복 적중 때 기존 객체와 새 데이터를 바이트 단위로 비교한다. 해시 충돌이나 저장 데이터 손상을 발견하면 기존 객체를 덮어쓰지 않고 쓰기를 거부한다. 추가 읽기가 발생해 중복 적중 경로가 기본 방식보다 대략 10배 느려질 수 있어 기본값은 꺼짐이다.

-snapshot-interval은 재구성·저장소 인덱스를 JSON 스냅샷으로 저장하는 주기이며 기본값은 1분이다. 정상 종료 때는 마지막 스냅샷을 항상 기록한다. 이는 쓰기 전 로그가 아니므로 비정상 종료, kill -9, 정전 등으로 마지막 저장 이후의 데이터가 유실될 수 있다. 0은 주기 저장만 끄며 정상 종료 시 마지막 저장은 유지한다.

API와 개발·검증

서버는 실제 Xet CAS의 /v1·/v2 API와 Hub API 일부를 제공한다. CAS 경로에는 xorb·shard 업로드, xorb 가져오기, 파일 재구성, 청크 중복 제거 조회, 다중 범위 재구성, 텔레메트리 응답과 저장 공간 통계가 포함된다. Hub 계층은 저장소 생성, revision 조회, 파일 목록, 브랜치 생성, 사전 업로드, 읽기·쓰기 토큰, 커밋, 파일 resolve를 지원한다. 별도로 제공하는 Xet Data API는 /v1/upload, /v1/files, /v1/stats를 사용하며 실제 Xet wire 프로토콜과 호환되지 않는다.

이 간단한 API에는 파일별 소유권이나 격리가 없다. file_id는 업로드 콘텐츠의 SHA-256이며, 인증이 켜져도 토큰은 API 전체의 읽기·쓰기 권한만 제어한다. 읽기 토큰이 있으면 알고 있는 모든 파일 ID를 가져올 수 있다. 다중 테넌트 환경에 격리 기능을 추가하지 않고 운영하지 말아야 한다.

테스트와 문서 생성 명령은 다음과 같다.

text
make test # unit tests, all packages
make test-race # unit tests under the race detector
make fuzz-seeds # replay every committed fuzz corpus (fast, deterministic)
make test-fuzz # fuzz every target, 30s each (override: FUZZTIME=5m)
make integration-test # bash integration suite against a live server

make test-fuzz는 새 입력을 탐색하기 전에 저장소에 포함된 과거 실패 사례를 재생한다. 경쟁 탐지와 퍼즈 테스트는 실행 속도 차이 때문에 별도 대상으로 운영한다. 통합 테스트는 실제 hf CLI 업로드·다운로드 왕복과 프록시 오프라인 이전을 검사하며, pipenv 환경이 없으면 실제 CLI 왕복 테스트를 실패가 아니라 건너뛴다. 여러 클라이언트 테스트는 각기 다른 캐시와 토큰을 쓰는 클라이언트가 서버의 전역 중복 제거 응답을 실제로 사용하는지 로그로 확인한다.

주요 문서는 미러링 안내서, 아키텍처, 프로토콜 분석, FAQ, 기여 안내, 변경 기록에서 볼 수 있다. 라이선스는 MIT다.

프로젝트 측은 이전 버전에서 남아 있던 실제 revision·브랜치, V2 재구성, 전역 청크 중복 제거, 재시작 후 저장, 인증 기능의 차이를 해소했다고 설명한다. v1.0.0에서는 실제 huggingface.co를 대상으로 하는 프록시의 종단 간 동작도 추가했다. 알려진 제한은 Hub 포트가 upstream 요청을 중계하기 전 CAS 포트가 실제 CAS 주소를 알 수 없다는 점이다. 실제 Xet CAS 주소가 고정된 공개 주소가 아니라 저장소별 Hub 토큰 응답으로 전달되기 때문이라고 프로젝트는 설명한다.