diff --git a/README.md b/README.md index 41571ec..c816f4e 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,107 @@ # AMDL wrapper -The Apple Music account side of [amdl](https://github.com/AMDL-Web/apple-music-downloader), -in two parts: +Holds an Apple Music account session for [amdl](https://github.com/AMDL-Web/apple-music-downloader). +amdl asks it for playlists, decryption keys, lyrics, web-playback data, licenses +and the account's music token over HTTP (amdl's `wrapper.address`); it does the +Apple-side work amdl cannot do itself. | Part | Directory | What it does | License | | --- | --- | --- | --- | -| **wrapper-lite** | this root (`lite/`, launchers, `rootfs/`, `qemu/`) | Runs Apple Music's Android libraries for one logged-in account and serves `/m3u8`, `/key`, `/lyrics`, `/webplayback`, `/license` and `/token` on one HTTP port. | MIT | -| **wrapper-manager** | [`manager/`](manager/) | Starts, logs in, restarts and restores wrapper-lite instances behind one wrapper-lite-compatible port, with `POST /login` (two-step 2FA) and `POST /logout`. | AGPL-3.0 ([`manager/LICENSE`](manager/LICENSE)) | - -amdl talks to either one over HTTP (`wrapper.address`). A bare wrapper-lite is -enough for downloading; the manager adds the login API that amdl's console and -desktop app use to sign an account in. amdl's desktop app can also run -wrapper-lite itself in a QEMU guest built by this repository's `build-lite` -workflow. - -Neither part has authentication: never expose its port beyond the machines that -need it. `GET /token` returns the account's music token. - -## wrapper-lite - -> **AMDL-Web fork.** wrapper-lite is upstream -> [`WorldObservationLog/wrapper@lite`](https://github.com/WorldObservationLog/wrapper/tree/lite) -> plus two patches to `lite/playback.cpp`. It releases the -> FairPlay decrypt context of each `/key` once its template has been copied: -> upstream keeps every context, and after 1024 successful `/key` calls in one -> process every later `/key` fails with `KDCanProcessCKC status: -42786` until -> the process restarts. And it fetches the persistent key once per `/key` -> instead of twice: each fetch is a license round trip to Apple, so `/key` -> takes about half as long. The fork also adds `GET /token`. Builds come from -> upstream's `build-lite` workflow, with its macOS job moved to `macos-15` since -> GitHub retired `macos-14`; a change under `manager/` alone does not run it. -> Upstream changes are merged in, not rebased. The manager comes from -> [`WorldObservationLog/wrapper-manager@v2`](https://github.com/WorldObservationLog/wrapper-manager/tree/v2) -> and is merged in under `manager/` with `git subtree`. - -A lightweight single-port HTTP wrapper for Apple Music decryption. - -It provides six endpoints on one HTTP port: M3U8, Key, Lyrics, License, WebPlayback, and Token. - -> **Recommended way to run**: use the `wrapper-lite-qemu` launcher — it boots a -> self-contained QEMU guest and forwards the HTTP API to the host, so you don't -> need a rooted device or a native chroot. See -> [Run with QEMU](#run-with-qemu-recommended). - -## Build +| **wrapper-lite** | this root (`lite/`, launchers, `rootfs/`, `qemu/`) | Runs Apple Music's Android libraries for one logged-in account and serves the [HTTP API](#http-api) on one port. | MIT | +| **wrapper-manager** | [`manager/`](manager/) | Starts, logs in, restarts and restores up to 10 wrapper-lite instances behind one wrapper-lite-compatible port, with `POST /login` (two-step 2FA) and `POST /logout`. | AGPL-3.0 ([`manager/LICENSE`](manager/LICENSE)) | + +A bare wrapper-lite can serve downloads. The manager adds the login API that +amdl's console and desktop app use to sign accounts in. Neither part has +authentication: keep its port on the machines that need it (`GET /token` +returns the account's music token). The Cloudflare deployment adds a key. + +## Run it + +**amdl desktop app.** Nothing to do here. The app downloads the QEMU guest +(`manager/guest/`), pinned by digest, and runs the manager inside it on this +machine. + +**Docker, or a Linux host.** Run the manager, then log in from amdl's console. + +```shell +git clone -b lite https://github.com/AMDL-Web/wrapper +cd wrapper/manager +docker compose up -d # privileged container; publishes 8080 on every interface +``` + +Without Docker (Linux x86_64, user namespaces and chroot available): + +```shell +cd wrapper/manager +go build -o wrapper-manager . +./wrapper-manager # binds localhost:8080; -host sets the address +``` + +The API has no authentication, so keep 8080 off networks you do not trust (to +bind the Docker port to localhost only, change `ports` in +`manager/docker-compose.yml` to `127.0.0.1:8080:8080`). On first start the manager installs the wrapper-lite payload into `data/wrapper/`. +Flags, endpoints and payload pinning are in [`manager/README.md`](manager/README.md). + +**Cloudflare.** One-click deploy of the manager behind a static key, with an +encrypted backup in R2 and a daily token renewal. See +[`cloudflare/README.md`](cloudflare/README.md). + +Then set the address (and key, for Cloudflare) in amdl's Settings; the amdl docs +cover [the wrapper](https://github.com/AMDL-Web/apple-music-downloader/blob/dev/docs/install.md#the-wrapper) +and [its configuration](https://github.com/AMDL-Web/apple-music-downloader/blob/dev/docs/configuration.md#wrapper). + +## Release assets + +Every successful push build on `lite` publishes its artifacts as GitHub +pre-releases ([`publish.yml`](.github/workflows/publish.yml)); an asset is never +replaced once published, so its sha256 can be pinned: + +| Tag | Asset | From | +| --- | --- | --- | +| `guest-` | `manager-qemu-guest.zip` | `manager.yml`: the QEMU guest of the manager (`manager/guest/`) | +| `lite-` | `wrapper-lite-linux-x86_64.zip` | `build-lite.yml`: the native payload the manager installs | + +`` is the first 12 characters of the commit. The native payload carries +22 of the 98 libraries in `rootfs/system/lib64`, the ones wrapper-lite loads; +`tools/slim-rootfs.py` removes the rest when the package is assembled, and its +`KEEP` list says why each stays. The repository's `rootfs/` stays whole. + +## What differs from upstream + +This is a fork of [`WorldObservationLog/wrapper@lite`](https://github.com/WorldObservationLog/wrapper/tree/lite) +with [`wrapper-manager@v2`](https://github.com/WorldObservationLog/wrapper-manager/tree/v2) +merged in under `manager/`. Upstream changes are merged in, not rebased. + +wrapper-lite (`lite/`, launchers): + +- **1024-key fix.** Upstream keeps the FairPlay decrypt context of every `/key`; + after 1024 successful calls in one process every later `/key` fails with + `KDCanProcessCKC status: -42786` until the restart. The fork frees each + context once its template has been copied. It also fetches the persistent key + once per `/key` instead of twice, which halves a `/key` (each fetch is a + license round trip to Apple). +- **`GET /token`**, with `refreshed_at` and `next_refresh_at`. +- **Stale token refresh at start.** A process that starts when the cached token + is older than `--token-refresh-interval` refreshes it a few seconds in, not a + full interval later (a woken container, a machine back from a restart). +- **`APPLE_ID` file and `/status` `name`.** Login records the Apple ID in + `/APPLE_ID`; `/status` reports it so clients can label the account. +- **Root launcher in a private mount namespace.** `wrapper-lite` run as root + mounts in its own namespace instead of the host's. +- **Slimmed native payload** (see above) and `macos-15` in `build-lite` (GitHub + retired `macos-14`). + +wrapper-manager (`manager/`): up to 10 accounts, `/status.instances`, +`/token?instance=`, login cancel, `POST /shutdown` and durable writes for the +QEMU guest, the admin API, and a payload pinned by URL and sha256. The details +are in [`manager/README.md`](manager/README.md). + +A change under `manager/`, `cloudflare/` or a `.md` file alone does not run +`build-lite`. + +## wrapper-lite reference + +### Build ```bash mkdir -p build && cd build @@ -63,7 +121,7 @@ Build outputs: - `wrapper-lite` — privileged host launcher - `wrapper-lite-rootless` — rootless host launcher (user namespaces) -## Run natively +### Run natively ```bash # Login, cache tokens, then exit @@ -78,21 +136,15 @@ Build outputs: - Interactive prompt when a TTY is available. - With `--code-from-file`, the code is read from `data/2fa.txt`. -The native package (the `wrapper-lite-linux-x86_64` artifact) carries 22 of the -98 libraries in `rootfs/system/lib64`: the ones wrapper-lite loads. -`tools/slim-rootfs.py` removes the rest when the package is assembled, so the -repository's `rootfs/` stays whole. Its `KEEP` list says why each library stays. - -## Run with QEMU (recommended) +### Run with QEMU -`wrapper-lite-qemu` is the recommended launcher. It boots the prebuilt guest +`wrapper-lite-qemu` boots the prebuilt guest (the `qemu/` assets) in QEMU and forwards the guest's HTTP service to the host, -so no rooted device or native chroot is required. Prebuilt releases already -bundle QEMU plus its firmware in `qemu/bin/` for Linux, macOS, Windows and -Android. +so no rooted device or native chroot is required. Packages that bundle QEMU +and its firmware put them in `qemu/bin/`. ```bash -# one-time: build the launcher (or download a prebuilt release) +# one-time: build the launcher c++ -std=c++11 -O2 -o wrapper-lite-qemu wrapper-lite-qemu.cpp # login first (forwards --login to the guest lite), then serve @@ -121,7 +173,7 @@ packages place them next to the binary). See [QEMU launcher arguments](#qemu-launcher-arguments) for the full option list. -## Docker +### Docker ```bash docker build -t wrapper-lite:local . @@ -131,7 +183,7 @@ docker run --privileged -p 12340:12340 \ wrapper-lite:local ``` -## HTTP API +### HTTP API All responses use: @@ -149,7 +201,7 @@ All responses use: | `/status` | GET | returns `regions` (list of storefront codes this wrapper can serve) and `name` (the Apple ID the account logged in with, from `/APPLE_ID`; `""` for an account logged in before lite recorded it) | | `/token` | GET | returns `region`, `music_token`, `refreshed_at`, `next_refresh_at`; 503 until a music token exists | -## lite arguments +### lite arguments | Argument | Default | Description | |----------|---------|-------------| @@ -166,7 +218,7 @@ All responses use: | `--token-refresh-interval` | `1800` | background refresh interval in seconds; at start, the first refresh comes when `/MUSIC_TOKEN` (written only when a new token is obtained) reaches that age, at least 5 s in | | `--cors` | off | send CORS headers (`Access-Control-Allow-Origin: *`) and answer `OPTIONS` preflights so browser pages on any origin can call the API; the API has no authentication, so only enable it on instances you trust to be reachable | -## Device info +### Device info `lite` identifies itself to Apple's servers with a device-info string of the form: @@ -187,7 +239,7 @@ The trailing `AndroidID` is derived automatically: Pass `--device-info ` to override the entire string explicitly. -## QEMU launcher arguments +### QEMU launcher arguments | Argument | Default | Description | |----------|---------|-------------| @@ -213,11 +265,13 @@ Environment variables (fallbacks; command-line flags take precedence): | `LITE_QEMU_ACCEL` | auto | force acceleration | | `QEMU_BIN` | auto | QEMU binary path | -## Build notes -- Release: SSL verification is enabled by default. Pass `--debug` to disable it temporarily. -- Debug: SSL verification is disabled by default. +## License and credit -## License +wrapper-lite is MIT ([`LICENSE`](LICENSE)); wrapper-manager is AGPL-3.0 +([`manager/LICENSE`](manager/LICENSE)). Both come from +[WorldObservationLog](https://github.com/WorldObservationLog)'s +[wrapper](https://github.com/WorldObservationLog/wrapper) and +[wrapper-manager](https://github.com/WorldObservationLog/wrapper-manager); +this fork keeps their history. -This project is licensed under the [MIT License](LICENSE). diff --git a/manager/README.md b/manager/README.md index 10bbe77..93c4e1b 100644 --- a/manager/README.md +++ b/manager/README.md @@ -1,32 +1,30 @@ # wrapper-manager -> **AMDL-Web fork, in [AMDL-Web/wrapper](https://github.com/AMDL-Web/wrapper) under `manager/`.** -> Identical to -> [upstream `v2`](https://github.com/WorldObservationLog/wrapper-manager/tree/v2) -> except that it installs wrapper-lite from -> [AMDL-Web/wrapper's `lite` branch](https://github.com/AMDL-Web/wrapper/tree/lite), -> which frees the FairPlay decrypt context of each `/key`; upstream's build fails -> every `/key` with `KDCanProcessCKC status: -42786` after 1024 per process. A -> payload installed from anywhere else (no matching `data/wrapper/.payload-source`) -> is replaced on the next start; account data is kept. Upstream changes are merged -> in, not rebased. - -An HTTP management/proxy gateway that runs and supervises **multiple -[wrapper-lite](https://github.com/WorldObservationLog/wrapper) account -instances** (branch `lite`) behind one HTTP endpoint. +An HTTP gateway that runs and supervises **multiple +[wrapper-lite](../README.md) account instances** behind one port, for +[amdl](https://github.com/AMDL-Web/apple-music-downloader). This is the +[AMDL-Web/wrapper](https://github.com/AMDL-Web/wrapper) fork of +[upstream `v2`](https://github.com/WorldObservationLog/wrapper-manager/tree/v2), +merged in under `manager/` (upstream changes are merged in, not rebased). What +differs, besides the payload below: up to 10 accounts, `instances` in `/status`, +`/token`, cancelable logins, `/shutdown`, durable writes, the admin API and +`-launcher`, `-payload-url` and `-payload-sha256`. Each logged-in account is a long-lived `wrapper-lite-rootless` child process -(service mode) listening on a private random port. wrapper-manager starts, -logs in, logs out, restarts and restores those instances, and exposes a -wrapper-lite-compatible HTTP API that aggregates them: a request carrying an -`adamId` is forwarded to an instance whose Apple storefront region can serve -that item, and the wrapper-lite response envelope is passed through unchanged. - -> **Not a decryptor.** This project no longer provides sample decryption and no -> longer speaks gRPC. Decryption is performed locally by -> [AppleMusicDecrypt](https://github.com/WorldObservationLog/AppleMusicDecrypt) -> v3 (Temari); wrapper-manager only supplies the `/key` `ctx`/`state` template -> and `/m3u8`/`/license` upstreams that the decryptor consumes over HTTP. +(service mode) listening on a private random port. The manager starts, logs in, +logs out, restarts and restores them, and exposes a wrapper-lite-compatible HTTP +API that aggregates them: a request carrying an `adamId` is forwarded to an +instance whose Apple storefront region can serve that item, and the wrapper-lite +response envelope is passed through unchanged. + +The manager does not decrypt. It supplies the `/key` `ctx`/`state` template and +the `/m3u8` and `/license` upstreams; the client (amdl) decrypts locally. + +The payload it installs is this repository's `lite` branch build, which frees +the FairPlay decrypt context of each `/key`; upstream's fails every `/key` with +`KDCanProcessCKC status: -42786` after 1024 per process. A payload installed +from anywhere else (no matching `data/wrapper/.payload-source`) is replaced on +the next start; account data is kept. ## Features @@ -43,9 +41,9 @@ that item, and the wrapper-lite response envelope is passed through unchanged. logged out). - **Startup recovery** — persisted accounts in `data/instances.json` are relaunched automatically (service mode only, no re-login). -- **First-run self-provisioning** — downloads the native wrapper-lite payload - from nightly.link automatically when `data/wrapper/` is missing or was - installed from another source. +- **First-run self-provisioning** — installs the native wrapper-lite payload + when `data/wrapper/` is missing or was installed from another source; a + payload can be [pinned by URL and sha256](#payload). ## Requirements @@ -57,7 +55,7 @@ that item, and the wrapper-lite response envelope is passed through unchanged. user namespaces); see [Deploy](#deploy-docker-compose). With `-launcher root` the manager uses `wrapper-lite` instead, which needs root (it mounts and chroots itself) but no user namespace. -- Internet access to `nightly.link` on first run (payload download) and to +- Internet access on first run (payload download, see [Payload](#payload)) and to Apple services (`amp-api.music.apple.com`, etc.) for login, region probing and upstream requests. A configured `-proxy` applies to all of them. @@ -68,12 +66,25 @@ go build -o wrapper-manager . ./wrapper-manager ``` -On first start the manager downloads and unpacks the wrapper-lite native -package for your architecture from -`https://nightly.link/AMDL-Web/wrapper/workflows/build-lite/lite/wrapper-lite-linux-x86_64.zip`. -It is stored under `data/wrapper/`, with the URL it came from in -`data/wrapper/.payload-source`. Use `-prepare` to perform only this download and exit -(handy for pre-seeding images). +`-prepare` performs only the payload install and exits (handy for pre-seeding +images); `-update-wrapper` reinstalls the newest payload and exits. + +### Payload + +By default the manager installs the newest `build-lite` artifact on `lite` +(`wrapper-lite-linux-x86_64.zip`; no arm64 build is published) into `data/wrapper/`, +recording where it came from in `data/wrapper/.payload-source`. To run a fixed +build, pin a release asset of this repository (tags `lite-`, see the +[root README](../README.md#release-assets)) and its digest: + +```shell +./wrapper-manager -payload-url \ + https://github.com/AMDL-Web/wrapper/releases/download/lite-/wrapper-lite-linux-x86_64.zip \ + -payload-sha256 +``` + +A download that does not match the digest is refused and the manager exits. +`-mirror` only rewrites the default URL, not one given with `-payload-url`. ### Command-line flags @@ -81,12 +92,13 @@ It is stored under `data/wrapper/`, with the URL it came from in |---|---|---| | `-host` | `localhost` | Host the HTTP server binds to (use `0.0.0.0` in containers). | | `-port` | `8080` | Port of the HTTP server. | -| `-mirror` | `false` | Download wrapper-lite through `gh-proxy.com` instead of nightly.link (for users in China). | +| `-mirror` | `false` | Download the default payload through `gh-proxy.com` (for users in China). | | `-debug` | `false` | Enable debug output. | | `-proxy` | *(empty)* | HTTP(S) proxy used by the manager for downloads/Apple probes and passed through to wrapper-lite (`--proxy`). | -| `-prepare` | `false` | Only download the required wrapper-lite payload, then exit. | +| `-prepare` | `false` | Only install the wrapper-lite payload, then exit. | +| `-update-wrapper` | `false` | Reinstall the newest payload even when one is installed, then exit. | | `-launcher` | `rootless` | `rootless` starts each wrapper-lite with `wrapper-lite-rootless` (user namespaces); `root` uses `wrapper-lite`, which needs root and suits containers that refuse a user namespace's `/proc` mount, such as Cloudflare Containers. | -| `-payload-url` | *(empty)* | wrapper-lite payload zip to install instead of the newest `build-lite` artifact on `lite`, e.g. one run's `https://nightly.link/AMDL-Web/wrapper/actions/runs//wrapper-lite-linux-x86_64.zip`. | +| `-payload-url` | *(empty)* | wrapper-lite payload zip to install instead of the newest `build-lite` artifact on `lite`; see [Payload](#payload). | | `-payload-sha256` | *(empty)* | Lowercase hex sha256 the `-payload-url` zip must have. A download that differs is refused, nothing is installed and the manager exits, even when another payload is already installed; the digest is part of `data/wrapper/.payload-source`, so a new digest reinstalls. Needs `-payload-url`. | | `-admin-port` | `0` (off) | Port of the [admin API](#admin-api) on `-host`. Never expose it. | | `-device-info` | `Music/5.0.2/Android/10/Pixel 8/7663314/en-US/en-US` | Optional Apple Music device-info string (`--device-info` pass-through to wrapper-lite). | @@ -116,7 +128,7 @@ instance"` (retry later). | Method | Path | Parameters | Notes | |---|---|---|---| | `GET` | `/m3u8` | `adamId` | Playlist for the item. | -| `GET` | `/key` | `adamId`, `uri` (required) | Key/decryption context — `data` carries the `ctx`/`state` template used by AppleMusicDecrypt v3 + local Temari. The prefetch URI `skd://itunes.apple.com/P000000000/s1/e1` is only accepted with `adamId=0` (upstream rejects it for a real track). | +| `GET` | `/key` | `adamId`, `uri` (required) | Key/decryption context — `data` carries the `ctx`/`state` template that amdl decrypts with locally. The prefetch URI `skd://itunes.apple.com/P000000000/s1/e1` is only accepted with `adamId=0` (upstream rejects it for a real track). | | `GET` | `/lyrics` | `adamId`, `language?`, `syllable?`, `script?` | Prefers an instance whose storefront supports the language and has lyrics (probed), falls back to region selection. `script` (transliteration, default `en-Latn`, e.g. `ja-Latn`) is forwarded to the instance. | | `GET` | `/webplayback` | `adamId` | Web-playback session data. | | `POST` | `/license` | JSON body `{"adamId","challenge","uri","drm-type"?}` | License request forwarded to the selected instance. Optional `drm-type`: `wv` (Widevine, default) or `pr` (PlayReady). | @@ -233,7 +245,7 @@ Everything lives under `data/` relative to the working directory: ``` data/ -├── wrapper/ # wrapper-lite payload (auto-downloaded) +├── wrapper/ # wrapper-lite payload (see Payload) │ ├── wrapper-lite-rootless # rootless launcher (login + service entry) │ ├── wrapper-lite │ └── rootfs/ # chroot userland used by the launcher @@ -258,27 +270,34 @@ data/ ## Run on any OS via QEMU (wrapper-manager-qemu) The native manager requires Linux (it launches `wrapper-lite-rootless`, which -needs user namespaces + chroot). To run it on **Windows / macOS / any Linux** -without a Linux host, boot the whole manager inside a single QEMU Linux guest: +needs user namespaces + chroot). To run it on Windows, macOS or any Linux without +a Linux host, boot the whole manager inside a single QEMU Linux guest. amdl's +desktop app does this for you; this section is for running it by hand. ``` -Windows / macOS / Linux host +host └─ wrapper-manager-qemu (Go launcher, cross-platform) └─ QEMU x86_64 guest: vmlinuz-lite-qemu + wrapper-manager-initramfs └─ wrapper-manager -host 0.0.0.0 -port 8080 (Linux native) └─ wrapper-lite-rootless instances (per account) ``` -The launcher is modeled after upstream -[wrapper-lite-qemu](https://github.com/WorldObservationLog/wrapper/blob/lite/wrapper-lite-qemu.cpp): +The launcher: -- Locates `qemu-system-x86_64` (`--qemu-bin` > `QEMU_BIN` > `PATH` > bundled). -- Auto-selects acceleration: KVM (Linux) / WHPX (Windows) / TCG (macOS & - fallback) with automatic fallback to TCG when the accel is unavailable. +- Locates `qemu-system-x86_64` (`--qemu-bin` > `QEMU_BIN` > `PATH` > bundled + in `/qemu/bin`). +- Auto-selects acceleration: KVM (Linux) / WHPX (Windows) / TCG (macOS and + fallback). - Boots the guest kernel + initramfs, attaches a persistent `data.img` and - forwards `hostPort` → guest manager port (8080). -- Guest images live in `/guest/` (`--assets-dir` to override); the - persistent data image defaults to `~/.wrapper-manager/data.img`. + forwards `--host-port` to the guest manager port (8080). +- Reads guest images from `/guest/` (`--assets-dir` to override); the + data image defaults to `~/.wrapper-manager/data.img`. + +The guest itself is built by [`guest/`](guest/README.md) and published as the +`manager-qemu-guest.zip` release asset of each `guest-` tag +([root README](../README.md#release-assets)); the launcher binaries for +linux x86_64/aarch64, windows and macOS are 14-day artifacts of the `manager` +workflow. ### Build @@ -286,19 +305,11 @@ The launcher is modeled after upstream # 1. the launcher (any platform) go build -o wrapper-manager-qemu ./cmd/wrapper-manager-qemu -# 2. the guest (on Linux; see guest/README.md). The kernel and its e1000 and -# qemu_fw_cfg modules come from the build-lite qemu-assets artifact. -curl -fsSL -o qemu-assets.zip \ - "https://nightly.link/AMDL-Web/wrapper/workflows/build-lite/lite/qemu-assets.zip" -unzip -o qemu-assets.zip vmlinuz-lite-qemu lite-initramfs.cpio.gz -d kernel-assets/ +# 2. the guest (on Linux), from kernel-assets/ as described in guest/README.md KERNEL_ASSETS=kernel-assets ./guest/build.sh out # kernel + initramfs KERNEL_ASSETS=kernel-assets CREATE_DATA_IMG=1 ./guest/build.sh out # also a fresh data.img ``` -Prebuilt artifacts (launchers for linux/windows/macos + guest image) are -published by the `build-manager-qemu` workflow and are fetchable through -nightly.link. - ### Run ```shell @@ -311,9 +322,9 @@ Flags: `--host --host-port --guest-port --memory --smp --accel --qemu-bin --assets-dir --data-dir --kernel --initrd`; environment fallbacks `QEMU_BIN / HOST_PORT / GUEST_PORT / MEMORY / SMP`. -Guest networking uses QEMU user-mode NAT (`10.0.2.15`); the guest downloads the -wrapper-lite payload itself on first boot (nightly.link) and keeps everything -inside `data.img`, so account state and payload survive restarts. +Guest networking uses QEMU user-mode NAT (`10.0.2.15`); the guest installs the +wrapper-lite payload itself on first boot and keeps everything inside +`data.img`, so account state and payload survive restarts. ## Deploy (docker compose) @@ -323,7 +334,7 @@ Docker blocks by default — the compose file runs the container with seccomp/apparmor profile that permits `unshare`/user namespaces instead. ```shell -git clone https://github.com/AMDL-Web/wrapper +git clone -b lite https://github.com/AMDL-Web/wrapper cd wrapper/manager docker compose up -d ``` @@ -339,5 +350,5 @@ For users in China: uncomment the Go module proxy line in `Dockerfile` and add ## Related projects -- [WorldObservationLog/wrapper](https://github.com/WorldObservationLog/wrapper) — upstream wrapper / wrapper-lite (branch `lite`). -- [WorldObservationLog/AppleMusicDecrypt](https://github.com/WorldObservationLog/AppleMusicDecrypt) — decryptor that consumes this manager's `/m3u8` `/key` `/license` API. +- [AMDL-Web/apple-music-downloader](https://github.com/AMDL-Web/apple-music-downloader) — amdl, the client of this API. +- [WorldObservationLog/wrapper-manager](https://github.com/WorldObservationLog/wrapper-manager) — upstream wrapper-manager (branch `v2`). diff --git a/manager/guest/README.md b/manager/guest/README.md index b84a18a..90de2a0 100644 --- a/manager/guest/README.md +++ b/manager/guest/README.md @@ -1,7 +1,7 @@ # wrapper-manager QEMU guest Assets for booting the whole wrapper-manager inside a QEMU Linux guest so it -runs on Windows / macOS / any Linux host (see the parent README). +runs on Windows / macOS / any Linux host (see the [manager README](../README.md#run-on-any-os-via-qemu-wrapper-manager-qemu)). ## Layout @@ -18,21 +18,24 @@ certificates and the kernel modules. ## Build +Run from `manager/`. Requires go, cpio, gzip, e2fsprogs, file, kmod, glibc and +ca-certificates (any Debian/Ubuntu). + ```shell -# requires: go, cpio, gzip, e2fsprogs, file, kmod, glibc + ca-certificates (any Debian/Ubuntu) -curl -fsSL -o qemu-assets.zip \ - "https://nightly.link/AMDL-Web/wrapper/workflows/build-lite/lite/qemu-assets.zip" -unzip -o qemu-assets.zip vmlinuz-lite-qemu lite-initramfs.cpio.gz -d kernel-assets/ KERNEL_ASSETS=kernel-assets ./guest/build.sh out # kernel + initramfs KERNEL_ASSETS=kernel-assets CREATE_DATA_IMG=1 ./guest/build.sh out # also a fresh 512MB data.img ``` +`kernel-assets/` holds `vmlinuz-lite-qemu` and `lite-initramfs.cpio.gz` from the +`qemu-assets` artifact of a `build-lite` run on `lite` (the "Fetch the build-lite +kernel and its modules" step of `.github/workflows/manager.yml` does this). CI +boots the result and publishes it as the `manager-qemu-guest.zip` asset of a +`guest-` release ([root README](../../README.md#release-assets)). + ## Kernel -`vmlinuz-lite-qemu` is **not** built here. It is the kernel produced by the -[AMDL-Web/wrapper](https://github.com/AMDL-Web/wrapper) (a fork of -[WorldObservationLog/wrapper](https://github.com/WorldObservationLog/wrapper)) -`build-lite` workflow and is distributed in its `qemu-assets` artifact. +`vmlinuz-lite-qemu` is **not** built here. It is the kernel produced by this +repository's `build-lite` workflow, in its `qemu-assets` artifact. build.sh copies it next to the initramfs, and takes `e1000.ko` and `qemu_fw_cfg.ko` from the same artifact's `lite-initramfs.cpio.gz`: modules built for another kernel do not load, which leaves the guest without network diff --git a/qemu/README.md b/qemu/README.md index e50f7ca..bbd5181 100644 --- a/qemu/README.md +++ b/qemu/README.md @@ -75,6 +75,5 @@ Windows WHPX uses: ## QEMU lookup order -1. `QEMU_BIN` -2. `PATH` -3. `qemu/bin/` +`--qemu-bin` / `QEMU_BIN`, then the bundled `qemu/bin/` when it is usable, then +`PATH`. See the [root README](../README.md#run-with-qemu).