moonsuffix

    A portable Public Suffix List engine for registrable-domain decisions in MoonBit.

    public-suffix
    domain
    etld
    cookie
    wasm
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    8 hours ago
    Downloads
    1

    #MoonSuffix

    MoonSuffix is a pure MoonBit Public Suffix List rule-selection engine for answering two deceptively difficult questions about a hostname:

    • What is its public suffix?
    • What is the registrable domain (often called eTLD+1)?

    It parses caller-supplied Public Suffix List text, implements exact, wildcard, exception, longest-rule, and implicit * behavior, recognizes the official ICANN and PRIVATE sections, and returns an explainable match. The reusable core has no file-system or network dependency and is designed for MoonBit's Wasm, Wasm-GC, JavaScript, and native targets.

    #Why this project exists

    URL parsing tells an application where the hostname is; it does not tell the application which labels are controlled by a registry. That boundary matters for Cookie policy, same-site decisions, crawler deduplication, certificate tooling, and domain analytics.

    MoonSuffix is complementary to MoonBit's existing URL and IDNA libraries. It does not parse URLs, perform DNS, or replace UTS #46 canonicalization.

    #Review in one minute

    From a fresh clone, run moon update and then:

    moon run examples/review

    The self-contained example prints the lookup and the before/after effect of adding one PRIVATE PSL rule:

    lookup api.shop.example.co.uk: suffix=co.uk, registrable=example.co.uk PSL update: +PRIVATE glideos.app api.glideos.app: registrable glideos.app -> api.glideos.app Cookie Domain=.glideos.app: glideos.app accepted -> cookie Domain attribute 'glideos.app' is a public suffix changed hosts=1, changed Cookie scopes=1

    The fixture is deliberately small; the same APIs are tested in CI against two pinned, complete upstream PSL revisions. moon run cmd/main demonstrates wildcards, exceptions and policy choices; moon run examples/idna covers Unicode domains.

    For an application checking both hostname and Cookie inventories, use old_snapshot.compare_to(new_snapshot) once, then call analyze_impact and analyze_cookie_scope_impact on the comparison. It compiles each pinned rule set once and keeps the two reports on the same snapshot pair and lookup policy.

    The CI workflow checks Wasm, Wasm-GC, and JavaScript tests. It also downloads a pinned full upstream PSL and official conformance cases, checks their hashes, and exercises a real rule update that changes two hostname classifications and one Cookie scope. The source revisions and licenses are recorded in SOURCES.md.

    MoonSuffix's independent contribution is caller-supplied PSL rule processing and reproducible update auditing. It overlaps Crater's PSL helper in registrable-domain lookup but does not implement Crater's browser HTTP stack; see the dated ecosystem comparison. MoonSuffix does not bundle an up-to-date PSL, parse URLs, or implement a complete Cookie jar.

    #Quick start

    let rules =
    #|com
    #|co.uk
    #|*.ck
    #|!www.ck

    let suffixes = @moonsuffix.SuffixList::parse(rules).unwrap()
    let result = suffixes.lookup("api.shop.example.co.uk").unwrap()

    println(result.public_suffix()) // co.uk
    println(result.registrable_domain()) // Some(example.co.uk)
    println(result.matched_rule()) // co.uk

    Applications that must reject privately managed or unknown suffixes can select an explicit policy:

    let result = suffixes.lookup_with_options(
    "api.example.com",
    @moonsuffix.LookupOptions::strict_icann(),
    ).unwrap()

    Run the included example:

    moon run cmd/main

    For Unicode hostnames, use the separate IDNA adapter so PSL rules and input hostnames are converted to the same A-label form:

    let list = @suffix_idna.parse_psl("公司.cn\ncom\n").unwrap()
    let result = @suffix_idna.lookup(list, "商店.公司.cn").unwrap()
    println(result.public_suffix()) // xn--55qx5d.cn
    println(result.registrable_domain().unwrap()) // xn--czrs0t.xn--55qx5d.cn

    Run this example with moon run examples/idna. Import cn-wn/moonsuffix/idna as suffix_idna; it depends on moonbit-community/idna. The core package remains usable without importing that adapter.

    For a Unicode hostname collection, the adapter's batch API keeps invalid inputs as rows and continues with later names:

    let report = @suffix_idna.lookup_batch(
    list,
    ["商店.公司.cn", "商店..公司.cn", "xn--czrs0t.xn--55qx5d.cn"],
    )
    println(report.to_csv())

    The same adapter also accepts Unicode Cookie request hosts and Domain attributes. It converts both names to A-labels before applying the core's public-suffix and domain-match rules:

    let scope = @suffix_idna.resolve_cookie_scope(
    list, "api.商店.公司.cn", Some(".商店.公司.cn"),
    ).unwrap()
    println(scope.domain()) // xn--czrs0t.xn--55qx5d.cn
    println(@suffix_idna.cookie_scope_matches_host(scope, "别的.商店.公司.cn")) // Ok(true)

    shareable_cookie_scopes is available from the IDNA adapter too. These APIs cover Cookie domain scope and matching, not path, Secure, expiry, or a complete Cookie jar.

    For HTTP site comparisons, pass already extracted scheme and ASCII hostname:

    let first = suffixes.schemeful_site("https", "shop.example.com").unwrap()
    let second = suffixes.schemeful_site("https", "api.example.com").unwrap()
    let third = suffixes.schemeful_site("http", "api.example.com").unwrap()
    println(first.same_site(second)) // true
    println(first.same_site(third)) // false

    To find the Domain values a server may use to share a Cookie with sibling hosts, ask the same PSL-aware core for scopes ordered from narrowest to broadest:

    let scopes = suffixes.shareable_cookie_scopes("api.shop.example.com").unwrap()
    for scope in scopes {
    println(scope.domain())
    }

    This prints api.shop.example.com, shop.example.com, then example.com.

    The public suffix (com here) is never suggested. For a host that is itself a public suffix, the result is empty; an explicit equal Domain attribute would create a host-only Cookie, not a shareable one. Inputs must be ASCII/A-label DNS names; callers with Unicode names can first use the separate IDNA adapter.

    #Build a PSL snapshot

    Build a single-file snapshot from an explicitly chosen local PSL revision:

    moon run --target native cmd/snapshot \ examples/audit/old.psl \ old.snapshot \ --revision example-v1

    The output includes canonical rules, revision, SHA-256 and rule count. The command verifies its own bundle before writing and refuses to replace an existing output file.

    For a Unicode PSL source, pass --idna; the stored canonical rules use A-labels. For example:

    moon run --target native cmd/snapshot \ examples/idna/rules.psl unicode.snapshot \ --revision unicode-example-v1 --idna

    Query a stored snapshot directly:

    moon run --target native cmd/lookup \ unicode.snapshot 商店.公司.cn --idna --strict-icann

    The command verifies the bundle and prints its revision, digest, selected public suffix, registrable domain, and prevailing rule. Unicode input requires an A-label snapshot built with --idna.

    #Check PSL conformance cases

    Run caller-supplied checkPublicSuffix cases against a local PSL file:

    moon run --target native cmd/conformance \ examples/conformance/rules.psl examples/conformance/cases.txt

    The command prints the rule-set digest, case counts, and a CSV failure list. It exits unsuccessfully if a case fails, the fixture is empty or malformed, or the rules cannot be parsed. Use --bundle when the first input is a verified snapshot bundle. Add --idna for Unicode rules and test domains; it converts both inputs and expected results to A-labels with the same UTS #46 profile. The included examples are small, synthetic cases. CI also downloads a pinned revision of the full upstream PSL and its CC0 test file, checks both source hashes, and runs the complete suite with --idna. See SOURCES.md for the exact revision and hashes. Neither upstream file is redistributed here.

    #Measure a real PSL workload

    Use the benchmark command with a local copy of the pinned upstream PSL and a representative hostname inventory:

    moon run --target native cmd/bench \ public_suffix_list.dat examples/bench/hosts.txt --idna

    It reports the canonical rule-set digest, rule and host counts, average compile time over five runs, and average lookup time over 1,000 inventory passes. Time units are microseconds. --idna includes UTS #46 conversion in compile time; inventory normalization and file I/O are excluded from the timed regions. The workload and timings are machine-dependent, so compare results on the same machine and toolchain. CI runs this command on the same pinned PSL revision as the conformance check, without enforcing a noisy timing threshold.

    CI also exercises a real upstream PSL update: adding the PRIVATE rule glideos.app changes the registrable boundary for api.glideos.app and makes Domain=.glideos.app invalid for a Cookie from that host. The two pinned revisions are downloaded and hash-checked, converted to snapshots, and audited against small example inventories. This deliberately fails the impact gate while verifying the expected two hostname changes and one Cookie-scope change; the PSL files themselves are not bundled.

    #Embed a pinned PSL in a MoonBit application

    For Wasm or JS applications without runtime file access, generate MoonBit source from a locally obtained PSL revision:

    moon run --target native cmd/embed \ public_suffix_list.dat embedded_psl.mbt \ --revision <upstream-commit-sha> --idna

    Place embedded_psl.mbt in the consuming package and import cn-wn/moonsuffix as moonsuffix. Call embedded_suffix_snapshot(); it returns a verified snapshot and its parsed SuffixList in one pass. The generator refuses to overwrite an existing output file. The generated source includes PSL-derived data, so applications distributing it must retain the upstream MPL-2.0 source and notices. CI compiles and runs a generated small example; the repository does not carry a copy of the full PSL.

    #Audit a PSL update

    The native audit command compares a deployed PSL with a candidate list, then evaluates an ordered hostname inventory against both snapshots:

    moon run --target native cmd/audit \ examples/audit/old.psl \ examples/audit/new.psl \ examples/audit/hosts.txt \ --from example-v1 \ --to example-v2 \ --strict-icann

    It writes one deterministic report containing snapshot revisions and digests, semantic rule changes, summary counts, and CSV rows for hostnames whose lookup outcome changed. Blank inventory lines and lines beginning with # are ignored; remaining lines preserve their order and duplicates. The command only reads local files and does not fetch or redistribute PSL data.

    To audit pinned bundles, build an old and candidate snapshot with cmd/snapshot, then run:

    moon run --target native cmd/audit \ old.snapshot new.snapshot examples/audit/hosts.txt \ --bundles --strict-icann --fail-on-impact

    Bundle mode verifies both files before comparing them and reads revision labels from their manifests. The --from and --to options apply only to raw PSL inputs.

    Add --fail-on-impact to use it as an upgrade gate in CI. It still prints the full report, then exits unsuccessfully if any hostname in the inventory has a changed lookup outcome. A rule-only change with no effect on the supplied inventory passes; maintain an inventory representative of your deployment. The gate also fails when both the hostname and optional Cookie inventories are empty, because such a run has checked no outcomes. A Cookie-only inventory is valid when supplied intentionally.

    To audit Cookie storage as well, add a Cookie inventory. Each non-comment line contains an ASCII request host by default; an optional tab and second field specify the Cookie Domain attribute. A one-field line means that attribute is absent. For example, this invocation keeps the hostname inventory unchanged but finds three Cookie-scope changes and fails the upgrade gate:

    moon run --target native cmd/audit \ examples/audit/old.psl examples/audit/new.psl \ examples/audit/stable-hosts.txt \ --from example-v1 --to example-v2 --strict-icann \ --cookie-inventory examples/audit/cookies.tsv --fail-on-impact

    The report adds Cookie counts and changed-row CSV only when the option is present. The gate fails if either a hostname or Cookie scope changes. These inputs cover the DNS domain component of Cookie storage, not path, expiry, or Secure. Without --idna, prepare Unicode names as A-labels before using this inventory. Hostnames rejected by the core lookup syntax and malformed ASCII Cookie DNS fields fail with their source line instead of being silently counted as unchanged.

    For Unicode PSL rules and DNS names in either inventory, add --idna. The command converts raw rules, request hosts, Cookie Domain attributes, and hostname inventory entries to A-labels before comparison. In --bundles mode, the bundles must already contain A-label rules (for example, built with cmd/snapshot --idna); the flag normalizes only inventory names. Report rows show the normalized A-labels.

    moon run --target native cmd/audit \ examples/audit/old-unicode.psl examples/audit/new-unicode.psl \ examples/audit/unicode-hosts.txt \ --from old --to new --idna --strict-icann \ --cookie-inventory examples/audit/unicode-cookies.tsv --fail-on-impact

    This synthetic update changes both the registrable-domain boundary for the hostname and whether the Cookie Domain is a public suffix, so the gate exits unsuccessfully.

    #Audit a lookup-policy migration

    The native cmd/policy-audit command checks a different kind of change: it holds one verified PSL snapshot fixed and compares browser-default lookup with strict ICANN lookup over your hostname inventory. This exposes, for example, hosts affected by ignoring PRIVATE rules or rejecting unlisted suffixes.

    moon run --target native cmd/snapshot \ examples/audit/old.psl old.snapshot --revision example-v1 moon run --target native cmd/policy-audit \ old.snapshot examples/audit/hosts.txt --fail-on-impact

    The second command prints a deterministic summary and changed-row CSV, then exits unsuccessfully when an outcome changes or the inventory is empty. It preserves input order and duplicates; malformed hostnames fail with their source line. Add --idna for Unicode inventory names, provided the snapshot was built with A-label rules (for example, cmd/snapshot --idna). No PSL data is fetched by either command.

    #Classify a hostname inventory

    cmd/classify applies one verified snapshot to a line-oriented hostname file and prints a CSV row for every non-comment entry. A bad hostname stays in the output as an error row; later entries are still processed.

    moon run --target native cmd/snapshot \ examples/audit/old.psl classify.snapshot --revision example-v1 moon run --target native cmd/classify \ classify.snapshot examples/classify/hosts.txt --strict-icann

    The report includes the snapshot revision and digest, policy, and success/error counts. Add --fail-on-error to return an unsuccessful status after printing the report if any row failed or the inventory is empty. Blank and # comment lines are ignored; remaining entries retain their order and duplicates, with zero-based CSV indexes. By default, supply ASCII or pre-normalized A-label hostnames. For Unicode input, use --idna with a snapshot built from IDNA-normalized PSL rules:

    moon run --target native cmd/snapshot \ examples/idna/rules.psl unicode.snapshot \ --revision unicode-example-v1 --idna moon run --target native cmd/classify \ unicode.snapshot examples/classify/unicode-hosts.txt \ --idna --strict-icann

    The IDNA mode keeps the original input in the CSV and records its A-label form in normalized_domain. IDNA conversion failures become error rows instead of aborting the inventory.

    #Check rule coverage in a hostname sample

    The portable core can count how often each explicit PSL rule is actually selected by an application-supplied hostname sample:

    let coverage = suffixes.analyze_rule_coverage(
    ["api.example.com", "www.www.ck", "service.internal"],
    @moonsuffix.LookupOptions::browser_default(),
    )
    println(coverage.observed_rule_count())
    println(coverage.to_csv())

    The report includes every rule and section membership, even those selected zero times, and separates implicit-wildcard lookups from invalid or unlisted hosts. It is useful for checking whether a test or production sample exercises the rules you care about. A zero count means only “not observed in this sample”; it is not proof that a PSL rule is unnecessary.

    #Current API

    • SuffixList::parse compiles PSL text into a reverse-label trie.
    • lookup returns the case-normalized domain, public suffix, optional registrable domain, prevailing rule, rule kind, and source section. It keeps browser-style behavior by considering both ICANN and PRIVATE rules and falling back to *.
    • lookup_with_options supports ICANN-only or all-section matching and either an implicit wildcard or an error for unknown suffixes.
    • trace_lookup_with_options exposes every matching rule candidate, its source section, policy eligibility, and whether it won. A valid but unlisted host retains the strict-policy error alongside the trace.
    • to_psl_text exports a deterministic, parseable representation for pinned snapshots, hashing, and reproducible builds.
    • snapshot binds canonical PSL text to an opaque source revision, a SHA-256 digest, its rule count, and a deterministic line-oriented manifest.
    • Snapshot::restore verifies stored canonical PSL text against every manifest field before reconstructing a trusted snapshot.
    • Snapshot::bundle_text and restore_bundle_text store the manifest and canonical rules in one strictly verified text artifact.
    • cmd/snapshot builds a single-file snapshot from a local PSL source and an explicit revision without overwriting existing output. Its --idna mode normalizes Unicode rules to A-labels first.
    • cmd/lookup verifies a stored snapshot before classifying one hostname, with optional IDNA and strict ICANN lookup policies.
    • Snapshot::diff produces deterministic additions, removals, and unambiguous ICANN/PRIVATE section moves between two snapshots.
    • Snapshot::analyze_impact evaluates a hostname inventory against old and new snapshots, classifies only changed outcomes, and exports deterministic CSV.
    • analyze_policy_impact compares two lookup policies over one hostname inventory, exposing domains affected by a stricter deployment policy.
    • analyze_rule_coverage counts selected explicit rules by section and distinguishes implicit, unlisted, and invalid inputs in a deterministic CSV.
    • lookup_batch and lookup_batch_with_options preserve input order and retain invalid hostnames as row-level errors; BatchReport::to_csv exports every row.
    • site_key and same_registrable_site expose canonical registrable-hostname boundaries for Cookie policy and hostname-level same-site integration.
    • schemeful_site creates an HTTP(S) site identity from an ASCII hostname and compares both scheme and registrable domain, including PRIVATE PSL rules.
    • resolve_cookie_scope converts an optional Cookie Domain attribute into its canonical stored domain and host-only flag, rejecting public-suffix scope escalation, unrelated domains, and malformed non-ASCII server values.
    • shareable_cookie_scopes enumerates valid Domain Cookie scopes from the request host down to its registrable boundary under the selected PSL policy.
    • CookieScope::matches_host checks whether that stored domain scope covers a later request hostname, distinguishing host-only and Domain cookies.
    • Snapshot::analyze_cookie_scope_impact tests an ordered inventory of request hosts and Cookie Domain attributes against old and candidate PSL snapshots, reporting changed acceptance or stored scopes as deterministic CSV.
    • CookieScopeInput::validate_syntax checks ASCII request-host and Domain syntax independently of PSL policy, for fail-closed inventory ingestion.
    • cmd/audit joins snapshots, semantic rule diffs, and hostname impact analysis into a native, local-file workflow with deterministic text and CSV output. It can verify stored bundles and audit an optional Cookie inventory; --fail-on-impact can block a candidate update in CI.
    • cmd/policy-audit compares browser-default and strict ICANN lookup against one verified snapshot and can gate a policy migration in CI.
    • cmd/classify turns a hostname inventory into ordered batch-lookup CSV, preserving row-level errors and optionally failing a CI input-quality gate.
    • idna converts Unicode PSL rules and hostnames with UTS #46 before querying the core; returned domain strings are A-labels. Its batch API preserves Unicode inputs and IDNA errors as ordered rows with deterministic CSV. It also resolves Unicode Cookie scopes and matches later Unicode request hosts.
    • verify_psl_test_file runs upstream-style checkPublicSuffix cases, retains ordered mismatch diagnostics, and exports failures as deterministic CSV.
    • public_suffix, registrable_domain, and is_public_suffix provide focused convenience queries.
    • Rules may be exact (co.uk), wildcard (*.ck), or exception (!www.ck).
    • Unknown suffixes use the PSL algorithm's implicit * rule.
    • A single trailing root dot is preserved in returned domain strings.

    #Deliberate v0.1 boundaries

    • No PSL snapshot is bundled. Applications inject a pinned or freshly fetched list, so data freshness and MPL-2.0 obligations remain explicit.
    • Snapshot revision labels are supplied by the caller. SHA-256 detects canonical content changes but does not authenticate the source or fetch upstream data.
    • The core lowercases rules and hostnames but does not perform IDNA conversion. Use the separate idna adapter for Unicode input; its results are ASCII A-labels and do not preserve the original display spelling.
    • Callers must extract a hostname before lookup; URLs and IP literals are outside this API's input contract.

    #Roadmap

    1. Continue measuring performance on representative application inventories and track regressions across toolchain updates.

    #Validation

    moon fmt moon info moon check --target wasm --deny-warn moon test --target wasm --deny-warn moon test --target wasm-gc --deny-warn moon test --target js --deny-warn moon run cmd/main moon run examples/idna moon check cmd/audit --target native --deny-warn moon test cmd/audit --target native --deny-warn moon check cmd/snapshot --target native --deny-warn moon test cmd/snapshot --target native --deny-warn moon check cmd/lookup --target native --deny-warn moon test cmd/lookup --target native --deny-warn moon check cmd/conformance --target native --deny-warn moon test cmd/conformance --target native --deny-warn moon check cmd/policy-audit --target native --deny-warn moon test cmd/policy-audit --target native --deny-warn moon check cmd/classify --target native --deny-warn moon test cmd/classify --target native --deny-warn moon check cmd/bench --target native --deny-warn moon test cmd/bench --target native --deny-warn moon check cmd/embed --target native --deny-warn moon test cmd/embed --target native --deny-warn

    See DESIGN.md, ECOSYSTEM.md, and SOURCES.md for design boundaries and evidence.

    #License

    MoonSuffix source code is licensed under Apache-2.0. No copy of the PSL data is distributed in this repository. The upstream list has its own MPL-2.0 license; the upstream conformance test is dedicated to the public domain under CC0.

    Before publishing, inspect moon package --list; the archive intentionally excludes the local MoonSuffix.md contest document. Publishing also requires logging into Mooncakes as the cn-wn account named in moon.mod. From a repository checkout, run moon run tools/release-preflight.mbtx for a non-publishing Mooncakes validation. The script checks the login and calls moon publish --dry-run. With the tested moon 0.1.20260920 / mooncake-bin 0.1.20260911 toolchain, the server confirms the dry run with HTTP 202 but the CLI exits nonzero. The script accepts only that explicit confirmation for cn-wn/moonsuffix and never invokes a real publish.

    BatchItem

    pub struct BatchItem {
    index_ : Int
    input_ : String
    result_ : Result[Lookup, DomainError]
    }

    One ordered result in a batch hostname lookup.

    BatchItem::index

    fn BatchItem::index(self : BatchItem) -> Int

    BatchItem::input

    fn BatchItem::input(self : BatchItem) -> String

    BatchItem::result

    fn BatchItem::result(self : BatchItem) -> Result[Lookup, DomainError]

    BatchReport

    pub struct BatchReport {
    items_ : ReadOnlyArray[BatchItem]
    success_count_ : Int
    error_count_ : Int
    }

    Ordered results and summary counts for a recoverable batch lookup.

    BatchReport::error_count

    fn BatchReport::error_count(self : BatchReport) -> Int

    BatchReport::items

    fn BatchReport::items(self : BatchReport) -> ReadOnlyArray[BatchItem]

    BatchReport::length

    fn BatchReport::length(self : BatchReport) -> Int

    BatchReport::success_count

    fn BatchReport::success_count(self : BatchReport) -> Int

    BatchReport::to_csv

    fn BatchReport::to_csv(self : BatchReport) -> String

    Export the complete batch to deterministic CSV with RFC 4180-style field quoting.

    All fields are quoted, the input order is preserved, and output uses LF line endings with a final newline.

    ConformanceActual

    pub(all) enum ConformanceActual {
    NullInput
    RejectedDomain(DomainError)
    RejectedHostname(String)
    NoRegistrableDomain
    RegistrableDomain(String)
    } derive(Eq,
    Debug
    )

    The observable result produced for one conformance input.

    ConformanceActual::equal

    ConformanceActual::not_equal

    fn ConformanceActual::not_equal(x : ConformanceActual, y : ConformanceActual) -> Bool

    ConformanceFailure

    pub struct ConformanceFailure {
    line_ : Int
    input_ : String?
    expected_ : String?
    actual_ : ConformanceActual
    }

    One conformance case whose actual result differs from its expectation.

    ConformanceFailure::actual

    ConformanceFailure::expected

    fn ConformanceFailure::expected(self : ConformanceFailure) -> String?

    ConformanceFailure::input

    fn ConformanceFailure::input(self : ConformanceFailure) -> String?

    ConformanceFailure::line

    fn ConformanceFailure::line(self : ConformanceFailure) -> Int

    ConformanceParseError

    pub(all) enum ConformanceParseError {
    InvalidConformanceCase(Int, String, String)
    } derive(Eq,
    Debug
    )

    An invalid non-comment line in a PSL conformance test file.

    ConformanceParseError::equal

    ConformanceParseError::message

    fn ConformanceParseError::message(self : ConformanceParseError) -> String

    ConformanceParseError::not_equal

    ConformanceReport

    pub struct ConformanceReport {
    total_count_ : Int
    passed_count_ : Int
    failures_ : ReadOnlyArray[ConformanceFailure]
    }

    Summary and ordered failures from an upstream-style PSL conformance run.

    ConformanceReport::failed_count

    fn ConformanceReport::failed_count(self : ConformanceReport) -> Int

    ConformanceReport::failures

    fn ConformanceReport::failures(self : ConformanceReport) -> ReadOnlyArray[ConformanceFailure]

    ConformanceReport::is_success

    fn ConformanceReport::is_success(self : ConformanceReport) -> Bool

    ConformanceReport::passed_count

    fn ConformanceReport::passed_count(self : ConformanceReport) -> Int

    ConformanceReport::to_csv

    fn ConformanceReport::to_csv(self : ConformanceReport) -> String

    Export failed cases in source order as deterministic, fully quoted CSV.

    ConformanceReport::total_count

    fn ConformanceReport::total_count(self : ConformanceReport) -> Int

    CookieScope

    pub struct CookieScope {
    domain_ : String
    host_only_ : Bool
    } derive(Eq,
    Debug
    )

    The hostname scope a user agent would store for a cookie.

    CookieScope::domain

    fn CookieScope::domain(self : CookieScope) -> String

    Return the canonical ASCII domain stored with the cookie.

    CookieScope::equal

    fn CookieScope::equal(CookieScope, CookieScope) -> Bool

    CookieScope::is_host_only

    fn CookieScope::is_host_only(self : CookieScope) -> Bool

    Return whether the cookie is restricted to exactly the request host.

    CookieScope::matches_host

    fn CookieScope::matches_host(self : CookieScope, request_host : String) -> Result[Bool, CookieScopeError]

    Test only the domain component of Cookie delivery to a request host.

    A host-only cookie matches exactly one canonical DNS hostname. A Domain cookie also matches descendants at a label boundary. The caller must separately enforce path, Secure, expiration, and other Cookie attributes.

    CookieScope::not_equal

    fn CookieScope::not_equal(x : CookieScope, y : CookieScope) -> Bool

    CookieScopeError

    pub(all) enum CookieScopeError {
    InvalidRequestHost(String, String)
    InvalidDomainAttribute(String, String)
    PublicSuffixDomain(String)
    DomainMismatch(String, String)
    } derive(Eq,
    Debug
    )

    A request host or Domain attribute that cannot produce a safe cookie scope.

    CookieScopeError::equal

    CookieScopeError::message

    fn CookieScopeError::message(self : CookieScopeError) -> String

    CookieScopeError::not_equal

    fn CookieScopeError::not_equal(x : CookieScopeError, y : CookieScopeError) -> Bool

    CookieScopeImpact

    pub struct CookieScopeImpact {
    index_ : Int
    input_ : CookieScopeInput
    kind_ : CookieScopeImpactKind
    before_ : Result[CookieScope, CookieScopeError]
    after_ : Result[CookieScope, CookieScopeError]
    }

    CookieScopeImpact::after

    CookieScopeImpact::before

    CookieScopeImpact::index

    fn CookieScopeImpact::index(self : CookieScopeImpact) -> Int

    CookieScopeImpact::input

    CookieScopeImpact::kind

    CookieScopeImpactKind

    pub(all) enum CookieScopeImpactKind {
    CookieBecameAccepted
    CookieBecameRejected
    CookieScopeChanged
    }

    A behavioral change in Cookie domain acceptance or stored scope.

    CookieScopeImpactReport

    pub struct CookieScopeImpactReport {
    from_revision_ : String
    to_revision_ : String
    from_sha256_ : String
    to_sha256_ : String
    scanned_count_ : Int
    unchanged_count_ : Int
    impacts_ : ReadOnlyArray[CookieScopeImpact]
    }

    Ordered Cookie-scope changes caused by replacing a pinned PSL snapshot.

    CookieScopeImpactReport::changed_count

    fn CookieScopeImpactReport::changed_count(self : CookieScopeImpactReport) -> Int

    CookieScopeImpactReport::from_revision

    fn CookieScopeImpactReport::from_revision(self : CookieScopeImpactReport) -> String

    CookieScopeImpactReport::from_sha256

    fn CookieScopeImpactReport::from_sha256(self : CookieScopeImpactReport) -> String

    CookieScopeImpactReport::impacts

    CookieScopeImpactReport::is_empty

    CookieScopeImpactReport::scanned_count

    fn CookieScopeImpactReport::scanned_count(self : CookieScopeImpactReport) -> Int

    CookieScopeImpactReport::to_csv

    Export changed rows to deterministic, fully quoted CSV. A separate presence field distinguishes a missing Domain attribute from an empty one.

    CookieScopeImpactReport::to_revision

    fn CookieScopeImpactReport::to_revision(self : CookieScopeImpactReport) -> String

    CookieScopeImpactReport::to_sha256

    fn CookieScopeImpactReport::to_sha256(self : CookieScopeImpactReport) -> String

    CookieScopeImpactReport::unchanged_count

    fn CookieScopeImpactReport::unchanged_count(self : CookieScopeImpactReport) -> Int

    CookieScopeInput

    pub struct CookieScopeInput {
    request_host_ : String
    domain_attribute_ : String?
    }

    One Cookie domain-scope decision to re-evaluate across PSL snapshots. Hosts and Domain attributes must already be ASCII/A-label DNS names.

    CookieScopeInput::domain_attribute

    fn CookieScopeInput::domain_attribute(self : CookieScopeInput) -> String?

    CookieScopeInput::new

    fn CookieScopeInput::new(request_host : String, domain_attribute : String?) -> CookieScopeInput

    CookieScopeInput::request_host

    fn CookieScopeInput::request_host(self : CookieScopeInput) -> String

    CookieScopeInput::validate_syntax

    fn CookieScopeInput::validate_syntax(self : CookieScopeInput) -> Result[Unit, CookieScopeError]

    Check ASCII DNS syntax without consulting a PSL or applying lookup policy. A syntactically valid Domain attribute may still be rejected later because it is a public suffix or does not domain-match the request host.

    DomainError

    pub(all) enum DomainError {
    EmptyDomain
    EmptyLabel(Int)
    InvalidDomainCharacter(Int, Char)
    UnlistedSuffix(String)
    } derive(Eq,
    Debug
    )

    A hostname that cannot be split into valid lookup labels.

    DomainError::equal

    fn DomainError::equal(DomainError, DomainError) -> Bool

    DomainError::message

    fn DomainError::message(self : DomainError) -> String

    DomainError::not_equal

    fn DomainError::not_equal(x : DomainError, y : DomainError) -> Bool

    DomainImpact

    pub struct DomainImpact {
    index_ : Int
    input_ : String
    kind_ : ImpactKind
    before_ : Result[Lookup, DomainError]
    after_ : Result[Lookup, DomainError]
    }

    One changed hostname in a snapshot impact analysis.

    DomainImpact::after

    fn DomainImpact::after(self : DomainImpact) -> Result[Lookup, DomainError]

    DomainImpact::before

    fn DomainImpact::before(self : DomainImpact) -> Result[Lookup, DomainError]

    DomainImpact::index

    fn DomainImpact::index(self : DomainImpact) -> Int

    DomainImpact::input

    fn DomainImpact::input(self : DomainImpact) -> String

    DomainImpact::kind

    ImpactKind

    pub(all) enum ImpactKind {
    BecameAccepted
    BecameRejected
    BoundaryChanged
    RuleMetadataChanged
    } derive(Eq,
    Debug
    )

    The externally visible effect of a PSL snapshot change on one hostname.

    ImpactKind::equal

    fn ImpactKind::equal(ImpactKind, ImpactKind) -> Bool

    ImpactKind::not_equal

    fn ImpactKind::not_equal(x : ImpactKind, y : ImpactKind) -> Bool

    ListParseError

    pub(all) enum ListParseError {
    InvalidRule(Int, String, String)
    } derive(Eq,
    Debug
    )

    A malformed rule in caller-supplied PSL text.

    ListParseError::equal

    ListParseError::message

    fn ListParseError::message(self : ListParseError) -> String

    ListParseError::not_equal

    fn ListParseError::not_equal(x : ListParseError, y : ListParseError) -> Bool

    Lookup

    pub struct Lookup {
    normalized_domain : String
    public_suffix : String
    registrable_domain : String?
    matched_rule : String
    rule_kind : RuleKind
    rule_section : RuleSection?
    }

    An explainable public-suffix lookup result.

    normalized_domain is case-normalized only. Callers are responsible for applying the same IDNA profile to the list and hostname before lookup.

    Lookup::matched_rule

    fn Lookup::matched_rule(self : Lookup) -> String

    Return the prevailing PSL rule, including *. or ! when applicable.

    Lookup::normalized_domain

    fn Lookup::normalized_domain(self : Lookup) -> String

    Return the case-normalized input domain, preserving one trailing root dot.

    Lookup::public_suffix

    fn Lookup::public_suffix(self : Lookup) -> String

    Return the public suffix selected by the prevailing rule.

    Lookup::registrable_domain

    fn Lookup::registrable_domain(self : Lookup) -> String?

    Return the public suffix plus one label, if that label exists.

    Lookup::rule_kind

    fn Lookup::rule_kind(self : Lookup) -> RuleKind

    Return the kind of the prevailing rule.

    Lookup::rule_section

    fn Lookup::rule_section(self : Lookup) -> RuleSection?

    Return the source section of the prevailing explicit rule. The implicit default wildcard has no source section.

    LookupOptions

    pub struct LookupOptions {
    scope : SectionScope
    unknown_suffix : UnknownSuffixPolicy
    }

    Explicit policy controls for a public-suffix lookup.

    LookupOptions::browser_default

    fn LookupOptions::browser_default() -> LookupOptions

    Return browser-style defaults: ICANN and PRIVATE rules plus implicit *.

    LookupOptions::new

    fn LookupOptions::new(scope : SectionScope, unknown_suffix : UnknownSuffixPolicy) -> LookupOptions

    Construct lookup options from an eligible-section scope and unknown-suffix policy.

    LookupOptions::section_scope

    fn LookupOptions::section_scope(self : LookupOptions) -> SectionScope

    LookupOptions::strict_icann

    fn LookupOptions::strict_icann() -> LookupOptions

    Return a conservative policy that accepts only listed ICANN suffixes.

    LookupOptions::unknown_suffix_policy

    fn LookupOptions::unknown_suffix_policy(self : LookupOptions) -> UnknownSuffixPolicy

    LookupTrace

    pub struct LookupTrace {
    outcome_ : Result[Lookup, DomainError]
    candidates_ : ReadOnlyArray[RuleCandidate]
    }

    The rule-selection evidence for one syntactically valid hostname. A strict lookup with no listed suffix has an error outcome, but still exposes the implicit wildcard as an excluded candidate.

    LookupTrace::candidates

    fn LookupTrace::candidates(self : LookupTrace) -> ReadOnlyArray[RuleCandidate]

    Candidates are ordered by suffix depth, then exact, wildcard, exception, and finally by ICANN, PRIVATE, unsectioned membership. The implicit * is first. This order does not depend on PSL input order.

    LookupTrace::outcome

    fn LookupTrace::outcome(self : LookupTrace) -> Result[Lookup, DomainError]

    PolicyImpactReport

    pub struct PolicyImpactReport {
    before_options_ : LookupOptions
    after_options_ : LookupOptions
    scanned_count_ : Int
    unchanged_count_ : Int
    impacts_ : ReadOnlyArray[DomainImpact]
    }

    Ordered hostname changes caused by switching lookup policies.

    PolicyImpactReport::after_options

    PolicyImpactReport::before_options

    PolicyImpactReport::changed_count

    fn PolicyImpactReport::changed_count(self : PolicyImpactReport) -> Int

    PolicyImpactReport::impacts

    fn PolicyImpactReport::impacts(self : PolicyImpactReport) -> ReadOnlyArray[DomainImpact]

    PolicyImpactReport::is_empty

    fn PolicyImpactReport::is_empty(self : PolicyImpactReport) -> Bool

    PolicyImpactReport::scanned_count

    fn PolicyImpactReport::scanned_count(self : PolicyImpactReport) -> Int

    PolicyImpactReport::to_csv

    fn PolicyImpactReport::to_csv(self : PolicyImpactReport) -> String

    Export only policy-sensitive rows as deterministic, fully quoted CSV.

    PolicyImpactReport::unchanged_count

    fn PolicyImpactReport::unchanged_count(self : PolicyImpactReport) -> Int

    RuleCandidate

    pub struct RuleCandidate {
    rule_ : String
    kind_ : RuleKind
    section_ : RuleSection?
    eligible_ : Bool
    selected_ : Bool
    }

    One rule that matches a hostname's suffix path. A candidate may be excluded by the selected section policy, or lose to a longer rule or an exception. The implicit * candidate has no source section.

    RuleCandidate::is_eligible

    fn RuleCandidate::is_eligible(self : RuleCandidate) -> Bool

    RuleCandidate::is_selected

    fn RuleCandidate::is_selected(self : RuleCandidate) -> Bool

    RuleCandidate::kind

    RuleCandidate::rule

    fn RuleCandidate::rule(self : RuleCandidate) -> String

    RuleCandidate::section

    fn RuleCandidate::section(self : RuleCandidate) -> RuleSection?

    RuleCoverageEntry

    pub struct RuleCoverageEntry {
    rule_ : String
    section_ : RuleSection
    kind_ : RuleKind
    selected_count_ : Int
    }

    One explicit PSL rule membership and its observed selection count.

    RuleCoverageEntry::kind

    RuleCoverageEntry::rule

    fn RuleCoverageEntry::rule(self : RuleCoverageEntry) -> String

    RuleCoverageEntry::section

    RuleCoverageEntry::selected_count

    fn RuleCoverageEntry::selected_count(self : RuleCoverageEntry) -> Int

    RuleCoverageReport

    pub struct RuleCoverageReport {
    entries_ : ReadOnlyArray[RuleCoverageEntry]
    scanned_count_ : Int
    explicit_count_ : Int
    implicit_count_ : Int
    unlisted_count_ : Int
    invalid_count_ : Int
    observed_rule_count_ : Int
    }

    Selection frequency of every explicit rule for one hostname inventory.

    RuleCoverageReport::entries

    fn RuleCoverageReport::entries(self : RuleCoverageReport) -> ReadOnlyArray[RuleCoverageEntry]

    RuleCoverageReport::explicit_count

    fn RuleCoverageReport::explicit_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::implicit_count

    fn RuleCoverageReport::implicit_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::invalid_count

    fn RuleCoverageReport::invalid_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::observed_rule_count

    fn RuleCoverageReport::observed_rule_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::scanned_count

    fn RuleCoverageReport::scanned_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::to_csv

    fn RuleCoverageReport::to_csv(self : RuleCoverageReport) -> String

    Export every explicit rule membership, including unobserved ones, as CSV.

    RuleCoverageReport::unlisted_count

    fn RuleCoverageReport::unlisted_count(self : RuleCoverageReport) -> Int

    RuleCoverageReport::unobserved_rule_count

    fn RuleCoverageReport::unobserved_rule_count(self : RuleCoverageReport) -> Int

    RuleKind

    pub(all) enum RuleKind {
    DefaultRule
    ExactRule
    WildcardRule
    ExceptionRule
    } derive(Eq,
    Debug
    )

    The kind of rule selected by the Public Suffix List algorithm.

    RuleKind::equal

    fn RuleKind::equal(RuleKind, RuleKind) -> Bool

    RuleKind::not_equal

    fn RuleKind::not_equal(x : RuleKind, y : RuleKind) -> Bool

    RuleKind::to_repr

    RuleSection

    pub(all) enum RuleSection {
    UnsectionedRule
    IcannSection
    PrivateSection
    } derive(Eq,
    Debug
    )

    The PSL source section that supplied a prevailing rule.

    RuleSection::equal

    fn RuleSection::equal(RuleSection, RuleSection) -> Bool

    RuleSection::not_equal

    fn RuleSection::not_equal(x : RuleSection, y : RuleSection) -> Bool

    SchemefulSite

    pub struct SchemefulSite {
    scheme_ : String
    registrable_domain_ : String
    }

    The scheme and registrable domain of a DNS-backed web site.

    Ports and paths do not affect the site identity. The hostname must already be in ASCII/A-label form; use the IDNA adapter before constructing a site from a Unicode hostname.

    SchemefulSite::key

    fn SchemefulSite::key(self : SchemefulSite) -> String

    SchemefulSite::registrable_domain

    fn SchemefulSite::registrable_domain(self : SchemefulSite) -> String

    SchemefulSite::same_site

    fn SchemefulSite::same_site(self : SchemefulSite, other : SchemefulSite) -> Bool

    SchemefulSite::scheme

    fn SchemefulSite::scheme(self : SchemefulSite) -> String

    SectionScope

    pub(all) enum SectionScope {
    IcannAndPrivate
    IcannOnly
    } derive(Eq,
    Debug
    )

    The PSL sections eligible for a lookup.

    SectionScope::equal

    SectionScope::not_equal

    fn SectionScope::not_equal(x : SectionScope, y : SectionScope) -> Bool

    SiteError

    pub(all) enum SiteError {
    InvalidSiteDomain(DomainError)
    NoRegistrableDomain(String)
    InvalidSiteScheme(String)
    InvalidSiteHost(String, String)
    } derive(Eq,
    Debug
    )

    A domain that cannot identify a registrable site.

    SiteError::equal

    fn SiteError::equal(SiteError, SiteError) -> Bool

    SiteError::message

    fn SiteError::message(self : SiteError) -> String

    SiteError::not_equal

    fn SiteError::not_equal(x : SiteError, y : SiteError) -> Bool

    Snapshot

    pub struct Snapshot {
    source_revision_ : String
    psl_text_ : String
    sha256_ : String
    rule_count_ : Int
    }

    A reproducible canonical PSL snapshot and its provenance metadata.
    fn Snapshot::analyze_cookie_scope_impact(self : Snapshot, newer : Snapshot, inputs : Array[CookieScopeInput], options : LookupOptions) -> CookieScopeImpactReport

    Re-evaluate an ordered inventory of Cookie Domain decisions under two verified snapshots using the same lookup policy.

    Changes to Cookie acceptance or the stored domain/host-only flag are reported. Two rejections count as unchanged even if their error text differs. Invalid inputs are retained as row-level outcomes and do not stop the scan.

    Snapshot::analyze_impact

    fn Snapshot::analyze_impact(self : Snapshot, newer : Snapshot, domains : Array[String], options : LookupOptions) -> SnapshotImpact

    Compare the lookup outcome for every hostname under the same policy.

    Unchanged rows are counted but omitted. Changed rows retain caller order, duplicate inputs, their original index, and both lookup outcomes.

    Snapshot::bundle_text

    fn Snapshot::bundle_text(self : Snapshot) -> String

    Store the versioned manifest and canonical PSL in one deterministic file.

    One empty line separates the four-field manifest from the PSL text. The revision and digest are verified when the bundle is restored.

    Snapshot::compare_to

    fn Snapshot::compare_to(self : Snapshot, newer : Snapshot) -> SnapshotComparison

    Compile both snapshots for repeated impact analyses.

    Snapshots can only be created from parsed rules or restored with manifest, canonical-text, digest, and rule-count verification.

    Snapshot::diff

    fn Snapshot::diff(self : Snapshot, newer : Snapshot) -> SnapshotDiff

    Compare this snapshot with a newer snapshot.

    A rule is classified as moved only when exactly one old section disappears and exactly one new section appears. Ambiguous multi-section changes remain explicit additions and removals.

    Snapshot::manifest_text

    fn Snapshot::manifest_text(self : Snapshot) -> String

    Return a deterministic line-oriented manifest. The source revision is percent-encoded as UTF-8 so arbitrary labels cannot alter its structure.

    Snapshot::psl_text

    fn Snapshot::psl_text(self : Snapshot) -> String

    Snapshot::restore

    fn Snapshot::restore(psl_text : String, manifest : String) -> Result[Snapshot, SnapshotLoadError]

    Restore a snapshot from canonical PSL text and its stored manifest.

    The manifest structure, revision encoding, digest, rule count, PSL syntax, and canonical byte representation are all verified before a Snapshot is returned.

    Snapshot::restore_bundle_text

    fn Snapshot::restore_bundle_text(bundle : String) -> Result[Snapshot, SnapshotLoadError]

    Restore and verify a single-file snapshot bundle.

    The first blank line ends the manifest. The remainder must be the exact canonical PSL bytes whose digest and rule count appear in the manifest.

    Snapshot::restore_compiled_bundle_text

    fn Snapshot::restore_compiled_bundle_text(bundle : String) -> Result[(Snapshot, SuffixList), SnapshotLoadError]

    Restore a verified bundle together with its already parsed rule table. This avoids parsing the PSL a second time when the caller needs lookups.

    Snapshot::rule_count

    fn Snapshot::rule_count(self : Snapshot) -> Int

    Snapshot::sha256

    fn Snapshot::sha256(self : Snapshot) -> String

    Snapshot::source_revision

    fn Snapshot::source_revision(self : Snapshot) -> String

    Snapshot::to_moonbit_source

    fn Snapshot::to_moonbit_source(self : Snapshot) -> String

    Generate a MoonBit source file that embeds this verified snapshot without runtime file-system or network access. The generated function returns a Result so a corrupt or edited bundle cannot silently become trusted.

    The generated file contains PSL-derived data; redistributors remain responsible for the upstream list's MPL-2.0 license obligations.

    SnapshotChange

    pub(all) enum SnapshotChange {
    AddedRule(String, RuleSection)
    RemovedRule(String, RuleSection)
    MovedRule(String, RuleSection, RuleSection)
    } derive(Eq,
    Debug
    )

    A semantic change between two canonical PSL snapshots.

    SnapshotChange::equal

    SnapshotChange::not_equal

    fn SnapshotChange::not_equal(x : SnapshotChange, y : SnapshotChange) -> Bool

    SnapshotComparison

    pub struct SnapshotComparison {
    older_ : Snapshot
    newer_ : Snapshot
    before_list_ : SuffixList
    after_list_ : SuffixList
    }

    A reusable comparison of two pinned PSL snapshots. Both rule sets are compiled once, so hostname and Cookie inventories can share them.
    fn SnapshotComparison::analyze_cookie_scope_impact(self : SnapshotComparison, inputs : Array[CookieScopeInput], options : LookupOptions) -> CookieScopeImpactReport

    Compare inventoried Cookie Domain decisions without recompiling rules.

    SnapshotComparison::analyze_impact

    fn SnapshotComparison::analyze_impact(self : SnapshotComparison, domains : Array[String], options : LookupOptions) -> SnapshotImpact

    Compare hostname outcomes without recompiling either rule set.

    SnapshotDiff

    pub struct SnapshotDiff {
    from_revision_ : String
    to_revision_ : String
    from_sha256_ : String
    to_sha256_ : String
    changes_ : ReadOnlyArray[SnapshotChange]
    added_count_ : Int
    removed_count_ : Int
    moved_count_ : Int
    }

    A deterministic semantic comparison between two snapshots.

    SnapshotDiff::added_count

    fn SnapshotDiff::added_count(self : SnapshotDiff) -> Int

    SnapshotDiff::changes

    fn SnapshotDiff::changes(self : SnapshotDiff) -> ReadOnlyArray[SnapshotChange]

    SnapshotDiff::from_revision

    fn SnapshotDiff::from_revision(self : SnapshotDiff) -> String

    SnapshotDiff::from_sha256

    fn SnapshotDiff::from_sha256(self : SnapshotDiff) -> String

    SnapshotDiff::is_empty

    fn SnapshotDiff::is_empty(self : SnapshotDiff) -> Bool

    SnapshotDiff::moved_count

    fn SnapshotDiff::moved_count(self : SnapshotDiff) -> Int

    SnapshotDiff::removed_count

    fn SnapshotDiff::removed_count(self : SnapshotDiff) -> Int

    SnapshotDiff::report_text

    fn SnapshotDiff::report_text(self : SnapshotDiff) -> String

    Return a deterministic, line-oriented semantic change report.

    SnapshotDiff::to_revision

    fn SnapshotDiff::to_revision(self : SnapshotDiff) -> String

    SnapshotDiff::to_sha256

    fn SnapshotDiff::to_sha256(self : SnapshotDiff) -> String

    SnapshotImpact

    pub struct SnapshotImpact {
    from_revision_ : String
    to_revision_ : String
    from_sha256_ : String
    to_sha256_ : String
    scanned_count_ : Int
    unchanged_count_ : Int
    impacts_ : ReadOnlyArray[DomainImpact]
    }

    Ordered hostname changes caused by replacing one snapshot with another.

    SnapshotImpact::changed_count

    fn SnapshotImpact::changed_count(self : SnapshotImpact) -> Int

    SnapshotImpact::from_revision

    fn SnapshotImpact::from_revision(self : SnapshotImpact) -> String

    SnapshotImpact::from_sha256

    fn SnapshotImpact::from_sha256(self : SnapshotImpact) -> String

    SnapshotImpact::impacts

    fn SnapshotImpact::impacts(self : SnapshotImpact) -> ReadOnlyArray[DomainImpact]

    SnapshotImpact::is_empty

    fn SnapshotImpact::is_empty(self : SnapshotImpact) -> Bool

    SnapshotImpact::scanned_count

    fn SnapshotImpact::scanned_count(self : SnapshotImpact) -> Int

    SnapshotImpact::to_csv

    fn SnapshotImpact::to_csv(self : SnapshotImpact) -> String

    Export only changed rows as deterministic, fully quoted CSV.

    SnapshotImpact::to_revision

    fn SnapshotImpact::to_revision(self : SnapshotImpact) -> String

    SnapshotImpact::to_sha256

    fn SnapshotImpact::to_sha256(self : SnapshotImpact) -> String

    SnapshotImpact::unchanged_count

    fn SnapshotImpact::unchanged_count(self : SnapshotImpact) -> Int

    SnapshotLoadError

    pub(all) enum SnapshotLoadError {
    InvalidManifest(String)
    InvalidRevisionEncoding
    InvalidDigest(String)
    InvalidRuleCount(String)
    InvalidSnapshotRules(ListParseError)
    NonCanonicalPsl
    DigestMismatch(String, String)
    RuleCountMismatch(Int, Int)
    } derive(Eq,
    Debug
    )

    A stored snapshot whose PSL text or manifest cannot be trusted.

    SnapshotLoadError::equal

    SnapshotLoadError::message

    fn SnapshotLoadError::message(self : SnapshotLoadError) -> String

    SnapshotLoadError::not_equal

    fn SnapshotLoadError::not_equal(x : SnapshotLoadError, y : SnapshotLoadError) -> Bool

    SuffixList

    pub struct SuffixList {
    children : Array[Map[String, Int]]
    exact_sections : Array[Array[RuleSection]]
    wildcard_sections : Array[Array[RuleSection]]
    exception_sections : Array[Array[RuleSection]]
    rule_count_ : Int
    }

    A compiled, immutable-by-API Public Suffix List rule set.

    SuffixList::analyze_policy_impact

    fn SuffixList::analyze_policy_impact(self : SuffixList, domains : Array[String], before_options : LookupOptions, after_options : LookupOptions) -> PolicyImpactReport

    Compare each hostname under two lookup policies against the same rule set.

    Unchanged rows are counted but omitted. Changed rows preserve caller order, duplicates, their original index, and both lookup outcomes.

    SuffixList::analyze_rule_coverage

    fn SuffixList::analyze_rule_coverage(self : SuffixList, domains : Array[String], options : LookupOptions) -> RuleCoverageReport

    Count which explicit rules prevail for a hostname inventory.

    Each rule-section membership is counted separately. Invalid hostnames and unlisted suffixes are counted but do not interrupt later inputs. A zero count means only that the supplied inventory did not select that rule; it does not prove the rule is unreachable or unnecessary.

    SuffixList::is_public_suffix

    fn SuffixList::is_public_suffix(self : SuffixList, domain : String) -> Result[Bool, DomainError]

    SuffixList::lookup

    fn SuffixList::lookup(self : SuffixList, domain : String) -> Result[Lookup, DomainError]

    Apply the PSL prevailing-rule algorithm to a hostname.

    This method performs case normalization, not IDNA conversion. The hostname and parsed list must already use the same U-label or A-label representation.

    SuffixList::lookup_batch

    fn SuffixList::lookup_batch(self : SuffixList, domains : Array[String]) -> BatchReport

    Look up hostnames in input order with browser-style defaults.

    Invalid inputs are retained as row-level errors and do not stop the batch.

    SuffixList::lookup_batch_with_options

    fn SuffixList::lookup_batch_with_options(self : SuffixList, domains : Array[String], options : LookupOptions) -> BatchReport

    Look up hostnames in input order with explicit lookup options.

    Invalid inputs are retained as row-level errors and do not stop the batch.

    SuffixList::lookup_with_options

    fn SuffixList::lookup_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[Lookup, DomainError]

    Apply the PSL prevailing-rule algorithm with explicit section and unknown-suffix policies.

    Unsectioned rules remain eligible in both scopes so compact and custom rule sets keep their expected behavior. This method performs case normalization, not IDNA conversion.

    SuffixList::node_count

    fn SuffixList::node_count(self : SuffixList) -> Int

    Return the number of trie nodes used by this compiled list.

    SuffixList::parse

    fn SuffixList::parse(source : String) -> Result[SuffixList, ListParseError]

    Parse caller-supplied Public Suffix List text into a reverse-label trie.

    Blank lines and comment lines are ignored. A rule ends at the first whitespace, matching the PSL text format and allowing trailing comments.

    SuffixList::public_suffix

    fn SuffixList::public_suffix(self : SuffixList, domain : String) -> Result[String, DomainError]

    SuffixList::registrable_domain

    fn SuffixList::registrable_domain(self : SuffixList, domain : String) -> Result[String?, DomainError]

    fn SuffixList::resolve_cookie_scope(self : SuffixList, request_host : String, domain_attribute : String?) -> Result[CookieScope, CookieScopeError]

    Resolve an optional Cookie Domain attribute using browser-style PSL policy.

    The inputs must be already extracted DNS hostnames. A missing Domain attribute produces a host-only scope. A present attribute may have one compatibility leading dot and must be an ASCII DNS name.
    fn SuffixList::resolve_cookie_scope_with_options(self : SuffixList, request_host : String, domain_attribute : String?, options : LookupOptions) -> Result[CookieScope, CookieScopeError]

    Resolve a Cookie Domain attribute under an explicit PSL lookup policy.

    SuffixList::rule_count

    fn SuffixList::rule_count(self : SuffixList) -> Int

    Return the number of distinct rules compiled into this list.

    SuffixList::same_registrable_site

    fn SuffixList::same_registrable_site(self : SuffixList, left : String, right : String) -> Result[Bool, SiteError]

    Test whether two hostnames have the same registrable site key using the browser-style PSL policy.

    This compares hostname boundaries only. Schemeful same-site callers must additionally compare their already-parsed URL schemes.

    SuffixList::same_registrable_site_with_options

    fn SuffixList::same_registrable_site_with_options(self : SuffixList, left : String, right : String, options : LookupOptions) -> Result[Bool, SiteError]

    Test whether two hostnames have the same registrable site key under an explicit lookup policy. The left hostname is validated first.

    SuffixList::schemeful_site

    fn SuffixList::schemeful_site(self : SuffixList, scheme : String, hostname : String) -> Result[SchemefulSite, SiteError]

    Construct a DNS-backed web site using browser-style PSL semantics.

    SuffixList::schemeful_site_with_options

    fn SuffixList::schemeful_site_with_options(self : SuffixList, scheme : String, hostname : String, options : LookupOptions) -> Result[SchemefulSite, SiteError]

    Construct a web site under an explicit PSL section and fallback policy. Scheme is case-insensitive; only HTTP and HTTPS DNS hosts are supported.
    fn SuffixList::shareable_cookie_scopes(self : SuffixList, request_host : String) -> Result[ReadOnlyArray[CookieScope], CookieScopeError]

    Enumerate valid, shareable Cookie Domain scopes from narrowest to broadest.

    The request host is included when it is not itself a public suffix. The public suffix is never included. A public-suffix request host therefore yields no shareable scope, even though an equal Domain attribute resolves to a host-only cookie for compatibility.
    fn SuffixList::shareable_cookie_scopes_with_options(self : SuffixList, request_host : String, options : LookupOptions) -> Result[ReadOnlyArray[CookieScope], CookieScopeError]

    Enumerate shareable Cookie Domain scopes under an explicit PSL policy.

    SuffixList::site_key

    fn SuffixList::site_key(self : SuffixList, domain : String) -> Result[String, SiteError]

    Return the canonical registrable site key using browser-style PSL policy.

    The key is the registrable domain without a trailing DNS root dot. A public suffix by itself cannot identify an independently controlled site.

    SuffixList::site_key_with_options

    fn SuffixList::site_key_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[String, SiteError]

    Return the canonical registrable site key under an explicit lookup policy.

    SuffixList::snapshot

    fn SuffixList::snapshot(self : SuffixList, source_revision : String) -> Snapshot

    Build a snapshot from this rule set and an opaque upstream revision label.

    SuffixList::to_psl_text

    fn SuffixList::to_psl_text(self : SuffixList) -> String

    Serialize the compiled rule set to deterministic, parseable PSL text.

    Rules are lowercased and sorted lexicographically within their source section. Comments, blank lines, trailing fields, and original rule order are intentionally not retained. Non-empty output always ends in one newline.

    SuffixList::trace_lookup

    fn SuffixList::trace_lookup(self : SuffixList, domain : String) -> Result[LookupTrace, DomainError]

    Explain every matching rule under browser-style lookup policy.

    SuffixList::trace_lookup_with_options

    fn SuffixList::trace_lookup_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[LookupTrace, DomainError]

    Explain the decision made by lookup_with_options without changing its fast lookup path. Invalid hostname syntax is returned as an outer error; a valid but unlisted hostname is retained as the trace's error outcome.

    SuffixList::verify_psl_test_file

    fn SuffixList::verify_psl_test_file(self : SuffixList, test_text : String) -> Result[ConformanceReport, ConformanceParseError]

    Run checkPublicSuffix(input, expected); cases in upstream file order.

    Blank and // comment lines are ignored. An expected null accepts either a rejected domain or a valid public suffix with no registrable domain, while the report preserves that distinction for failed non-null expectations.

    SuffixList::verify_psl_test_file_with_normalizer

    fn SuffixList::verify_psl_test_file_with_normalizer(self : SuffixList, test_text : String, normalize : (String) -> Result[String, String]) -> Result[ConformanceReport, ConformanceParseError]

    Verify upstream-style cases after normalizing both inputs and expected registrable domains with the same profile used to compile PSL rules.

    The caller owns the normalization policy. This is useful for IDNA-aware conformance checks without adding Unicode dependencies to the core.

    UnknownSuffixPolicy

    pub(all) enum UnknownSuffixPolicy {
    UseDefaultWildcard
    RequireListedSuffix
    } derive(Eq,
    Debug
    )

    The behavior used when no eligible explicit rule matches a hostname.

    UnknownSuffixPolicy::equal

    UnknownSuffixPolicy::not_equal