| Type: | Package |
| Title: | Public Suffix List Engine |
| Version: | 1.1.1 |
| Description: | A focused implementation of the Public Suffix List (PSL). Bundles a reproducible, pinned PSL snapshot and implements the official prevailing-rule algorithm to answer public-suffix (eTLD) and registrable-domain (eTLD+1) queries. Distinguishes ICANN and PRIVATE rule sections, accepts Unicode and ASCII hostnames via 'punycoder' canonicalization, and supports an explicit, validated offline refresh path. The matcher is compiled with 'cpp11' and requires no external system library. Used as the PSL engine by the 'rurl' package. |
| License: | MIT + file LICENSE |
| Language: | en-US |
| URL: | https://bart-turczynski.github.io/pslr/, https://github.com/bart-turczynski/pslr, https://CRAN.R-project.org/package=pslr, https://bart-turczynski.r-universe.dev |
| BugReports: | https://github.com/bart-turczynski/pslr/issues |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.1.0) |
| Imports: | punycoder (≥ 1.1.0), tools, utils |
| LinkingTo: | cpp11 |
| Suggests: | cucumber (≥ 2.0.0), curl, digest, knitr, oysteR, rmarkdown, rosv, testthat (≥ 3.0.0), withr |
| Config/testthat/edition: | 3 |
| VignetteBuilder: | knitr |
| Config/roxygen2/version: | 8.0.0 |
| X-schema.org-keywords: | public suffix list, PSL, eTLD, eTLD+1, registrable domain, ICANN, domain parsing, domain extraction, domain names, TLD, cookie domains, IDNA, public suffix |
| NeedsCompilation: | yes |
| Packaged: | 2026-07-18 22:45:27 UTC; bartturczynski |
| Author: | Bart Turczynski |
| Maintainer: | Bart Turczynski <bartek@turczynski.pl> |
| Repository: | CRAN |
| Date/Publication: | 2026-07-19 07:20:02 UTC |
pslr: Public Suffix List Engine
Description
A focused implementation of the Public Suffix List (PSL). Bundles a reproducible, pinned PSL snapshot and implements the official prevailing-rule algorithm to answer public-suffix (eTLD) and registrable-domain (eTLD+1) queries. Distinguishes ICANN and PRIVATE rule sections, accepts Unicode and ASCII hostnames via 'punycoder' canonicalization, and supports an explicit, validated offline refresh path. The matcher is compiled with 'cpp11' and requires no external system library. Used as the PSL engine by the 'rurl' package.
Author(s)
Maintainer: Bart Turczynski bartek@turczynski.pl (ORCID)
Authors:
Bart Turczynski bartek@turczynski.pl (ORCID)
See Also
Core queries: public_suffix(), registrable_domain(),
is_public_suffix(), suffix_extract(), public_suffix_rule().
List management and provenance: psl_use(), psl_refresh(),
psl_version(), psl_rules().
The introduction vignette is a full tour:
vignette("introduction", package = "pslr").
Is a host itself a public suffix?
Description
TRUE exactly when the valid canonical host equals its own public suffix
under the selected policy. Returns NA whenever public_suffix() would
return NA (missing or invalid input, or an unresolved host under
unknown = "na"). Under the default unknown = "default", an unlisted
single label such as "madeuptld" is TRUE via the implicit * rule; ask
unknown = "na" to test explicit membership instead.
Usage
is_public_suffix(
domain,
section = "all",
unknown = "default",
invalid = "na",
engine = psl_default_engine()
)
Arguments
domain |
Character vector of DNS hostnames (not URLs). Each element may be a mixed-case ASCII, Unicode, or A-label hostname, a single label, or a hostname with exactly one terminal root dot. See Input contract. |
section |
Which rule sections are eligible: |
unknown |
|
invalid |
|
engine |
The |
Value
A logical vector with length(domain), preserving the names of
domain.
Input contract
NA is treated as missing (returns NA), not invalid. Invalid elements
include empty or whitespace-only strings, leading or consecutive dots, URL
syntax, IPv6 addresses, canonical dotted-decimal IPv4 literals, and labels
that fail hostname/IDNA validation. Wrong argument types and non-scalar or
unknown option values always abort regardless of invalid.
See Also
Examples
is_public_suffix("com")
is_public_suffix("example.com")
is_public_suffix("madeuptld")
is_public_suffix("madeuptld", unknown = "na")
Prune stale on-disk PSL cache snapshots
Description
Removes superseded psl-<hex>.dat snapshot files from the user cache
directory, always keeping the snapshot named by the active commit marker plus
the keep most-recent other snapshots by modification time.
Usage
psl_cache_prune(keep = 1L)
Arguments
keep |
Number of previous snapshots to retain in addition to the
active one, as a single non-negative whole number. The default |
Details
Each psl_refresh() that finds changed upstream content writes a new
content-addressed snapshot and repoints the commit marker at it, but never
removes the snapshot it supersedes; across many refreshes these accumulate.
psl_cache_prune() reclaims that space.
This operates on the on-disk snapshot files and is distinct from
psl_cache_clear(), which flushes the in-memory match-result cache for the
current session: pruning deletes stale .dat files from disk to reclaim
space, whereas clearing only discards computed query results. Pruning never
changes which list is active and never removes the active snapshot, so the
active matcher and a later psl_use("cache") keep working.
When there is no cache directory or no commit marker (nothing has been published yet), there is no active snapshot to anchor retention on, so the call is a no-op that returns an empty vector rather than an error.
Value
Invisibly, a character vector of the removed snapshot file paths, empty when nothing was pruned.
See Also
psl_refresh(), which writes the snapshots this prunes;
psl_use().
Examples
if (interactive()) {
psl_refresh(force = TRUE)
psl_cache_prune() # keep the current snapshot and one previous
psl_cache_prune(keep = 0) # keep only the active snapshot
}
Construct a self-contained PSL engine
Description
Builds a process-local Public Suffix List engine bound to a specific
snapshot, without switching the session-global active list that psl_use()
controls.
Usage
psl_engine(source = "bundled", path = NULL)
## S3 method for class 'psl_snapshot'
print(x, ...)
## S3 method for class 'psl_engine'
print(x, ...)
Arguments
source |
Where to load the list from: |
path |
For |
Details
The engine is process-local: its compiled matcher is a C++ external pointer that does not serialize across R sessions or parallel workers. Saving and reloading an engine, or sending one to a worker, does not carry the matcher. To persist an engine, serialize its snapshot descriptor and rebuild the engine from it in the target process.
Value
An engine object that the query functions can be pointed at (via
their engine= argument) to resolve hosts against the chosen snapshot in
isolation from the session-global list.
See Also
Examples
engine <- psl_engine("bundled")
engine
# A custom list from a file, entirely offline.
dat <- tempfile(fileext = ".dat")
writeLines(
c(
"// ===BEGIN ICANN DOMAINS===",
"com",
"// ===END ICANN DOMAINS===",
"// ===BEGIN PRIVATE DOMAINS===",
"example.com",
"// ===END PRIVATE DOMAINS==="
),
dat
)
psl_engine("path", path = dat)
Is the active Public Suffix List snapshot stale?
Description
An offline staleness check: compares the list_date of the list currently
active in this session (see psl_version()) against the current time and
reports whether it is older than max_age days. The Public Suffix List
changes continually upstream, so a long-lived bundled snapshot drifts from
the live list over time; this is the signal to consider psl_refresh().
Usage
psl_outdated(max_age = 180)
Arguments
max_age |
Maximum age, in days, before the active snapshot is considered outdated. A single positive number; defaults to 180. |
Details
The check reads only the already-loaded active metadata: it never touches the
network and never activates a different list. When the active list's
list_date is unknown (NA) or cannot be parsed, the result is NA –
staleness is undetermined rather than assumed either way.
The age of the active snapshot in days is attached to the result as the
"age_days" attribute (a double, or NA when the date is unknown), so a
caller that wants the magnitude as well as the verdict need not recompute it.
Value
A single logical, with an "age_days" attribute: TRUE when the
active list is more than max_age days old, FALSE when it is fresher,
and NA when its date is unknown.
See Also
Examples
# Is the active snapshot older than the default threshold?
psl_outdated()
# The age in days is available without recomputing it:
attr(psl_outdated(), "age_days")
# Use a stricter threshold:
psl_outdated(max_age = 30)
Refresh the cached Public Suffix List from upstream
Description
Downloads, validates, and publishes a fresh Public Suffix List into the user cache. This is the only function in the package that accesses the network, and only when you call it explicitly.
Usage
psl_refresh(
url = "https://publicsuffix.org/list/public_suffix_list.dat",
force = FALSE,
activate = FALSE
)
Arguments
url |
Absolute |
force |
When |
activate |
When |
Details
Cache age is measured from the successful network retrieval timestamp; reusing a fresh cache does not advance that timestamp. The download goes to a temporary file in binary mode and must be no larger than a documented maximum (16 MiB). The source is then fully validated – UTF-8, section markers, rule grammar, conflicting rules, and successful canonicalization of every rule – and exact same-section duplicates warn once and are deduplicated. Source and metadata are published only after validation succeeds, using an atomic commit that never exposes a partial or mismatched snapshot. A failed refresh never replaces a valid cache or the active matcher.
Value
Invisibly, a one-row data.frame shaped like psl_version()
describing the selected cache snapshot, whether or not it was activated.
See Also
Examples
if (interactive()) {
psl_refresh()
psl_refresh(force = TRUE, activate = TRUE)
}
Rules of the active Public Suffix List
Description
Returns the explicit rules of the active list as a base data.frame, one row
per rule. The implicit default * rule is not included.
Usage
psl_rules(section = "all")
Arguments
section |
Which rule sections to return: |
Value
A base data.frame with columns, in order: rule (original source
rule text), canonical_rule (the canonicalized rule, including the *. or
! marker), kind ("normal", "wildcard", or "exception"), section
("icann" or "private"), and labels (integer rule depth, counting a
wildcard label). Rows are ordered first by section (ICANN before PRIVATE)
and then by source-file order.
See Also
psl_version(), public_suffix_rule()
Examples
head(psl_rules("icann"))
nrow(psl_rules("private"))
Choose the active Public Suffix List for this session
Description
Switches the list backing every query in the current R session. The change is session-only and is validated before any session state changes; a failure leaves the previously active list usable. A successful switch invalidates the match-result cache.
Usage
psl_use(source = "bundled", path = NULL)
Arguments
source |
Where to load the list from: |
path |
For |
Details
A custom path is held to the same runtime duplicate policy as
psl_refresh(): exact same-section duplicates warn once and are
deduplicated, while conflicting rule kinds for the same labels are fatal.
Cache and custom-path sources are read in source form and indexed under the
runtime normalizer; they never reuse the bundled generated index.
Value
Invisibly, the psl_version() row for the newly active list.
See Also
psl_refresh(), psl_version(), psl_rules()
Examples
psl_use("bundled")
if (interactive()) {
psl_use("cache")
psl_use("path", path = "my_list.dat")
}
Identity of the active Public Suffix List
Description
Returns a one-row data.frame describing the list currently active in this R session: its source-snapshot provenance and the normalization identifiers actually used to index the active matcher. Reproducing a query result requires both the active-list identity and these normalization identifiers (PRD s10), so a reproducibility-sensitive workflow should record this row.
Usage
psl_version()
Details
The columns, in order, are:
source"bundled","cache", or"path".urlSource URL of the active snapshot: the upstream download URL for the bundled list;
NAfor a"cache"or"path"source.pathFile path of a
"cache"or"path"source;NAotherwise.retrieved_atNetwork retrieval timestamp, or
NA.list_dateUpstream list date, or
NAwhen unknown.commitUpstream commit SHA, or
NAwhen unknown.sizeSource byte size (integer).
checksumSource checksum, including its algorithm prefix (e.g.
"sha256:...").normalizerThe dependency providing canonicalization, currently
"punycoder".normalizer_versionIts installed package version.
normalization_profileIts stable case-mapping / IDNA / validation profile identifier.
unicode_versionThe Unicode data version used by that profile.
Unavailable metadata is a typed NA, never omitted. The normalization
identifiers describe the implementation used by the current session, whether
the active list came from the bundled snapshot, the user cache, or a custom
path; an in-memory compatibility rebuild (PRD s8.3) updates them without
altering the shipped source identity or checksum.
Value
A one-row base data.frame with the columns described in Details.
See Also
psl_use(), psl_refresh(), psl_rules()
Examples
psl_version()
Public suffix of a host
Description
Returns the public suffix (effective top-level domain, eTLD) of each host under the selected Public Suffix List policy, following the official prevailing-rule algorithm.
Usage
public_suffix(
domain,
section = "all",
output = "ascii",
unknown = "default",
invalid = "na",
engine = psl_default_engine()
)
Arguments
domain |
Character vector of DNS hostnames (not URLs). Each element may be a mixed-case ASCII, Unicode, or A-label hostname, a single label, or a hostname with exactly one terminal root dot. See Input contract. |
section |
Which rule sections are eligible: |
output |
|
unknown |
|
invalid |
|
engine |
The |
Value
A character vector with length(domain), preserving the names of
domain. Other attributes are dropped.
Input contract
NA is treated as missing (returns NA), not invalid. Invalid elements
include empty or whitespace-only strings, leading or consecutive dots, URL
syntax, IPv6 addresses, canonical dotted-decimal IPv4 literals, and labels
that fail hostname/IDNA validation. Wrong argument types and non-scalar or
unknown option values always abort regardless of invalid.
See Also
registrable_domain(), is_public_suffix(), suffix_extract(),
public_suffix_rule()
Examples
public_suffix("www.example.com")
public_suffix("example.co.uk")
public_suffix("example.com.")
public_suffix("madeuptld", unknown = "na")
Inspect the prevailing PSL rule for each host
Description
Inspect the prevailing PSL rule for each host
Usage
public_suffix_rule(
domain,
section = "all",
unknown = "default",
invalid = "na",
engine = psl_default_engine()
)
Arguments
domain |
Character vector of DNS hostnames (not URLs). Each element may be a mixed-case ASCII, Unicode, or A-label hostname, a single label, or a hostname with exactly one terminal root dot. See Input contract. |
section |
Which rule sections are eligible: |
unknown |
|
invalid |
|
engine |
The |
Value
A base data.frame with one row per input and columns, in order:
input (original), host_ascii (canonical A-label host), rule (the
canonical rule including *. or !, "*" for the implicit default),
kind ("normal", "wildcard", "exception", or "default"),
rule_section ("icann", "private", or NA for the default/no result),
and public_suffix_ascii (the derived A-label public suffix). Invalid rows
are NA in every derived column. A valid host left unresolved by
unknown = "na" keeps host_ascii while the rule and suffix columns are
NA. An exception rule retains its ! for auditability. Zero-length
input returns a zero-row frame; all-invalid input keeps one row per input.
Input contract
NA is treated as missing (returns NA), not invalid. Invalid elements
include empty or whitespace-only strings, leading or consecutive dots, URL
syntax, IPv6 addresses, canonical dotted-decimal IPv4 literals, and labels
that fail hostname/IDNA validation. Wrong argument types and non-scalar or
unknown option values always abort regardless of invalid.
See Also
public_suffix(), suffix_extract()
Examples
public_suffix_rule("www.example.co.uk")
public_suffix_rule("madeuptld")
Registrable domain of a host
Description
Returns the registrable domain (eTLD+1) of each host: its public suffix plus
one host label to the left. It is NA when no such label exists (the host is
itself a public suffix) or when the public suffix is NA.
Usage
registrable_domain(
domain,
section = "all",
output = "ascii",
unknown = "default",
invalid = "na",
engine = psl_default_engine()
)
Arguments
domain |
Character vector of DNS hostnames (not URLs). Each element may be a mixed-case ASCII, Unicode, or A-label hostname, a single label, or a hostname with exactly one terminal root dot. See Input contract. |
section |
Which rule sections are eligible: |
output |
|
unknown |
|
invalid |
|
engine |
The |
Value
A character vector with length(domain), preserving the names of
domain. Other attributes are dropped.
Input contract
NA is treated as missing (returns NA), not invalid. Invalid elements
include empty or whitespace-only strings, leading or consecutive dots, URL
syntax, IPv6 addresses, canonical dotted-decimal IPv4 literals, and labels
that fail hostname/IDNA validation. Wrong argument types and non-scalar or
unknown option values always abort regardless of invalid.
See Also
public_suffix(), is_public_suffix(), suffix_extract()
Examples
registrable_domain("www.example.co.uk")
registrable_domain("com")
registrable_domain("foo.madeuptld", unknown = "na")
Split hosts into subdomain, registrant label, and public suffix
Description
Split hosts into subdomain, registrant label, and public suffix
Usage
suffix_extract(
domain,
section = "all",
output = "ascii",
unknown = "default",
invalid = "na",
engine = psl_default_engine()
)
Arguments
domain |
Character vector of DNS hostnames (not URLs). Each element may be a mixed-case ASCII, Unicode, or A-label hostname, a single label, or a hostname with exactly one terminal root dot. See Input contract. |
section |
Which rule sections are eligible: |
output |
|
unknown |
|
invalid |
|
engine |
The |
Value
A base data.frame with one row per input and columns, in order:
input (original, unchanged), host (canonical host in output form),
subdomain (labels left of the registrable domain; "" when none),
domain (the single registrant label left of the suffix), suffix (the
public suffix), and registrable_domain (eTLD+1). domain, subdomain,
and registrable_domain are NA when the host is itself a public suffix.
If public-suffix resolution is NA, every derived column except input
and a successfully normalized host is NA. Zero-length input returns a
zero-row frame; all-invalid input keeps one row per input. Root dots are
preserved on host, suffix, and registrable_domain only.
Input contract
NA is treated as missing (returns NA), not invalid. Invalid elements
include empty or whitespace-only strings, leading or consecutive dots, URL
syntax, IPv6 addresses, canonical dotted-decimal IPv4 literals, and labels
that fail hostname/IDNA validation. Wrong argument types and non-scalar or
unknown option values always abort regardless of invalid.
See Also
public_suffix(), public_suffix_rule()
Examples
suffix_extract("www.example.co.uk")
suffix_extract(c("example.com", "com", NA))