zshot/cliDownload

Authentication

The server authorizes a request by a credential header or by a signed URL.

MethodFlagClient sends
Basic--server-basic-authAuthorization: Basic <base64 of user:password>
Token--server-token-authAuthorization: Bearer <token>
Signed URL--server-signed-urlexpires and signature query parameters

A missing or wrong credential answers 401. With both Basic and token set, only the token authorizes.

Precedence

A valid credential header authorizes outright, and a wrong one is rejected without checking a signature. A request with no credential header is checked for a signature when signed URLs are on.

Signatures apply to GET /, DELETE / and GET /assets. POST / and /mcp authorize by credential header only, so a server with signed URLs and no Basic or token auth accepts any POST and any MCP request.

Signed URLs

Sign the request’s query parameters, expires included:

  1. Set expires to a Unix time in seconds.
  2. Sort the parameters by key, then by value, and form-encode them.
  3. Compute HMAC-SHA256 of that string with the secret, as lowercase hex.
  4. Append it as signature.
import hashlib, hmac, time, urllib.parse

params = {
    "url": "https://zshot-cli.com/example_assets/zrm/",
    "output_type": "png",
    "expires": str(int(time.time()) + 300),
}
query = urllib.parse.urlencode(sorted(params.items()))
signature = hmac.new(b"SECRET", query.encode(), hashlib.sha256).hexdigest()
print("http://127.0.0.1:3000/?" + query + "&signature=" + signature)
ResponseReason
401no signature
403the signature does not match, expires is missing or past, or expires is beyond --server-signed-url-max-age

The server signs the asset links in a render response with the same secret; see Additional Assets.

Metrics Endpoint

--server-prometheus-auth sets a Basic credential that is the only one /metrics accepts. Without it, /metrics takes the main server’s credentials, and is open when the server has none.