aptriever 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. aptriever-0.1.0/.gitignore +8 -0
  2. aptriever-0.1.0/CONTEXT.md +266 -0
  3. aptriever-0.1.0/LICENSE +661 -0
  4. aptriever-0.1.0/PKG-INFO +192 -0
  5. aptriever-0.1.0/README.md +170 -0
  6. aptriever-0.1.0/aptriever.example.toml +26 -0
  7. aptriever-0.1.0/pyproject.toml +51 -0
  8. aptriever-0.1.0/src/aptriever/__init__.py +1 -0
  9. aptriever-0.1.0/src/aptriever/addresses.py +121 -0
  10. aptriever-0.1.0/src/aptriever/aliases.py +31 -0
  11. aptriever-0.1.0/src/aptriever/cache.py +329 -0
  12. aptriever-0.1.0/src/aptriever/config.py +273 -0
  13. aptriever-0.1.0/src/aptriever/ec2.py +171 -0
  14. aptriever-0.1.0/src/aptriever/events.py +270 -0
  15. aptriever-0.1.0/src/aptriever/fetch.py +1441 -0
  16. aptriever-0.1.0/src/aptriever/geo.py +416 -0
  17. aptriever-0.1.0/src/aptriever/keyrings/debian-archive-keyring.gpg +0 -0
  18. aptriever-0.1.0/src/aptriever/keyrings/ubuntu-archive-keyring.gpg +0 -0
  19. aptriever-0.1.0/src/aptriever/keyrings/versions.txt +3 -0
  20. aptriever-0.1.0/src/aptriever/main.py +301 -0
  21. aptriever-0.1.0/src/aptriever/mirrors.py +776 -0
  22. aptriever-0.1.0/src/aptriever/on_demand.py +53 -0
  23. aptriever-0.1.0/src/aptriever/probe.py +366 -0
  24. aptriever-0.1.0/src/aptriever/release.py +166 -0
  25. aptriever-0.1.0/src/aptriever/repositories.py +124 -0
  26. aptriever-0.1.0/src/aptriever/scores.py +262 -0
  27. aptriever-0.1.0/src/aptriever/server.py +695 -0
  28. aptriever-0.1.0/src/aptriever/signature.py +58 -0
  29. aptriever-0.1.0/src/aptriever/systemd.py +24 -0
  30. aptriever-0.1.0/tests/certs/server.crt +19 -0
  31. aptriever-0.1.0/tests/certs/server.key +28 -0
  32. aptriever-0.1.0/tests/conftest.py +369 -0
  33. aptriever-0.1.0/tests/keys/debian-trixie-security.inrelease +415 -0
  34. aptriever-0.1.0/tests/keys/generate-fixtures.sh +23 -0
  35. aptriever-0.1.0/tests/keys/nameless.inrelease +16 -0
  36. aptriever-0.1.0/tests/keys/resolute-fresh.inrelease +18 -0
  37. aptriever-0.1.0/tests/keys/resolute-old.inrelease +18 -0
  38. aptriever-0.1.0/tests/keys/resolute-security.inrelease +18 -0
  39. aptriever-0.1.0/tests/keys/resolute-updates-fresh.inrelease +18 -0
  40. aptriever-0.1.0/tests/keys/resolute-updates-old.inrelease +18 -0
  41. aptriever-0.1.0/tests/keys/stranger.inrelease +18 -0
  42. aptriever-0.1.0/tests/keys/test-archive-keyring.gpg +0 -0
  43. aptriever-0.1.0/tests/keys/ubuntu-resolute-backports.inrelease +1375 -0
  44. aptriever-0.1.0/tests/test_addresses.py +92 -0
  45. aptriever-0.1.0/tests/test_aliases.py +68 -0
  46. aptriever-0.1.0/tests/test_cache.py +515 -0
  47. aptriever-0.1.0/tests/test_config.py +332 -0
  48. aptriever-0.1.0/tests/test_ec2.py +399 -0
  49. aptriever-0.1.0/tests/test_events.py +224 -0
  50. aptriever-0.1.0/tests/test_fetch.py +2599 -0
  51. aptriever-0.1.0/tests/test_fetch_units.py +511 -0
  52. aptriever-0.1.0/tests/test_geo.py +934 -0
  53. aptriever-0.1.0/tests/test_main.py +764 -0
  54. aptriever-0.1.0/tests/test_mirrors.py +1604 -0
  55. aptriever-0.1.0/tests/test_on_demand.py +397 -0
  56. aptriever-0.1.0/tests/test_probe.py +1165 -0
  57. aptriever-0.1.0/tests/test_release.py +149 -0
  58. aptriever-0.1.0/tests/test_repositories.py +106 -0
  59. aptriever-0.1.0/tests/test_review_findings.py +249 -0
  60. aptriever-0.1.0/tests/test_review_findings_pool.py +114 -0
  61. aptriever-0.1.0/tests/test_scores.py +371 -0
  62. aptriever-0.1.0/tests/test_server.py +2087 -0
  63. aptriever-0.1.0/tests/test_signature.py +187 -0
  64. aptriever-0.1.0/tests/test_systemd.py +38 -0
