The Public Suffix List website
catalogs implementations in C, C#, C++, Go, Haskell, Java, JavaScript,
Perl, PHP, Python, Ruby, Rust, Swift, and more — but no R.
pslr fills that gap, and it is built to be a
reproducibility- and correctness-first engine rather than a
quick suffix splitter.
The table compares pslr with a representative set of the
most established libraries from that catalog, across the dimensions that
matter for correct, auditable suffix handling. ✅ first-class · ◐
partial/limited · ⚠ with a caveat · ❌ absent.
| Library (language) | Full algorithm (*/!) |
ICANN / PRIVATE / both | IDN + Punycode | Offline default + explicit refresh | Queryable provenance | Compiled core | Strict input validation | Unlisted-TLD policy configurable |
|---|---|---|---|---|---|---|---|---|
| pslr (R) | ✅ | ✅ 3-way | ✅ in & out | ✅ bundled; validated HTTPS-only
psl_refresh() |
✅ list and normalization identity | ✅ cpp11 |
✅ rejects URLs, IPv4, IPv6 | ✅ unknown= |
| libpsl (C) | ✅ | ✅ 3-way | ✅ | ✅ bundled; OS-package update | ◐ sha1 + mtime | ✅ | ◐ | ✅ |
| x/net/publicsuffix (Go) | ✅ | ◐ icann flag |
❌ ASCII only | ✅ embedded; bump module | ◐ date constant | ✅ | ◐ | ❌ |
| publicsuffix-go (Go) | ✅ | ◐ toggle private | ✅ | ✅ embedded | ❌ | ❌ | ◐ | ✅ |
| psl crate (Rust) | ✅ | ◐ per-suffix type | ✅ | ✅ compiled-in | ◐ dated releases | ✅ | ❌ | ◐ |
| tldextract (Python) | ✅ | ◐ private on/off | ✅ | ⚠ network-first; offline opt-in | ❌ | ❌ | ◐ | ❌ |
| publicsuffixlist (Python) | ✅ | ◐ exclude private | ✅ | ✅ bundled + updater | ◐ date in version | ❌ | ❌ | ❌ |
| public_suffix (Ruby) | ✅ | ◐ ignore_private |
❌ caller pre-encodes | ✅ bundled | ❌ | ❌ | ◐ | ✅ |
| php-domain-parser (PHP) | ✅ | ✅ 3-way | ✅ | ⚠ not bundled; PSR-16 cache | ◐ isKnown flags |
❌ | ◐ | ◐ |
| tldts (JS/TS) | ✅ | ◐ allowPrivateDomains |
✅ | ✅ embedded; npm bump | ◐ submodule pin | ◐ optional WASM | ✅ | ◐ |
Guava InternetDomainName (Java) |
◐ | ✅ registry vs public | ✅ | ◐ in-jar; bump Guava | ❌ | ❌ | ◐ | ❌ |
pslr does differentlypsl_version() records the list identity (source, commit,
date, SHA-256) and the normalization identity (normalizer
package + version, profile, Unicode version). A PSL answer depends on
both which list answered and how the host was
normalized — pslr is the only surveyed library that
surfaces both, so a result is genuinely reproducible.tests.txt vectors, pinned in lockstep
with the bundled snapshot and run on every check.psl_refresh()
is the only code that touches the network — HTTPS-only,
credential- and downgrade-rejecting, size-capped, conditional when it
can be, published append-only. This is stricter than network-first
designs and pairs with the reproducibility story rather than fighting
it.psl_status() separates “a check is due” from “a newer list
was actually observed”, and never infers the second from snapshot age.
Surveyed libraries report a date or nothing at all.psl_diff()
reports what actually changed between two snapshots — canonically,
offline, keyed on each rule’s logical identity so a wildcard/exception
change or an ICANN/PRIVATE move reads as one change rather than an
unrelated removal and addition. No surveyed library offers this.invalid = "na" / "error" lets you choose
silent NA or a hard stop. Many libraries are deliberately
lenient.NA-safe, name-preserving.
Every function operates on a whole character vector — the idiomatic
shape for data work in R.psl_use() /
psl_refresh()) unless a call passes engine =,
a psl_engine() built for one specific snapshot.rurl.