Egress
The egress field in lumio.config.json controls which external hosts your extension's server actions can make HTTP requests to via ctx.fetch().
Configuration
{
"egress": {
"allowHosts": [
"site.api.espn.com",
"*.scdn.co",
"api.openweathermap.org"
],
"allowPorts": [8443]
}
}
Fields
allowHosts
An array of allowed hostnames. Only hosts listed here can be reached from ctx.fetch().
"allowHosts": [
"api.example.com",
"*.example.com",
"cdn.example.com"
]
Wildcard support: * matches any single subdomain level:
| Pattern | Matches | Does not match |
|---|---|---|
"api.example.com" | api.example.com | staging.api.example.com |
"*.example.com" | api.example.com, cdn.example.com | example.com, a.b.example.com |
Wildcards must cover a registrable domain. The part after *. has to be longer than its public suffix in the ICANN section of the Public Suffix List. *.example.com, *.example.co.uk and *.myshopify.com are accepted; *.com, *.co.uk and *.io are not. * is only allowed as the leading *. label, so api.*.com and *example.com are rejected too. Manifest validation enforces this rule, so lumio deploy fails with Egress host '…' is not allowed instead of shipping a pattern that would open a whole top-level domain.
allowPorts
An optional array of additional allowed ports. Port 443 (HTTPS) is always allowed and does not need to be listed. HTTP (port 80) is never allowed.
"allowPorts": [8443, 9443]
Security rules
All ctx.fetch() calls are subject to these rules regardless of allowlist:
| Rule | Description |
|---|---|
| HTTPS only | HTTP is blocked. All requests must use https:// |
| Public addresses only | Only globally routable unicast addresses can be reached — see Blocked address ranges. This applies to IP literals in the URL (https://10.0.0.1/, https://[::1]/) before any DNS lookup, and to every address a hostname resolves to |
| No DNS rebinding | The connection is pinned to the validated address, so the host cannot re-resolve to an internal IP |
| No redirects | Redirect responses are returned as-is and never followed |
| 30s timeout | Requests exceeding 30 seconds are cancelled |
| 10 requests/invocation | Maximum 10 ctx.fetch() calls per action run |
| 10 MiB response limit | A response body larger than 10 MiB fails the call |
Blocked address ranges
| Family | Blocked |
|---|---|
| IPv4 | 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4 (multicast), 240.0.0.0/4 (reserved, incl. 255.255.255.255) |
| IPv6 | everything outside global unicast 2000::/3 (::, ::1, IPv4-compatible ::/96, 64:ff9b:1::/48, fc00::/7, fe80::/10, ff00::/8, …), plus 2001::/32 (Teredo), 2001:2::/48, 2001:10::/28, 2001:20::/28, 2001:db8::/32 and 3fff::/20 |
| IPv6 carrying IPv4 | ::ffff:0:0/96 (IPv4-mapped), 64:ff9b::/96 (NAT64) and 2002::/16 (6to4) are judged by the embedded IPv4 address — blocked when that address is in a blocked IPv4 range |
Common patterns
External sports API
{
"egress": {
"allowHosts": ["site.api.espn.com", "cdn.espn.com"]
}
}
Weather service
{
"egress": {
"allowHosts": ["api.openweathermap.org"]
}
}
Spotify album art
{
"egress": {
"allowHosts": ["i.scdn.co", "*.scdn.co"]
}
}
Webhook delivery
{
"egress": {
"allowHosts": ["discord.com", "hooks.slack.com"]
}
}
Review
The egress allowlist is reviewed during extension approval. Reviewers check:
- Do the declared hosts match the extension's stated purpose?
- Are wildcards appropriately scoped?
- Are the hosts known, reputable services?
Allowlists that are broader than the extension needs, or allowlists that include analytics/tracking services not needed for the extension's function will result in rejection with a request to narrow the scope.