Cloudflare Workers
The nine edge services behind Orchard, why they exist, and how to provision each one for a self-hosted deployment.
Nine Cloudflare Workers back the parts of Orchard that need a credential the app must not
ship, or a service that should not run per-client. They live under workers/, each as its
own npm project with its own wrangler.jsonc.
If you self-host Orchard’s backend, this page is your index. Every worker has its own README with exact provisioning steps.
The workers
| Worker | What it does | Backing |
|---|---|---|
bpm | Tempo and musical-key lookup | GetSongBPM |
concerts | Nearby music-event discovery | Ticketmaster |
lastfm | Signs and forwards Last.fm auth and scrobbles | Last.fm |
listening-party | Room coordination and WebRTC signaling | Durable Objects |
song-links | Cross-service share links | D1 |
support | Two-way support reports | D1, R2, Discord, GitHub |
artwork-proxy | Animated artwork MP4 to cacheable GIF | External converter |
artist-metadata | Broad artist genre resolution | iTunes, D1 |
artist-packs | Official artist pack distribution | Static content |
Why they exist
The recurring reason is credential ownership. Orchard is open source and its binaries are
distributed publicly, so any API key inside the app is a published API key. The lastfm
worker owns Orchard’s Last.fm API key and shared secret and signs requests on the client’s
behalf; the bpm worker owns the GetSongBPM key; concerts owns the Ticketmaster key.
None of those secrets are committed or bundled.
The second reason is quota and caching. bpm ranks and normalizes results and caches
successful lookups specifically to protect GetSongBPM’s hourly quota, which per-client
requests would burn through.
Common workflow
Each worker is provisioned the same way:
cd workers/<name>
npm install
npx wrangler login
npx wrangler secret put <SECRET_NAME>
npm run deploy
Most have npm run check for validation before deploying. After deployment, use the
assigned workers.dev URL or configure a custom domain.
Worker notes
bpm
GET /bpm?title=Master%20of%20Puppets&artist=Metallica
title is required, artist optional. Returns bpm, key, openKey, timeSignature,
and matched song metadata. GET /health is a liveness check.
The root page carries the backlink GetSongBPM’s API terms require, and the deployed URL must be registered at GetSongBPM’s API page.
concerts
/events?location=Example%20City,%20CA
/events?location=00000
/events?lat=0&lng=0
Needs the Ticketmaster key as a secret.
lastfm
Needs both LASTFM_API_KEY and LASTFM_SHARED_SECRET. The desktop keeps each user’s
session key encrypted locally; neither credential reaches the client.
listening-party
Coordination and signaling only. It never proxies or streams media. Endpoints and socket messages are documented in Listening Parties.
song-links
D1-backed, storing one canonical Orchard song row per track.
npm run d1:create
# copy the printed database_id into wrangler.jsonc
npm run d1:migrate:local
YouTube and YouTube Music resolve directly from a video id. Apple Music and Deezer resolve
through public lookup APIs. TIDAL resolves through the official API when credentials are
configured. Spotify stays a branded search link, because Spotify’s catalog search API is
blocked for this application. Set ARTWORK_API_ORIGIN to a compatible Apple Music artwork
service to fill in missing cover art.
support
The most involved one. It stores anonymous report identities and conversations in D1, keeps screenshots in a private R2 bucket, opens one private Discord forum thread per report, and mirrors each report description into a public-safe GitHub issue.
npx wrangler d1 create orchard-support
npx wrangler r2 bucket create orchard-support-screenshots
Set SUPPORT_URL and GITHUB_REPOSITORY in wrangler.jsonc. Attribution uses a GitHub App
with Device Flow enabled and read/write access to Issues; only the app’s public client
id goes in GITHUB_CLIENT_ID. The client secret and private key are never added to
Orchard, because the desktop flow does not use either.
Never commit ids, bot tokens, or local .dev.vars files.
artwork-proxy
/convert.gif?url=<encoded Apple artwork MP4 URL>
The worker validates input, authenticates to the conversion service, streams its response,
and caches successful artwork at the edge. Actual conversion runs on a separate server
(services/artwork-converter/). Worker and converter share a random token, stored as a
worker secret. Set CONVERTER_URL in wrangler.jsonc.
artist-metadata
curl -sG 'https://<worker>/artist' \
--data-urlencode 'artist=SZA' \
--data-urlencode 'album=SOS' \
--data-urlencode 'youtubeBrowseId=UC...'
Orchard sends an artist name and a known album. The worker only returns a genre when the album belongs to the same iTunes artist id, which is what stops two artists sharing a name from being merged. Confirmed mappings are cached in D1.
artist-packs
Serves official artist pack content from workers/artist-packs/content/. See
Artist Packs.
Testing
Workers with a test/ directory run their tests through their own npm scripts. The support
worker uses Vitest.
Service dependency warning
Every one of these depends on an upstream that can change without notice. When a feature stops working, the worker is usually reporting an upstream problem rather than having one. See Troubleshooting.