Skip to content

Serving Manifests and Packages

Rod Christiansen edited this page Sep 5, 2026 · 2 revisions

Serving Manifests and Packages

BootstrapMate needs two things from your web host: one manifest document at a stable URL, and every package the manifest names. This page covers what the client actually requires of a host, what it does not enforce, and how to check a host before you point a fleet at it.

The manifest URL

Set the URL once, in policy or in machine settings, rather than passing --url on every invocation — see Preferences. A single absolute URL is fetched per run.

Requirements the client imposes:

  • The URL must be absolute. It is passed straight to HttpClient, and the host portion is parsed out and remembered for the Authorization scoping rule below. A URL that does not parse as absolute leaves that host unset, which disables the header for every package download in the run.
  • The response body must be the manifest itself. It is read as a string and parsed directly. A login page, an HTML error page returned with a 200, or a directory listing will fail to parse and the run will exit 1.
  • The file extension decides the format, before the content is examined. If the URL's path — with anything from the first ? or # onwards stripped — ends in .yaml or .yml, the body is parsed as YAML. Otherwise, if the trimmed body's first character is neither { nor [, it is also parsed as YAML. Everything else is parsed as JSON. The response's Content-Type is never consulted, so serving application/octet-stream or text/plain is harmless, and serving JSON from a .yaml URL is not.

A query string is fine. If your host needs a signed URL, put the signature in the query and the extension rule still works, because the query is stripped before the extension is tested.

Package URLs

Each manifest item carries its own absolute url and a file name. The file value — not anything derived from the URL or from a Content-Disposition header — is the name the download is written under inside C:\ProgramData\ManagedBootstrap\cache. Two items that share a file value will overwrite each other, so keep them distinct.

Any status outside the 2xx range fails that item with Download failed: {StatusCode} and the run moves on to the next one. Nothing else about the response is validated: no size check, no content-type check, and no hash — see Security and Package Verification.

Package URLs may point at a different host from the manifest. That works, with one consequence, described next.

Authentication

The only authentication mechanism is a single AuthorizationHeader value, sent verbatim as the Authorization request header. There is no support for client certificates, for a second header, for arbitrary custom headers, or for per-item credentials. The GUI emits a --headers switch, but the CLI has no case for it and discards it; configure the value in the registry.

The header is attached to:

  • the manifest request, always; and
  • a package download only when the package URL's host equals the manifest URL's host, compared case-insensitively.

A package on any other host is fetched anonymously, and the run logs Authorization header withheld for cross-host download: {url} at Debug level — so you will only see it with --verbose. Plan around this: if your packages need authentication, serve them from the same host as the manifest. If they are on a different host, that host must either allow anonymous reads or carry its own authorisation inside the URL, such as a pre-signed query string.

The reverse case bites too. Some object stores return 403 Forbidden for a public object requested with an Authorization header they cannot validate. That is the failure the host-scoping rule was added to prevent; if you see Download failed: Forbidden across a fleet, check whether the packages are on the same host as the manifest and whether the header is being attached when it should not be.

Every request carries User-Agent: BootstrapMate/{version}. Do not build access control on that string, but do allow it through any user-agent filtering on the host.

Redirects

Redirects are followed. The client uses a default HttpClient, whose default handler follows redirects, and nothing in the tool changes that.

The FollowRedirects setting — present in the configuration object, in the ADMX template, and in the GUI — is never read by the command-line tool. Leaving it disabled does not stop redirects being followed, and enabling it changes nothing. The only place it takes effect anywhere in the product is the GUI's "Preview manifest" button. See Troubleshooting and Gotchas.

Two practical notes follow from that. First, a redirect that crosses hosts moves the request off the manifest host; the Authorization header decision is made from the URL as written in the manifest, not the final location. Second, HttpClient does not forward an Authorization header across a redirect to a different origin, so an authenticated endpoint that redirects elsewhere will not stay authenticated. Prefer serving content directly over redirecting to it.

HTTPS

Use HTTPS. The client does not require it — an http:// URL is fetched without complaint — but over plain HTTP the manifest, the packages and any Authorization header are exposed, and substituting the manifest is equivalent to running code as SYSTEM on the device.

TLS validation is the platform's default: the machine trust store and schannel decide. The tool does not pin certificates and does not relax validation, so a host presenting a certificate the device does not trust will fail the download. That matters during OOBE, when a device may not yet have received an internal root certificate — if you serve from an internally-issued certificate, make sure it is trusted before BootstrapMate runs.

Timeouts

The manifest and package requests use .NET's default 100-second HttpClient timeout.

The NetworkTimeout setting is not applied to those requests. Configuring it, in policy or anywhere else, does not change how long a slow download is allowed to take. Size your payloads and your host's throughput against 100 seconds per request, not against whatever NetworkTimeout says. See Retries and Timeouts.

There is no download retry. A failed download fails its item.

Verifying a host

Run these from a machine that is representative of your fleet's network path — a device on the provisioning VLAN, not an admin workstation.

Confirm the manifest is served with a 200 and note the content type and any redirect:

curl -sSI https://example.com/bootstrap/bootstrapmate.json

Confirm the body is the document you expect and not a portal or an error page:

curl -sS https://example.com/bootstrap/bootstrapmate.json

Confirm the manifest parses as the format its extension implies:

curl -sS https://example.com/bootstrap/bootstrapmate.json | ConvertFrom-Json

Confirm each package URL answers, and that its size is what you expect:

curl -sSI https://example.com/bootstrap/packages/Example-1.0.msi

If your host is authenticated, confirm it accepts the exact header value you configured, and confirm the packages are reachable the way BootstrapMate will actually reach them:

curl -sSI -H "Authorization: Bearer <token>" https://example.com/bootstrap/bootstrapmate.json

Then walk the checklist:

  • The manifest URL is absolute and HTTPS.
  • Its path extension matches the format of the body (.yaml/.yml for YAML, anything else for JSON).
  • Every package URL in the manifest returns 200 on its own.
  • Every item's file value is unique within the manifest.
  • If a package host needs authentication, it is the same host as the manifest — otherwise the request will be anonymous.
  • If the packages are public, the manifest host is the same host only if it is safe for the header to be sent there.
  • The TLS certificate chains to a root the device trusts during OOBE.
  • Nothing on the path (proxy, WAF, user-agent filter, conditional access) blocks BootstrapMate/{version}.
  • No individual download needs more than 100 seconds.
  • Every msi and exe payload is Authenticode-signed, unless you have deliberately set AllowUnsigned; see Security and Package Verification.

Then do the real check: run the tool once against the host with --verbose on a test device, and read the newest bootstrap.log under C:\ProgramData\ManagedBootstrap\logs. An empty cache directory afterwards means every item installed; anything left in C:\ProgramData\ManagedBootstrap\cache is a failure kept for inspection. Do not rely on the exit code — see Command Line Reference.

See also

Clone this wiki locally