> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Passthrough api_base SSRF Guard

> api_base URLs are DNS-resolved and screened against private, loopback and metadata addresses

`passthrough` / `apassthrough` check `api_base` from start to finish, so a public hostname can't be pointed at an internal address when the connection is made.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Passthrough api_base Guard"
        A[📋 Caller api_base] --> B[🔍 Resolve + screen every IP]
        B -->|public| C[📌 Pin connection to screened IP]
        C --> D[✅ Send request]
        B -->|private / loopback / metadata| E[⛔ ValueError]
    end

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    classDef warn fill:#F59E0B,stroke:#7C90A0,color:#fff

    class A input
    class B,C process
    class D output
    class E warn
```

> [!NOTE]
>
> The guard runs on the direct HTTP path: when the installed `litellm` has no `llm_passthrough_route`, `passthrough` sends the request itself, and that path validates `api_base`. Other LLM and embedding calls are unaffected.

## Quick Start

**1. Public endpoint — works**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.capabilities import passthrough

result = passthrough(
    endpoint="/v1/models",
    method="GET",
    api_base="https://api.openai.com",
    api_key="sk-proj-abc123",
)
print(result.status_code)
```

**2. Localhost — rejected**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.capabilities import passthrough

try:
    passthrough(endpoint="/v1/models", method="GET", api_base="http://localhost:11434")
except ValueError as e:
    print(e)  # api_base URL cannot be a localhost address (direct HTTP path)
```

## How it works

1. The `api_base` hostname is resolved, and **every** A/AAAA record is checked. A public A record cannot hide a private AAAA record.
2. If any address is in a restricted class, the call raises `ValueError` before anything is sent.
3. The connection is pinned to the checked IP. The `Host` header and TLS SNI keep the original hostname, so DNS rebinding at connect time is blocked.
4. In `apassthrough`, DNS resolution runs in a worker thread (`asyncio.to_thread`), so the event loop is not blocked.

## Rejected address classes

| Class | Example |
| - | - |
| Localhost names | `localhost`, `127.0.0.1`, `0.0.0.0`, `::1` |
| Loopback | `127.0.0.0/8`, `::1` |
| Private (RFC 1918) | `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` |
| Link-local / cloud metadata | `169.254.0.0/16` (e.g. `169.254.169.254`), `fe80::/10` |
| Multicast / Reserved / Unspecified | `224.0.0.0/4`, `240.0.0.0/4`, `0.0.0.0` |

Only `http` and `https` schemes are accepted.

## Error shape

Every error message starts with `api_base URL`, so you can filter logs for it:

| Message | Cause |
| - | - |
| `api_base URL must use http or https scheme` | Scheme is not `http`/`https` |
| `Invalid api_base URL` | No hostname in the URL |
| `api_base URL cannot be a localhost address` | Hostname is a localhost name |
| `api_base URL hostname could not be resolved` | DNS lookup failed |
| `api_base URL resolves to a private or restricted network address` | A resolved IP is in a rejected class |

> **Can I point passthrough at a local Ollama or vLLM server?**
>
> Not through this direct path. The block is intentional. Use the regular LLM integration for local model servers.

> **Is this the same check used for webhooks?**
>
> Yes. It reuses the same resolve-and-screen helper as job webhook URLs (`praisonai.jobs.models`).

## Related

> **[Capabilities](/docs/capabilities/index.md)**
>
> All LiteLLM-backed capabilities, including `passthrough`.

> **[Robustness](/docs/features/robustness.md)**
>
> Connection pooling and other wrapper guarantees.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.