Plugin Crash Reports — Quickstart

A crash-reporting system for audio plugins. A single shared CrashReporter agent (installed once, serves every plugin) scans the OS crash logs, matches each crash to a registered plugin, and uploads it to the site, which symbolicates it server-side from debug symbols you upload at release time.

You then read the symbolicated crashes on the website or from Claude via the MCP server.

How it fits together

your plugin crashes in a host
        │
        ▼
the OS writes its normal crash log (.ips / minidump)
        │
        ▼
next time any plugin launches, the shared CrashReporter runs once,
scans the logs, matches them to registered plugins by your JSON,
and uploads the raw crash  ──►  crashreports.rabiensoftware.com
                                        │
                                        ▼
              a worker symbolicates it using the symbols you
              uploaded from CI (matched by build UUID / debug-id)
                                        │
                                        ▼
                     view it on the site or via the MCP

Key idea: there is no in-process crash handler. Many plugins share one host and can't each own the handler — so we let the OS write its normal crash log and scan it out-of-process on the next launch.


1. Create an account and register your plugin

  1. Sign up at https://crashreports.rabiensoftware.com/register/.
  2. Create a team (or get added to one).
  3. Register a plugin. You provide:
    • Name — matched against the module name in Windows crash dumps (e.g. MyPlugin).
    • Plugin code — the macOS CFBundleIdentifier, e.g. com.vendor.myplugin.
  4. On the plugin's Keys page you get two keys:
    • Crash API key — goes in the registration JSON below. It ships inside installers (world-readable), so treat it as a public account tag, not a secret.
    • Symbol API key — used by CI to upload symbols. Keep this secret.
  5. (Optional) On the Account page, generate a personal API key to browse crashes from Claude via the MCP server.

2. Create the registration JSON

One JSON descriptor per plugin. Your installer drops it into the CrashReporter's Plugins folder (see step 3). The reporter reads every descriptor and tests each system crash log against all of them.

{
    "name": "MyPlugin",
    "pluginID": "com.vendor.myplugin",
    "crashUrl": "https://crashreports.rabiensoftware.com/post/",
    "apiKey": "<your plugin's crash API key>"
}
Field Meaning
name Matched against the crashing module on Windows.
pluginID Matched against the CFBundleIdentifier on macOS.
crashUrl Upload endpoint — https://crashreports.rabiensoftware.com/post/.
apiKey Your plugin's crash API key.

3. Bundle the CrashReporter into your installer

The CrashReporter is one shared background app that serves all plugins. Install it as a shared component: install only if newer, never uninstall, and drop your registration JSON next to it.

Fetch the latest signed build from the public distribution channel (or build from source):

# platform = mac | win
curl -fsSL "https://crashreports.rabiensoftware.com/reporter/latest/?platform=mac" -o CrashReporter_Mac.zip

Install the shared app here (upgrade only if newer, never uninstall):

App / exe
macOS /Library/Application Support/Rabien Software/Crash Reporter/CrashReporter.app
Windows C:\Program Files\Rabien Software\Crash Reporter\CrashReporter.exe

The reporter looks for registration JSONs in a Plugins/ folder under two roots — a system root (written by installers, elevated) and a per-user root (written by a plugin at runtime). Drop myplugin.json in whichever fits:

System root Per-user root
macOS /Library/Application Support/Rabien Software/Crash Reporter/Plugins/ ~/Library/Application Support/Rabien Software/Crash Reporter/Plugins/
Windows C:\ProgramData\Rabien Software\Crash Reporter\Plugins\ %APPDATA%\Rabien Software\Crash Reporter\Plugins\

Windows (Inno Setup)

[Components]
Name: "crashreporter"; Description: "Crash reporter (shared component, only updated if newer)"; Types: full custom; Flags: checkablealone

[Files]
; App: shared, only updated if newer, never removed on uninstall.
Source: "bin\CrashReporter\CrashReporter.exe"; DestDir: "{commonpf}\Rabien Software\Crash Reporter"; Flags: skipifsourcedoesntexist uninsneveruninstall; Components: crashreporter
; Registration JSON: always installed, never removed.
Source: "bin\CrashReporter\myplugin.json";     DestDir: "{commonappdata}\Rabien Software\Crash Reporter\Plugins"; Flags: ignoreversion uninsneveruninstall

macOS (pkgbuild)

