> For the complete documentation index, see [llms.txt](https://developer.musehub.com/muse-partners-help/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.musehub.com/muse-partners-help/resources/integration-faqs.md).

# Integration FAQs

Practical gotchas and answers partners ask most

*A general, product-agnostic reference for partners integrating and publishing on MuseHub. It captures the practical questions and gotchas that come up most often during real integrations, covering nuances that aren't always obvious from the documentation.*

*For full technical documentation, always refer to the official guides:*

* [*MuseSDK*](https://developer.musehub.com/muse-partners-help/resources/muse-sdk)
* [*MuseDRM*](https://developer.musehub.com/muse-partners-help/resources/muse-drm)
* [*Webhooks*](https://developer.musehub.com/muse-partners-help/resources/web-hooks)

*Last updated: 2026-09-24*

***

#### 1. Choosing an Integration Method (SDK vs DRM)

**Q: Do I need MuseDRM or MuseSDK for a free product?**

A: No. Free products require no security integration at all; submit them as-is (e.g. a VST3 bundle). DRM and SDK apply only to paid products.

**Q: What's the difference between MuseDRM and MuseSDK, and can I use both?**

A: They are **mutually exclusive**: pick one per paid product.

* **MuseDRM** wraps an unprotected binary with no code changes. The product stays unaware of the protection and is redirected to MuseHub when not authorized. Good when you want the least integration work.
* **MuseSDK** requires code integration but gives you license control, lets you keep a single build across all distribution channels, can coexist with your own authentication, and lets you read license/user data at runtime.

**Q: When should I prefer the SDK over DRM?**

A: Choose the SDK when you need any of: a single build for every channel (rather than a dedicated MuseHub binary), knowledge of the user's identity (UUID) for a cloud-backed service, subscription tiers, or coexistence with an existing license system. Choose DRM when you just want the simplest no-code protection.

**Q: I have a C# (UWP/Unity) app. Which one do I use?**

A: MuseSDK, using the C# bindings. The main executable is a small generic launcher and the real code lives in a shared, non-project-specific `UnityPlayer` library, for this reason MuseSDK is the best solution for that.

**Q: My plugin/app is built on a web-view stack (Electron, WebView2/WebKit, Qt Web Engine) rather than JUCE. DRM or SDK?**

A: MuseSDK. It ships as a native C API with node.js/JS bindings, so it fits an Electron-style architecture cleanly, and MuseHub already distributes several web-view-based products this way.

**Q: MuseHub doesn't seem to support a freemium / in-app-purchase model. How do I handle upgrades?**

A: There is no native IAP or in-app upgrade path. Two supported patterns:

1. Publish the free and paid editions as **separate products**, and point from the free one to the paid store page using the MuseHub Custom URI protocol.
2. For subscriptions, use MuseSDK **pricing tiers**: the product detects which tier was activated and exposes features accordingly. Free and paid editions are always treated as separate products regardless.

***

#### 2. The SDK "enable" Step (most common submission error)

**Q: I ran `musedrm --sdk_enable` and it succeeded, but submission fails with `BusinessRuleError: Have not called endpoint to setup SDK`. Why?**

A: The SDK-enable step must run **after** the draft submission exists on the server, not as part of your build pipeline. The correct order is:

1. Upload your assets **with all version fields filled in** and **Save** the draft.
2. Run the SDK-enable step locally, **once per platform** with all its assets together, or once per asset as long as every asset gets it.
3. Only then click **Submit**. If both platforms are filled in and submitted simultaneously before enabling, you get this error.

**Q: What's the difference between `--sdk_enable` and `--sdk_enable_permissive` (and the web "Enabled" vs "Enabled (no validation)" dropdown)?**

A: They must match each other:

* `--sdk_enable` ↔ **Enabled** (strict)
* `--sdk_enable_permissive` ↔ **Enabled (no validation)** Use **no validation** for products that self-update outside MuseHub, which includes products also sold on your own website (users can update from your channel and still authorize through MuseHub), and for testing locally built debug binaries during integration. The first install must still come from MuseHub. Use **strict** for regular MuseHub-managed releases. A mismatch between the flag you ran and the web setting causes a hash mismatch, which shows a license screen to authorized users.

**Q: The SDK-enable step keeps failing to detect my SDK.**

A: Two frequent causes: (1) you're running it on the installer/PKG instead of the actual built asset: run it on the plugin/app binaries (`.vst3` **folder bundle**, `.app`, `.component`), not the package, and pass the matching `--asset_type`, `--product_id`, and `--product_version`; (2) you're on an old CLI: update to the latest MuseDRM CLI, since older versions fail to detect the SDK import. Always target the bundle folder, not an individual file inside it.

**Q: Does the "Enable SDK Locally" / `sdk_enable` step modify my binary, the same way MuseDRM wrapping does?**

A: No. Even though it uses the same `musedrm` tool, this step is **read-only** for SDK integrations: it sends information about your SDK-calling binary to MuseHub's servers. It does not wrap or alter the binary in any way, unlike MuseDRM protection.

**Q: Is there any tooling to help an AI coding agent (e.g. Claude Code) walk through SDK integration and the enable step?**

A: Yes, recent SDK packages ship two agent skills in the package's `.claude/skills` folder (hidden by default, as it starts with a dot): `integrate-muse-sdk` for the integration and `enable-and-submit-muse-sdk` for this enable step and the submission. Copy them into your own repo's `.claude/skills/` and run them in that order.

**Q: What does the SDK actually add to my binary, and does it phone home?**

A: The SDK library is a small (a few MB) dylib/DLL. It never calls out to the internet itself; it opens a securely-encrypted local connection to the installed MuseHub desktop app, purely to reply to your API queries.

***

#### 3. SDK Runtime Behaviour & API

**Q: My SDK connects (`initialize` returns SUCCESS) but `getUserInfo()` / `getReceipt()` return a connection error. What's wrong?**

A: Your connection is fine. Data APIs only return real data once the product has gone through the platform. Until then the Hub refuses data queries. Submit a private **Beta** (or send the binary to MuseHub to enable one), then the data calls will work.

**Q: Does the SDK talk to MuseHub's servers directly?**

A: No. The SDK communicates with the **MuseHub desktop application**, which must be installed and running. If you get a connection error on init, the most common reason is that the app isn't installed/running. In manual test mode you must open the app yourself; production builds launch it automatically.

**Q: Do I need to call a "finalize" API, and when?**

A: Yes, call `MuseSdk_finalize()` after every completed authorization/transaction to release the connection. Skipping it may cause resource exhaustion and fail subsequent connections. If you're using an object-oriented wrapper, destroy the SDK object at the end of the auth sequence (including on failure paths) so `finalize()` always runs.

**Q: Can I cache the license/receipt locally instead of calling the SDK on every launch?**

A: Yes, this is an optional optimization (not required, since `initialize()` already returns quickly). Cache the **signed receipt** returned by `getReceipt()`, not a plain boolean/expiry flag, since a flag is trivial to tamper with. At startup, validate the cached receipt's signature, system ID, and expiry locally before trusting it. Optionally encrypt the cache at rest for extra hardening.

**Q: Is there a way to silently detect whether MuseHub/a license is available without launching the Hub app or blocking my UI?**

A: There's no separate silent-detection API on either Windows or macOS, but you don't need one: as with any SDK call, run it on a background thread rather than blocking your UI thread while waiting. Also, `initialize()` returns immediately if MuseHub isn't installed, and if it is installed the call is fast (under \~1s if auth is provided, worst case a few seconds otherwise). The connection is local to the installed app, not a network round-trip, so it also works offline.

**Q: Is the SDK client library (DLL/dylib) a hard runtime dependency? Can I redistribute it, or does it need a static build?**

A: It's not a hard dependency. On Windows the SDK is stubbed by its static library: it looks for the DLL next to your binary, then on the system path, and simply returns a connection error at `initialize()` if it can't find it anywhere, and your app keeps working. Ship `MuseClientSdk.x.y.z.dll` next to your binary; relying on MuseHub to install it is legacy behaviour. macOS behaves the same way (dylib embedded in your bundle under `Contents/Frameworks/`).

**Q: Which API should I use to check whether a product is licensed?**

A: `getActivationStatus()`: it's the quickest true/false license check. Note:

* `getSku()` is for identifying product **variants**, not license state.
* `getSubscriptionOption()` distinguishes **subscription tiers** (e.g. entry vs pro), not free/trial/paid states.

**Q: Is activation status scoped to the user or the product?**

A: Both. `getActivationStatus()` returns active only if the currently logged-in user has a valid authorization on **that machine** for **the specific product that called the API**. You cannot query whether the user owns a *different* product. The same scoping applies to `getReceipt()`.

**Q: How do timed trials behave through the SDK?**

A: A trial is treated as a fully functional product: authorized while the trial is valid, unauthorized once it expires without purchase. There is no separate "trial" data state to detect.

**Q: How do I read the license from a receipt correctly?**

A: Read `item_id` from the `payload.items[]` array, not a top-level `product_id`. Then verify the `item_id` matches **the specific product**. A frequent bug is checking only for the *presence* of any Muse receipt, which lets a purchase of one product unlock all of your others. Verify per-product IDs.

**Q: How do I get the user's email/identity?**

A: For users who opted in, call `getUserInfo` (email, name, avatar). For users who opted out, call `requestPersonalDataOptin` to prompt them, and register a notification listener to receive the opt-in confirmation. The bundled JUCE demo shows the full flow.

**Q: Test mode vs production mode: how should I structure my build?**

A: Keep identical logic and switch the init call by build configuration, e.g. call the test-mode initializer only in debug builds and the real initializer in release:

```cpp
#ifndef NDEBUG
    // MuseSdk_initializeTestMode()
#else
    // MuseSdk_initialize()
#endif
```

**Never ship test mode in a Release build.** This is one of the most common review rejections.

**Q: How do I test the real (non-test) authorization flow before going live?**

A: Submit a private Beta. Install it from the MuseHub app (Library ▸ Beta), then run your local debug build in non-test mode; the Hub replies with real license data as long as the requests come from the installed product's process. Grant/revoke test licenses by adding/removing tester emails on the Beta page.

**Q: I'm building an Electron or desktop app, not a plugin. Anything different?**

A: For Electron apps use the Electron initializer / `--electron` enable flag, and make sure the submission page has the app's **bundle ID** filled in; MuseHub cannot launch the app via the OS without it (a missing bundle ID shows a "product failed to load" error). Some app-info APIs are exposed to Electron but not to the plain C bindings.

**Q: What macOS deployment target do the SDK libraries require?**

A: The SDK libraries themselves have a floor of roughly macOS 11.6. Separately, MuseHub's own **distribution** minimum is currently macOS 12, and may rise in the future, so build/declare against whichever of the two is higher for your product.

***

#### 4. MuseDRM (binary wrapping)

**Q: How do I wrap multiple plugin formats that ship in one installer?**

A: Pass **all** the format assets to a **single** `musedrm --protect` call, e.g. `./musedrm --protect Product.vst3 Product.component …`. This yields **one** `musedrm_id` to enter when uploading the package. Assets protected individually cannot be reused together inside a single PKG.

**Q: I entered a musedrm\_id but the product won't authorize on one platform.**

A: Check that you didn't paste the **same** `musedrm_id` into both the macOS and Windows fields. Each platform/asset needs its own ID from its own wrap. Duplicated IDs are a common cause of one-platform auth failures.

**Q: Wrapping is rejected with "Binary processing rejected … debug symbols or a previously existing protection."**

A: Three usual causes:

1. **Debug symbols not stripped**: strip them as part of your release post-processing (a large exported-symbol count is flagged as reverse-engineering risk). On macOS, you can check what symbols are visible in your binary using `nm -a <binary>` and `xcrun dyld_info -exports <binary>`. It is good practice to always remove them, independently of MuseDRM, to protect your IP.
2. **A previous protection is still present**, e.g. a leftover MuseDRM integration left was found. Apply DRM to a newly built, unprotected product.
3. **"Very low patchable rate / insufficient protection strength"**: the release build does not offer sufficient code paths for protection. Adjust release optimization/linker settings (for example, disabling identical-code folding and reference removal, and keeping some debug info) so the binary is patchable enough to protect.

**Q: DRM wrapping returns a network/server error.**

A: Please update to the latest MuseDRM CLI, which handles automatic retries in case of network instability.

**Q: Can I run DRM wrapping in my CI pipeline?**

A: Yes, many partners add the CLI wrapping step in CI (on macOS, before code signing). Alternatively, send unprotected build artifacts to MuseHub to wrap for you, at least for the first launch.

**Q: I renamed my plugin and now the protected build is broken.**

A: Rename the plugin **before** DRM-wrapping. Wrapping is tailored to the exact binary name and folder structure; renaming afterward breaks it and orphans the `.musedrm` file. Also ensure your installer script preserves every file MuseDRM adds to the bundle.

**Q: In what order do I codesign and wrap?**

A: Codesign **after** DRM wrapping, using `codesign --force --deep`.

**Q: Should I keep my "check for updates" feature in the MuseHub build?**

A: No; remove self-update logic from MuseHub builds. MuseHub handles updates automatically, and a self-update would overwrite the DRM-protected build with your web version, presenting MuseHub users with a new authorization panel. For the same reason, remove any custom auth panel from the MuseHub distribution build; paying MuseHub users should never see it.

***

#### 5. Code Signing & Certificates

**Q: I don't have code-signing certificates. Can MuseHub help?**

A: MuseHub offers a **free "signed by MuseHub" Windows service** (Authenticode) to prevent SmartScreen/UAC warnings. There is **no macOS signing service**; macOS signing and notarization must be done with your own Apple Developer ID.

**Q: What actually needs to be signed on Windows?**

A: Only **installers** and **standalone `.exe`/apps**, the artefacts that trigger Windows warnings. Individual VST3 DLLs do **not** need signing, because MuseHub installs them automatically. (This requirement was relaxed; you no longer need to sign every VST3.)

**Q: What's the Windows signing workflow with MuseHub?**

A: You send the unsigned `.exe` → MuseHub signs it and returns it → you pack the signed binary into your installer → MuseHub signs the **installer** as a second step → it's submitted. There is currently no self-service signing API; it's an ad-hoc, per-request process.

**Q: If MuseHub signs my binary, can I distribute that copy elsewhere?**

A: No. MuseHub-signed binaries are **MuseHub-exclusive**. The partnership is non-exclusive, so you can still distribute your **own unsigned/independently-signed** builds through other channels. If multi-channel distribution matters, sign independently (e.g. your own certificate or a cloud signing service).

**Q: How do I sign and notarize on macOS?**

A: Codesign (with `--force --deep`, hardened runtime, your Developer ID Application), notarize, and staple, all after DRM wrapping. Watch for embedded components: every embedded dylib/XPC service must share the **same Team ID** as the main binary, or the app is rejected ("Library not loaded … different Team ID").

**Q: How does AAX signing work?**

A: AAX requires **your own Avid/PACE private certificate** and signing tools; MuseHub cannot sign AAX for you. Codesign the AAX with `--force --deep` **before** running PACE, so the tool sees only your signature rather than mixed identities.

***

#### 6. Plugin Formats & Packaging

**Q: What's the correct Windows VST3 format?**

A: A **folder/bundle**, not a single `.vst3` file (Steinberg deprecated the single-file form). JUCE 8 produces the bundle automatically. Structure: the DLL goes in `Contents/x86_64-win`, and resources (e.g. a `Samples` folder) go in a `Resources` folder alongside `Contents`. See the [VST3 docs](https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Locations+Format/Plugin+Format.html).

**Q: Do I need an installer?**

A: Not for a plugin-only release; MuseHub installs plug-ins to the correct folder automatically. You **do** need an installer when you must place extra files (samples, support DLLs, ONNX models) or ship a standalone app on Windows. Rules for installers: one per product **per platform**, code-signed, running in admin mode. Windows: MSI / NSIS / InnoSetup. macOS: PKG.

**Q: Can I upload a standalone `.exe` and plugins as separate Windows assets in the same submission?**

A: No. If you upload a `.exe` for Windows, it must be an **installer** — and it becomes the only Windows asset for that submission; you cannot upload additional Windows files alongside it. If you want to distribute both a standalone app and plugins (VST3, AAX), package them together into a single signed installer (InnoSetup, NSIS, MSI/MSIX). If bundling them together isn't feasible, duplicate the submission and submit just the plugins for Windows without the standalone.

**Q: How should I submit a macOS app or plug-in bundle?**

A: As a `.app.zip`, `.vst3.zip`, `.component.zip`, `.aaxplugin.zip`, keeping the original extension before `.zip` so the Partner Portal recognises the asset type. DMG is not accepted. Create the zip with `zip -ry` (or Finder ▸ Compress); the `-y` flag is essential because it **preserves symbolic links**. Symlinks lost during a plain browser upload are a common cause of a macOS build that won't launch. Several formats can each be uploaded as their own zip; if you use a PKG instead, all plugin formats must go in a **single PKG**, since multiple PKGs are not accepted.

**Q: My Windows plugin uninstall doesn't work.**

A: In most cases, leave the Windows **application ID** blank: MuseHub detects it for well-known installers (InnoSetup, MSI, NSIS). If testing your beta from the Library tab shows the product can't be launched or uninstalled, enter the ID exactly as the registry key under `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\`. With InnoSetup the key gets an `_is1` suffix (e.g. `MyProduct_is1`, and GUID installers become `{GUID}_is1`). A key ending in `}}_is1` is also valid: it happens when the script writes the AppId as `{{GUID}}`.

**Q: I get a DLL "Entry Point Not Found" / missing-DLL crash on Windows.**

A: Bundle your dependency DLLs inside the plugin's `Resources` folder and **delay-load** them (e.g. via the delay-load notify hook) rather than relying on system paths. Do **not** install DLLs to `System32`; you risk overwriting system files. Note also that some older dependencies (e.g. a previous activation DLL) may no longer be installed by current MuseDRM, so don't link against them.

**Q: Is CLAP supported? AAX? Linux?**

A: AAX is supported (with your own PACE signing). CLAP and Linux distribution are not currently supported on MuseHub.

**Q: My submission bundles a plugin plus a standalone app (or samples, or a manual). Zip or PKG/installer?**

A: A plain notarized/stapled `.app.zip` (or bare plugin zip) is enough **only** if that's the entire macOS product. Windows standalone apps always require an installer. On both platforms, as soon as you're installing anything beyond the single bundle itself (samples, content, etc.), alongside a standalone app or plug-in, you need a proper installer (PKG on macOS, MSI/NSIS/InnoSetup on Windows) so all pieces land in the right places. A manual/PDF can either ship inside the installer or be uploaded separately via the submission page's own user-guide upload field.

**Q: Do I need to provide a Windows uninstaller? What about macOS?**

A: On Windows, yes: bundle a real, Windows-generated uninstaller inside your installer (e.g. InnoSetup/MSI's auto-generated `unins000.exe`); don't submit it as a separate asset. On macOS, uninstallers don't officially exist as a concept and MuseHub doesn't require one. If your plugin needs to detect "was this reinstalled/are my samples still here," you can't rely on an uninstaller running on macOS — build your own check instead (e.g. clearing or checking a marker/link file your installer writes at install time), since leftover in-plugin settings alone are not reliable for detecting a real reinstall.

**Q: What version string format is required?**

A: A standard semver-style version, e.g. `1.0.0`. Non-standard strings (e.g. a text suffix instead of a number) aren't compatible with MuseHub's versioning system and will need to be corrected before the product can go live.

**Q: What's the correct `--asset_type` value for the `musedrm`/SDK CLI?**

A: Set it to match what the **backend reports for that asset once scanned**, not just what you uploaded, check the partner portal after upload rather than assuming. Common values: `app` for a macOS app bundle, `exe` for a Windows executable, `zip` for a plain zipped plugin bundle. For any Electron-based product always add the `--electron` flag alongside the correct type.

***

#### 7. Submission, Testing & Release Workflow

**Q: How do I test a real authorization end-to-end before release?**

A: Use a **private Beta**. It's visible only to you and the MuseHub team via the MuseHub app (Library ▸ Beta). Add tester emails to grant licenses; remove them to revoke. A Beta is fine as long as it's stable and doesn't crash.

**Q: What's the safe way to ship a fix or an update?**

A: Use the **duplicate-submission** pattern: duplicate the existing submission, replace only the affected assets, bump the version, Save, run the SDK-enable step if you use the SDK, then Submit. Leave the working platform's assets untouched when you're only fixing one platform.

**Q: Why does my submission fail for a "missing version"?**

A: The version field is mandatory: it drives authorization and update notifications, and the SDK-enable step can't run without it. Always fill it in before saving.

**Q: How long does approval take, and does every update need re-approval?**

A: Each update is reviewed, but it's faster after the first verification: allow up to one week if the submission is correct. Notify the team by email when submitting a product hotfix to have it fast-tracked. Releasing on a weekday helps with review timing and organic discovery.

**Q: My submission is blocked before I can upload anything.**

A: Check that you've reviewed and **accepted the Distribution Agreement** in your dashboard; submissions are blocked until it's accepted.

**Q: Where are the MuseHub logs to check MuseHub integration in my product?**

A: Windows: `…\AppData\Local\Muse Hub\cache\Logs\`. macOS: `~/Library/Logs/com.muse.musehub`.

***

#### 8. Licensing, Webhooks & User Data

**Q: I run a cloud-backed service. How do I connect MuseHub purchases to user accounts?**

A: Configure **webhooks** in the Partner Portal. MuseHub sends server-signed messages on purchase/subscription events, which you use to create or map an account (typically keyed on the user's MuseHub email). Users opt in to share their data; combine webhook data with the SDK's user-info APIs. This is the standard pattern for subscription and account-based products.

**Q: How do I verify a webhook's signature?**

A: The `Authorization` header carries `HMAC-SHA256 Signature=<base64 digest>`. Compute the HMAC-SHA256 over the **raw request body exactly as received** (compact JSON, before any parsing), using the secret shown in the Partner Portal as a plain string key (don't hex-decode it), base64-encode it, and compare. In node.js: `crypto.createHmac('sha256', secret).update(rawBody).digest('base64')`. The examples in the [Webhooks](https://developer.musehub.com/muse-partners-help/resources/web-hooks) docs are signed with a published example secret, so you can test against them. If the secret leaks, rotate it in the Partner Portal.

**Q: What is the "require customer personal data" checkbox for?**

A: It applies only to SDK implementations that genuinely need customer data to fulfil an order (e.g. account provisioning). If you don't need it, leave it off.

**Q: I need to identify which of my backend accounts corresponds to a MuseHub purchase, without collecting email/personal data upfront. What's the pattern?**

A: Correlate on the anonymised MuseHub-issued UUID. The SDK gives you this UUID at runtime (via `getUserInfo`/receipt data); your webhook receiver gets the same UUID on the purchase event. Use it to silently create/match a backend account server-side. This satisfies MuseHub's "no login/license screen on first open" requirement, since you never have to prompt for credentials just to identify the buyer. If you need an email (e.g. for transactional account setup), the webhook payload can include it prior MuseHub team review and agreement, but it must not be used for marketing without explicit user opt-in.

**Q: Where do I get the public key(s) to verify a receipt's signature, and do they rotate?**

A: They ship inside the SDK package itself (header `MuseSdkApiCrypto.h`). Each receipt carries a key ID (KID) selecting which key signed it. Rotation is irregular; when it happens, new keys and KIDs ship with a new SDK package release. You can verify server-side (recommended, since you may not want to trust the client) or client-side using the embedded keys, either is supported, embedding is mainly a convenience for local/offline verification.

**Q: How many machines can a single license/subscription be activated on?**

A: Up to 5 simultaneously activated computers per subscription. Past the cap, the user deactivates a machine from their MuseHub online account area to free up a slot. If you maintain your own device/seat cap in a custom license layer alongside MuseSDK, don't set it stricter than this.

**Q: How do I point from a free product to a paid one?**

A: Use the Custom URI protocol to open the paid product's MuseHub store page from your free product's UI. Keep the free edition fully usable; the pointer shouldn't degrade the free experience, note pointers will be reviewed during approval and aggressive upsells rejected.

***

#### 9. Platform Gotchas

**Q: I built against the latest macOS SDK and now older-macOS users can't load the plugin.**

A: Set an explicit **minimum deployment target** in Xcode (e.g. macOS 14 or lower) and rebuild, or hide the product on the affected macOS versions in the submission page. Building against the newest libraries silently raises the minimum OS.

**Q: My macOS build can't find its bundled dylib / SDK library at runtime.**

A: The rpath is likely pointing at a build-machine absolute path. Use a relative rpath such as `@loader_path/../Frameworks/` so the library resolves inside the bundle.

***

#### 10. Audacity Compatibility

Audacity is one of the largest sources of MuseHub traffic, so plugins that misbehave there have an outsized impact on partner reach. It's worth testing against explicitly, since it runs plugins differently from other hosts and surfaces bugs that never show up elsewhere.

**Q: My plugin's UI garbles Audacity's own menus/UI on Windows at high display scaling (e.g. 4K, 150%).**

A: Audacity itself is DPI-unaware and relies on Windows bitmap-scaling its UI. Some JUCE versions promote the *host process* to per-monitor-DPI-aware the first time a plugin window is created, which strips that scaling and garbles Audacity's own menus.

If you're on JUCE, the simplest fix is to **update to JUCE 8.0.15 or later**, which addresses this. Note that Audacity 4 (in beta at the time of writing) is DPI-aware, so plugins running inside it won't hit this issue and won't need the workaround below; until it's the norm, plugins still need to handle it for users on Audacity 3.x.

If you can't update JUCE, you can work around it by detecting Audacity as the host and locking the process to DPI-unaware before JUCE gets a chance to promote it:

```cpp
#if JUCE_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

namespace {
#ifndef DPI_AWARENESS_CONTEXT_UNAWARE
#define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1)
#endif
#ifndef DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)
#endif

void pinDpiUnawareForAudacity() {
  static const bool done = [] {
    if (!juce::PluginHostType::getHostPath().toLowerCase().contains("audacity"))
      return false;

    using GetCtx = DPI_AWARENESS_CONTEXT(WINAPI *)();
    using SetCtx = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT);
    using AreEq = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT);

    if (auto *user32 = GetModuleHandleA("user32.dll")) {
      auto getCtx = (GetCtx)GetProcAddress(user32, "GetThreadDpiAwarenessContext");
      auto setCtx = (SetCtx)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
      auto areEq = (AreEq)GetProcAddress(user32, "AreDpiAwarenessContextsEqual");

      if (getCtx != nullptr && setCtx != nullptr && areEq != nullptr) {
        const auto cur = getCtx();
        if (areEq(cur, DPI_AWARENESS_CONTEXT_UNAWARE) ||
            areEq(cur, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED))
          setCtx(DPI_AWARENESS_CONTEXT_UNAWARE);
      }
    }
    return true;
  }();
  juce::ignoreUnused(done);
}
}
#endif
```

Call `pinDpiUnawareForAudacity()` from your plugin's constructor. Every other host is left untouched. If you're not on JUCE, expect the same class of bug in whatever GUI/rendering layer you depend on for Windows DPI awareness, and apply the same host-detection approach.

**Q: My plugin works in most DAWs but crashes in Audacity.**

A: A recurring root cause is a use-after-free during Audacity's fast plugin scan: it tears down the plugin instance efficiently, so a worker-thread callback ends up touching freed memory. Guard your init code, e.g. hold the SDK provider alive with a shared pointer and use a weak reference for callbacks on the message thread across any async authorization call.

**Q: Audacity only plays my plugin's left channel, or treats it as mono-to-stereo, but other DAWs are fine.**

A: This usually comes from JUCE's legacy bus declaration. Check `isBusesLayoutSupported()` and the order of `JucePlugin_PreferredChannelConfigurations`: list `{2,2}` (stereo) before `{1,1}` (mono). Audacity picks the first matching layout, so config order matters even when the logic is otherwise correct.
