Skip to content

Commit c5c74a9

Browse files
sodiumsunclaude
andcommitted
fix: Cloudflare 403 broke Power-trial start (missing User-Agent)
The REAL cause of 'Start trial does nothing' and the sign-in auto-start miss — not a race. api.heard.dev is behind Cloudflare, which 403s a bare urllib request (default UA). The daemon's /v1/me poll and its trial net send User-Agent, so they worked; the sign-in enroll (_maybe_start_power_trial), the Settings button (_act_start_power_trial), and the byok /v1/me refresh (_refresh_byok_enabled) did NOT — so all three silently 403'd. Log confirmed: 'sign-in autostart failed: HTTP Error 403: Forbidden' while 'power_trial_autostarted via=daemon_poll' succeeded. Adds User-Agent to all three. Same CF-1010/403 class as the Groq key needing a UA. persona.py already had one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019GTBZjBGurqsHUHsjaU1gA
1 parent 93d64fc commit c5c74a9

2 files changed

Lines changed: 11 additions & 3 deletions

File tree

heard/home_window.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,12 @@ def _work():
816816
req = urllib.request.Request(
817817
f"{base}/v1/power/trial/start",
818818
method="POST",
819-
headers={"Authorization": f"Bearer {token}"},
819+
# User-Agent REQUIRED — Cloudflare 403s a bare urllib
820+
# request, which made this button silently do nothing.
821+
headers={
822+
"Authorization": f"Bearer {token}",
823+
"User-Agent": "Heard-app/1.0",
824+
},
820825
)
821826
with urllib.request.urlopen(req, timeout=10) as r: # noqa: S310
822827
data = json.loads(r.read().decode() or "{}")

heard/url_scheme.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ def _maybe_start_power_trial(token: str) -> None:
9393
req = urllib.request.Request(
9494
f"{base}/v1/power/trial/start",
9595
method="POST",
96-
headers={"Authorization": f"Bearer {token}"},
96+
# User-Agent is REQUIRED: Cloudflare (in front of api.heard.dev) 403s
97+
# a bare urllib request. Without it the trial silently never starts.
98+
headers={"Authorization": f"Bearer {token}", "User-Agent": "Heard-app/1.0"},
9799
)
98100
with urllib.request.urlopen(req, timeout=10) as r: # noqa: S310
99101
data = json.loads(r.read().decode() or "{}")
@@ -124,7 +126,8 @@ def _refresh_byok_enabled(token: str) -> None:
124126

125127
base = config.load().get("heard_api_base") or "https://api.heard.dev"
126128
req = urllib.request.Request(
127-
f"{base}/v1/me", headers={"Authorization": f"Bearer {token}"}
129+
f"{base}/v1/me",
130+
headers={"Authorization": f"Bearer {token}", "User-Agent": "Heard-app/1.0"}
128131
)
129132
with urllib.request.urlopen(req, timeout=10) as r: # noqa: S310
130133
data = json.loads(r.read().decode() or "{}")

0 commit comments

Comments
 (0)