carrier-explode Explode and decode carrier data GitHub About

How carrier-explode works

carrier-explode is three Cloudflare Workers over one R2 bucket and one D1 database. The extractor finds new builds and carrier files, extracts them into the bucket (carrier-explode-ingest) and indexes them into the database (carrier-explode-index). The site and the API only read.

Flow

StageRuns inReadsWrites
Feed checkextractor, on a crona feed; the bucket’s held recordsone Workflow instance per new unit
Unita Workflow instance, step by stepthe firmware or file, by HTTP Rangeobj/ artifacts, norm/ profiles
Release recordthe unit’s last stepwhat the steps left in tmp/releases/<platform>/…json; an index message
Indexingthe index queue’s consumerrecords, profilesD1 rows; a purge message
Purgethe purge queue’s consumereach reader’s cache dropped
Pages, APIsite and API WorkersD1 rows, R2 objectsnothing

A unit is the smallest thing ingested on its own: an iOS build, one Pixel’s OTA of one build, one Galaxy firmware, or one snapshot of an OTA feed. Its Workflow instance is named after it (galaxy-build-S942UOYN4BZID), so it is never started twice. A unit is held once its record is in the bucket; planning compares a feed against R2 key listings, not D1. The record is written last, so a failed unit is planned again by the next check.

Pipelines

From apps/extractor/src/pipelines.ts; times are UTC.

PipelineUnitFeedCheckedArticle
ios-buildan iOS build: its in-scope IPSWsipsw.me, AppleDBevery 20 minutesApple ingest
apple-otaa snapshot of Apple’s manifestthe carrier bundle manifestevery 6 hours, at :07Apple ingest
pixel-deviceone Pixel’s OTA of one buildGoogle’s OTA page, source.android.com build numbersdaily, 03:35Pixel ingest
pixel-otaa snapshot of the update service’s answersGoogle’s carrier settings update serviceevery 6 hours, at :07Pixel ingest
galaxy-buildone model’s firmwareGoogle Play’s device list, Samsung version.xml, FUSevery 20 minutesGalaxy ingest
labelsa weekcodes no feed namesMondays, 04:17
dataseta daythe public APIdaily, 03:50Datasets
reindexa record, or every held recordthe bucketby handIndexing

A check starts every unit it plans at once, except that the two pipelines that need a container (ios-build, galaxy-build) start at most their share of the container pool: 3 and 1 of 5 in production; the spare covers a released container whose slot frees minutes later.

Scope

Production scope, from apps/extractor/wrangler.jsonc:

FamilyDevicesBefore 5 October 2026From 5 October 2026
iPhonereleased since September 2023every release, and the newest major’s betasevery build
Pixelreleased since October 2020each Pixel’s newest build of each trainevery build
GalaxyUS S, Z Fold and Z Flip (FE aside) launched since January 2025each model’s newest build of the newest 3 Android majors, on each family’s newest generation with itthe same
Apple OTAiOS: every file; iPadOS, watchOS: each source’s file for the newest OS it is listed for

Failures

What failsThen
a stepretried 3 times, 10 s apart and doubling; a container step 2 times
a step, permanentlya 4xx other than 408 and 429, a record that fails its schema, or a format error: the instance stays errored until rebuilt
an index messageretried 10 times, 5 minutes apart, then kept in a dead-letter queue

Running costs

DesignWhere
The bulk of the data (artifacts, profiles) is in R2, written once under its hash; R2 does not charge for egress (R2 pricing)obj/, norm/; writes use If-None-Match: *
D1 holds an index, not the files: a source’s full settings and concepts are kept for its newest version only; older versions are read from their profile in R2settings, concepts tables
Worker steps read firmware by HTTP Range and stream it: a Pixel OTA’s partitions are read from inside its payload.bin, a Galaxy member is decrypted and inflated as it arrivespackages/firmware
A step asks D1 which hashes are already held, in one query, and stores only the restheldShas, R2 listings
A container is used only where a Worker cannot do the job: an iOS root filesystem and a Galaxy AP memberapps/extractor/container
Each container attempt gets its own container, which is destroyed when the job answers, at the latest after 58 minutes; an idle one stops after 5src/container.ts
Derivation runs once per platform after a burst of units, not once per unitthe index queue
Pages and API answers are cached at the edge and dropped only when the index changesone cache tag, index

The container instance type is 1 vCPU, 3 GiB, 20 GB, the smallest with the disk the Galaxy job needs.

See also