OrchardDocs

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

WorkerWhat it doesBacking
bpmTempo and musical-key lookupGetSongBPM
concertsNearby music-event discoveryTicketmaster
lastfmSigns and forwards Last.fm auth and scrobblesLast.fm
listening-partyRoom coordination and WebRTC signalingDurable Objects
song-linksCross-service share linksD1
supportTwo-way support reportsD1, R2, Discord, GitHub
artwork-proxyAnimated artwork MP4 to cacheable GIFExternal converter
artist-metadataBroad artist genre resolutioniTunes, D1
artist-packsOfficial artist pack distributionStatic 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.

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.