Skip to content

Add --host flag to ty serve - #699

Merged
bborn merged 1 commit into
mainfrom
task/5224-add-host-flag-to-ty-serve-so-the-board-c
Sep 1, 2026
Merged

Add --host flag to ty serve#699
bborn merged 1 commit into
mainfrom
task/5224-add-host-flag-to-ty-serve-so-the-board-c

Conversation

@bborn

@bborn bborn commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Why

ty serve bound fmt.Sprintf(":%d", port) — every interface, including whatever untrusted network the laptop is on. The board can create and execute tasks (spawning agents with the user's credentials), so that reachability is a real exposure. There was also no way to bind a specific address, which is what tailnet-only access needs (tailscale serve is not workable on macOS — the sandboxed App Store client fails to proxy to localhost services).

What changed

  • ty serve --host <addr> sets the bind address; listen address becomes host:port.

  • Empty/unset --host produces :port exactly as before — no behaviour change for anyone.

  • Host validation up front: an IP literal (with optional IPv6 zone) or an RFC 1123 hostname. Anything else (http://127.0.0.1, 127.0.0.1:8080, bad_host) fails with invalid host "...": not a valid IP address or hostname instead of a confusing net.Listen error. Port range is checked too.

  • IPv6 literals are bracketed via net.JoinHostPort (::1[::1]:8080); an already-bracketed [::1] is accepted and not double-bracketed.

  • Startup prints the bind address, and flags the all-interfaces case explicitly:

    Binding :8080 (all interfaces - reachable from your local network)
    Binding 100.107.3.120:8899
    

    web.Server.Start already logs the resolved ln.Addr() on top of that.

Address construction lives in serveListenAddr (cmd/task/serve_addr.go) so it is testable without binding a socket.

Recommendation on the default (not changed here)

I recommend flipping the default to 127.0.0.1 in a follow-up, but did not do it in this PR.

Reasoning for the flip:

  • Both in-repo consumers already use loopback and would be unaffected:
    • Tauri sidecar (desktop/src-tauri/src/supervisor.rs:168) spawns ty serve --port <port> and health-checks/connects over localhost.
    • extensions/ty-chrome hardcodes http://127.0.0.1:8080 / http://localhost:8080 (sw.js:5,91-92, sidepanel.html:30).
  • An unauthenticated board that can execute arbitrary tasks should not be LAN-reachable by default.

Reasoning for holding off in this PR:

  • The default is user-visible behaviour, and someone may already be relying on reaching the board from a phone or a second machine on their LAN. That is an intentional-feeling workflow to break silently in a patch that is otherwise additive. It deserves its own PR with a release note, and now that --host 0.0.0.0 exists there is a clean migration path for anyone who wants the old behaviour back.

Tests

cmd/task/serve_addr_test.go: empty host → :port; whitespace-only host → :port; IPv4; a Tailscale IP with a custom port; hostname; IPv6 literal bracketing; pre-bracketed IPv6; ::; IPv6 with a zone; plus an invalid-input table (scheme, embedded port, path, space, underscore, leading dash, port 0, port 70000).

All pass; go build ./..., go vet ./cmd/task/ and gofmt are clean.

Out of scope

No TLS, no auth, no tailscale integration — bind address only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VyWdPGetDrC7KeZXeg2jWd

ty serve hardcoded ":port", listening on every interface — including
whatever untrusted network the machine happens to be on. The board can
create and execute tasks, so that reachability matters.

--host sets the bind address, so the board can be pinned to a tailnet
address (ty serve --host 100.x.y.z) or to loopback. An empty host keeps
today's ":port" behaviour exactly, so the Tauri sidecar and ty-chrome
(both localhost) are unaffected.

Invalid hosts fail fast with a clear message instead of a confusing
net.Listen error, and IPv6 literals are bracketed via net.JoinHostPort.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyWdPGetDrC7KeZXeg2jWd
@bborn
bborn merged commit 936bfee into main Sep 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant