Integrating with non-Electron apps

Use a small Node.js sidecar process to use the DSDK with Tauri, Swift, and other native apps.

The Desktop Recording SDK is distributed as an npm package. When using Electron apps, it is straightforward to use it directly. In apps built on other frameworks (e.g. Tauri), you can still use the SDK by running it inside a small Node.js sidecar process that your app spawns and communicates with over standard I/O.

How the sidecar works

Your app spawns a Node.js process you ship inside it. That process loads @recallai/desktop-sdk, which in turn spawns the Recall native binary. Your app and the sidecar talk to each other over stdin/stdout.

Keep the sidecar small. It should expose the SDK's methods over some kind of IPC protocol (typically line-delimited JSON over stdio) and forward SDK events back to your app. All UI, state, and business logic can stay in your main app.

🚧

MacOS: Don't bundle Homebrew's node

Homebrew's node on macOS is dynamically linked against libnode.dylib and other dylibs in /opt/homebrew/Cellar/. It will not run when copied into your app bundle on its own. Use the official self-contained binary from nodejs.org instead.

Tauri

Tauri has first-party documentation for embedding a Node.js sidecar in a Tauri v2 app. You can find that here.

The Tauri guide assumes a self-contained Node script. The SDK locates its native binary and Frameworks/ directory at runtime relative to its own JS file, so the guide's pkg / bun build --compile path won't work as-is — you'll need to make sure the SDK's node_modules/@recallai/desktop-sdk/ directory ends up in your packaged app. One workable approach is to ship an official Node binary via bundle.externalBin and put your sidecar script plus node_modules under Tauri's resources, but the specific layout is up to you.

A few smaller things specific to a Tauri integration:

  • The sidecar is long-lived. Tauri's guide example calls .output() for a one-shot command. For an ongoing bidirectional sidecar, use .spawn() instead — it returns a (Receiver<CommandEvent>, CommandChild) tuple. Hold onto the CommandChild for the lifetime of the sidecar; if you drop it, the child process is killed.
  • Tauri's setup() hook is synchronous and has no live Tokio reactor. If you spawn the sidecar from setup(), wrap the spawn in tauri::async_runtime::block_on(...) and use tauri::async_runtime::spawn (not tokio::spawn) for background tasks.

Swift / Native macOS

There's no first-party guide for embedding a Node.js sidecar in a Swift app, but Foundation's Process and Pipe cover everything you need. The rough shape:

  1. Put a Node binary, your sidecar script, and node_modules (with the SDK intact) into YourApp.app/Contents/Resources/.
  2. Spawn Process with executableURL pointing at the bundled Node and arguments pointing at the sidecar script.
  3. Wire up three Pipes for stdin, stdout, and stderr.
  4. Write IPC requests to stdin; read events and responses from stdout.

A few smaller things specific to a Swift integration:

  • Pipe.fileHandleForReading.readabilityHandler delivers raw byte chunks, not lines. If your IPC is line-based, you'll need to buffer and split on \n yourself.
  • You don't need an Xcode project. Swift Package Manager produces an executable you can assemble into a .app bundle by hand.

Other frameworks

The same pattern works for any framework that can spawn a child process and read and write its standard I/O. The specifics will differ — how you spawn, how you read stdout, how you bundle Node — but the architecture is the same. If you're integrating from a framework not covered here and run into trouble, reach out to support.