@@ -0,0 +1,8 @@
1
+ .superpowers/
2
+ .venv/
3
+ __pycache__/
4
+ *.egg-info/
5
+ aptriever.toml
6
+ aptriever-metrics.jsonl
7
+ .worktrees/
8
+ /dist/
@@ -0,0 +1,266 @@
1
+ # aptriever
2
+
3
+ A metric-aware, parallel, self-tuning download accelerator for apt (later apk) package clients: by downloading from several mirrors at once — adding connections when that increases speed — it delivers packages faster than a pinned mirror, apt's `mirror://` lists, or a generic caching proxy (though not faster than a full local mirror). To clients it is a plain http mirror, so it also works where `mirror://` historically broke, e.g. `do-release-upgrade`.
4
+
5
+ ## The goal
6
+
7
+ **Make `apt update` and `apt full-upgrade` finish as fast as possible.** Every term below exists to serve that, and every one of them can be reopened by a measurement showing apt finishing sooner some other way. The only thing that outranks it is byte correctness: apt must never receive wrong bytes.
8
+
9
+ The corollary governs the request path: nothing may stand between a client's request and the first byte moving unless it earns its latency. Measurement, bookkeeping, and name resolution belong beside the request or behind it, never in front of it.
10
+
11
+ ## Language
12
+
13
+ **aptriever**:
14
+ This tool.
15
+ _Avoid_: the proxy, the tool
16
+
17
+ **Origin mode**:
18
+ Deployment where clients point their `.sources` URIs at aptriever's own hostname; http and https both terminate on aptriever.
19
+ _Avoid_: mode 1, http server mode
20
+
21
+ **Proxy mode**:
22
+ Deployment where clients set aptriever as `Acquire::http::Proxy`; requests arrive in absolute-form (`GET http://mirror/path`).
23
+ _Avoid_: mode 2, forward proxy
24
+
25
+ **Archive**:
26
+ A client-facing path prefix that maps to the mirror set serving one archive root: a configured name from `[archives.*]` (e.g. `/ubuntu`), any of that pool's mirrors spelled as host+path (see Pool mirror alias), or, on demand, an absolute upstream URL embedded in the path.
27
+ _Avoid_: repo, repository
28
+
29
+ **On-demand upstream**:
30
+ A repository served with zero configuration by embedding its absolute URL in the request path (`/https://host/…`); each origin that is no pool member (see Pool mirror alias) lazily gets a one-mirror set, and the cache/events archive is its netloc, scheme collapsed. The way aptriever caches https-only repositories that a CONNECT-tunneling proxy cannot.
31
+
32
+ **Pool mirror alias**:
33
+ A request prefix equal to any pool mirror's host+path, scheme-less (`/archive.ubuntu.com/ubuntu/…`) or embedded (`/http://archive.ubuntu.com/ubuntu/…`), resolving to the owning archive's racing pool, canonical cache tree, and events archive — the spelling `do-release-upgrade`'s mirror check accepts (docs/plans/2026-07-18-pool-mirror-aliases.md). Longest mirror path wins; a configured archive name always beats an alias.
34
+
35
+ **Mirror**:
36
+ A single upstream host serving repository content, identified by hostname; racing and events key on its endpoints (see Endpoint), scoring and connection pacing on their destinations (see Destination), never the mirror itself.
37
+
38
+ **Configured mirror**:
39
+ A mirror listed under `[archives.*]` in the operator's config (a non-synthesized pool). Its provenance vouches for any address but link-local, so it may name private space — a company mirror, a LAN instance to chain to (see Address policy).
40
+ _Avoid_: operator mirror, trusted host
41
+
42
+ **Listed mirror**:
43
+ A mirror of a synthesized zero-config pool — from its pool's lists (the Launchpad feed and the geo list for ubuntu, the Debian mirror masterlist for debian) or the seed. Its provenance vouches for no internal address, so neither a hostile listed host nor its DNS can make aptriever dial the operator's networks (see Address policy).
44
+ _Avoid_: feed mirror, untrusted host
45
+
46
+ **Additional mirror**:
47
+ A mirror the operator adds to one synthesized pool beside its lists (`additional_mirrors`, keyed by archive), for a public mirror no list carries — an unregistered one, or one the lists lose. Membership only: seated, scored, swept, demoted and benched exactly like a listed mirror, never pinned; measured by the first fan-out after it appears (see Reserved exploration slot), not by the next sweep. Its provenance vouches for no internal address, as a listed mirror's does: the operator names it but need not control it, so neither the mirror nor its DNS can steer aptriever onto the operator's networks (see Address policy); a mirror in private space belongs under `[archives.*]` (see Configured mirror), and a non-empty `additional_mirrors` beside `[archives.*]` is a configuration error.
48
+ _Avoid_: extra mirror, custom mirror, own mirror, unlisted mirror
49
+
50
+ **EC2 mirror**:
51
+ The Ubuntu archive mirror Canonical names for an AWS region, `<region>.ec2.archive.ubuntu.com` — the name cloud-init configures on EC2 — added to the ubuntu pool when the instance finds itself on AWS and the name resolves to a mirror of its own rather than to `archive.ubuntu.com` or `ports.ubuntu.com`; some regions' names are CNAMEs to another region's mirror. Carries an additional mirror's provenance and its reserve priority (see Additional mirror, Reserved exploration slot), but joins a pool already live, so unlike one it is a mirror arrival (see Mirror arrival).
52
+ _Avoid_: in-region mirror, cloud mirror, AWS mirror, regional mirror
53
+
54
+ **Partial mirror**:
55
+ A listed mirror carrying the archive for a subset of its architectures — a `ubuntu-ports` mirror from the Launchpad feed (no amd64 or i386), a Debian mirror synced with an architecture excluded (no arm64) — so a missing architecture's `binary-<arch>/` indexes and `_<arch>.deb` pool files answer 404 there while release files, `_all.deb` files and the rest of the tree are served whole. One 404 cannot tell it from a lagging mirror: lag misses only what has not synced yet, partiality misses the architecture's oldest file too. Ubuntu's two trees serve the same signed release file, naming every architecture, while each carries only its own: `ubuntu` amd64, i386 and, from 25.10, amd64v3 and arm64 (24.04 has arm64 under `ubuntu-ports` alone); `ubuntu-ports` arm64, armhf, ppc64el, riscv64 and s390x — so no Ubuntu mirror is whole, the seed included (checked 2026-09-11).
56
+ _Avoid_: incomplete mirror, arch-limited mirror, broken mirror
57
+
58
+ **Address policy**:
59
+ The rule deciding whether an address about to be dialed may be dialed at all, applied where the dial happens — DNS answers, redirect targets, literal-IP origins — so no parser routes around it. Trust comes from provenance (see Configured mirror, Listed mirror, Additional mirror, EC2 mirror, On-demand upstream), never the address; link-local is refused everywhere, and a client-named on-demand origin may name an internal address only behind a loopback-only listener. The mirror-list sources, the location lookups and the instance metadata service are outside it (addresses.py, #89).
60
+ _Avoid_: allow-list, IP filter, egress filter
61
+
62
+ **Endpoint**:
63
+ One (mirror, IP address, egress address) triple — the unit of racing selection and events identity, carrying its own TTFB (the mirror's URL says the scheme, and the handshake is part of what the next request pays) and the archive's own memory about that address as seen over that egress address (release memory, lag observations); its throughput, bench and connection slot belong to its destination (see Destination), shared with every other archive's endpoint at the same hostname, address and egress address. A mirror's IPv6 address is one endpoint per egress address the instance holds; an IPv4 address, or any address on an instance holding no egress address, is one endpoint with none. A literal-host mirror (an IP address given directly) has one endpoint per egress address, or exactly one where the instance holds none: itself.
64
+
65
+ **Destination**:
66
+ One (hostname, IP address, egress address) triple, with the port when the mirror's URL spells one — where a request goes and where it leaves from, as aptriever tells its ways to a server apart: a host's default listeners, http and https, are one server, a spelled port names one listener, and one server reached over two egress addresses is two destinations, because throughput, transient failures and the connection slot are facts about the way there as much as about the server. Every endpoint dialing it shares it, across archives, so what is a property of the server rather than of the archive is learned once (#140). Shared addresses are never identity: CDN vhosting reuses one address across distinct origins.
67
+ _Avoid_: link, peer, server, host address
68
+
69
+ **Endpoint resolution**:
70
+ Turning a mirror's hostname into its endpoints: seating dials the addresses it already remembers and never waits for DNS, launching a refresh as it takes each seat and awaiting a lookup only where a walk would otherwise seat nothing (docs/plans/2026-07-28-resolution-off-the-request-path.md). One name re-resolves at most once per `RESOLVE_MIN_INTERVAL_SECONDS`, shared by every mirror on that hostname across archives while what each makes of the answer stays its own, and the whole pool is resolved once at startup. Known addresses keep their scores, new ones join unscored, each address becoming one endpoint per egress address the instance holds of its family, or one with none, and an address the latest answer omits stays dialable — a wrong one fails the dial and fails over, which is the same path any mirror that goes down already takes. A name that stops resolving benches the mirror, not its endpoints, and that bench is what takes its remembered addresses out of the order.
71
+
72
+ **Mirror set**:
73
+ The group of mirrors serving the same repository content; the pool the fetcher selects racing endpoints from.
74
+ _Avoid_: mirror list, pool
75
+
76
+ **Fetcher**:
77
+ aptriever's outbound download engine, owner of mirror selection, racing, range scheduling, and redirect handling.
78
+ _Avoid_: downloader, racer, client
79
+
80
+ **Racing**:
81
+ Fetching one client request from several mirror endpoints in parallel and keeping the winner(s), in one of two modes: latency (first full body wins) or throughput (range chunks reassembled). A mirror's IPv4 and IPv6 endpoints race like endpoints of different mirrors — dialing simultaneously with no head start, replacing aiohttp's Happy Eyeballs connect stagger — and so do one address's endpoints over different egress addresses.
82
+
83
+ **Latency race**:
84
+ Racing mode for a file whose known total fits in one chunk: every fanned-out endpoint streams its whole body, the first to finish wins and is served, and the rest are cancelled at that point, scored on TTFB and whatever bytes they had delivered. A response of unknown size (no usable length) never latency-races; it falls back to a single stream.
85
+
86
+ **Throughput race**:
87
+ Racing mode for a file larger than one chunk: the fastest-to-respond racer anchors the forward stream for chunk 0, the other racers leave a TTFB sample and are released, and the remaining range chunks are fetched in parallel and reassembled in order.
88
+
89
+ **Transfer**:
90
+ One body-carrying upstream request within a throughput race: the anchor, a chunk's primary or the substitute that follows its failure, or a duplicate. Ends delivered, lost to its chunk's other transfer, failed, or torn down with the download. The unit whose start and end the fill timeline records.
91
+
92
+ **Single-connection rule**:
93
+ At most one active connection per destination at any time, across all downloads, clients and archives; a server reached over two egress addresses is two destinations and may carry one connection over each.
94
+
95
+ **Parallelism**:
96
+ The maximum number of mirror endpoints engaged concurrently for one download; because of the single-connection rule this equals the download's connection count. One fan-out seats at most two endpoints of any one mirror — its two families, or one address over two egress addresses, whichever two rank best — while a hostname answering with many addresses cannot absorb the whole width; outside the fan-out a server may hold one connection per destination, so a dual-stack server at a two-provider site can carry three chunk workers at once.
97
+ _Avoid_: threads, connection count
98
+
99
+ **Reserved exploration slot**:
100
+ Of a download's fan-out, a seat set aside for an endpoint with no throughput sample yet rather than the top-ranked candidates, so a fixed-width fan-out keeps sampling the whole pool instead of freezing onto its first few scored endpoints; seats the lowest-TTFB unmeasured endpoints first, uniform only while nothing is measured. An endpoint stays explorable until it has moved a transfer large enough to measure its link (the throughput sample floor); a probe sweep, whose bodies are far smaller, leaves it explorable. An additional or EC2 mirror's endpoints with no TTFB sample and no failure mark seat first, ahead of the frontier, so such a mirror is measured by the first fan-out after it appears. For an additional mirror that fan-out is the only measurement coming, the pool having been built with it; an EC2 mirror is a mirror arrival (see Mirror arrival), so a sweep due at once races the same fan-out.
101
+
102
+ **Self-tuning**:
103
+ The fetcher's rule that connection parallelism follows measured throughput: connections are added when they make the download faster.
104
+ _Avoid_: adaptive, dynamic
105
+
106
+ **Range chunk**:
107
+ A bounded byte range of one file; the unit of parallel download and of cancellation.
108
+ _Avoid_: segment, part, slice
109
+
110
+ **Mutable path**:
111
+ A repository URL whose content changes over time; the release file (`InRelease`, `Release`) is raced and elected (see Release election), a `dists/` file the last served InRelease names redirects to its by-hash object (see By-hash redirect), and `Release.gpg` plus every unmapped `dists/` file is pinned to the elected reference; never cached on disk (release elections keep in-memory release memory — see Release memory).
112
+
113
+ **Immutable path**:
114
+ A repository URL whose content is fixed once published (`by-hash/` metadata, `pool/` files); raced freely, and an endpoint's 404 only removes that endpoint from the download. Small immutable files are latency-raced, large ones throughput-raced. An empty body is a definitive failure unless the by-hash name states the empty digest. A `by-hash/` body is checked against the digest its own path names: a mismatch in a latency race fails that endpoint and the race goes on; a mismatch on the single stream fails the download outright when it has no declared length, or otherwise has already reached the client with only the cache entry failing to land; a mismatch reassembled from several endpoints (throughput race) fails the fill; so a body that is not what it claims never lands in the cache (#81).
115
+
116
+ **Reference endpoint**:
117
+ The elected winner of the archive's most recent completed release election, used to pin `Release.gpg` and unmapped legacy indexes and as the failover-preferred single-stream source; falls back to the best-scored endpoint until a first election, and an election that serves no body moves it no more than it moves the stored copy.
118
+
119
+ **By-hash redirect**:
120
+ Answering a canonical `dists/` metadata GET with a 307 to the `by-hash/SHA256/` object named by that suite's last served InRelease, when the elected release advertises `Acquire-By-Hash: yes`: the by-hash name pins the generation the client just saw, and the follow-up request is ordinary immutable-path traffic — raced, cached, shared. Unmapped shapes — no election yet, a path the list does not name, `Acquire-By-Hash: no`, HEAD, a query string — keep the pinned fetch (docs/plans/2026-08-24-canonical-by-hash-redirect.md).
121
+ _Avoid_: canonical mapping, index rewrite
122
+
123
+ **Election reuse**:
124
+ Answering an `InRelease` or `Release` request from the last completed election instead of holding a new one, when the request's `Cache-Control: max-age=` covers that election's age. The policy is the client's alone — apt sends `max-age=0` unless a host sets `Acquire::http::Max-Age`, and `no-cache`, `no-store` and an absent header all mean elect — so aptriever holds no reuse TTL of its own. A request the stored copy does not cover joins the election already in flight for that path rather than starting a second, and is recorded as `tail`; a reuse is recorded as `reuse` and carries an `Age` header. Starting or failing an election never invalidates the stored copy, so an outage still serves every client whose declaration covers it (docs/plans/2026-07-28-election-reuse-window.md).
125
+
126
+ **Release election**:
127
+ The vote held on every `InRelease` or `Release` request without a client `Range` that election reuse did not already answer: like a latency race, the losing racers are cancelled once the race settles — the first racer that is authenticated and not past its own `Valid-Until:` settles the election, and the fastest eligible responder is elected (usually the same racer, since a settled election normally has one responder) — served to the client and recorded as the reference endpoint. Racers validate with per-endpoint If-Modified-Since from release memory; a 304 completes at its headers and votes with the fields recorded from that endpoint's last full body. A full body that parses no `Date:` is a protocol failure, not a participant. On keyring archives the winning body is authenticated first: only a signature-verified body served at its own suite's path can settle the election (spec: release signature verification, Suite match).
128
+
129
+ **Freshness window**:
130
+ Staleness bound (`RELEASE_STALENESS_WINDOW_SECONDS`, one day) used by the probe sweep's `stale` tally and by the laggard test (see Laggard). Not consulted by the election itself — the counterweight to a fast-but-stale winner is seating-side (#66).
131
+ _Avoid_: staleness window
132
+
133
+ **Release watermark**:
134
+ Per election path, the newest `Date:` among verified release bodies ever observed there — from elections, sweeps, the laggard recheck, and 304 revalidations; monotonic, persisted, and vantage-independent. The reference a copy's lag is measured against.
135
+ _Avoid_: freshest known, high-water date
136
+
137
+ **Suite match**:
138
+ The path-binding half of the trust boundary: a verified body's `Suite:`/`Codename:` measured against the path's suite segment. Observations and watermarks require a positive match; election eligibility and release memory require only the absence of a contradiction — on verified archives a body naming another suite fails its racer under the `suite` error kind and is never recorded, while a flat path or a release naming neither field has nothing to contradict and stays eligible (docs/plans/2026-08-24-election-suite-match.md). Unverified archives are unchecked: the fields are unauthenticated text, and apt stays the gate.
139
+ _Avoid_: codename check, path binding check
140
+
141
+ **Laggard**:
142
+ An endpoint whose laggard verdict is laggard: recently observed serving a stale release copy — see Laggard verdict. Demotion rests only on recent evidence; a single fresh observation ends it.
143
+ _Avoid_: stale mirror (ambiguous with the sweep tally), lagging endpoint
144
+
145
+ **Laggard verdict**:
146
+ The per-endpoint, per-path outcome of the laggard test: laggard when the observed lag — the path's release watermark minus the served `Date:`, fixed at observation time — exceeds the freshness window and the observation is younger than the sweep-due interval; fresh when the observation is that recent and within the window; unknown when no observation that young exists. A path with no verdict of its own borrows the endpoint's newest recent observation from any sibling election path — mirror lag is usually archive-wide, so the newest evidence is the best prior for a path never seen (measured 2026-08-23: per-suite divergence is real but the newest observation called every probed case right) — and only an endpoint with no recent observation anywhere is unknown, seating normally, so demotion never outlives its evidence. The age bound is a backstop for paths that stop electing; the laggard recheck keeps evidence current everywhere else.
147
+ _Avoid_: conviction, lag status
148
+
149
+ **Laggard recheck**:
150
+ The background pass after a settled election that sends each of that path's laggards one conditional release request and records the observation — off the request path, at most one per laggard per completed election. A laggard never races an election in which any non-laggard seats (a fast valid-but-stale comeback would win it), so the recheck is how a recovered mirror returns to fresh and a lagging one stays demoted on live evidence.
151
+ _Avoid_: rehab probe, laggard sweep
152
+
153
+ **Release memory**:
154
+ Per-endpoint, per-path record of the last fully-delivered release body — its `Last-Modified` validator and parsed fields — plus retained bodies keyed by `Date:` (deduplicating identical signed publications) that an elected 304 serves from; in-memory only, relearned each start, bodies kept while any record still cites their date. A body is remembered only where both dates parse: without a `Date:` it could neither vote nor key a retained body, and a validator no date parser accepts would be replayed as an `If-Modified-Since` that buys nothing, so such an endpoint stays unconditional, like one that sends no `Last-Modified`.
155
+ _Avoid_: release cache
156
+
157
+ **Release signature verification**:
158
+ On an archive with a `keyring` (per-archive TOML key; the zero-config ubuntu archive defaults to the system ubuntu-archive-keyring when gpgv and the keyring file are both present), every `InRelease` body is verified with gpgv before any use: election eligibility, release memory, retained bodies, and probe scores all see only verified bodies, so a mirror cannot forge freshness or throughput credit — though a racer cut while its own verification is still running still leaves a TTFB sample, like any cut loser. A failed verification is the `signature` error kind (elections) or the `bad_signature` sweep bucket — a failing endpoint like any other. A broken verifier (gpgv gone, keyring unreadable, timeout) is `VerifierError`: the election refuses with an error and no mirror is blamed. Plain `Release` keeps the `Date:` heuristic (detached `Release.gpg` verification is a tracked follow-up).
159
+
160
+ **Mirror score**:
161
+ Historical TTFB/throughput/reliability state that drives candidate selection: TTFB per endpoint, since it is what the next request over that endpoint's URL pays, TLS handshake included; throughput and reliability per destination, learned once for every archive's endpoint on it; TTFB is the primary ranking dimension and throughput the tie-break for release elections, the reference pin and the fan-out, while a throughput race's chunk workers draw throughput-primary with TTFB as their tie-break, because a range chunk's cost is its transfer rather than its headers wait; an endpoint carrying a TTFB score but no throughput score ranks at the median of the throughput scores in that draw, so a link measured for latency but not yet for rate is still reachable by the draw that would measure it. TTFB excludes the initial dial's DNS lookup: requests dial an already-resolved address, so it starts at connection initiation — it does cover the lookup a cross-host redirect target needs, along with every redirect hop and the TLS handshake, because that is what the next request pays. TTFB samples come from every answered request — downloads, race losers, probe sweeps; throughput samples only from transfers of at least 4 MiB, or of at least the configured chunk when that is smaller (the throughput sample floor: half a default chunk, so a cut stream that moved most of one still counts), because a smaller transfer's bytes over seconds measures TCP slow start rather than the steady-state link; a sample's denominator is the body transfer alone — the attempt's elapsed time minus its TTFB — so the denominator keeps connection setup out and the floor keeps slow start out; persisted to `scores.json` in the state directory across restarts.
162
+ _Avoid_: ranking, weight
163
+
164
+ **Probe sweep**:
165
+ The lazy, single-flight pass that measures every pool mirror's dialable endpoints, over every egress address the instance holds of an address's family, at a cap scaling with the egress addresses it probes over, with a conditional GET of the archive's last-served release-election path: TTFB always; release `Date:` and release memory from valid 200s, never throughput (the body is under the throughput sample floor); a 200 parsing no `Date:` records a failure (the host is not mirroring). Request-triggered and due a week after the last one, at once on a mirror arrival (see Mirror arrival) or an epoch declaration, and a day after the last one once an address has arrived carrying neither a measurement nor a failure. Epoch-triggered sweeps re-baseline TTFB instead of blending. On keyring archives each probed `InRelease` body is signature-verified first; failures count in the `bad_signature` bucket.
166
+ _Avoid_: mirror scan, benchmark
167
+
168
+ **Mirror arrival**:
169
+ A mirror joining a pool that is already live — from a mirror list refresh, from EC2 mirror detection, from anywhere a later membership source is added — as against an address materializing for a mirror the pool already holds, which a rotating answer produces continuously. The pool learning of a host it did not know is what makes a probe sweep due at once; the same host answering from one more address is not, and waits for the sweep's daily floor. A mirror the pool is built with has not arrived.
170
+ _Avoid_: new mirror, merge, pool change
171
+
172
+ **Probe echelon**:
173
+ A pool mirror's rank in the probe sweep's order, from the Launchpad feed's country for that mirror against the egress country: 0 the same country, 1 the same continent, 2 everything else. An echelon is a step in an ordered formation, and that is all this is — it decides when a mirror is measured, never whether it is seated. Within one echelon the order is previous measurement first and the feed's declared bandwidth as a tie-break between the unmeasured, over a shuffle, so it is never the feed's own stable order. An unknown egress country, or a pool the feed does not describe, puts every mirror in echelon 2, where the shuffle is the whole order.
174
+ _Avoid_: tier, geo tier, locality tier
175
+
176
+ **Egress address**:
177
+ One of the instance's own global IPv6 addresses on the interface its upstream traffic leaves through, stable (not temporary) and preferred (not deprecated), taken from the system rather than configured: the end of a connection the instance chooses, and the choice that picks the provider at a site whose router routes each delegated prefix to its own provider. A site with two providers gives the instance two; an IPv4 dial has none, since the router behind NAT owns that choice.
178
+ _Avoid_: source address, source IP, egress IP, local address, bind address, uplink, path
179
+
180
+ **Egress country**:
181
+ The country the daemon's traffic actually leaves from, read at the start of each probe sweep from an endpoint that reports where the connection terminated rather than where the address block is registered — the two disagree wherever a block is used a country away from its registration, and the terminating answer is the one that predicts mirror latency. Unknown is a valid value, not an error. Distinct from where the machine physically sits, which is not what mirrors respond to.
182
+ _Avoid_: own country, vantage country, home country, location
183
+
184
+ **Egress network**:
185
+ The network an instance's upstream traffic leaves through, identified by a digest of the public address the egress lookup reports (an IPv6 address by its /48) rather than the address itself. The unit fills are grouped by when comparing a link's ceiling or a seat's marginal gain: one laptop is several egress networks over a week, and a multihomed instance is two at once, told apart per endpoint by the egress address its records name.
186
+
187
+ **Network epoch**:
188
+ A change of vantage point, declared when consecutive elections measure every prior-scored racer far above its TTFB EWMA; answered by an epoch sweep, never by dropping scores.
189
+
190
+ **Stall**:
191
+ A connection that stops delivering payload for the stall timeout; a stalled chunk is cancelled and its endpoint substituted.
192
+
193
+ **Bench**:
194
+ A destination set aside from seating for `BENCH_SECONDS` — after `BENCH_AFTER_FAILURES` consecutive transient failures (connect, stall, http, protocol), or at once after one definitive failure — for every archive's endpoint on it: a server lying in one tree is suspect in the other, and the cost is one bench. Transient failures bench the one destination they happened on, its egress address included, since the way there is what usually failed; a definitive failure benches the server's destinations over every egress address, since the server lied whichever way it was reached. A mirror whose name stops resolving is benched as a whole (see Endpoint resolution).
195
+ _Avoid_: blacklist, cooldown, penalty box
196
+
197
+ **Definitive failure**:
198
+ A response proving the endpoint is not serving the object: an empty body where the object cannot be empty (`empty`), a by-hash body hashing to another digest than its name (`hash`), a release body failing signature verification (`signature`) or naming another suite (`suite`). Benches at once, because strikes cannot reach it — a mirror fast at its headers is cut as a loser in other races, and every cut is a success that resets the count. The probe sweep's own recording of a failed signature (`bad_signature`) keeps to strikes, since a sweep measures and does not seat.
199
+ _Avoid_: byzantine mirror, lying mirror, hard failure
200
+
201
+ **Mirror budget**:
202
+ The maximum number of distinct endpoints one download may try (initial racers plus substitutions); at the boundary the download keeps rotating through its already-tried healthy endpoints, and fails only when none remain (404 if every endpoint 404ed, 503 if all were benched by earlier downloads' failures, otherwise 502/504 matching the dominant cause).
203
+
204
+ **Racing gain**:
205
+ The ratio of a download's aggregate throughput to the throughput of its fastest single participating endpoint; the tool's built-in measure of whether racing helped.
206
+
207
+ **Cache**:
208
+ The on-disk store of completed immutable-path downloads, keyed by archive — a configured name or an on-demand netloc — and archive-relative path so any mirror's copy serves every client; mutable paths are never cached.
209
+
210
+ **Laptop mode**:
211
+ Cache-sizing default for a localhost-only listener: the cache is a small tenant, capped at a percent of the filesystem (footprint cap).
212
+ _Avoid_: local mode, private mode
213
+
214
+ **LAN mode**:
215
+ Cache-sizing default for an externally reachable listener: the cache may fill the filesystem up to the disk high-water.
216
+ _Avoid_: server mode
217
+
218
+ **Footprint cap**:
219
+ Limit on the cache's own size — `cache_max_bytes`, or `cache_max_percent` of the live filesystem total.
220
+
221
+ **Geo mirror list**:
222
+ Canonical's GeoIP-selected mirror list (`mirrors.ubuntu.com/mirrors.txt`), merged into a synthesized pool at refresh time — a locality prior beside the Launchpad feed's membership, and the fallback line alone from an egress GeoIP cannot map; cached on disk so startup reads it offline and an idle daemon never fetches it.
223
+
224
+ **Launchpad mirror feed**:
225
+ Launchpad's `+archivemirrors-rss`: every registered Ubuntu archive mirror regardless of where the daemon's egress maps — the ubuntu pool's membership source, refreshed and cached like the geo list, its arrivals resolved on merge so the next fan-out can seat them. Each item's country, continent and self-declared bandwidth are cached beside its URL and order the probe sweep (see Probe echelon); none of the three ever reaches seating, which measurement alone decides.
226
+ _Avoid_: launchpad list, RSS list
227
+
228
+ **Debian mirror masterlist**:
229
+ Debian's `Mirrors.masterlist`: every registered Debian archive mirror with its country and declared architectures — the debian pool's membership source, refreshed and cached like the Launchpad feed. Its country orders the probe sweep (see Probe echelon) through the continent map the Launchpad feed teaches, it declares no bandwidth, and its declared architectures are no seating input (see Partial mirror).
230
+ _Avoid_: Debian mirror list, mirror-master list, masterlist alone
231
+
232
+ **Bare pool**:
233
+ A synthesized pool holding nothing beyond its seed (archive.ubuntu.com, deb.debian.org) — the daemon serving from one mirror like stock apt, so a failed list refresh retries out of it every minute rather than hourly.
234
+
235
+ **Canonical-name dedup**:
236
+ Collapsing pool entries whose DNS canonical names (CNAME-chain end), ports, and paths coincide — one physical mirror under several hostnames. Shared addresses are never identity: CDN vhosting reuses one address across distinct origins.
237
+
238
+ **Disk high-water**:
239
+ Filesystem-fullness bound (`disk_high_water_percent`): the sweep evicts oldest entries while the filesystem — whatever filled it — is fuller than this.
240
+
241
+ **Fill**:
242
+ The background task that races mirrors and writes one in-flight cache file to completion; it outlives the client that triggered it, and every client streams from its output.
243
+
244
+ **Tail-following**:
245
+ Serving a request from a fill's growing temp file, reading behind the writer and finishing when the fill completes; a fill failure propagates to all followers.
246
+
247
+ **Meet-in-the-middle**:
248
+ Racing strategy for a large file whose current source ignores ranges: the established forward stream continues undisturbed while range-capable mirrors download the tail backwards, and the file is complete when the two fronts meet.
249
+ _Avoid_: bidirectional download
250
+
251
+ **Throttled fallback**:
252
+ Keeping a slower duplicate stream alive at minimal rate purely as insurance while a faster mirror races ahead; the throttle lifts if the faster mirror fails. (Advanced post-MVP mechanism.)
253
+
254
+ **Endgame duplication**:
255
+ In a throughput race, once every chunk is in flight, a worker that finishes its chunk refetches the remainder of the in-flight chunk expected to finish last (remaining bytes over its delivery rate so far; no first byte yet counts as last) while that chunk's own stream keeps going; the first to finish wins and the other is cut, so the tail is bounded by the fastest idle mirror rather than the slowest busy one — the fix for the "99% for minutes" stall familiar from BitTorrent clients. Workers carry their seat from chunk to chunk and take an admissible unstarted chunk before duplicating anything; when chunks are still unstarted but no admission permit can return because the chunk at the head of the in-order drain is itself still in flight, a finished worker duplicates that head instead of waiting for it (head-of-line duplication) — a head that is complete but not yet consumed means the consumer, the cache fill's writer or the client on a streamed path, is the bottleneck, and then it waits; every duplicate is recorded on the fill event (`duplicates`), outcome, bytes discarded and estimated wait saved included.
256
+ _Avoid_: endgame mode, preemption
257
+
258
+ **Redirect swallowing**:
259
+ Following a mirror's 3xx inside the fetcher instead of forwarding it to the client.
260
+
261
+ **HTTPS upgrade memory**:
262
+ Per-mirror record that its http URLs redirect to https on the same netloc, so the fetcher goes straight to https on later requests; a redirect that also changes the port is followed for that hop but never remembered, since the memory can only flip the scheme.
263
+
264
+ **Wide event**:
265
+ One JSON line per unit of work — a client request, a cache fill, a probe sweep, or a laggard recheck, discriminated by `kind` — carrying all context that unit saw (docs/plans/2026-07-17-wide-event-log.md). An upstream answer a score or a verdict is taken from rides its row with the headers it answered with, every field as received except `Set-Cookie`, so which cache or front end answered reads beside the first byte it explains (docs/plans/2026-09-17-response-headers.md, which also names the two answers that reach no row). The stream at `events_path` is both the access log and the metrics record; journald never is.
266
+ _Avoid_: metrics record, log entry