code wiki / _hdl_build / nx_domain_map.nx
nx_domain_map.nx
buildroot/runtime/_hdl_build/nx_domain_map.nx
about
nx_domain_map.nx -- DOMAIN MAP: every URL under a domain BEFORE crawling it -- the /compare/webscraping R8 contracts
dm_scan (the composed census) and dm_soft404 (the fingerprint that keeps a 200-that-is-really-404 out of it).
Concept source: crawl4ai DomainMapper (eight discovery sources + soft-404 fingerprint). Sovereign form composes the
estate's own parsers, never a second one:
robots nx_sitemap_extract.sm_robots_sitemaps (Sitemap: lines) + dm_robots_paths (Allow/Disallow paths a site
ACKNOWLEDGES exist) ............................................................ source=robots
sitemap sm_extract_locs over every sitemap, RECURSING sitemapindex (sm_is_subsitemap) and inflating .gz
bodies (nx_gzip_wrap) ........................................................... source=sitemap
feed nx_feed_extract on the homepage (feed href -> items) ............................ source=feed
wayback the Internet Archive CDX API, original urls, 200s only ............................ source=wayback
crt crt.sh certificate-transparency JSON -> subdomains ............................... source=crt (kind=host)
probe knowledge/domain_map_probe.conf paths (robots-checked) classified against the SOFT-404 FINGERPRINT taken
first from a path that cannot exist ............................................. source=probe
EVERY ROW IS APPENDED AS IT IS DECIDED (a run killed at 95 pct keeps 95 pct; the estate's long-sweep law), to
knowledge/status/domain_map/<domain>.tsv: ts TAB source TAB kind(url|host|path) TAB verdict TAB value. The final
line prints the partition (rows per source) and it SUMS to rows written; fetches used, refused (robots), oversize
(a <loc> past the url slot) and truncated (a body that filled the cap) are counted, never silent.
PACED: every fetch goes through nx_crawl_pace (per host, Retry-After, robots Crawl-delay); probes are refused when
robots forbids the path, and a refusal is a ROW, not a silence.
usage: nx_domain_map <domain> [max_fetches] [out.tsv] (rows and caps: knowledge/domain_map.conf)
exit: 0 rows written | 2 usage | 3 trust store | 4 nothing reachable (0 fetches succeeded)
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 11 imports · 1 importers
diagram shows first 10 each side; +1 more imports, +0 more importers in the complete lists below.
imports: nx_syscalls.nxnx_lane_conf.nxnx_x509_trust_store.nxnx_trust_store_load_from_certdata.nxnx_https_fetch_follow.nxnx_gzip_wrap.nxnx_sitemap_extract.nxnx_feed_extract.nxnx_robots.nxnx_crawl_pace.nxnx_itoa_lib.nx
imported by: nx_domain_map_gate.nx
structs
| none |
consts
| 35 | const DM_CONF: *u8 = "knowledge/domain_map.conf" |
| 36 | const DM_PROBES: *u8 = "knowledge/domain_map_probe.conf" |
| 37 | const DM_OUTDIR: *u8 = "knowledge/status/domain_map" |
| 38 | const DM_CERTDATA: *u8 = "data/mozilla_certdata.txt" |
| 40 | const DM_DEF_MAX_FETCHES: i64 = 64 // one map is a reconnaissance, not a crawl: 64 fetches covers robots + a sitemap tree + probes |
| 41 | const DM_DEF_WAYBACK_LIMIT: i64 = 500 // CDX rows requested; the API accepts a limit and 500 keeps the body under the fetch cap |
| 42 | const DM_DEF_MAX_SITEMAPS: i64 = 32 // sitemap files followed through a sitemapindex before the map says so and stops |
| 43 | const DM_DEF_MAX_LOCS: i64 = 50000 // sitemaps.org's own per-file ceiling; a file past it is out of spec |
| 44 | const DM_DEF_MAX_HOSTS: i64 = 256 // distinct subdomains kept from crt.sh; the rest are counted as oversize |
| 45 | const DM_DEF_FETCH_CAP: i64 = 8388608 // 8 MiB body cap per fetch (announced when filled); crt.sh answers can be MBs |
| 46 | const DM_DEF_SOFT404_TOL_PERMIL: i64 = 100 // crawl4ai Soft404Fingerprint compares status, title and content length; +-10 pct length |
| 47 | const DM_DEF_MAX_HOPS: i64 = 6 // redirect hops, the crawler's own number |
| 48 | const DM_URL_SLOT: i64 = 2048 // one url slot; sitemaps.org allows a 2048-char <loc>, longer is out of spec |
| 49 | const DM_HOST_SLOT: i64 = 256 // one host slot (a DNS name is at most 253 bytes) |
| 50 | const DM_PATH_SLOT: i64 = 512 // one probe or robots path slot |
| 51 | const DM_TS_BUF: i64 = 32 |
| 52 | const DM_LINE_CAP: i64 = 4096 |
| 53 | const DM_SMALL: i64 = 1024 |
| 54 | const DM_TRUST_ANCHORS: i64 = 512 // the crawler's own trust-store sizing |
| 55 | const DM_TRUST_PARSE_CAP: i64 = 4194304 |
| 56 | const DM_MODE_0644: i64 = 420 |
| 57 | const DM_MODE_0755: i64 = 493 |
| 58 | const DM_GZ_ID1: i64 = 31 |
| 59 | const DM_GZ_ID2: i64 = 139 |
| 60 | const DM_UALEN: i64 = 8 // len("nishibot"), the crawler's UA token |
| 61 | const DM_TAB: i64 = 9 |
| 62 | const DM_NL: i64 = 10 |
| 63 | const DM_CR: i64 = 13 |
| 64 | const DM_HASH: i64 = 35 |
| 65 | const DM_QUOTE: i64 = 34 |
| 66 | const DM_BACKSLASH: i64 = 92 |
| 67 | const DM_SLASH: i64 = 47 |
| 68 | const DM_COLON: i64 = 58 |
| 69 | const DM_LT: i64 = 60 |
| 70 | const DM_GT: i64 = 62 |
| 71 | const DM_DIGIT0: i64 = 48 |
| 72 | const DM_DIGIT9: i64 = 57 |
| 73 | const DM_HEX_BASE: i64 = 16 |
| 74 | const DM_HASH_SEED: i64 = 5381 |
| 75 | const DM_HASH_MASK: i64 = 0x7fffffffffffffff |
| 76 | const DM_PROBE_DEFAULTS: i64 = 15 |
| 78 | const DM_V_DECLARED: *u8 = "DECLARED" |
| 79 | const DM_V_LISTED: *u8 = "LISTED" |
| 80 | const DM_V_ARCHIVED: *u8 = "ARCHIVED" |
| 81 | const DM_V_CERT: *u8 = "CERT" |
| 82 | const DM_V_LIVE: *u8 = "LIVE" |
| 83 | const DM_V_SOFT404: *u8 = "SOFT404" |
| 84 | const DM_V_MISSING: *u8 = "MISSING" |
| 85 | const DM_V_BLOCKED: *u8 = "BLOCKED" |
| 86 | const DM_V_UNREACHABLE: *u8 = "UNREACHABLE" |
| 87 | const DM_V_ROBOTS_REFUSED: *u8 = "ROBOTS-REFUSED" |
| 88 | const DM_V_FINGERPRINT: *u8 = "FINGERPRINT" |
| 89 | const DM_HTTP_OK: i64 = 200 |
| 90 | const DM_HTTP_NOTFOUND: i64 = 404 |
| 91 | const DM_HTTP_ERR_FLOOR: i64 = 400 |
| 92 | const DM_STATUS_BUDGET: i64 = 0 - 1 // status-box value meaning: this fetch was refused by OUR budget, the host was never asked |
| 93 | const DM_V_BUDGET: *u8 = "BUDGET-REFUSED" |
functions
| 123 | func dm_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 124 | func dm_puts(s: *u8) -> i64 { sys_write(1, s, dm_len(s)); return 0 } |
| 125 | func dm_num(v: i64) -> i64 { nxi_out(v); return 0 } calls 1: nxi_out |
| 126 | func dm_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } |
| 127 | func dm_catn(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; var p: i64 = o; while i < n { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } called by 1: dm_row |
| 128 | func dm_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] == b[i] { if a[i] == (0 as u8) { return 1 } i = i + 1 } return 0 } |
| 129 | func dm_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } |
| 130 | func dm_conf_one(key: *u8, dflt: i64) -> i64 |
| 136 | func dm_load_conf() -> i64 |
| 149 | func dm_hash(b: *u8, n: i64) -> i64 called by 1: dm_title_hash |
| 157 | func dm_title_hash(html: *u8, n: i64) -> i64 |
| 176 | func dm_soft404_classify(fp_status: i64, fp_len: i64, fp_title: i64, status: i64, len: i64, title: i64, tol_permil: i64) -> i64 |
| 187 | func dm_soft404(status: i64, len: i64, title: i64) -> i64 |
| 192 | func dm_robots_paths(rob: *u8, n: i64, out: *u8, slot: i64, max: i64) -> i64 |
| 229 | func dm_crt_hosts(js: *u8, n: i64, domain: *u8, out: *u8, slot: i64, max: i64, oversize: *i64) -> i64 |
| 284 | func dm_is_gzip(b: *u8, n: i64) -> i64 called by 1: main |
| 291 | func dm_row(source: *u8, kind: *u8, verdict: *u8, value: *u8, vlen: i64) -> i64 |
| 311 | func dm_fetch(url: *u8, store: *TrustStore, out: *u8, cap: i64, stbox: *i64) -> i64 |
| 336 | func dm_url(dst: *u8, domain: *u8, path: *u8) -> i64 |
| 343 | func dm_nonce_path(dst: *u8, domain: *u8) -> i64 |
| 353 | func dm_source_sitemaps(domain: *u8, store: *TrustStore, robots: *u8, rn: i64, body: *u8, cap: i64, stbox: *i64) -> i64 |
| 402 | func dm_source_feed(domain: *u8, store: *TrustStore, body: *u8, cap: i64, stbox: *i64, feedxml: *u8) -> i64 |
| 429 | func dm_source_wayback(domain: *u8, store: *TrustStore, body: *u8, cap: i64, stbox: *i64) -> i64 |
| 451 | func dm_source_crt(domain: *u8, store: *TrustStore, body: *u8, cap: i64, stbox: *i64) -> i64 |
| 475 | func dm_probe_paths_load(out: *u8, slot: i64, max: i64) -> i64 |
| 532 | func dm_source_probe(domain: *u8, store: *TrustStore, robots: *u8, rn: i64, body: *u8, cap: i64, stbox: *i64) -> i64 |
| 593 | func dm_scan(domain: *u8, store: *TrustStore, body: *u8, feedxml: *u8, robots: *u8, cap: i64, stbox: *i64) -> i64 |
| 618 | func main(argc: i64, argv: *i64) -> i64 |
| 671 | func wc_atoi_dm(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= DM_DIGIT0 { if c <= DM_DIGIT9 { v = v * 10 + (c - DM_DIGIT0) } } i = i + 1 } return v } |