Skip to content

Curated feed

The Exosys Curated Domain Lists are a signed, on-box category feed — malware, phishing, gambling, social media, streaming and 60+ more — that ships with every edition and lets you write .policy rules against a category instead of maintaining your own domain lists. This page gets it running on a fresh install and shows how to write policy against it. For the full attribute grammar see Managed categories; for every [feed] config key see engine reference.

How it's delivered

The feed is a single signed file (.egsf, /var/lib/enforcegate/managed.egsf by default) loaded into memory at engine boot. There is no per-request cloud lookup — matching a URL against a category never leaves the box, and a deployment stays fully air-gapped after the file is in place. See architecture for how the file itself is produced and verified.

Getting that file onto disk has two paths:

  • Automatic delivery — the engine polls the Exosys Control Server for newer feed generations itself and hot-swaps them in with no restart. This is the fastest path to a working feed on a fresh install and the one below.
  • Manual file drop — for air-gapped deployments with no outbound path to the Control Server at all. Contact support to obtain a feed file, copy it to the configured path, then apply it with request feed reload — no engine restart needed.

Enabling on a fresh install

A fresh install needs nothing done — from 2026.56.0, automatic delivery ships on by default. The engine primes its first poll about 90 seconds after boot; on a fresh install there is no local file yet, so if that first attempt doesn't land a feed it retries every 60 seconds (up to five attempts) rather than waiting out the full interval, so you typically have a feed within a few minutes of first boot. Confirm it loaded:

> show feed
Feed loaded:                   yes
Generation:                    3
Sealed at:                     2026-09-09 14:22:10 UTC (1789054930) — 3m ago
Categories:                    61
Domains indexed:               42118003
Entitlement:                   licensed
Loaded from:                   control server (applied in memory — no on-disk copy)
Feed file:                     /var/lib/enforcegate/managed.egsf (offline drop path — not currently in use)
Auto-update:                   on
Last checked:                  2026-09-09 14:22:10 UTC — 3m ago  (updated to generation 3)
Last installed:                2026-09-09 14:22:10 UTC — 3m ago  (generation 3 (61 categories, 42118003 domains))
Next check:                    2026-09-09 20:28:10 UTC — in 5h 57m

Entitlement: licensed confirms the feed is active for your licence. See show feed for what the other entitlement states mean. Don't want to wait even a few minutes? Pull a generation on demand:

host# request feed update
Feed updated — generation 3 → 4, 61 categories, 42.3M domains

Upgrading an existing deployment does not change this. If you installed before 2026.56.0, your [feed] section already has auto_update written to disk (false, by default at the time), and an upgrade never overwrites a value that's already there — the new on-by-default behaviour only applies to a [feed] section that doesn't exist yet. Enable it explicitly if you want it:

$ eghost feed auto-update on
Automatic feed delivery: off -> on. Recreate the engine to apply? [y/N]: y

See the full enabling automatic updates reference for every tuning knob, and eghost feed for the verb.

Air-gapped deployments

Turn automatic delivery off — eghost feed auto-update off, or ENFORCEGATE_FEED_AUTO_UPDATE=false in .env — and use the manual file-drop path above instead. Nothing about writing policy against categories changes; the feed just arrives by hand rather than over the network.

Default update interval

With automatic delivery on, the engine checks for a newer generation every 6 hours (poll_interval_seconds, default 21600), spread by up to ±10 minutes of jitter (poll_jitter_seconds, default 600) so a fleet of engines doesn't hit the Control Server in the same instant. Both are .env-overridable — see [feed] for the full key list. A poll that fails for any reason — network blip, an unreachable Control Server — leaves the currently-loaded feed enforcing exactly as before; it is never dropped.

Categories

match-category: gates a rule on one or more of these tokens. Coverage grows over time as new categories clear our precision bar for shipping, so treat this as the vocabulary rather than a promise every token is populated on your loaded generation — show feed reports your generation's live category count, and show feed lookup <domain> tells you exactly what the feed says about one domain before you write a rule against it.

Group Categories
Security threats malware, phishing, cryptojacking, crypto_scam
Filter evasion & dual-use tools proxy_evasion, hacking, remote_access
Disposable / abuse-prone infrastructure disposable_infra, file_sharing, url_shortener
Adult & mature adult, dating, gambling, drugs, alcohol_tobacco, weapons
Legal but high-liability piracy, violence, hate_extremism, ai_abuse
Controversial / advocacy abortion, politics, political_extremism, cult_sect
Privacy & advertising data_broker, advertisements
Uncertain suspicious, gray_market
Business & general-interest finance, search_engines, business, government, technology, health, education, travel, job_search, defense, aviation, ai_tools, history, associations
Lifestyle & entertainment social_networks, streaming, podcasts, games, shopping, webmail, news, sports, religion, cryptocurrency, outdoor_recreation, supplements, pharmacy, chat, music, events, blogs
No real content parked, unknown

Writing policy against a category

A rule matches when the requested domain carries any of its listed categories. Put category rules early in your rule set (lower file-number prefix) if you want them to win over broader match-domain-list or match-uri-prefix rules — see rule files and evaluation order.

Single category:

20-block-gambling.policy
block-gambling: {
    match-category: gambling
    action: deny
    description: Exosys curated feed — gambling
}

Multiple categories on one rule — use this when several categories should get the same treatment, rather than duplicating the rule:

10-block-security-threats.policy
deny-security-threats: {
    match-category: [ "malware", "phishing", "cryptojacking", "crypto_scam" ]
    action: deny
    description: Exosys curated feed — known-bad infrastructure
}

Different categories, different actions — this is the common real-world shape: hard-block what's unambiguous, and gate the judgment calls behind a warning instead:

10-curated-feed.policy
deny-security-threats: {
    match-category: [ "malware", "phishing", "cryptojacking", "crypto_scam" ]
    action: deny
    description: Known-bad — no exceptions
}

warn-gambling-drugs: {
    match-category: [ "gambling", "drugs", "alcohol_tobacco" ]
    action: warn
    description: Proceed with acknowledgement
}

deny-social-during-hours: {
    match-category: social_networks
    time-window: "Mon-Fri 09:00-17:00"
    action: deny
    description: Social media blocked during business hours
}

That last rule composes match-category: with time-window: — every other rule dimension (match-client-ip:, match-method:, ack_scope:) composes the same way, and all four actions (permit / deny / warn / aup) are available.

Tuning confidence and freshness per rule

Every category entry in the feed carries a confidence score and an age (how long ago it was classified). The [feed] block sets the deployment-wide floor for both — see min_confidence / max_age_bucket / unknown_age — and a rule can tighten either one for itself with min-confidence: and max-age:. A rule can only tighten, never loosen below the deployment floor.

20-high-confidence-gambling.policy
deny-gambling-strict: {
    match-category: gambling
    min-confidence: 2
    max-age: 2
    action: deny
    description: Only act on gambling classifications we're most sure of and were made recently
}
Attribute Range Meaning
min-confidence: 03 Reject a categorisation below this confidence bucket. 0 = 0.80–0.90, 1 = 0.90–0.95, 2 = 0.95–0.99, 3 = 0.99+. Higher means fewer false positives, at the cost of missing some genuine matches.
max-age: 05 Reject a categorisation older than this bucket. 0 = under 1 day, 1 = under 7 days, 2 = under 30 days, 3 = under 90 days, 4 = under 365 days, 5 = any known age (the default — no staleness restriction).

Omit either attribute to inherit the [feed] global default for that threshold — min-confidence and max-age are independent, so tightening one does not require setting the other.

See also