Files
bare-operating-system/packages/bare-os-booter/CLI_PARITY.md
T
Raven Scott 7171618c74
Release rolling / release (push) Successful in 9m59s
Update Docs
2026-08-12 21:10:14 -04:00

6.5 KiB

curl / wget CLI parity (Bare OS booter)

HTTP in Pear/Bare uses WHATWG Fetch (Node fetch or bare-fetch on bare-http1 / bare-https), not libcurl or GNU wget (C). The lists below track how close the booter shims are to common CLI flags.

History: -O / -o mutual exclusion, -I -L with manual redirects and %{num_redirects}, and wget -c (Range / 206 / 416) landed in a7e08e09. Later work added cookie jar, -J, --connect-timeout, **--cacert / -k**, BARE_OS_DNS_ALLOWLIST, Pear-safe fetch bootstrap (bare-os-ensure-bare-fetch.js), and wget --no-clobber.

curl (lib/curl-cli.js)

  • URL interleaving, scheme guess (http://, https: for //)supportedhttp-fetch-url.js
  • -X / --requestsupported
  • -H / --headersupported
  • -A / --user-agentsupported — Default DEFAULT_CURL_USER_AGENT; last -A or -H User-Agent wins
  • -d, --data*, --jsonsupported
  • -o / --outputsupported — VFS path; multiple URLs → FILE.N
  • -O / --remote-namesupported — Saves using URL basename (like curl)
  • -T / --upload-filesupported — PUT
  • -I / --headsupported
  • -i / --includesupported
  • -L / --locationsupported-I -L uses HEAD then GET after redirect (curl semantics); plain GET uses redirect: follow
  • -f / --failsupported — Exit 22
  • -s / -S / -vsupported
  • -u / --usersupported — Basic auth only
  • -m / --max-timesupported — Whole-request AbortSignal timeout; combined with --connect-timeout as min(max-time, connect-timeout) when both are set
  • -w / --write-outpartial%{http_code}, %{url_effective}, %{size_download}; %{num_redirects} non-zero only for -I -L manual hop path
  • -V / --version, -h / --helpsupported
  • Cookie jar, --cookie, -b, -c / --cookie-jarsupported — JSON map hostname → { name → value } on ~/.config/bare-os/curl/cookies.json (or path from -c); -b file or name=value; see curl-cli.js
  • --connect-timeoutsupported — Seconds (integer or decimal); sets the abort deadline when used alone, or min with --max-time when both are set. **-Y / -y** remain out of scope
  • --cacert, -k / --insecuresupported — Global fetch: Node undici Agent. When ctx.httpFetch is set, TLS intent is passed as init.bareOsCurlTls (insecure, optional caPem, optional pinnedSha256 from BARE_OS_TLS_PIN_SHA256) for the host implementation
  • TLS client certs (mutual TLS)out of scope — Stack only
  • HTTP/2, HTTP/3, SOCKS, FTP, SCPout of scope
  • -J content-disposition filenamesupported — With -O; uses Content-Disposition basename only; rejects .. and absolute paths in the server-provided name

wget (lib/wget-cli.js)

  • URL interleaving, short clusters (-qO-, -T30)supported
  • -O / --output-documentsupported- = stdout; one URL only with -O
  • -P / --directory-prefixsupported — Cannot combine with -O
  • -q / --quietsupported
  • -U / --user-agentsupported — Default DEFAULT_WGET_USER_AGENT
  • -T / --timeoutsupported — Same semantics as curl --max-time (whole request)
  • --header, --post-data, --post-filesupported
  • -c / --continuesupportedRange: bytes=<existing>- when output file exists; 206 appends, 200 replaces full file
  • -V / -hsupported
  • Recursive / spider / FTP / WARCout of scope
  • --no-clobber / -ncsupported — Skip download when the target file already exists and has size > 0 (not combined with --continue)
  • -N timestampingout of scope

Fetch / bare-fetch vs curl (-I + -L)

curl sends HEAD, and after a 3xx follows with GET on the new URL. bare-fetch reuses the original Request init on redirect, so HEAD stays HEAD on every hop, which breaks many servers/CDNs.

We do not depend on bare-fetch for that case: fetchHeadWithLocationFollow implements manual hops (HEAD → GET after redirect). See Upstream (bare-fetch) below if you want this fixed in the library.

Upstream (bare-fetch)

Tracking: Bare OS implements curl -I -L with manual redirects so the first hop can stay HEAD and a 3xx response can be followed with GET (see fetchHeadWithLocationFollow). Consider an upstream bare-fetch option for “curl-like” redirect method switching so other callers get the same behavior without a custom loop.

Issue tracker row (keep updated):

Area Upstream repo Issue / PR Status Last checked
HEAD + redirect method switching (curl -I -L semantics) holepunchto/bare-fetch TBD (add link once filed) open 2026-04-26

When the issue is filed, replace _TBD_ with the direct GitHub URL and keep this row current during release checklist updates.

Suggested issue title: Redirect following preserves original method (HEAD on every hop); optional curl-like mode?

Body (paste into GitHub):

When using fetch(url, { method: 'HEAD', redirect: 'follow' }), redirects reuse the outer init, so each hop stays HEAD. Real curl -IL uses HEAD on the first request, then GET after a redirect. That behavior matters for CDNs that return 302 to a URL that only answers GET.

Request: Document this as spec-accurate Fetch behavior, and/or add an option (e.g. on Request init or fetch options) to switch redirect method to GET after a redirect when the initial method was HEAD, matching common CLI expectations.

Context: Bare OS implements a fetch-based curl shim and currently uses redirect: 'manual' plus manual hops for -I -L instead of redirect: 'follow' for that reason.

Repository: holepunchto/bare-fetch. Workspace pin is bare-fetch@^3.2.0 (major 3: Headers.getSetCookie(), response.type). In-guest web_fetch forwards those fields when present.