3.3 KiB
Executable File
archive-org-collection-harvest
Type: Hermes skill (~/.hermes/hermes-whale/skills/research/archive-org-collection-harvest/SKILL.md)
Created: 2026-09-21
Origin: Old iPad iOS 9.3.5 IPA hunt — archive.org was unreachable, this pattern was
worked out to get around it.
What it does
Enumerate and bulk-download files from any archive.org collection when
archive.org does not resolve on the current network (home router blocks the DNS name).
Covers the metadata API, the "filenames are the index" filtering technique, and the
double --resolve needed because downloads 302 to a second host.
The two things that make it non-obvious
1. Filenames carry the metadata
Archive.org item filenames often embed app ID + bundle ID + version + min OS:
iOS 4/jp.co.korg.iMS-20/iMS-20-(jp.co.korg.iMS-20)-1.5.2-(iOS_4.2)-b907eac….ipa
So one curl of /metadata/<id> + jq -r '.files[].name' + grep -iE '<terms>' gives you
full search, version targeting, and curation. No per-file API calls, no page scraping.
2. Downloads need the redirect host pinned separately
archive.org/download/... returns 302 to https://dn####.ca.archive.org/....
Pinning only archive.org yields http=302 size=0 — looks like a broken file, is actually
an unpinned redirect. You must read the Location header, resolve that host (dig @1.1.1.1),
and add a second --resolve:
ORIGIN_IP=$(dig +short archive.org @1.1.1.1 | tail -1)
DN=$(curl -sI --resolve archive.org:443:"$ORIGIN_IP" "$url" \
| grep -i '^location' | tr -d '\r' | sed 's/^[Ll]ocation: //')
HOST=$(printf '%s' "$DN" | sed -E 's#https://([^/]+)/.*#\1#')
IP=$(dig +short "$HOST" @1.1.1.1 | tail -1)
curl -sL --resolve archive.org:443:"$ORIGIN_IP" --resolve "$HOST:443:$IP" -o out.bin "$url"
Other pitfalls recorded
- Spaces in filenames are literal → must
sed 's/ /%20/g'.+is not accepted. web_extractonarchive.org/details/...returnsBlocked: URL targets a private or internal network address— the extractor's own guard.web_searchon the same topic can also return empty. Fall back tocurl --resolvefast instead of retrying the web tools.- Big metadata items (17k files) truncate in tool output and break
json.loads. Alwayscurl -o file.jsonthenjq, never parse the response inline. HOST/IPchange per download host — re-derive, never hardcodedn####.- Cheap probe before a big fetch:
curl -r 0-4095 -w 'http=%{http_code} size=%{size_download}'→ expectHTTP 206+ non-zero bytes. - Verify with
file— an empty file orHTML documentmeans the resolve failed. - Write harvests as a script file run with
bash file.sh; inline multi-linecurl | jqchains are fragile and each inline call may need approval.
Verification checklist
- Control site returns 200 (network up, only archive.org DNS blocked)
/metadata/<id>returns JSON with plausible.filescount- Filename list non-empty, filtered hits match expectations
-r 0-4095probe → HTTP 206, non-zero sizefileon probe reports expected archive/binary type
Applied in
personal/how-to/old-ipad-ios935-ipa-sources.md— iOSObscura collection (17,652 IPAs), curated iOS 9.3.5 music/MIDI + utility shortlistpersonal/how-to/scripts/fetch-ios935-ipas.sh— working downloader built on this pattern