Skip to content

Add proxy middleware with circuit breaker fallback#177

Open
timvw wants to merge 1 commit intodeborahgu:mainfrom
timvw:pr/proxy
Open

Add proxy middleware with circuit breaker fallback#177
timvw wants to merge 1 commit intodeborahgu:mainfrom
timvw:pr/proxy

Conversation

@timvw
Copy link
Contributor

@timvw timvw commented Feb 17, 2026

Summary

Adds a smart proxy middleware that can forward requests to the real Bose servers with automatic fallback to soundcork's local handlers.

Addresses #152

How it works

A new ProxyMiddleware (Starlette middleware) intercepts requests matching known Bose path prefixes:

Prefix Upstream
/marge/* https://streaming.bose.com
/bmx/* https://content.api.bose.io
/updates/* https://worldwide.bose.com

Modes (controlled by SOUNDCORK_MODE env var):

  • local (default) — all requests handled locally, middleware is a no-op
  • proxy — tries upstream first, falls back to local on failure

Circuit breaker tracks upstream health per-server:

  • Opens on connection errors, timeouts (10s), HTTP 404, and HTTP 5xx
  • When open, skips upstream for 5 minutes (configurable)
  • After cooldown, probes once (half-open state)

Traffic logging in proxy mode — JSONL file with full request/response details.

Files changed

  • New: soundcork/proxy.py — middleware + circuit breaker + logging
  • Modified: soundcork/config.py — adds soundcork_mode and soundcork_log_dir settings
  • Modified: soundcork/main.py — registers the middleware
  • Modified: requirements.txt — adds httpx==0.28.1

Why this is useful

  • During initial setup: verify your speaker is correctly talking to soundcork while still getting real server responses
  • For testing: capture real Bose server responses to improve local handlers
  • The circuit breaker handles the current state gracefully (BMX/updates already 404ing, marge still alive)

Recommendation

Default mode is local (middleware does nothing). I'd recommend keeping local as default for production since the marge server's /streaming/software/update/ endpoint could potentially push firmware updates.

Smart proxy to Bose servers with automatic fallback to local handlers.
Controlled via SOUNDCORK_MODE env var (default: local).

- ProxyMiddleware intercepts /marge, /bmx, /updates prefixes
- Circuit breaker per upstream server (opens on errors/404/5xx)
- 5-minute cooldown before retrying failed upstreams
- Traffic logging to JSONL file
- Adds httpx dependency

Addresses deborahgu#152
@gmuth
Copy link
Contributor

gmuth commented Feb 18, 2026

We really appreciate contributions to research the Bose APIs. However the tooling to achieve this is currently not limiting us. We need more people testing and documenting the API bits and pieces that are missing or not working because they are not (or only partially) implemented by soundcork. So if your tooling helps you to do this please go ahead, use it and come back with your API communication captures to specify what should be implemented or fixed in soundcork.

It is also important that we document the requests to the device that trigger those Bose server APIs calls. Check this example API Spec for "remove-preset" #146.

@timvw
Copy link
Contributor Author

timvw commented Feb 18, 2026

Thanks for the feedback — fair point. The real value is in the API specs, not the tooling itself.

We've been using tshark/Wireshark to capture and research the Spotify-related traffic and documented our findings in #107. Following the format from #146, we've filed #199 for the Spotify OAuth token refresh endpoint (POST /oauth/device/{deviceId}/music/musicprovider/15/token/cs3) — this is the endpoint the speaker calls periodically to refresh its Spotify access token during active playback.

We'll continue capturing traffic and filing API specs for any other missing endpoints we find. The proxy is useful for our own research; we'll focus contributions on the specs and implementations.

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.

2 participants