# Cloudflare Worker Backend — Unspooled

## Workers (May 2026 — Merged)

As of 2026-05-31, the auth and config workers have been **merged** into a single worker. All routes now live on `unspooled-config-v3`.

| Name | URL | Purpose |
|------|-----|---------|
| `unspooled-config-v3` | `https://unspooled-config-v3.tw24fr.workers.dev` | **ALL routes** — Debrid auth, addon URL entry, addon config sessions, smoke tests |
| `unspooled-auth-v2` | `https://unspooled-auth-v2.tw24fr.workers.dev` | **[DEPRECATED]** Use config worker instead. Still deployed but no longer needed. |

The merge fixed a 404 bug: DebridAuthApiClient and AddonUrlApiClient pointed to `addon-config-worker` URL (via `ADDON_CONFIG_WORKER_URL` BuildConfig), but auth routes only existed on `unspooled-auth-v2`. By merging both `AuthSessionDO` and `ConfigSessionDO` into one worker, all API paths work regardless of which URL the Android client uses.

**IMPORTANT:** Future Android builds should use separate BuildConfig fields (`ADDON_AUTH_WORKER_URL` + `ADDON_CONFIG_WORKER_URL`) to avoid depending on the merge. The merge was a server-side workaround for the already-shipped APK.

Source: `~/Unspooled/services/addon-config-worker/`
Deploy: `cd ~/Unspooled/services/addon-config-worker && CLOUDFLARE_API_TOKEN="***" npx wrangler deploy --config <config>.toml`

## Auth Worker Routes

### Debrid Auth
- `POST /api/debrid/auth-sessions` — create auth session (body: `{ providerId }`). OAuth services (realdebrid, premiumize) get device-code flow; API key services (torbox, alldebrid, debrider, offcloud) return a form URL.
- `GET /api/debrid/auth-sessions/{code}` — poll auth status
- `POST /api/debrid/auth-sessions/{code}/cancel` — cancel
- `GET /debrid/auth/form/{code}` — phone web form (shows user_code + QR for OAuth, API key input for key services)
- `GET /debrid/auth/{code}` — alias (same as form)
- `GET /debrid/oauth/start/{service}` — direct OAuth start (returns device code + verification URL)
- `GET /debrid/key/start/{service}` — direct key session start
- `POST /debrid/auth/submit/{code}` — phone submits API key
- `GET /debrid/auth/status/{code}` — poll endpoint
- `GET /debrid/auth/cancel/{code}` — cancel

### Addon URL Entry
- `POST /api/addons/url-sessions` — create URL session
- `GET /api/addons/url-sessions/{code}` — poll status
- `POST /api/addons/url-sessions/{code}/cancel` — cancel
- `GET /addon/url/start` — direct URL session start
- `GET /addon/url/form/{code}` — phone web form with URL input
- `GET /addons/url/{code}` — alias (same as form)
- `POST /addon/url/submit/{code}` — phone submits manifest URL (validates via fetch)
- `GET /addon/url/status/{code}` — poll
- `GET /addon/url/cancel/{code}` — cancel

### Smoke Tests
- `POST /api/v1/smoke-test/run` — run smoke tests on all enabled addons (~30s, returns snapshots)
- `GET /api/v1/smoke-test/status` — latest smoke test results

## Config Worker Routes

- `POST /api/addons/config-sessions` — create config session
- `GET /api/addons/config-sessions/{code}` — poll
- `POST .../complete` — submit configured manifest URL
- `POST .../cancel` — cancel
- `GET /addons/config/{code}` — phone web config page

## Technical Notes

- **Free plan limitation:** `stub.methodName()` RPC does NOT work. Use `stub.fetch(request)` with URL-path dispatch instead. DO class must implement `async fetch(request: Request): Promise<Response>`.
- **Migrations:** Free plan requires `new_sqlite_classes` for new DO namespaces. Existing namespaces created with `new_classes` cannot be changed — use a fresh worker name to recreate.
- **Token deployment:** CF_API_TOKEN stored in `/home/rurouni/Unspooled/services/addon-config-worker/deploy.sh`. Use Python subprocess for deploy (avoids shell token mangling with `***` redaction).
- **API aliases:** Android clients use `/api/addons/url-sessions` and `/api/debrid/auth-sessions` — the worker maps these to its internal flat routes.
