rest-api manual
Reference for the Asfaload REST API.
Authentication
Authenticated endpoints require four HTTP headers, signing the request with the caller’s Ed25519 secret key:
X-asfld-timestamp— request timestamp, RFC 3339 format (e.g.2025-06-17T14:03:22.123456789+00:00). It must be in canonical UTC form: offset written as+00:00(neverZor another offset), and fractional seconds with no trailing zeros (omitted entirely when zero). This is exactly what chrono’sDateTime::<Utc>::to_rfc3339()emits. The server normalizes any valid RFC 3339 timestamp to this form before verifying the signature, so a validly-signed request with a non-canonical timestamp string is rejected.X-asfld-nonce— random UUID v4, unique per request.X-asfld-sig— base64-encoded (unpadded) Ed25519 signature, computed as described below.X-asfld-pk— caller’s public key in asfaload format: the literal prefixasfaload-pub:followed by the base64 encoding (standard alphabet, unpadded) of the 32 raw key bytes (e.g.asfaload-pub:b5S+CxuqICIUn/DGBdMKeTMZCgQcg78ohiWQ1sC00c8).
Computing the signature
-
Build the canonical request string by joining the timestamp, the nonce and the request payload with the
##separator:{timestamp}##{nonce}##{payload}timestamp— the exact string sent inX-asfld-timestamp, which must already be in the canonical form described above.nonce— the exact string sent inX-asfld-nonce.payload— the raw request body as a UTF-8 string. For requests without a body (e.g.GET /v1/ping), use the empty string.
-
Compute the SHA-512 digest of the canonical request string. The digest is used raw (64 bytes); do not hex- or base64-encode it.
-
Sign the raw digest bytes with the Ed25519 secret key: the signature covers the digest itself, not the canonical string.
-
Base64-encode (unpadded) the 64-byte signature and send it in
X-asfld-sig.
Pseudocode:
canonical = timestamp + "##" + nonce + "##" + payload
digest = SHA-512(canonical) // 64 raw bytes
signature = Ed25519-sign(secret key, digest)
sig header = base64(signature) // unpadded
pk header = "asfaload-pub:" + base64(public key) // unpadded, 32 raw bytes
The server rebuilds the canonical request string from the received headers and body, computes its SHA-512 digest, and verifies the signature with the transmitted public key. A request is rejected when its timestamp’s age — truncated to whole minutes — exceeds 5 minutes (so requests up to just under 6 minutes old are accepted), when it is more than 10 seconds in the future, when the nonce was already used, or when the signature does not verify.
Registration
POST /v1/register_repo— register a new project with the signing serverPOST /v1/update_signers— propose an update to a project’s signers file
Signatures
POST /v1/signatures— submit signatures for a fileGET /v1/signatures/{file_path}— query signature collection status for a fileGET /v1/pending_signatures— list files awaiting the caller’s signature
Files
GET /v1/files/{file_path}— fetch raw file content from the repositoryGET /v1/files-to-sign/{file_path}— fetch file contents needed for signing
Signers
GET /v1/get_signers/{file_path}— get the signers configuration for a pathGET /v1/get_signers_chain/{artifact_path}— get the signers history chain for a signed artifact
Revocation
POST /v1/revoke— revoke a previously signed file
Assets
POST /v1/assets— register assets from a GitHub release or checksums files