Network Guard API¶
kavalai.net decides which addresses a request to a model-chosen URL may
reach. The bundled http_request and crawl_url tools use it by default,
and PublicOnlyTransport gives the same protection to any
httpx client in your own tools. It is not re-exported from kavalai, because
it needs httpx and import kavalai has to work without it.
For what the guard covers and where it stops, read the Outbound requests section of Safety; for the tools, Bundled tools.
Copyright 2026 OÜ KAVAL AI (registry code 17393877)
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Outbound requests to URLs a model chooses: the guard against SSRF.
A bundled web tool fetches whatever URL a model composes, and a model can be
talked into composing http://169.254.169.254/ or http://localhost:5432/.
This module refuses such targets.
is_public_address()decides whether one IP address is globally routable unicast.ensure_public_url()checks a URL: scheme, authority, host name, and every address the host resolves to. It is a pre-check — by itself it is defeated by DNS rebinding, because the client that fetches the URL resolves the name a second time and may receive a different answer.PublicOnlyTransportcloses that gap for httpx. Its network backend resolves the host, checks every address and then connects to the address it checked, so no second lookup takes place. TLS still verifies the certificate for the host name and sends it as SNI, and theHostheader is the one in the URL. Each redirect hop is a new request through the same transport, so it is checked in the same way.
The module is not imported by kavalai/__init__.py: it needs httpx, and
import kavalai has to work under Pyodide without it.
- kavalai.net.Resolver¶
An ASCII host name to the IP addresses it resolves to, in preference order.
An empty list means the name does not resolve.
resolve_host()is the default; tests pass their own.
-
kavalai.net.METADATA_ADDRESSES =
frozenset({IPv4Address('100.100.100.200'), IPv4Address('168.63.129.16'), IPv4Address('169.254.169.254'), IPv4Address('169.254.170.2'), IPv6Address('fd00:ec2::254')})¶ Cloud metadata and platform endpoints.
AWS, GCP, Azure, Oracle and DigitalOcean serve instance metadata at
169.254.169.254, ECS task credentials at169.254.170.2, Alibaba at100.100.100.200and AWS over IPv6 atfd00:ec2::254. All of these fall in ranges refused anyway; they are listed so the refusal does not depend on the interpreter’s address tables. Azure’s platform endpoint168.63.129.16is a global address, so without this entry it would be allowed.
-
kavalai.net.BLOCKED_NETWORKS =
(IPv4Network('0.0.0.0/8'), IPv4Network('100.64.0.0/10'), IPv4Network('192.0.0.0/24'), IPv4Network('198.18.0.0/15'), IPv6Network('fec0::/10'), IPv6Network('3fff::/20'), IPv6Network('5f00::/16'))¶ Networks refused in addition to what
ipaddresscalls non-global.0.0.0.0/8(reaches the local host on Linux), carrier-grade NAT100.64.0.0/10, IETF protocol assignments192.0.0.0/24, benchmarking198.18.0.0/15, deprecated IPv6 site-localfec0::/10(global to Python 3.12), IPv6 documentation3fff::/20(RFC 9637, global to Python 3.12) and SRv6 segment identifiers5f00::/16.
- exception kavalai.net.UnsafeUrlError[source]¶
Bases:
ValueErrorA URL, or an address its host resolves to, is not a public target.
- kavalai.net.is_public_address(address: str | IPv4Address | IPv6Address) bool[source]¶
Whether
addressis globally routable unicast.Refuses loopback, private, link-local, multicast, reserved, unspecified, carrier-grade NAT, benchmarking and documentation ranges,
0.0.0.0/8and the cloud metadata endpoints inMETADATA_ADDRESSES.An IPv4-mapped address (
::ffff:a.b.c.d) and a NAT64 address (64:ff9b::a.b.c.d) are judged as the IPv4 address they reach. An IPv6 address that tunnels to an IPv4 one — IPv4-compatible, 6to4, Teredo — is refused when that IPv4 address is not public, and is otherwise judged as an IPv6 address.- Parameters:¶
- address: str | IPv4Address | IPv6Address¶
An address as text or as an
ipaddressobject. A zone index (fe80::1%eth0) is ignored.
- Raises:¶
ValueError –
addressis not an IP address.
- kavalai.net.parse_ip_literal(host: str) IPv4Address | IPv6Address | None[source]¶
The address
hostspells out, orNonewhen it is a name.Accepts IPv6 with or without brackets and IPv4 in every form the C library’s
inet_atonaccepts: dotted, decimal, octal, hex and the short forms (127.1). One trailing dot is ignored.
- async kavalai.net.resolve_host(host: str) list[str][source]¶
host’s addresses from the system resolver, in its preference order.Returns an empty list when the name does not resolve.
-
async kavalai.net.ensure_public_url(url: str, *, resolver: Callable[[str], Awaitable[list[str]]] | None =
None) str[source]¶ Return
urlunchanged when it points at a public host; raise otherwise.Refuses a scheme other than
http/https; credentials, a backslash or whitespace in the URL, which different URL parsers read differently;localhostand*.localhost; a host whose first label ismetadata;*.internaland*.local; and a host with any address for whichis_public_address()is false. IP literals are read in every encoding (http://2130706433/is127.0.0.1) and never sent to the resolver; non-ASCII names are IDNA-encoded first.This is a pre-check: the client that fetches the URL resolves the name again. Use
PublicOnlyTransportwhere the connection itself is under the SDK’s control.
-
class kavalai.net.PublicOnlyTransport(*, resolver: Callable[[str], Awaitable[list[str]]] | None =
None, network_backend: AsyncNetworkBackend | None =None, verify: Any =True, cert: Any =None, trust_env: bool =True, http1: bool =True, http2: bool =False, limits: Limits | None =None, retries: int =0)[source]¶ Bases:
AsyncHTTPTransportAn httpx transport that connects only to public addresses.
Every connection resolves its host once, refuses it unless every address is public (
is_public_address()), and dials the address it checked, so a DNS answer that changes between the check and the connection cannot redirect it. The certificate is still verified against the host name, which is also sent as SNI. Redirects are followed by httpx as new requests through this transport, so each hop is checked. A refusal raisesUnsafeUrlErrorfrom the request.verify,cert,trust_env,http1,http2,limitsandretriesmean what they mean forhttpx.AsyncHTTPTransport. There is noproxy: through a proxy the proxy resolves the name, so the guard could not apply. The constructor builds its own connection pool rather than calling the parent’s, because that one offers no way to choose the network backend.