diff --git a/.github/scripts/build.py b/.github/scripts/build.py index ca4b257..4b8e293 100644 --- a/.github/scripts/build.py +++ b/.github/scripts/build.py @@ -11,6 +11,7 @@ from __future__ import annotations import argparse +import hashlib import ipaddress import json from datetime import datetime, timezone @@ -33,7 +34,14 @@ def load_plane(plane: str) -> dict: raise SystemExit(f"missing {path} — refusing to build") doc = json.loads(path.read_text()) # Release assets are the public document. $schema is only for editors in the repo. - return {"endpoints": doc["endpoints"]} + return {"sync_token": sync_token(doc["endpoints"]), "endpoints": doc["endpoints"]} + + +def sync_token(endpoints: list[dict]) -> str: + """Deterministic hash of the endpoint list. Changes only when an address or hostname changes, + so automation can compare one field instead of diffing the whole file.""" + canonical = json.dumps(endpoints, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(canonical.encode()).hexdigest()[:16] def write_json(path: Path, payload: dict) -> None: @@ -41,11 +49,12 @@ def write_json(path: Path, payload: dict) -> None: print(f"wrote {path.relative_to(ROOT)}") -def write_text(path: Path, plane: str, version: str, timestamp: str, ips: list[str]) -> None: +def write_text(path: Path, plane: str, version: str, timestamp: str, token: str, ips: list[str]) -> None: header = [ f"# TrueFoundry {plane.upper()} outbound NAT IPs", f"# version: {version}", f"# generated: {timestamp}", + f"# sync_token: {token}", f"# {REPO}", ] path.write_text("\n".join(header + ips) + "\n") @@ -68,6 +77,7 @@ def main() -> int: plane, args.version, timestamp, + doc["sync_token"], flat_ips(doc["endpoints"]), ) diff --git a/README.md b/README.md index 19d3f4e..be29c44 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,35 @@ # TrueFoundry IP ranges -Outbound IP addresses TrueFoundry uses when it connects to your systems. Allowlist these if you restrict inbound access to your model endpoints, webhooks, databases, or private APIs. +Outbound IP addresses TrueFoundry uses when it connects to your systems — model endpoints, MCP servers, webhooks, databases, or private APIs. Allowlist them if you restrict inbound access to those systems. -The directory is the control plane: +These files are meant for automation. Build your allowlist from them rather than copying addresses by hand. See [Connect Private Models and MCP Servers](https://www.truefoundry.com/docs/ai-gateway/connect-private-models-mcp-servers#option-2-allowlist-truefoundry-ip-addresses) in the TrueFoundry docs for the full guide. -| Control plane | List | -| --- | --- | -| Global | [`global/ip-ranges.json`](global/ip-ranges.json) | -| US | [`us/ip-ranges.json`](us/ip-ranges.json) | +> [!IMPORTANT] +> These addresses are shared by every tenant on a control plane. An IP allowlist proves a request came from TrueFoundry, not that it came from **your** tenant. Keep authentication — an API key, OAuth, or mTLS — on any endpoint you allowlist. + +## Pick the list for your control plane -The EU control plane is the global control plane. Always-latest URLs are on GitHub Pages: +Each control plane has its own list. Use the one that matches your tenant URL: -- https://truefoundry.github.io/ip-ranges/global.json -- https://truefoundry.github.io/ip-ranges/us.json +| Your tenant URL | Control plane | Gateways | Always-latest list | +| --- | --- | --- | --- | +| `.truefoundry.cloud` | Global, hosted in Europe (Ireland) | Worldwide | https://truefoundry.github.io/ip-ranges/global.json | +| `.us.truefoundry.cloud` | US | US | https://truefoundry.github.io/ip-ranges/us.json | -Replace `.json` with `.txt` for one CIDR per line. +The source files are [`global/ip-ranges.json`](global/ip-ranges.json) and [`us/ip-ranges.json`](us/ip-ranges.json). -Each [GitHub Release](https://github.com/truefoundry/ip-ranges/releases) attaches the same lists, so a version can be pinned: `https://github.com/truefoundry/ip-ranges/releases/download/v2026.10.12/global.json`. +> [!NOTE] +> The US control plane is being rolled out. `us.json` currently lists only the control plane. US gateway addresses will be added through releases as they come online, following the [15-day notice](#when-addresses-change). + +Replace `.json` with `.txt` for one CIDR per line. Each [GitHub Release](https://github.com/truefoundry/ip-ranges/releases) attaches the same lists, so you can pin a version: `https://github.com/truefoundry/ip-ranges/releases/download/v2026.10.12/global.json`. ## What to allowlist -Find the hostname you connect to. `nat_ips` is every address that hostname can connect from. +Each list has one entry per hostname. `nat_ips` is every address TrueFoundry can send traffic from for that hostname. ```json { + "sync_token": "4d074f6e6d95f47e", "endpoints": [ { "type": "control_plane", @@ -45,9 +51,17 @@ Find the hostname you connect to. `nat_ips` is every address that hostname can c } ``` -`type` is `control_plane` or `gateway`. Airport endpoints include `airport_code`. Continent hostnames combine the regional gateways that can serve them, including the documented fallback, and `nat_ips` is that combined list. Which gateway you connect to is in the [globally distributed SaaS gateway](https://www.truefoundry.com/docs/ai-gateway/globally-distributed-saas) docs: +Allowlist **two** entries: + +1. The **`control_plane`** entry. The control plane also connects to your services. +2. The **`gateway`** entry for the gateway endpoint your tenant sends traffic through: -| Hostname | Addresses | +| Your tenant's gateway endpoint | Gateway entry to allowlist | +| --- | --- | +| `gateway.truefoundry.ai` (the default) | `gateway.truefoundry.ai`, which contains every regional gateway | +| A [multi-regional endpoint](https://www.truefoundry.com/docs/ai-gateway/globally-distributed-saas#configure-a-multi-regional-gateway-endpoint) | That region's hostname below, which already includes its fallback locations | + +| Hostname | Gateway locations included | | --- | --- | | `gateway.truefoundry.ai` | Every regional gateway | | `us.gateway.truefoundry.ai` | North Virginia, San Francisco, Dallas, and Toronto | @@ -56,13 +70,31 @@ Find the hostname you connect to. `nat_ips` is every address that hostname can c | `au.gateway.truefoundry.ai` | Sydney, Melbourne, and Singapore | | `as.gateway.truefoundry.ai` | Mumbai, Singapore, Doha, and Melbourne | +If you pinned a single location such as `orf.gateway.truefoundry.ai`, use the entry with that `airport_code`. Cape Town (`cpt`) is not part of any multi-regional endpoint, so it is only covered by `gateway.truefoundry.ai` and `cpt.gateway.truefoundry.ai`. + +The gateway endpoint is configured per tenant. See [globally distributed SaaS gateway](https://www.truefoundry.com/docs/ai-gateway/globally-distributed-saas) for the locations and how to change it. + +### Build the allowlist + +```bash +# Global control plane, US multi-regional gateway endpoint. +# Use gw=gateway.truefoundry.ai for the default global endpoint, +# and us.json if your tenant is on the US control plane. +curl -s https://truefoundry.github.io/ip-ranges/global.json \ + | jq -r --arg gw us.gateway.truefoundry.ai \ + '.endpoints[] | select(.type == "control_plane" or .hostname == $gw) | .nat_ips[]' \ + | sort -u +``` + ## When addresses change -Address changes are published as a GitHub Release. +> [!WARNING] +> TrueFoundry does not notify customers individually when these addresses change. Keeping your allowlist current is your responsibility. Subscribe to releases and automate your allowlist so changes are picked up. -A new address is announced in a release and starts carrying traffic **15 days** after that release. Allowlist it during those 15 days. +Every change is published as a [GitHub Release](https://github.com/truefoundry/ip-ranges/releases), and the always-latest lists update with it. -When an address is removed, a new release is published. Drop it from your allowlist after that release. +- A **new address** is announced in a release and starts carrying traffic **15 days** after that release. Allowlist it during those 15 days. The lists include it from the release that announces it. +- When an address is **removed**, a new release is published. Drop it from your allowlist after that release. To be notified, choose **Watch → Custom → Releases** on this repository, or subscribe to: @@ -70,6 +102,12 @@ To be notified, choose **Watch → Custom → Releases** on this repository, or https://github.com/truefoundry/ip-ranges/releases.atom ``` +### Automate it + +Run the [allowlist command](#build-the-allowlist) on a schedule — daily is plenty given the 15-day notice — and apply the result to your firewall, security group, or WAF. + +Each JSON file has a top-level `sync_token`, which is also in the `.txt` header. It is a hash of the endpoint list and changes only when an address or hostname changes. Compare it with the last value you applied to skip work when nothing has changed. + ## Reporting a problem Something wrong or missing? [Open an issue](https://github.com/truefoundry/ip-ranges/issues) or contact TrueFoundry support. For anything security-sensitive, email security@truefoundry.com.