Ephemera
ephemera.spacebuilt 2026-09-03 21:33 UTC
Every eight hours, SpaceX publishes where each of about eleven thousand Starlink satellites will be for the next three days, and how sure it is. Eight hours later that set is gone, replaced by the next one. Ephemera keeps them, and measures what the public catalogue can see.
The files carry a full position and velocity uncertainty every sixty seconds, read from the feed on 30 August 2026. Starlink is the only operator with public covariance; other operators enter via CelesTrak SupGP without covariance.
Headline figures from cycle a39dfdfee0fd, scored 2026-09-03 13:26 UTC, measured at the first instant of each operator file. Archive figures as of 2026-09-03 21:33 UTC.
What the public catalogue can see
At the first instant of each operator file, the public catalogue puts 81% of the scored Starlink satellites within 10 km of where the operator says they are. The middle satellite is 3.0 km away, with the public information a median of 13 hours old. Where the public information is two to three days old, the two predictions are about 25 km apart. Both sides are predictions, and most of that spread is planned trajectory changes the public catalogue cannot know about.
Everyone outside SpaceX sees Starlink through the public catalogue of orbital elements. Ephemera takes that public element set for each satellite, propagates it with SGP4, and compares it against the operator's own published trajectory for the same satellite at the same instants. The distance between the two is what this section measures. It is not an error of the satellite, and it is not a statement about which side is right.
41 satellites (0.4%) are more than 1,000 km from the public prediction at publication, which is a large part of an orbit. For those the public element set is not on the operator's trajectory at all. Ephemera does not label any separation as a manoeuvre; that needs a detector this project has not built.
Latest scored cycle a39dfdfee0fd, first seen 2026-09-03 12:09 UTC, scored 2026-09-03 13:26 UTC against the public catalogue snapshot fetched 2026-09-03 10:10 UTC (12,811 element sets). See it on the globe: every satellite drawn twice, the operator trajectory and the public catalogue's ghost.
| element age | comparisons | median km | 90th pct km | within 10 km | within 30 km |
|---|---|---|---|---|---|
| 0-6 h | 748 | 1.4 | 12.3 | 88% | 99% |
| 6-12 h | 5,149 | 2.4 | 13.9 | 86% | 96% |
| 12-24 h | 17,318 | 4.6 | 25.6 | 75% | 91% |
| 24-48 h | 42,740 | 10.9 | 74.6 | 48% | 80% |
| 48-72 h | 43,756 | 24.8 | 220.7 | 35% | 54% |
| over 72 h | 33,496 | 39.6 | 234.2 | 27% | 46% |
Element age is how old the public catalogue's information was at the instant compared. It runs larger than catalogue staleness because a single operator file predicts three days ahead, so a comparison late in the file is both further ahead and matched against an older element set. The two effects are not separated within one cycle, which is why the headline above uses only the first instant of each file.
By altitude shell
| shell | comparisons | median km | within 10 km |
|---|---|---|---|
| under 400 km | 14,148 | 102.5 | 25% |
| 400-500 km | 117,666 | 13.0 | 44% |
| 500-600 km | 11,393 | 4.7 | 67% |
Feed health for this cycle. 11,090 of 11,117 operator files scored. 27 have no entry in the public catalogue; 0 marked decayed; 0 failed to propagate; 0 unreadable. Public element sets were a mean of 15.1 h old when the snapshot was taken (median 12.3 h, 90th percentile 27.6 h, 26 over 72 h). Averaged over every comparison, which run out to three days into the operator file, element age is 51.2 h: the difference is the file's own prediction horizon, not catalogue staleness. Space-Track: 12 snapshots held, last fetched 2026-09-03 20:10 UTC with 12,811 element sets. Poller heartbeat coverage over the last 24 h: 100.0%.
Every scored cycle
| cycle first seen | cycle | scored | pairing | median age h | median km | within 10 km | lost |
|---|---|---|---|---|---|---|---|
| 2026-09-03 12:09 | a39dfdfee0fd | 11,090 | scored live | 12.6 | 3.0 | 81% | 41 |
| 2026-09-03 04:13 | 1e726fe006d9 | 11,091 | scored live | 20.5 | 4.6 | 73% | 43 |
| 2026-09-02 22:54 | b37c74c03907 | 10,655 | scored live | 15.3 | 3.4 | 80% | 39 |
| 2026-09-02 20:15 | 3572d8775913 | 11,091 | scored live | 12.5 | 2.7 | 85% | 38 |
| 2026-09-02 12:08 | f5112bb77a2a | 11,091 | scored with hindsight | 4.5 | 1.5 | 91% | 33 |
| 2026-09-02 04:12 | 0a85a65d5dbe | 11,091 | scored with hindsight | -3.4 | 1.3 | 91% | 32 |
| 2026-09-01 23:24 | 51d5c047650a | 10,901 | scored with hindsight | -8.3 | 1.2 | 90% | 42 |
| 2026-09-01 20:09 | 7414ea02eac1 | 11,048 | scored with hindsight | -11.4 | 1.1 | 89% | 80 |
Showing the 8 most recent of 11 scored cycles; the rest are in ledger.json. A cycle marked scored with hindsight was matched against a catalogue snapshot fetched after it, because the cycle predates this project's catalogue feed. Its element sets already know what the satellite did during the file, so its numbers look better than they should and are not comparable with the rest. 7 of 11 scored cycles are in that state, and the count only falls from here.
How this was measured
Public element set (Space-Track gp snapshot) propagated with SGP4 (skyfield 1.55, python-sgp4 2.27; TEME to GCRS by Skyfield, taken as J2000) and differenced against the operator-published trajectory (EME2000) at the file's own record epochs every 360 min. Distance in km; components in the operator's radial/in-track/cross-track frame; altitude is geocentric radius minus 6378.137 km; element age is evaluation epoch minus element-set epoch, signed. Both inputs are predictions; the operator's includes planned trajectory changes the public set cannot know about.
Snapshot 20260903T101011Z_ff964e6b31f2. Evaluation every 360 minutes across each file's own record epochs. Public element sets come from Space-Track.org and are redistributed under Space-Track's blanket approval for basic space situational awareness data, with citation.
The archive behind it
The operator side of every comparison above comes from files this project caught before they were replaced. Each cycle is hashed file by file into one Merkle root, the root is stamped into the Bitcoin blockchain through OpenTimestamps, and the manifest plus ten files chosen by the root itself are pushed into the Wayback Machine so an independent copy exists that this project does not control.
As of 2026-09-03 21:33 UTC: 15 cycles, 15 complete with roots, 15 anchored in Bitcoin, 7 with a defect in the independent copy. Source bytes archived: 336 GB across 165,776 files as served. Stored gzipped as 138 GB, of which 92 GB is on the poller's disk and 46 GB exists only in cold object storage. Cadence holds, meaning a set served longer than nine hours: 1. Poller heartbeat coverage over the last 24 hours: 100.0%. This project exists because SpaceX publishes the feed openly.
| first seen (utc) | cycle | files | source gb | merkle root | opentimestamps | wayback machine | held |
|---|---|---|---|---|---|---|---|
| 2026-09-03 12:09 | a39dfdfee0fd | 11,117 | 22.6 | 7ff9f56e1cc1 | block 965318 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-03 04:13 | 1e726fe006d9 | 11,118 | 22.5 | 449b43de57fa | block 965263 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-02 22:54 | b37c74c03907 | 10,682 | 21.7 | 3b31bf7689b6 | block 965241 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-02 20:15 | 3572d8775913 | 11,118 | 22.5 | e84216948e7b | block 965229 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-02 12:08 | f5112bb77a2a | 11,092 | 22.5 | b46aca785a05 | block 965172 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-02 04:12 | 0a85a65d5dbe | 11,093 | 22.5 | 381a895ce84e | block 965124 | manifest COPY DOES NOT MATCH (the archived copy is another cycle's manifest), 10/10 samples verified | spool and cold storage |
| 2026-09-01 23:24 | 51d5c047650a | 10,902 | 22.2 | b0f2521600e2 | block 965103 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-01 20:09 | 7414ea02eac1 | 11,093 | 22.5 | 58ba872f5507 | block 965085 | manifest COPY DOES NOT MATCH (the archived copy is another cycle's manifest), 10/10 samples verified | spool and cold storage |
| 2026-09-01 12:26 | 7be3c2c6fc2c | 11,094 | 22.5 | 4d03a6f8fe32 | block 965040 | manifest ok, 10/10 samples verified | spool and cold storage |
| 2026-09-01 02:22 | d03dc522da18 | 11,093 | 22.5 | 5d69cd6dc0c9 | block 964976 | manifest ok, 4/10 samples verified, 6 lost | spool and cold storage |
| 2026-08-31 20:22 | 513b92c4683e | 10,481 | 21.3 | 476705cfc526 | block 964940 | manifest NEVER CAPTURED, 0/10 samples verified, 10 lost | cold storage only |
| 2026-08-31 11:44 | 1d424255c9ae | 8,568 | 17.4 | 6df13e066f37 | block 964903 | manifest ok, 10/10 samples verified | cold storage only |
| 2026-08-31 04:09 | 5e4faf0f4e4e | 9,353 | 19.0 | 91ca361b797a | block 964904 | no independent copy: cycle superseded before witnessing completed: 1 manifest + 10 sample captures never made | cold storage only |
| 2026-08-30 20:02 | 92e6892c0f3e | 9,111 | 18.5 | 8c653747b19a | block 964904 | no independent copy: cycle superseded before witnessing completed: 1 manifest + 10 sample captures never made | cold storage only |
| 2026-08-30 13:32 | 72bcd7796b49 | 17,861 | 36.2 | 677d3043ef28 | block 964904 | no independent copy: cycle superseded before witnessing completed: 1 manifest + 10 sample captures never made | cold storage only |
Method: built from each cycle's cycle.json, witness.json and ship.json in the poller's spool; coverage is the fraction of the last 24 h's minutes with a watcher heartbeat under 10 minutes old (D18); a cadence hold is a manifest first-seen gap over 9 h; source bytes are the responses as served, stored bytes are the gzipped archive objects the shipper recorded. Poller: home (Cape Town, residential; single poller until poller A exists).
Daily roots
| date | root | leaves | opentimestamps | note |
|---|---|---|---|---|
| 2026-09-02 | 647c6951a21d | 4 | block 965241 | none |
| 2026-09-01 | 7ecb7906d01b | 3 | block 965103 | excluded 51d5c047650a (still pulling when the root was built) |
| 2026-08-31 | 948ecce3337d | 3 | block 964963 | none |
| 2026-08-30 | 1d7acc715f62 | 2 | block 964927 | none |
A cycle still pulling at midnight is left out of that day's root and stays out. It keeps its own cycle root and its own OpenTimestamps proof, so nothing is unwitnessed, but it sits under no daily root.
Check it yourself
None of this is worth anything if you have to take my word for it. Here is what you can run right now, and what still needs data this site does not publish yet. I would rather say which is which.
Fetch an independent copy and hash it. Each cycle's file list is pushed into the Wayback Machine
the moment it is caught, so a copy exists that I do not control. For cycle
a39dfdfee0fd that copy is
here, and its SHA-256 is
a39dfdfee0fd8f1a8a068e22d8428966a87b19c19d98721ba012192a60911d5e. Download it, hash it, and you have checked that the
list of files I claim to have caught is the list the source was actually serving. Ten files from each
cycle are copied the same way, and every copy URL and digest is in
ledger.json.
Check which ten files were copied. I do not get to choose them. File number
int(sha256(root + ":" + k), 16) mod n for k from 0 to 9, where n is the number of files in
the list you just downloaded. Run that and you get the same ten names this site published.
Check the timestamp. Each cycle root is stamped through OpenTimestamps. Running
ots verify root.txt.ots against a Bitcoin node shows this cycle's root committed in block
965318, which fixes the latest moment it could have been written. Nobody,
including me, can move a root backwards once it is in a block. The proof files ship with the data
release.
What you cannot do from this page alone is rebuild the Merkle root, because that needs the SHA-256 of every one of the 11,117 files in the cycle and this site publishes the summary rather than the full digest list. Those digests, and the exact byte format of the root file, come with the data release described below. The construction is the plain one: leaves are the file digests in manifest order, adjacent pairs are hashed as SHA-256 of the two digests joined end to end, and an odd trailing node is paired with a copy of itself.
Data, licence and citation
- ledger.json carries every cycle, its root, its attestation, its witness state and every scored summary on this page, as machine-readable JSON. It is the same file the page is built from.
- The globe's per-cycle pack, with the element sets and the scored separations, is at globe/pack.json for the newest scored cycle.
- The full per-comparison rows, about 140,000 per cycle, are kept in the archive and are available on request. They move to public object storage once the storage layer is finished.
- The raw operator files are held as published, hashed and witnessed. They are not re-published here while the licence question below is open.
Licence. Code is MIT. Figures derived by this project are intended for CC BY 4.0. The raw SpaceX files carry no stated licence; a written request went to SpaceX on 31 August 2026 and is unanswered as of this build, and any file comes down on request. Public element sets come from Space-Track.org and are redistributed under Space-Track's blanket approval for basic space situational awareness data, with citation.
Citation. Ryan, K. (2026). Ephemera: an archive of the public Starlink ephemerides and a daily measure of public catalogue visibility. https://ephemera.space, retrieved 2026-09-03 21:33 UTC.
Not here yet
4 days of archive is not a long record, and this page should not pretend otherwise. What is running: the poller, the hashing, the witnessing, cold storage, and the catalogue comparison above. What is not:
- A second poller on a different machine in a different country, so that a gap in one is visible in the other rather than invisible in both.
- The self-consistency scoreboard, which compares an earlier operator prediction with a later one from the same operator, normalised by the earlier uncertainty.
- The manoeuvre census, which will publish three differently defined counts side by side and never a ratio against anyone's declared figure.
- Other constellations, which reach this project through CelesTrak SupGP without covariance.
- A restore drill from cold storage, proving a cycle can be pulled back and re-verified end to end.
Who runs this
Kira Ryan, on my own time, on a home computer in Cape Town, with no funding and no affiliation to any operator or agency. If a figure here is wrong I want to know, and corrections get published rather than quietly fixed.