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

# Tunnels

> Expose local task services to hosted optimizer runs with SynthTunnel, cloudflared, or ngrok.

Hosted optimizer runs need a task service they can reach. If that service is running on your machine, `synth-optimizers` can open a tunnel before submit and keep it alive while the hosted run is active.

## Providers

Use one provider flag for both GEPA and GELO:

```bash theme={null}
--tunnel-provider synth_tunnel
```

Supported values:

| Provider       | Use when                          | Notes                                                                                                  |
| -------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `synth_tunnel` | Default hosted optimizer tunnel.  | Creates a SynthTunnel lease, starts the local agent, and can refresh backend-owned auth for long runs. |
| `cloudflared`  | You want a Cloudflare tunnel URL. | Requires the `cloudflared` binary on your machine. Submits as a public URL.                            |
| `ngrok`        | You want an ngrok URL.            | Requires the `ngrok` binary on your machine. Submits as a public URL.                                  |
| `auto`         | You want the SDK default.         | Currently resolves to `synth_tunnel`.                                                                  |

`cloudflared` and `ngrok` managed leases require a localhost target, such as `http://127.0.0.1:8943`.

## CLI

GEPA:

```bash theme={null}
synth-optimizers gepa submit \
  --config gepa.toml \
  --tunnel-url http://localhost:8765 \
  --tunnel-provider synth_tunnel \
  --follow
```

GELO:

```bash theme={null}
synth-optimizers gelo submit \
  --preset crafter_smoke \
  --tunnel-url http://localhost:8943 \
  --tunnel-provider synth_tunnel \
  --follow
```

`--follow` is required with `--tunnel-url`; it keeps the tunnel open until the hosted run reaches a terminal status. Without `--follow`, the CLI refuses to submit a local tunnel run.

## Python

```python theme={null}
from synth_optimizers.gelo import GeloPreset
from synth_optimizers.hosted import HostedOptimizerClient

client = HostedOptimizerClient()

with client.open_tunnel(
    "http://127.0.0.1:8943",
    provider="synth_tunnel",
    requested_ttl_seconds=3600,
) as tunnel:
    config = GeloPreset.crafter_smoke().materialize(
        container_tunnel=tunnel,
    )
    run = client.submit_gelo(config)
    client.wait_for_run(run.run_id, timeout_seconds=3600)
```

The same `open_tunnel(...)` lease can be passed to `submit_gepa_toml(...)` through `container_tunnel`.

## Readiness

Before submit, the SDK checks that the local target is reachable. After a lease is created, it waits for the public tunnel URL to answer before the run is submitted.

For best results:

* expose a `/health` endpoint on the local task service
* keep the local process running for the full hosted run
* use `--follow` for CLI submits
* close Python tunnel leases with a context manager

## Direct URLs and Pools

You do not need a tunnel if your task service is already reachable from the public internet. Use `--container-url` or a config-level direct URL instead.

You also do not need a tunnel when the task is already registered in a Synth container pool. Use `--container-pool` and optional `--container-task-id`.

## Common Errors

| Symptom                          | What to check                                                                          |
| -------------------------------- | -------------------------------------------------------------------------------------- |
| `401` or `403`                   | `SYNTH_API_KEY` is missing or points at the wrong environment.                         |
| Local service unreachable        | Check the local host, port, and `/health` route.                                       |
| `cloudflared` or `ngrok` missing | Install the selected binary or use `--tunnel-provider synth_tunnel`.                   |
| Lease closes early               | Submit with `--follow` or keep the Python context manager open until the run finishes. |
| Wrong backend                    | Set `SYNTH_BACKEND_URL` or pass `--base-url` to the CLI.                               |
