Internals (deep dive)
This page explains how each module works and how they interact.
wayparam.cli
Responsibilities:
- define the public CLI contract (argparse)
- validate CLI option interactions and numeric bounds
- normalize CLI inputs and build a frontend-independent RunConfig
The optional web UI builds the same RunConfig; neither frontend owns the
engine.
wayparam.core
core.run() owns concurrency and per-domain orchestration. For each raw URL
returned by CDX it:
1) filters boring URLs (filters.is_boring)
2) canonicalizes (normalize.canonicalize_url)
3) deduplicates the normalized result
4) emits output and progress callbacks
wayparam.analysis
Historical modes use a separate runner so the normal URL pipeline keeps its streaming and low-memory behavior unchanged.
The analysis runner requests capture metadata and forces collapse=None. Each accepted capture is filtered and normalized with the same code as a normal run, then grouped by the normalized URL. From that aggregate wayparam derives:
- endpoint history: first/last seen, capture count, status/MIME distributions
- parameter history: number of normalized endpoints, represented captures, first/last seen
- domain summary: fetched/accepted captures, unique URLs/parameters and aggregate distributions
--max-results is still a global budget, but in analysis mode it counts accepted capture rows before aggregation.
wayparam.wayback
Endpoint
The CDX endpoint used is:
https://web.archive.org/cdx/search/cdx
Paging / resumeKey
CDX may return a resume key at the end of the response. wayparam:
- reads the last line
- detects resumeKey: forms and heuristic forms
- parses capture rows requested as timestamp,statuscode,mimetype,original when metadata mode is enabled
- loops until no resumeKey (or repeat key safety break)
The resumeKey walk itself is faithful, but the server-side collapse is not:
while collapsing, the API drops one row at every page boundary. Measured on the
live API, paging one query 18 ways lost 15 of 4684 URLs.
Block paging (used by auto for multi-page results)
showNumPages + page/pageSize walks the index in fixed blocks instead,
and is lossless with collapse. Two things about it are easy to get wrong:
- the page count must be requested without
output/fl; with them the endpoint answers-, which reads as "pagination unavailable" and silently sends the run down the lossy path - the block count follows the size of the domain's index, not the size of the
filtered result, so a narrow
--from/--towindow does not make it cheaper
That second point is why the default is auto rather than blocks: one
resumeKey-shaped probe settles whether the result even spans pages. If it does
not — the common case — there is no boundary, nothing can be lost, and the run
is done in a single request.
Memory on a large run
Two things bound a long run, and they were measured rather than guessed (250 000 unique URLs from one 13 MB block page):
| peak | |
|---|---|
| buffered page + URL strings in the dedup set | 71.1 MB |
| streamed page + URL strings | 30.1 MB |
| streamed page + 16-byte fingerprints | 19.3 MB |
- The response body. Buffering a page costs twice its size — the raw bytes
and the decoded text — before a single URL reaches the caller. Block pages
are streamed line by line (
http.iter_lines), which also means results appear while the page is still downloading. The resumeKey walk still buffers: finding the cursor means looking at the last line, and its pages are capped at--limitrows anyway. - The dedup set. It holds one entry per emitted URL for the whole domain.
core.fingerprintstores a 16-byte blake2b digest instead of the URL, which measured at ~72 bytes per entry against ~148 for the string.
Note the set holds kept URLs, not fetched rows, and canonicalization
collapses hard — masking values turns thousands of ?id=… variants into one
entry. Reaching a million entries takes an enormous domain, or --all-urls.
wayparam.http
Resilience
get_text() performs:
- retries on transient errors and HTTP status errors
- exponential backoff
- special handling for 429/503
- includes final status=... or no-status in the raised error message
This makes troubleshooting much easier in real-world environments (VPNs, flaky networks).
wayparam.normalize
Canonicalization steps: - require absolute URLs with scheme + netloc - drop fragments - normalize host casing and default ports - parse query string, optionally drop tracking params - optionally replace values with placeholder - sort params for stable output - optionally drop URLs with no query params (default behavior)
wayparam.filters
Filtering is based primarily on path extension (e.g., .png, .css, .js) and optional regex rules.
Modes:
- blacklist only (default)
- whitelist mode (if --ext-whitelist is set) — the whitelist replaces the
blacklist instead of being narrowed by it, so a whitelisted extension is kept
even when it appears on the default blacklist
Path regex exclusions apply in both modes. A path with no extension is never judged by extension.
wayparam.output
Key rule: - keep stdout strictly machine-readable - send diagnostics to stderr
Formats:
- txt: URL per line
- jsonl: JSON object per line (record)
wayparam.ratelimit
A small async rate limiter that enforces a global RPS limit across all tasks.