fix: OAuth auth, gzip decompression, and adjustable base endpoints#22
Open
mmto-io wants to merge 1 commit intosteipete:mainfrom
Open
fix: OAuth auth, gzip decompression, and adjustable base endpoints#22mmto-io wants to merge 1 commit intosteipete:mainfrom
mmto-io wants to merge 1 commit intosteipete:mainfrom
Conversation
Several bugs prevent eightctl from authenticating and controlling the
adjustable base on current Eight Sleep firmware.
1. authTokenEndpoint() hardcodes empty client_secret instead of using
c.ClientID/c.ClientSecret from the struct. Every OAuth attempt
returns 400, falling back to legacy login which rate-limits (429).
2. do() sets Accept-Encoding: gzip but never decompresses responses.
Raw gzip bytes fed to json.Decode() cause parse errors.
3. Base endpoints are served by app-api.8slp.net, not client-api.
Add AppBaseURL field and doApp() helper for base commands.
4. SetAngle sends {head, foot} but API expects {torsoAngle, legAngle}.
5. RunPreset only updates the UI label server-side without commanding
motors. Now fetches preset angles and calls SetAngle to move.
Tested on TriMix v5 pod. All existing tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR: Fix auth, gzip handling, and base/motor API endpoints
Title
fix: OAuth auth, gzip decompression, and adjustable base endpointsDescription
Several bugs prevent eightctl from authenticating and controlling the adjustable base on current Eight Sleep firmware (tested on a TriMix v5 pod, firmware as of Feb 2026). This PR fixes all of them.
1. OAuth token endpoint sends empty
client_secret(critical)Bug:
authTokenEndpoint()hardcodes"client_id": "sleep-client"and"client_secret": ""instead of usingc.ClientIDandc.ClientSecret, which are correctly populated fromdefaultClientID/defaultClientSecretinNew().Result: Every OAuth attempt returns 400 Bad Request. The client falls back to the legacy
/loginendpoint, which rate-limits aggressively (429).Fix: Two-line change — use
c.ClientIDandc.ClientSecretfrom the struct.2. Gzip responses not decompressed
Bug:
do()setsAccept-Encoding: gzipbut never decompresses gzip responses. When the API responds withContent-Encoding: gzip, raw gzip bytes are passed tojson.Decode(), causing parse errors.Fix: Check
Content-Encodingheader indo(), wrapresp.Bodyingzip.NewReaderwhen needed. The decompressed reader is used for both error responses and JSON decoding.3. Base/motor endpoints use wrong API host
Bug: All base endpoints (
/users/{id}/base,/users/{id}/base/angle,/users/{id}/base/presets,/devices/{id}/vibration-test) are served byapp-api.8slp.net, notclient-api.8slp.net. Theclient-apihost returns 404 for these paths.Fix: Add
AppBaseURLfield toClient(defaults tohttps://app-api.8slp.net/v1), add adoApp()helper that temporarily swaps the base URL for the request, and update all base endpoints to usedoApp().4.
SetAnglesends wrong field namesBug:
SetAngle()sends{"head": N, "foot": N}but the API expects{"torsoAngle": N, "legAngle": N}. The request succeeds (200) but the bed doesn't move.Fix: Use the correct field names.
5.
RunPresetdoesn't actually move the bedBug:
RunPreset()doesPOST /base/presetswith{"name": "relaxing"}, which updates the UI preset label on the server but doesn't command the motors. The actual motor endpoint isPOST /base/anglewith torso/leg angles.Fix:
RunPresetnow fetches the presets list, resolves the matching preset's angles, and callsSetAngle()to physically move the base. Preset resolution follows this priority:name-custom) with non-zero anglesmetaOf == name) with non-zero anglesCLI improvements (minor)
base angleandbase preset-runnow accept positional arguments (eightctl base angle 20 10oreightctl base preset-run relaxing) in addition to flagsbase preset-runvalidates that a name is providedFiles changed
internal/client/eightsleep.go— auth fix, gzip decompression,doApp()helper,AppBaseURLfieldinternal/client/base.go— correct field names, correct API host, preset resolution logicinternal/cmd/base.go— positional arg support forangleandpreset-runTesting
eightctl base inforeturns base status (was 404)eightctl base angle 20 10physically moves the bedeightctl base preset-run relaxingresolves angles from preset list and moves the bedeightctl base presetslists all presets with correct anglesgo test ./...)