Skip to content

Export

Exports turn indicators into blocklists. They merge the selected sources, deduplicate values (case-insensitive; the same IP with different netmasks counts as different), apply whitelists and render the requested format.

Endpoints

Endpoint Sources ACL Cached
GET /api/v1/export all non-private sources export yes (CONFIG_REDIS_TTL)
GET /api/v1/export/:id one source, private allowed export/<id> no
GET /api/v1/export/private/:ids listed sources (5,6), private allowed export/private/<id> for every ID no
GET /api/v1/export/group/:ref members of a group + its blacklist export/group/<id or name> no

CLI: indicators:export (public sources) or indicators:export --group <ref>.

Responses are plain text (text/plain, text/dns for RPZ), errors too (403 Forbidden. Insufficient permissions., 404 Source not found.). Only live sources are exported – a deleted source's indicators never appear.

Conditional requests and limits

Every export sends a weak ETag computed over its content (the generation timestamp and SOA serial are ignored). Sending it back as If-None-Match returns 304 Not Modified without a body when nothing changed – tis-pull.sh uses this to skip downloading, comparing and reloading.

Exports are limited to CONFIG_RATE_LIMIT_EXPORTS (default 120) per token and minute; beyond that the API answers 429 with Retry-After.

Parameters

Parameter Values Default
format plain, hosts, rpz, zone, csv, nftables, ipset, ipfw, dnsmasq, unbound plain
type ipv4, ipv6, fqdn, url, json all
tag tag name – matches a tag of the indicator or of its source –
whitelist comma-separated IPs, CIDRs, domain globs –
rpz_ip response, client, nsip – RPZ only, see IP triggers –
country comma-separated country codes – only IP indicators in these GeoIP countries (other types pass) –
exclude_country comma-separated country codes – no IP indicators in these countries –

Authentication: Authorization: Bearer <token>, access-token: <token> header, or ?access_token=<token> (handy for appliances; it is redacted from logs).

Formats

plain

One value per line. IPs with a non-default netmask get their prefix (10.0.0.0/8); single addresses don't (192.0.2.1). URLs and JSON values are included as-is. Use type to get a single kind, e.g. type=ipv4 for a firewall table.

hosts

Domains only, as /etc/hosts lines pointing to 0.0.0.0:

# Threat Intel Scrany Hosts file
# Generated: 2026-10-12T10:00:00.000Z

0.0.0.0 evil.example.com

rpz

A complete Response Policy Zone for BIND/Unbound (domains only; $TTL 300, SOA serial from the generation time):

$TTL 300
@  IN SOA localhost. root.localhost. ( ... )
   IN NS  localhost.

safe.example.com  IN CNAME rpz-passthru. ; whitelist safe.example.com
*.safe.example.com  IN CNAME rpz-passthru. ; whitelist safe.example.com
27.73.2.168.192.rpz-ip  IN CNAME rpz-passthru. ; whitelist 192.168.2.73/27

evil.example.com  IN CNAME .
*.evil.example.com  IN CNAME .
www.bad.example  IN CNAME .
  • A blocked domain also blocks its subdomains (*.name), except names starting with www. (blocking *.bad.example from www.bad.example would over-block) and explicit wildcards.
  • Whitelist entries become explicit rpz-passthru records (IPs as reversed rpz-ip names) – from the request whitelist and from the whitelists of sources present in the export – so the zone documents the allowlist.

IP triggers

Since v1.26.0. By default the RPZ contains domains only. With rpz_ip= IPv4 and IPv6 indicators are added as RPZ IP triggers (<prefix>.<reversed address>.<trigger>, IPv6 in 16-bit groups with zz for ::):

rpz_ip Trigger Matches
response rpz-ip answers (A/AAAA records) pointing to the IP
client rpz-client-ip queries coming from the IP (BIND only)
nsip rpz-nsip names served by a nameserver at the IP
32.1.2.0.192.rpz-ip  IN CNAME .
128.1.zz.db8.2001.rpz-ip  IN CNAME .

Whitelisted IPs get their rpz-passthru under the same trigger. Unbound supports rpz-ip and rpz-nsip but not rpz-client-ip.

zone

BIND zone statements pointing each domain to an empty zone (domains only; leading *. stripped):

zone "evil.example.com" { type master; file "master/empty.db"; };

Include it from named.conf and provide master/empty.db.

csv

type,value,source,tags,last_observed,country – one row per indicator, with the indicator's and its source's tags and the GeoIP country of IPs (text/csv; country since v1.39.0). For spreadsheets and SIEM imports.

nftables

A script for nft -f <file>: declares the sets blocklist_v4 / blocklist_v6 (interval sets with auto-merge) in table inet tis, flushes and refills them – safe to load repeatedly. Use them in your own rules, e.g. ip saddr @blocklist_v4 drop. IPs only.

ipset

A file for ipset restore < <file>: creates (if missing), flushes and fills the hash:net sets tis-blocklist-v4 / tis-blocklist-v6. IPs only.

ipfw

Since v1.33.0. An ipfw command file for ipfw -q /path/to/file (FreeBSD) – the path must be absolute, otherwise ipfw takes it for a command, not a file: creates the addr table tis-blocklist if missing, fills a temporary table tis-blocklist_tmp, swaps the two and drops the temporary one – the live table is never empty or half-filled, and as ipfw stops at the first error, a failed load leaves it unchanged. Each address is listed once. Use it in your rules, e.g. ipfw add deny ip from table(tis-blocklist) to any. IPs only.

For your own table name, change detection and rollback, prefer tis-pull.sh --ipfw-table <name>.

dnsmasq

address=/evil.example/ per domain – NXDOMAIN for the domain and its subdomains. Exact whitelist entries become server=/name/# exceptions. Include with conf-file=<file>.

unbound

A server: block with local-zone: "evil.example." always_nxdomain per domain (covers subdomains); exact whitelist entries become more specific always_transparent zones. Include with include: <file>.

All formats except ipfw were validated with unbound-checkconf, dnsmasq --test, nft -c and repeated nft -f / ipset restore.

Whitelisting

Three whitelists apply, and an indicator is removed if any of them matches:

Whitelist Applies to Set via
request the whole export ?whitelist= / --whitelist / tis-pull.sh --whitelist
source indicators of that source only source options.whitelist
group the whole group export group:update --whitelist …

Matching rules:

  • IP / CIDR – an indicator is removed if its network lies within or equals a whitelisted network: whitelisting 10.0.0.0/16 removes 10.0.5.5 and 10.0.0.0/16, but not the wider 10.0.0.0/8. A single address (192.0.2.1) removes exactly that address.
  • Domains – case-insensitive globs: example.com removes only example.com; *.example.com removes every subdomain but not example.com itself. Whitelist both if you need both. (In RPZ, the passthru record for a plain example.com entry additionally covers its subdomains.)

Invalid entries (e.g. 1.2.3.4/40) are ignored.

Examples

# RPZ for a resolver, keep the local network
curl -H "Authorization: Bearer $TOKEN" \
  "https://tis.cap-net.cz/api/v1/export?format=rpz&tag=malware&whitelist=192.168.0.0/16"

# IPv4 list of two private sources for a firewall table
curl "https://tis.cap-net.cz/api/v1/export/private/5,6?format=plain&type=ipv4&access_token=$TOKEN"

# a customer's group as BIND zone statements
curl "https://tis.cap-net.cz/api/v1/export/group/customer-x?format=zone&type=fqdn&access_token=$TOKEN"

For scheduled deployment with atomic replace and rollback use tis-pull.sh.