Consuming files
Downloading from a static file registry takes any HTTP client and a credential. Plain files have no client-side resolver and no index protocol, so Haven answers “which version” itself, server-side, and hands the bytes out through its object storage.
Downloads always require a credential, on public registries too. The minimal credential for a public registry is a personal public-read token; see Tokens & access.
Downloading
Section titled “Downloading”curl -L -O -H "Authorization: Bearer $HAVEN_TOKEN" \ https://haven.example.com/r/acme-assets/files/some-package/1.0.0/some-file.tar.gzPass -L: when the object store supports it, the download answers a
short-lived redirect to a presigned URL and the bytes come straight
from storage; otherwise Haven streams them itself. Either way the
response carries the file’s checksum in X-Checksum-Sha256, and the
Content-Type the publisher uploaded with. To check the bytes you
received against that checksum, see
verifying a download.
A credential that is not entitled, a package that does not exist, and a held version all answer the same 404: the download endpoint discloses nothing. The reasons a held version refuses are one authenticated call away, on the version listing and on the package’s status in the UI.
Resolving
Section titled “Resolving”# The latest published versioncurl -H "Authorization: Bearer $HAVEN_TOKEN" \ https://haven.example.com/r/acme-assets/files/some-package
# The highest published version matching a range, cargo semanticscurl -H "Authorization: Bearer $HAVEN_TOKEN" \ "https://haven.example.com/r/acme-assets/files/some-package?req=^1.4"The response names the winning version and lists its files with sizes, checksums, and content types, so a script can pick its file and download it. Only published, non-yanked, semver-parseable versions resolve — never a draft, however high its number. “Latest” prefers the highest stable version; a prerelease wins only when nothing stable exists, the crates.io posture.
One level deeper, GET /r/{registry}/files/{package}/{version}
answers the same shape for one exact version, drafts and yanked
versions included, flagged rather than hidden. This is where a script
diagnosing a refused download finds held and held_because.
A ?req= range only means something against a package that numbers
its versions in semver. A package on any other
version scheme still
resolves without req= (you get its latest semver-parseable version,
or nothing), but a requirement has no vocabulary to match a date or a
counter. The package’s Versions tab in the UI has a box that resolves
a requirement against the live catalogue, on semver packages.
Downloading a whole version
Section titled “Downloading a whole version”When the registry has version archives turned on (an owner setting, off by default), every published version also serves all of its files as one premade archive, zip or tar.gz per the registry’s setting:
curl -L -O -J -H "Authorization: Bearer $HAVEN_TOKEN" \ https://haven.example.com/r/acme-assets/files/some-package/1.0.0/_archiveThe response is named {package}-{version}.{format} and carries the
archive’s own checksum in X-Checksum-Sha256. The _archive segment
can never collide with a real file: filenames must start with a letter
or digit.
Archives are built right after a version publishes, by a worker. In
the short window before the build lands the route answers
503 Service Unavailable with a Retry-After header — a script that
publishes and immediately downloads should retry, not conclude the
archive does not exist. Versions published before the registry turned
archives on answer the same 503 until an owner builds them from the
package page, so bound the retries when fetching older versions. A version whose build has permanently failed
(its files are over the server’s archive size cap, or the build ran
out of attempts) answers 404 with the reason instead: final, so a
retry loop written against the 503 contract terminates. The
version listing says where things stand: its archive
field carries {sha256, size, format, built_at} once built,
{"pending": true} while building,
{"failed": true, "reason": ...} when no archive is coming, and
null when the registry does not serve archives (or the version is a
draft).
The same download rules apply as for single files: a credential is always required, drafts have no archive, a held version answers 404, and a yanked version stays downloadable by exact version.
Verifying a download
Section titled “Verifying a download”Every file’s SHA-256 is computed by Haven from the bytes the publisher uploaded; it is the file’s identity in object storage, and publishers can verify it at upload time too. To verify a download end to end, read the expected checksum from the version listing, then hash what you received:
expected=$(curl -s -H "Authorization: Bearer $HAVEN_TOKEN" \ https://haven.example.com/r/acme-assets/files/some-package/1.0.0 \ | jq -r '.files[] | select(.filename == "some-file.tar.gz").sha256')
curl -L -O -H "Authorization: Bearer $HAVEN_TOKEN" \ https://haven.example.com/r/acme-assets/files/some-package/1.0.0/some-file.tar.gz
echo "$expected some-file.tar.gz" | sha256sum -cThe X-Checksum-Sha256 response header is not the thing to script
against: on a presigned download it rides the redirect, and most HTTP
clients following -L only surface the final response’s headers. The
version listing is the reliable place to read the expected value —
and it lets you pin the checksum before fetching the bytes.
When Haven streams the bytes itself instead of redirecting, it re-hashes them against the recorded checksum before serving: storage corruption surfaces as an error, never as silently wrong bytes.
Version archives verify the same way:
the version listing’s archive.sha256 is the expected digest of the
whole bundle, computed when the archive was built — and every file
inside the archive was itself verified against its recorded checksum
during the build, so the one digest covers the version end to end.
Public registries
Section titled “Public registries”A public files registry is browsable by any signed-in user, but the byte surfaces stay behind a credential: resolution and download need at least a personal public-read token, the same rule as public cargo registries. Held versions stay invisible to the public; the gate applies to strangers exactly as it does to you.
Grants
Section titled “Grants”Per-package grants work as they do for crates: an owner grants a named identity read access to one package, with a version requirement, without making anything else in the registry visible. The grantee’s existing token then resolves and downloads that package.
curl -X PUT -H "Authorization: Bearer $HAVEN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"subject": "customer-identity", "version_req": "^1"}' \ https://haven.example.com/api/v1/registries/acme-assets/files/some-package/grantsOne files-specific rule: versions are not guaranteed to be semver, and
a semver range cannot honestly match a version that is not one. *
grants the whole package and is the only requirement that covers
non-semver versions; narrower requirements cover only the versions
they can parse. A package that declares the semver
version scheme has no
non-semver versions to worry about, so every requirement means what it
says there. A package that declares calver or sequential has
promised the opposite, so a narrower requirement there is refused when
you write it: nothing it publishes could ever match. Use *, or change
the scheme first. A grant can be added before the package’s first
upload.
Withdrawing works the same as on crates, on the same path:
# One range, or every range this subject holds when version_req is omitted.curl -X DELETE -H "Authorization: Bearer $HAVEN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"subject": "customer-identity", "version_req": "^1"}' \ https://haven.example.com/api/v1/registries/acme-assets/files/some-package/grantsThe response lists the requirements actually removed. Withdrawing
something that was never granted is not an error: it answers 200 with
an empty list, because the access you asked for is already the access
that exists.