<!--
Sitemap:
- [Installation](/installation)
- [Upgrading](/upgrading): Version-specific steps for upgrading an existing Bento install.
- [Concepts](/concepts)
- [Build your first pipeline](/tutorials/pipeline-args)
- [Target a specific issue or PR from a URL](/tutorials/url-targeting)
- [Keep state across runs](/tutorials/pipeline-state)
- [Fire a pipeline on a schedule or on demand](/tutorials/schedule-and-fire)
- [Configuration](/configuration)
- [Knowledge Base](/knowledge-base/)
- [Method & delivery](/knowledge-base/modes)
- [Config](/knowledge-base/config)
- [MCP](/knowledge-base/mcp)
- [Pipeline configuration reference](/pipelines/config)
- [Filters](/pipelines/filters)
- [Triggers](/triggers/)
- [GitHub Trigger](/triggers/github)
- [Linear Trigger](/triggers/linear)
- [Webhook](/triggers/webhook)
- [Schedule Trigger](/triggers/schedule)
- [Manual Trigger](/triggers/manual)
- [Traces](/pipelines/traces)
- [Slack](/integrations/slack)
- [Public Access](/public-access)
- [Context Engineering](/context-engineering)
- [Best Practices](/best-practices)
- [Troubleshooting](/troubleshooting)
- [Architecture](/architecture/vision)
- [Workspaces](/workspaces)
- [Authentication](/authentication)
- [Identity](/identity)
- [Security](/security)
- [References](/references)
- [Changelog](/changelog): Bento release history.
- [CLI Reference](/cli/)
- [Setup](/cli/setup)
- [Lifecycle](/cli/lifecycle)
- [Sandbox Image](/cli/image)
- [Observability](/cli/observability)
- [Diagnostics](/cli/diagnostics)
- [Triggers](/cli/triggers)
- [Workbench](/cli/workbench)
- [Auth](/cli/auth)
- [Knowledge](/cli/knowledge)
- [Bento](/index)
- [Runtime Wrapper](/architecture/runtime-wrapper)
- [Skill Evolve](/architecture/skill-evolve)
-->

# Public Access

The daemon binds to `127.0.0.1:7890` by default. To accept GitHub webhooks or reach the daemon from another machine, expose it through a tunnel.

| | Cloudflare Quick Tunnel | Tailscale Funnel |
|---|---|---|
| **Account required** | No | Yes (free) |
| **URL stability** | Rotates on restart | Persistent (`*.ts.net`) |
| **Best for** | One-off testing, demos | Production webhooks, daily use |
| **Prerequisite** | `cloudflared` on PATH | Tailscale enrolled on host |

***

## Cloudflare Tunnel

Bento can spin up a [Cloudflare Quick Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) alongside the daemon, giving an ephemeral `*.trycloudflare.com` URL with no Cloudflare account required. Useful for one-off webhook receivers and demos.

:::note
Quick Tunnel URLs are public. The daemon's bearer-token middleware and webhook signature verification are the only auth — never disable them.
:::

### Prerequisites

* `cloudflared` on the daemon host's `PATH`. On macOS: `brew install cloudflared`. On Linux see [Cloudflare's install docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/).
* `bento doctor` validates this when the `tunnel:` block is present in `.bento/daemon.yaml`.

### Setup

Add a `tunnel:` block to `.bento/daemon.yaml`:

```yaml
tunnel:
  provider: cloudflare
  mode: quick
```

Then restart the daemon:

```bash
bento daemon restart
```

The daemon spawns `cloudflared`, parses the public URL out of its startup log, and emits one info line:

```
[INFO] [tunnel] public URL ready: https://essentially-carrying-recorder-merely.trycloudflare.com
```

`bento daemon status` surfaces the same URL:

```
local   : reachable (8ms) — @bento/daemon uptime=14s
tunnel  : reachable (83ms) — https://essentially-carrying-recorder-merely.trycloudflare.com
funnel  : not configured (tailscale funnel not active)
```

