Como baixar com segurança um arquivo de modelo hospedado: encontre o artifact, leia o plano de download, siga o redirect, confira o hash e ateste a conclusão.
Em resumo
- Pesquisar ou resolver um repo para um id de artifact.
- Ler GET /api/v1/artifacts/{id}/download-plan. Confie em
serve.downloadReadyeserveExplanation.serveVerdict— não nostatusde storage obsoleto. - Escolha uma linha
files[]comdownloadUrl. Open the file's downloadUrl (/api/downloads): it re-checks and redirects to a signed link that lasts at most 300s (default 300, configurable shorter). Reopen for a fresh link. A URL is not permission. - Baixe os bytes (use
Rangepara retomar arquivos grandes), verifiquesha256localmente e façaPOSTem/api/download-completions/{id}/{path}comX-Hugging-Bay-Completion-Tokendo redirect.
Subconjunto OpenAPI público: /openapi-public.json. Contrato completo: /openapi.json.
Endpoint changes in this source candidate
Protocol revision 2026-10-20-candidate-1. Package publication and product releases are recorded separately in the changelog.
/api/v1/search: Optional snapshot query pins complete public search metadata; unpinned behavior is unchanged./api/v1/resolve: Optional snapshot query pins resolved public identity; missing/unsupported references retain 404/400./api/v1/artifacts/{id}/files: Optional snapshot query pins bounded public file inventory; partial/degraded reads are never frozen./api/v1/artifacts/{id}/download-plan: Existing snapshot pins remain; clients carry the same pin across discovery and plan selection./openapi-public.json: Public read profile projects canonical schemas and their referenced components from the full contract./api/downloads/{id}/{path}: No authority change: every redirect is fresh and the final bytes must match the selected plan hash/size.
Pin the whole metadata path with snapshot: search, resolve, files, finder and plan. Pins retain per-instance answers for about one hour. Check X-Hugging-Bay-Snapshot-State; an unfrozen answer, restart or different instance does not guarantee identical metadata. Byte authorization is checked again when obtaining a fresh redirect. Keep the selected hash and size when renewing a URL.
Quando algo falha
- HTTP 429 or 503 on plan/search/resolve Wait Retry-After (or retry_after_s in JSON), add jitter, then retry the same read.
- HTTP 302 Location expires or returns 403 GET the same /api/downloads/{id}/{path} again without following redirects to mint a fresh signed URL (≤300s).
- Partial download interrupted Resume with Range: bytes=<received>- on the signed URL until sizeBytes match the plan row.
- serve.downloadReady is false or downloadUrl is missing Stop. Read serveExplanation.reason and reasonCodes; do not guess another path.
- Local SHA-256 ≠ files[].sha256 Discard the file. Do not attest. Fetch a fresh copy from a new redirect.
- POST /api/download-completions returns 200 Token already consumed; do not retry the POST.
Mais: campos do schema, erros e especificação legível por agentes
Campos do plano (gerados do schema)
schemaVersion (required): object = "hugging-bay.artifact-download-plan.v1"generatedAt (optional): stringartifactId (required): The one id key every surface uses (same as artifact.id).artifact (required): objectstatus (optional deprecated): DEPRECATED for availability: the canonical STORAGE state (e.g. complete-bytes = bytes are stored) — NOT an obtainability, runnability or verification claim. It stays complete-bytes on a withheld artifact. Read `serve.downloadReady` / `serveExplanation.serveVerdict` instead.serve (required): The single non-contradictory obtain contract (hugging-bay.serve-contract.nv5). Agrees with every per-file affordance below.serveVerdict (optional): Top-level obtain verdict, same evaluation as serveExplanation.serveVerdict.serveExplanation (required): The plain-words twin of `serve`, from the same evaluation (same serveEvaluationId).serveEvaluatedAt (optional): stringserveEvaluationId (optional): stringfiles (required): Hosted file rows. A row is LISTED for inventory even when withheld; only a row with a non-false `url`/`downloadUrl` is downloadable now.stats (optional): Counts over `files`: `files` = rows downloadable now, `listedFiles` = rows listed (including withheld), `withheldFiles` = listed minus downloadable.manifest (optional): objectcanonicalState (optional): objectcompletionAttestation (optional): objectfileIntegrity (optional): objectverify (optional): objectcommands (optional): arrayrunCommands (optional): Recorded shell invocations for the selected exact file when run-command evidence is available. Each command names the tool (llama.cpp, Ollama, LM Studio), exact path, SHA-256, shell command and provenance (testedOn host, testedAt, runnerVersion). status is not-tested-yet when no sealed run kit matches.
Cada linha files[] (gerada do schema)
path (required): stringsizeBytes (required): integersha256 (required): The full published SHA-256 of the exact bytes; verify locally with `sha256sum <file>` (older Mac: `shasum -a 256 <file>`).url (optional deprecated): Legacy alias of downloadUrl; false/null when withheld.downloadUrl (optional): Canonical byte-download URL. Responds HTTP 302 to signed storage; follow redirects.canonicalUrlField (optional): One canonical byte URL per row. = "downloadUrl"downloadUrlResponse (optional): objectverifiedDownloadUrl (optional): Server-stream-verified lane, when available.completionAttestationUrl (optional): POST here after a full download + local SHA-256 match, with the X-Hugging-Bay-Completion-Token from the redirect.contentType (optional): stringrole (optional): string
Códigos de erro
| code | HTTP | meaning / retry |
|---|---|---|
invalid_query | 400 | A query parameter is missing, malformed, or outside its documented enum or numeric range. Carries field and (for enums) allowed. Rejected at the edge before the database. |
artifact_not_found | 404 | No public artifact has that id (anonymous caller). next.command searches the catalog for it. |
file_withheld | 404 | The file lane refused bytes because the live file admission or artifact obtain verdict withheld them (410 for withdrawn bytes or reason_code artifact_obtain_not_ready). Files withheld — not faked; next.command reads the download-plan, whose serveExplanation says why. Artifact-verdict denials include obtainReady, serveVerdict, reasonCodes, reason, serveEvaluatedAt and serveEvaluationId. reason_code carries the lane code. |
rate_limited | 429 | Too many requests in the current window. Wait retry_after_s seconds (also in Retry-After), then retry. See x-rate-limit. |
service_unavailable | 503 | A bounded authority read failed closed or capacity was saturated. The document is never fabricated; wait retry_after_s seconds (always in the body), then retry. |
completion_token_invalid | 403 | Re-open the download URL (do not follow the 302) for a fresh X-Hugging-Bay-Completion-Token, then POST again. |
digest_mismatch | 409 | Do not retry the POST with the same bytes; re-download and re-hash locally. |
signed_url_expired | 403 | GET the plan downloadUrl again; signed storage links last at most 300s. |
range_not_satisfiable | 416 | Fix the Range header against sizeBytes from the plan row. |
Especificação legível por agentes (texto simples)
Hugging Bay download protocol
schemaVersion: hugging-bay.artifact-download-plan.v1
generatedFrom: openapi-developer-contract DOWNLOAD_PLAN_SCHEMA
digest: sha256:4e0ac3abf17f6c1cd5977a02d493f2e8321a4ec6f7a061e3bac4840dfdaf6803
Summary
- Find an artifact (search or resolve), read its download-plan, download only rows with downloadUrl when serve says READY.
- Open the file's downloadUrl (/api/downloads): it re-checks and redirects to a signed link that lasts at most 300s (default 300, configurable shorter). Reopen for a fresh link. A URL is not permission.
- Verify SHA-256 locally, then POST completion with the redirect token.
Obtain authority
- serve.downloadReady and serveExplanation.serveVerdict (READY / BLOCKED / PENDING / UNKNOWN)
- Legacy top-level status is storage-only: DEPRECATED for availability: the canonical STORAGE state (e.
Plan fields (from schema)
schemaVersion (required): object = "hugging-bay.artifact-download-plan.v1"
generatedAt (optional): string
artifactId (required): The one id key every surface uses (same as artifact.id).
artifact (required): object
status (optional deprecated): DEPRECATED for availability: the canonical STORAGE state (e.g. complete-bytes = bytes are stored) — NOT an obtainability, runnability or verification claim. It stays complete-bytes on a withheld artifact. Read `serve.downloadReady` / `serveExplanation.serveVerdict` instead.
serve (required): The single non-contradictory obtain contract (hugging-bay.serve-contract.nv5). Agrees with every per-file affordance below.
serveVerdict (optional): Top-level obtain verdict, same evaluation as serveExplanation.serveVerdict.
serveExplanation (required): The plain-words twin of `serve`, from the same evaluation (same serveEvaluationId).
serveEvaluatedAt (optional): string
serveEvaluationId (optional): string
files (required): Hosted file rows. A row is LISTED for inventory even when withheld; only a row with a non-false `url`/`downloadUrl` is downloadable now.
stats (optional): Counts over `files`: `files` = rows downloadable now, `listedFiles` = rows listed (including withheld), `withheldFiles` = listed minus downloadable.
manifest (optional): object
canonicalState (optional): object
completionAttestation (optional): object
fileIntegrity (optional): object
verify (optional): object
commands (optional): array
runCommands (optional): Recorded shell invocations for the selected exact file when run-command evidence is available. Each command names the tool (llama.cpp, Ollama, LM Studio), exact path, SHA-256, shell command and provenance (testedOn host, testedAt, runnerVersion). status is not-tested-yet when no sealed run kit matches.
Per-file row (from schema)
path (required): string
sizeBytes (required): integer
sha256 (required): The full published SHA-256 of the exact bytes; verify locally with `sha256sum <file>` (older Mac: `shasum -a 256 <file>`).
url (optional deprecated): Legacy alias of downloadUrl; false/null when withheld.
downloadUrl (optional): Canonical byte-download URL. Responds HTTP 302 to signed storage; follow redirects.
canonicalUrlField (optional): One canonical byte URL per row. = "downloadUrl"
downloadUrlResponse (optional): object
verifiedDownloadUrl (optional): Server-stream-verified lane, when available.
completionAttestationUrl (optional): POST here after a full download + local SHA-256 match, with the X-Hugging-Bay-Completion-Token from the redirect.
contentType (optional): string
role (optional): string
Serve verdicts
READY, BLOCKED, PENDING, UNKNOWN
Snapshot scope: search, resolve, file inventory, finder and plan accept snapshot. Pins freeze metadata per instance, not signed URLs or byte authority. Check X-Hugging-Bay-Snapshot-State; unfrozen is not an immutable answer. Restart or another instance can miss a prior answer. A 410 requires restarting the metadata chain with a fresh pin. Download bytes must still match the selected plan hash and size.
Endpoint changes in this source candidate (publication is separate)
- /api/v1/search: Optional snapshot query pins complete public search metadata; unpinned behavior is unchanged.
- /api/v1/resolve: Optional snapshot query pins resolved public identity; missing/unsupported references retain 404/400.
- /api/v1/artifacts/{id}/files: Optional snapshot query pins bounded public file inventory; partial/degraded reads are never frozen.
- /api/v1/artifacts/{id}/download-plan: Existing snapshot pins remain; clients carry the same pin across discovery and plan selection.
- /openapi-public.json: Public read profile projects canonical schemas and their referenced components from the full contract.
- /api/downloads/{id}/{path}: No authority change: every redirect is fresh and the final bytes must match the selected plan hash/size.
Retry rules
- HTTP 429 or 503 on plan/search/resolve: Wait Retry-After (or retry_after_s in JSON), add jitter, then retry the same read.
- HTTP 302 Location expires or returns 403: GET the same /api/downloads/{id}/{path} again without following redirects to mint a fresh signed URL (≤300s).
- Partial download interrupted: Resume with Range: bytes=<received>- on the signed URL until sizeBytes match the plan row.
- serve.downloadReady is false or downloadUrl is missing: Stop. Read serveExplanation.reason and reasonCodes; do not guess another path.
- Local SHA-256 ≠ files[].sha256: Discard the file. Do not attest. Fetch a fresh copy from a new redirect.
- POST /api/download-completions returns 200: Token already consumed; do not retry the POST.
Error codes (download path)
- invalid_query (400): A query parameter is missing, malformed, or outside its documented enum or numeric range. Carries field and (for enums) allowed. Rejected at the edge before the database.
- artifact_not_found (404): No public artifact has that id (anonymous caller). next.command searches the catalog for it.
- file_withheld (404): The file lane refused bytes because the live file admission or artifact obtain verdict withheld them (410 for withdrawn bytes or reason_code artifact_obtain_not_ready). Files withheld — not faked; next.command reads the download-plan, whose serveExplanation says why. Artifact-verdict denials include obtainReady, serveVerdict, reasonCodes, reason, serveEvaluatedAt and serveEvaluationId. reason_code carries the lane code.
- rate_limited (429): Too many requests in the current window. Wait retry_after_s seconds (also in Retry-After), then retry. See x-rate-limit.
- service_unavailable (503): A bounded authority read failed closed or capacity was saturated. The document is never fabricated; wait retry_after_s seconds (always in the body), then retry.
- completion_token_invalid (403): Re-open the download URL (do not follow the 302) for a fresh X-Hugging-Bay-Completion-Token, then POST again.
- digest_mismatch (409): Do not retry the POST with the same bytes; re-download and re-hash locally.
- signed_url_expired (403): GET the plan downloadUrl again; signed storage links last at most 300s.
- range_not_satisfiable (416): Fix the Range header against sizeBytes from the plan row.
# Worked example: resolve → plan → download (resume) → verify → attest
# Requires curl, jq and either sha256sum (Linux) or shasum (Mac). Choose a repo and an exact path from its plan.
set -eu
BASE='https://huggingbay.xyz'
REPO='Qwen/Qwen2.5-0.5B-Instruct-GGUF'
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
PIN=$(curl -fsS "$BASE/api/v1/snapshot" | jq -er '.snapshotId')
curl -fsS --get --data-urlencode "repo=$REPO" --data-urlencode "snapshot=$PIN" "$BASE/api/v1/resolve" > "$WORK/resolve.json"
ARTIFACT=$(jq -er '.artifactId' "$WORK/resolve.json")
curl -fsS --get --data-urlencode "snapshot=$PIN" "$BASE/api/v1/artifacts/$ARTIFACT/download-plan" > "$WORK/plan.json"
jq -e '.serve.downloadReady == true' "$WORK/plan.json" > /dev/null
# Inspect files[] and choose FILE explicitly; this example chooses the first READY GGUF.
jq -e '[.files[] | select(.downloadUrl != null and (.path | endswith(".gguf")))][0] | select(. != null)' "$WORK/plan.json" > "$WORK/file.json"
HASH=$(jq -er '.sha256 | sub("^sha256:"; "") | ascii_downcase | select(test("^[0-9a-f]{64}$"))' "$WORK/file.json")
BYTES=$(jq -er '.sizeBytes | select(type == "number" and . >= 0 and floor == .)' "$WORK/file.json")
DOWNLOAD=$(jq -er '.downloadUrl' "$WORK/file.json")
case "$DOWNLOAD" in "$BASE/api/downloads/$ARTIFACT/"*) ;; *) exit 1 ;; esac
# The signed redirect and capability are fresh, never snapshot-cached. Do not log them.
curl -fsS -D "$WORK/headers" -o /dev/null "$DOWNLOAD"
TOKEN=$(awk 'tolower($1)=="x-hugging-bay-completion-token:" {gsub("\r", "", $2); print $2}' "$WORK/headers")
LOC=$(awk 'tolower($1)=="location:" {gsub("\r", "", $2); print $2}' "$WORK/headers")
test -n "$TOKEN" && test -n "$LOC"
# Re-run this transfer with a fresh redirect if its signed URL expires. Keep HASH and BYTES.
curl -fsSL -C - -o model.gguf.part "$LOC"
test "$(wc -c < model.gguf.part | tr -d ' ')" = "$BYTES"
if command -v sha256sum > /dev/null 2>&1; then
printf '%s model.gguf.part\n' "$HASH" | sha256sum -c -
else
printf '%s model.gguf.part\n' "$HASH" | shasum -a 256 -c -
fi
# Completion route follows the plan-selected file, not a guessed filename.
COMPLETION=$(printf '%s' "$DOWNLOAD" | sed 's#/api/downloads/#/api/download-completions/#')
jq -n --arg sha256 "$HASH" --argjson sizeBytes "$BYTES" --arg completionToken "$TOKEN" '{sha256:$sha256,sizeBytes:$sizeBytes,completionToken:$completionToken}' > "$WORK/completion.json"
curl -fsS -X POST -H 'content-type: application/json' --data-binary @"$WORK/completion.json" "$COMPLETION"
mv model.gguf.part model.gguf