Egress
The egress field in lumio.config.json controls which external hosts your bot-module handlers can call with ctx.fetch(), and which hosts your UI may load images and links from. Handler-based server functions and automation nodes cannot make outbound requests, even when a host is listed here.
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]
Images and links in your UI
Lumio renders your UI on the host page, outside the extension sandbox. The host loads an image (Image src, <img src>) or a link (<a href>) from a third-party host only when that host matches allowHosts, over https on the default port. Other URLs render an empty placeholder and make no request; a style value that loads something (url(), image-set(), …) is dropped. allowPorts does not apply to images and links.
No entry is needed for data:image/* URLs, URLs on the Lumio page itself, assets on your extension's own runtime origin, and the Twitch, 7TV, BTTV, FFZ, YouTube, Discord and Kick avatar, emote and badge URLs that Lumio's image proxy serves. lumio dev applies the same rule, so an image that is missing in the sandbox is missing in production too.
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 |
| 5s timeout | Requests exceeding 5 seconds are cancelled |
| 10 requests/invocation | Maximum 10 ctx.fetch() calls per bot-module handler run |
| Response size | At most 1 MiB for synchronous command and moderate handlers, or 10 MiB for asynchronous handlers |
Data protection
Data sent to an allowed external host leaves Lumio. As the extension developer, you are responsible for the data your handlers send to that host and for explaining that use to people who install your extension. Declare only hosts your extension needs and avoid sending personal data unless it is necessary for the extension's purpose.
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.