Repository navigation
Let a browser talk to Ember: CORS, and media keys - #118
Merged
Merged
Conversation
Two things stood between KindleHub's Ember Link card and actually working.
CORS. Ember Link is reached by a BROWSER on a different origin — KindleHub runs
on kindlehub.pro, Ember runs on your LAN — and without these headers the browser
discards the reply. The only shape that worked at all was
fetch(..., {mode:"no-cors"}), which can send a command but can never READ one
back. That makes PAIRING impossible from a web page, because /api/pair answers
with the token in a body no-cors will not let the caller see. A JSON POST is also
not a "simple request", so the browser sends a preflight first; with no
do_OPTIONS handler that 501'd and the command was never sent at all.
This is not a weakening of the auth model, and it is worth being precise about
why. Every /api/* route still demands the PIN or a valid pairing token; the PIN
is still refused off the LAN by _is_lan_ip; remote shell is still refused over
the tunnel. CORS governs who may READ a reply inside a browser — it has never
been what stops a request arriving, since curl and any native app could always
reach these routes. The headers go on through an end_headers override rather
than at each send_response, so the next route somebody adds cannot forget them.
Media keys. `{"type":"media","action":"next"}` presses the OS transport key, so
it drives Spotify, a browser tab, VLC or a local file without Ember needing to
know which is in front. Actions are named by INTENT — playpause / next / prev /
stop / volup / voldown / mute — because the caller is a Kindle and should not
have to know the key is spelled "nexttrack". An unknown action is refused by
name rather than silently pressing nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTqV95KXLxBZuJhKULbmyp
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.
Two things stood between KindleHub's Ember Link card and actually working.
CORS
Ember Link is reached by a browser on a different origin — KindleHub runs on kindlehub.pro, Ember runs on your LAN — and without these headers the browser discards the reply. The only shape that worked at all was
fetch(..., {mode:"no-cors"}), which can send a command but can never read one back. That makes pairing impossible from a web page, because/api/pairanswers with the token in a bodyno-corswill not let the caller see.A JSON POST is also not a "simple request", so the browser sends a preflight first; with no
do_OPTIONShandler that 501'd and the command was never sent at all.This is not a weakening of the auth model, and it's worth being precise about why. Every
/api/*route still demands the PIN or a valid pairing token; the PIN is still refused off the LAN by_is_lan_ip; remote shell is still refused over the tunnel. CORS governs who may read a reply inside a browser — it has never been what stops a request arriving, since curl and any native app could always reach these routes.The headers go on through an
end_headersoverride rather than at eachsend_response, so the next route somebody adds cannot forget them.Media keys
{"type":"media","action":"next"}presses the OS transport key, so it drives Spotify, a browser tab, VLC or a local file without Ember needing to know which is in front.Actions are named by intent —
playpause/next/prev/stop/volup/voldown/mute— because the caller is a Kindle and should not have to know the key is spellednexttrack. An unknown action is refused by name rather than silently pressing nothing.Testing
python3 -m py_compilepasses, and themediabranch is confirmed to sit inside_apply_locked's elif chain rather than after it. Not run against a live Ember instance — that needs your machine.🤖 Generated with Claude Code
https://claude.ai/code/session_01LTqV95KXLxBZuJhKULbmyp
Generated by Claude Code