Skip to content

[bug] CLI daemon composes approvalUrl from its own listener origin, so behind TLS the link is http:// and unreachable #1954

Description

@tkstang

Executor version

1.6.2 observed; the composition is unchanged at v1.6.8 (see below).

How do you run Executor?

CLI (executor daemon run)

Operating system

macOS (Apple Silicon)

Integration involved

MCP server (browser elicitation / elicitation_mode=browser)

What happened

When a gated tool call pauses, the daemon returns structuredContent.approvalUrl anchored at the request's own origin. In packages/hosts/mcp/src/browser-approval.ts, approvalUrlForRequest does new URL(path, request.url), so the authority reflects the Host header but the scheme is whatever the daemon's own listener spoke — always http:// behind a TLS-terminating proxy, because the proxy talks plain HTTP to loopback:

http://<host>:<port>/resume/<execution-id>?mcp_session_id=<session-id>

The host and port are taken from the request, so a reverse proxy or a Tailscale serve/funnel route is reflected correctly — but the scheme is not. On a TLS-only listener the emitted link is unreachable exactly as given: the client must rewrite http to https before a human can open it.

Any deployment that reaches the daemon over TLS (which is the only sane way to expose it beyond loopback) must post-process the approval link before presenting it. Agent clients that render the URL verbatim — which the resumePrompt text explicitly instructs them to do — hand the user a dead link.

What you expected

The scheme to be derived the same way the authority already is — from the forwarded request — or to be overridable.

Proposed fix: compose the scheme from the same forwarded request context as the authority: honour X-Forwarded-Proto when the daemon is configured to trust its proxy, and otherwise let an explicit base URL (EXECUTOR_WEB_BASE_URL or a config key) win over both. Falling back to http:// only when neither is available keeps the loopback default unchanged.

Steps to reproduce

  1. Run executor daemon run --foreground --port <port> (binds loopback).
  2. Put any TLS-terminating reverse proxy in front of it on https://<host>:<tls-port>.
  3. POST https://<host>:<tls-port>/mcp?elicitation_mode=browser with the bearer, initialize, then call execute with a program that invokes a tool covered by a require_approval policy.
  4. The paused result's approvalUrl is http://<host>:<tls-port>/resume/<execution-id>?mcp_session_id=<session-id> — the authority is right, the scheme is not, and the URL does not resolve.

What I tried:

Attempt Result
EXECUTOR_WEB_BASE_URL=https://… ignored; link still http://
X-Forwarded-Proto: https ignored; link still http://
X-Forwarded-Host authority honoured, scheme not
TLS-terminating proxy in front link still http://

Diagnostics / logs

Observed on executor 1.6.2 (darwin-arm64, npm), macOS 26.4, TLS terminated by a reverse proxy on the same host. Checked against the v1.6.8 tag before filing: approvalUrlForRequest in packages/hosts/mcp/src/browser-approval.ts still builds from request.url, and no 1.6.3–1.6.8 release note touches approval-URL composition. No logs attached — the behaviour is fully described by the returned approvalUrl above; all identifiers are placeholders.

Before you submit

  • I searched the open issues for a duplicate.
  • I removed all keys, tokens, and credentials from this report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions