Skip to content

Pearl th-6dd202: th code sends the local token — fixes 401 on every connect#281

Merged
brentrager merged 2 commits into
mainfrom
th-6dd202-thcode-auth
Jul 26, 2026
Merged

Pearl th-6dd202: th code sends the local token — fixes 401 on every connect#281
brentrager merged 2 commits into
mainfrom
th-6dd202-thcode-auth

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

The bug

th code was completely unusable against a normal Big Smooth:

Error: Cannot connect to Big Smooth at http://localhost:4400:
WebSocket connection failed: HTTP error: 401 Unauthorized. Run: th up

th code called connect_async(&ws_url) with no authentication whatsoever, while the daemon runs the operator's strict-auth local flavor (strict_auth(true)) which rejects unauthenticated upgrades rather than degrading to anonymous. And the error told you to th up — a server that was already running — sending you after the wrong thing.

Verified empirically (live daemon on :4400, not guessed)

Attempt Result
no auth (what th code did) 401
Authorization: Bearer <token> 401 — the upgrade path doesn't consult headers
?token=<correct> 101 Switching Protocols
?token=WRONG 401

Fix

  • Resolve the token exactly the way the daemon does: SMOOTH_LOCAL_TOKEN~/.smooth/operator-token. Read-only — the daemon provisions the token; a client minting its own would only send a value the server never accepts. No token anywhere → connect unauthenticated (correct for a non-strict server).
  • Pass it as ?token=, percent-encoded so a user-supplied SMOOTH_LOCAL_TOKEN containing &, #, or a space can't silently truncate the query into a baffling 401.
  • On 401, say what's actually wrong (token mismatch, and where the token is read from) instead of misdirecting to th up.

Tests

token_is_percent_encoded_for_the_query_string (hex passes through, unreserved preserved, metacharacters escaped) and local_token_prefers_env_and_trims (env wins, whitespace trimmed, blank env doesn't resolve to an empty token). Serialized on a mutex since they mutate process-global env and cargo runs tests in parallel threads. Full crate suite green: 252 passed.

🤖 Generated with Claude Code

…onnect

th code called connect_async with NO auth while the daemon runs the operator's
strict-auth local flavor, so every session died on 401 Unauthorized and the
error misdirected to `Run: th up` for an already-running server.

Verified against the live daemon: no-auth=401, Authorization: Bearer=401 (the
upgrade path doesn't consult headers), ?token=<correct>=101 Switching
Protocols, ?token=WRONG=401. Resolve the token exactly as the daemon does
(SMOOTH_LOCAL_TOKEN -> ~/.smooth/operator-token, read-only since the daemon
provisions it) and pass it as ?token=, percent-encoded so & / # / space can't
truncate the query. 401 now explains the token mismatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwUg6nyMqXHzFDsibzTKmp
@changeset-bot

changeset-bot Bot commented Jul 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d57ed29

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager enabled auto-merge (squash) July 26, 2026 21:00
@brentrager
brentrager merged commit d18e080 into main Jul 26, 2026
3 checks passed
@brentrager
brentrager deleted the th-6dd202-thcode-auth branch July 27, 2026 14:18
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