Skip to content

fix: v0.2.35 — bind to localhost by default, auto-detect Tailscale#117

Merged
gbasin merged 1 commit intomasterfrom
fix/default-localhost
Mar 31, 2026
Merged

fix: v0.2.35 — bind to localhost by default, auto-detect Tailscale#117
gbasin merged 1 commit intomasterfrom
fix/default-localhost

Conversation

@gbasin
Copy link
Copy Markdown
Owner

@gbasin gbasin commented Mar 31, 2026

Summary

  • Default bind address changed from 0.0.0.0 to 127.0.0.1 — prevents unintentional network exposure on untrusted networks (coffee shop WiFi, corporate LAN, cloud VMs)
  • Auto-detect Tailscale: when using the default localhost binding, the server also binds to the Tailscale IP automatically, so remote access over tailnet works without configuration
  • Only advertises Tailscale URL if bind succeeded/api/server-info and startup log won't show a Tailscale URL that isn't actually reachable
  • Set HOSTNAME=0.0.0.0 to restore the old behavior
  • README updated with accurate defaults and a security note about network exposure

Breaking change

Users who relied on the previous 0.0.0.0 default for LAN access (without Tailscale) will need to explicitly set HOSTNAME=0.0.0.0.

Test plan

  • bun run lint && bun run typecheck clean
  • All 643 tests pass
  • Verified bun run dev binds to 127.0.0.1 + Tailscale IP, not LAN
  • Verified 192.168.x:4040 is connection refused
  • Verified 100.x.y.z:4040 works over Tailscale
  • Verify HOSTNAME=0.0.0.0 agentboard restores old behavior
  • Verify startup without Tailscale installed → localhost only, no warning

Default bind address changed from 0.0.0.0 to 127.0.0.1 to prevent
unintentional network exposure. When using the default localhost
binding, the server automatically detects Tailscale and binds to
the Tailscale IP as well, so remote access over tailnet works
without configuration. Set HOSTNAME=0.0.0.0 to restore the old
behavior.
@gbasin gbasin merged commit 96c99cb into master Mar 31, 2026
5 checks passed
@gbasin gbasin deleted the fix/default-localhost branch March 31, 2026 13:57
gbasin added a commit that referenced this pull request Apr 1, 2026
…ssure (#117)

Three-layer fix for slow terminal painting after kill → auto-select,
plus WebSocket backpressure handling for remote browsers.

**Terminal attach (31s → 100ms):**
- Use queueMicrotask for session-switch attach instead of 50ms debounce
  that could be cancelled by React effect re-runs, permanently losing
  terminal history
- Keep 50ms debounce only for reconnection/epoch-change dedup
- Add diagnostic logging: terminal_output_dropped, attach_debounce_cancelled

**Kill responsiveness:**
- flushSync in handleKillSession forces immediate DOM update so the
  session card disappears before React defers to WebSocket processing
- Yield after history send to flush WebSocket buffers before subsequent
  synchronous work can delay delivery

**WebSocket backpressure (20MB → 500KB):**
- Truncate lastUserMessage to 250 chars in agent-sessions broadcast
  (client already truncates to 200 for display). Drops payload from
  20.9MB to ~1.3MB for 4,869 inactive sessions
- Drop live terminal-output frames when getBufferedAmount() > 512KB
  to prevent buffer growing to 15-20MB on slow connections
- Yield in log poller and session refresh before blocking SQLite work
  so pending WebSocket frames flush first
gbasin added a commit that referenced this pull request Apr 1, 2026
…ssure (#117)

Three-layer fix for slow terminal painting after kill → auto-select,
plus WebSocket backpressure handling for remote browsers.

**Terminal attach (31s → 100ms):**
- Use queueMicrotask for session-switch attach instead of 50ms debounce
  that could be cancelled by React effect re-runs, permanently losing
  terminal history
- Keep 50ms debounce only for reconnection/epoch-change dedup
- Add diagnostic logging: terminal_output_dropped, attach_debounce_cancelled

**Kill responsiveness:**
- flushSync in handleKillSession forces immediate DOM update so the
  session card disappears before React defers to WebSocket processing
- Yield after history send to flush WebSocket buffers before subsequent
  synchronous work can delay delivery

**WebSocket backpressure (20MB → 500KB):**
- Truncate lastUserMessage to 250 chars in agent-sessions broadcast
  (client already truncates to 200 for display). Drops payload from
  20.9MB to ~1.3MB for 4,869 inactive sessions
- Drop live terminal-output frames when getBufferedAmount() > 512KB
  to prevent buffer growing to 15-20MB on slow connections
- Yield in log poller and session refresh before blocking SQLite work
  so pending WebSocket frames flush first
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