Two gotchas make the mac component fiddly:

REP_ROOT="$REP_STAGE/Library/Application Support/Rabien Software/Crash Reporter"
mkdir -p "$REP_ROOT/Plugins" "$REP_ROOT/.incoming"
unzip -qo CrashReporter_Mac.zip -d "$REP_STAGE"
mv "$REP_STAGE/CrashReporter.app" "$REP_ROOT/.incoming/"
cp myplugin.json "$REP_ROOT/Plugins/"

codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force "$REP_ROOT/.incoming/CrashReporter.app"

pkgbuild --root "$REP_STAGE" --install-location "/" \
         --identifier "com.vendor.myplugin.crashreporter.pkg" --version "$VERSION" \
         --component-plist reporter-component.plist \
         --scripts reporter-scripts \
         reporter.pkg

Sign the app with your Developer ID; it gets notarized when you notarize the combined installer.

Windows CI tip: native (non-MSYS) curl can't read Git-Bash paths like /d/a/.... Convert with cygpath -m "$path" before passing paths to curl.


4. Launch the reporter from your plugin

The reporter is a scan-once-then-exit agent. Launch it once per process when your first instance is created, so it picks up any crash from the previous session. It's invisible (LSUIElement on macOS, GUI subsystem on Windows — no window, no console flash).

// If the shared CrashReporter is installed, launch it once per process.
static void launchCrashReporterOnce()
{
    static std::once_flag flag;
    std::call_once (flag, []
    {
       #if JUCE_MAC
        juce::File app ("/Library/Application Support/Rabien Software/Crash Reporter/CrashReporter.app");
       #elif JUCE_WINDOWS
        auto app = juce::File::getSpecialLocation (juce::File::globalApplicationsDirectory)
                       .getChildFile ("Rabien Software").getChildFile ("Crash Reporter").getChildFile ("CrashReporter.exe");
       #else
        juce::File app;
       #endif

        if (app.exists())
            juce::Process::openDocument (app.getFullPathName(), {});
    });
}

Call launchCrashReporterOnce() from your processor constructor (or when the first editor opens). If the reporter isn't installed, it's a no-op.


5. Build with debug symbols (Release)

Symbolication needs symbols. Emit them in Release builds.

macOS (dSYM) — Xcode/CMake:

set_target_properties(${tgt} PROPERTIES
    XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")

Windows (PDB) — MSVC/CMake:

target_compile_options(${tgt} PRIVATE "$<$<CONFIG:Release>:/Zi>")     # full debug info
target_link_options(${tgt}    PRIVATE "$<$<CONFIG:Release>:/DEBUG>")  # produce the .pdb
target_link_options(${tgt}    PRIVATE "$<$<CONFIG:Release>:/OPT:REF>") # restore size opts
target_link_options(${tgt}    PRIVATE "$<$<CONFIG:Release>:/OPT:ICF>") # that /DEBUG disables

6. Strip the binaries you ship

This step is essential. Strip symbols from the binaries in your installer so the OS can't symbolicate crash logs locally — otherwise crash logs arrive already symbolicated and the whole server pipeline is pointless. Stripping preserves the build UUID / debug-id, so your uploaded symbols still match.

macOS — strip each format's Mach-O, before codesigning:

strip -x MyPlugin.vst3/Contents/MacOS/MyPlugin
strip -x MyPlugin.component/Contents/MacOS/MyPlugin
strip -x MyPlugin.clap/Contents/MacOS/MyPlugin

Windows — the PDB is a separate file, so there's nothing to strip: just don't ship the .pdb in your installer.


7. Upload symbols from CI

On each release build, zip the symbols and upload them keyed to the plugin version. Uploads are write-once per (plugin, platform, version, filename).

macOS — zip the dSYMs (generate any the build didn't emit with dsymutil):

zip -r Symbols_Mac.zip \
  AU/MyPlugin.component.dSYM VST3/MyPlugin.vst3.dSYM CLAP/MyPlugin.clap.dSYM

curl -fsS -H "X-API-Key: $SYMBOL_API_KEY" \
  -F "platform=mac" -F "version=$VERSION" -F "files[]=@Symbols_Mac.zip" \
  https://crashreports.rabiensoftware.com/symbols/

Windows — zip the PDBs and upload with platform=win.

Notes:


Viewing crashes


Checklist