:::warning
Quick Tunnel URLs **rotate on every daemon restart**. For a stable URL use a [named tunnel](#named-tunnels-stable-url) or Tailscale Funnel.
:::

### Use the public URL

**GitHub webhooks** — point the webhook delivery URL at:

```
https://<random>.trycloudflare.com/events
```

Make sure `webhooks.secret` in `.bento/daemon.yaml` matches the GitHub webhook secret so signature verification passes. The daemon also accepts `/webhooks/github` and `/webhooks/linear`.

**MCP clients** — configure Claude Code / Codex / pi against the tunnel URL — see [Authentication](/authentication) for the exact commands. Issue a bearer token first:

```bash
bento token issue --email you@example.com
```

### Lifecycle

* The tunnel is supervised as a child of the daemon. `bento daemon stop` sends `SIGTERM` to `cloudflared` before stopping the HTTP server.
* If `cloudflared` crashes while the daemon is up, the daemon logs the exit code at `warn` and keeps running. Restart to re-establish the tunnel.
* The daemon does **not** block on tunnel readiness during startup.

### Named tunnels (stable URL)

For a stable URL, pre-create a **named tunnel** in your Cloudflare account.

#### Prerequisites

* A free [Cloudflare account](https://dash.cloudflare.com/sign-up).
* `cloudflared` on the host.
* A hostname you control with a CNAME pointing at the tunnel:
  * **Cloudflare DNS** — `cloudflared tunnel route dns` creates the CNAME automatically.
  * **External DNS** — create `bento.example.com CNAME <tunnel-uuid>.cfargotunnel.com` yourself.

#### One-time setup

1. Authenticate `cloudflared`:

   ```bash
   cloudflared tunnel login
   ```

2. Create the tunnel:

   ```bash
   cloudflared tunnel create bento-prod
   ```

3. Route a hostname:

   ```bash
   # Cloudflare-managed DNS:
   cloudflared tunnel route dns bento-prod bento.example.com

   # External DNS: add a CNAME manually:
   # bento.example.com   CNAME   <uuid>.cfargotunnel.com
   ```

#### Point bento at the tunnel

```yaml
tunnel:
  provider: cloudflare
  mode: named
  name: bento-prod
```

Then `bento daemon restart`. The daemon runs `cloudflared tunnel run`, detects the registered hostname, and surfaces it in `bento daemon status`.

#### Delete the tunnel

```bash
cloudflared tunnel delete bento-prod
```

Remove the DNS CNAME separately via your DNS provider.

#### Files

| Path | Created by | Purpose | Needed at runtime? |
|---|---|---|---|
| `~/.cloudflared/cert.pem` | `tunnel login` | Manage tunnels (create/delete/route DNS) | No |
| `~/.cloudflared/<uuid>.json` | `tunnel create <name>` | Authenticate the running tunnel | **Yes** |
| `~/.cloudflared/config.yml` | hand-written (optional) | Ingress rules | Only if not passing `--url` |

### Notes

* Cloudflare may rate-limit Quick Tunnels under heavy traffic. For production webhook ingestion use a named tunnel.

***

## Tailscale Funnel

[Tailscale Funnel](https://tailscale.com/kb/1223/funnel) gives a persistent TLS-terminated `*.ts.net` URL routed back to your machine over the tailnet.

Bento can manage the funnel, or you can run it yourself:

* **Let bento manage it** — `tunnel:` block in `.bento/daemon.yaml`; bento calls `tailscale funnel --bg <port>` at start and teardown.
* **Manage it yourself** — run `tailscale funnel --bg 7890` once; `bento daemon status` still detects it.

### Prerequisites

* A [Tailscale](https://tailscale.com) account with the daemon's host enrolled.
* HTTPS certificates and Funnel enabled for your tailnet (admin console: **DNS → HTTPS Certificates** and **Access Controls → Funnel**).

### Bento-managed mode

```yaml
tunnel:
  provider: tailscale
  mode: funnel
```

`bento daemon restart` execs `tailscale funnel --bg 7890`, polls `tailscale funnel status` for the public URL, and surfaces it via `bento daemon status`.

On shutdown bento runs `tailscale funnel --bg off` — but only if it was the one that enabled the funnel. If a funnel was already active at startup, bento adopts the existing URL and skips both setup and teardown.

:::warning
`tailscale funnel --bg off` is **not port-scoped** — it disables every funnel on the host. If you run multiple funnels on the same machine, set them up before starting bento (so bento adopts and leaves them alone) or use manual mode.
:::

### Manual mode

```bash
tailscale funnel --bg 7890
```

`--bg` persists across shell exits; run this once per machine.

Verify:

```bash
tailscale funnel status
# https://8640p.tail673878.ts.net (Funnel on)
# |-- / proxy http://127.0.0.1:7890
```

`bento daemon status` picks it up automatically:

```
local   : reachable (4ms) — bento uptime=120s
funnel  : reachable (38ms) — https://8640p.tail673878.ts.net
```

### Use the public URL

**MCP clients** — configure Claude Code, Codex, or pi — see [Authentication](/authentication). Issue a token first:

```bash
bento token issue --email you@example.com
```

**GitHub webhooks** — point the delivery URL at:

```
https://<node>.tail<id>.ts.net/events
```

Set the same secret as `webhooks.secret` in `.bento/daemon.yaml`.

### Stopping the funnel

```bash
tailscale funnel --bg off
```

Unbinds the public URL but leaves the daemon running locally.

### Notes

* Funnel only serves ports `443`, `8443`, and `10000` on the public side. Tailscale maps `:443` → `:7890` automatically.
* Funnel traffic counts against tailnet bandwidth limits.
* The funnel URL is publicly reachable. Authentication is enforced by the daemon's bearer-token middleware — never disable it.
