Skip to main content
Migrate Already selling? Move your customers to Keylight without re-issuing a single key.
Keylight

Use case · Audio plugins

Licensing for audio plugins (VST/AU)

Sell your audio plugin direct and license it with Keylight's JUCE single-header adapter — activate a key from the plugin editor, gate pro features in processBlock via a lock-free atomic, and verify tamper-proof Ed25519 leases offline across any DAW host.

Start Free

For developers selling commercial audio plugins (VST3, AU, AAX) direct.

  • Audio plugins face heavy piracy.
  • One license must work across DAW hosts like Ableton, FL Studio, and Logic.
  • Offline sessions can't depend on a server.

Updated June 2026

Selling a commercial audio plugin is one of the more technically demanding things an independent developer can do — and that is before you get to the licensing layer. Between the VST3/AU/AAX target matrix, host-specific edge cases, and the reality that your plugin loads inside someone else’s process, the complexity accumulates quickly. Licensing should not add to that bill.

The Keylight JUCE adapter is a single header — keylight_single.hpp — that you drop into your project. It handles activation, offline lease storage, background revalidation, and revocation detection using juce::URL, and exposes a lock-free atomic you read safely from the audio thread. This page explains what plugin licensing requires and how to integrate in an afternoon.

Why audio plugins are a hard licensing target

Plugins do not launch from a dock icon. They load inside a host — Ableton Live, FL Studio, Logic Pro, Reaper, Pro Tools — and that host controls the process lifecycle, the audio thread, and in many cases the network sandbox. This creates constraints that desktop app licensing does not face.

The plugin must work inside any DAW. A license tied to a specific host would be unusable. Customers buy one key and expect it to load cleanly whether they switch DAWs, reinstall a host, or run both Ableton and Logic on the same machine. The license must be tied to the customer and their device, not to a specific host process.

Sessions are often offline. Recording sessions happen on isolated machines, on the road, or in studios that deliberately keep the production network air-gapped. A plugin that fails to initialize because it cannot reach a server will not ship.

The audio thread is sacred. Your plugin’s processBlock runs under real-time constraints. Any blocking call — network, disk, mutex — can cause a dropout. License checks that happen on the audio thread must be read-only, allocation-free, and non-blocking.

Piracy is a real problem. The plugin market has sustained cracking communities for decades. Licensing reduces casual copying and raises the effort required to distribute working cracked copies — it does not eliminate determined attackers, and no licensing system can promise that. What it does is make direct purchase the obviously easier path, which is enough to matter commercially.

What plugin licensing actually requires

A complete licensing implementation for an audio plugin covers three things.

Key issuance and activation. When a customer completes a purchase, a unique license key is issued and delivered by email and on the download page. On first launch, the plugin calls an activation endpoint from the editor UI with the key and a device identifier. The server checks the key is valid, not revoked, and below its activation limit, then returns a signed lease.

Offline-ready verification. The signed lease is a tamper-proof Ed25519 payload — the customer’s entitlements, the activation limit, and an expiry — signed with Keylight’s private key. Your plugin ships with the corresponding public key. On every subsequent launch, the plugin verifies the signature locally without a network call. If the signature is valid and the fields match, the plugin unlocks. This is how offline validation works.

Revocation on revalidation. The lease has a time-to-live. When it expires, the adapter makes a background revalidation call to refresh it. That call is the moment revocation is enforced — a refunded or fraudulent key gets rejected, and the plugin returns to an unlicensed state. Between revalidations, the cached lease governs behavior. A session already in progress is never interrupted.

Integrating with the JUCE single-header adapter

The adapter is keylight_single.hpp — one file, no build system changes beyond adding it to your source tree.

Install

# Drop into your JUCE project's Source/ directory
cp keylight_single.hpp Source/

No CMake changes, no new dependencies. The adapter uses juce::URL for HTTP, so everything that compiles your JUCE plugin already compiles this.

Initialize in your PluginProcessor

#include "KeylightJuce.h"   // the single-header JUCE adapter

class MyPluginAudioProcessor : public juce::AudioProcessor
{
public:
    MyPluginAudioProcessor()
        : licensing(makeConfig())
    {
        // Verifies any cached lease offline and refreshes in the
        // background. Never blocks the message thread.
        licensing.checkOnLaunch();
    }

    // Owns the keylight::Client, the JUCE transport, the on-disk store,
    // and the lock-free atomic the audio thread reads.
    keylight::juce_integration::Licensing licensing;

private:
    static keylight::Config makeConfig()
    {
        keylight::Config cfg;
        cfg.tenantId  = "your-tenant";
        cfg.productId = "your-plugin";
        // Trusted Ed25519 public keys (kid -> base64) from your dashboard.
        cfg.trustedKeys["k1"] = "<base64-public-key>";
        return cfg;
    }
};

Licensing wraps keylight::Client with JUCE’s own networking (juce::URL) and an on-disk lease store. checkOnLaunch() verifies a cached lease offline and refreshes in the background on a worker thread — your constructor returns immediately.

Activate from the plugin editor

Activation belongs in your editor UI — a text field for the key and a button. Call it from the message thread:

void MyPluginEditor::activateButtonClicked()
{
    auto key = licenseKeyField.getText().trim();

    // The SDK round-trip runs on a worker thread; the callback is delivered
    // on the message thread — so it's safe to touch JUCE components directly.
    processor.licensing.activate(key,
        [this](keylight::Result<keylight::State> r)
        {
            if (r.is_ok() && r.value() == keylight::State::Licensed)
                statusLabel.setText("Licensed", juce::dontSendNotification);
            else
                statusLabel.setText("Activation failed — check your key.",
                                    juce::dontSendNotification);
        });
}

activate() runs the network round-trip off the message thread and invokes your callback back on the message thread. You never marshal threads yourself, and the audio-thread flag is updated internally.

Gate features in processBlock

The audio thread calls hasFeature() — a single lock-free atomic read, no allocation, no blocking:

void MyPluginAudioProcessor::processBlock(
    juce::AudioBuffer<float>& buffer,
    juce::MidiBuffer& midiMessages)
{
    // Audio-thread-safe: hasFeature() reads a lock-free atomic snapshot.
    if (!licensing.hasFeature("pro"))
    {
        // Unlicensed path: pass through dry signal or silence pro bus
        applyFreeFeatures(buffer);
        return;
    }

    // Pro path: run the full algorithm
    applyProFeatures(buffer);
}

Licensing keeps that atomic up to date from its background thread after activation or a revalidation. The audio thread only ever reads it. This is the correct division: background thread does the I/O, message thread manages UI, audio thread reads a flag.

Full state

Licensing::state() exposes four states, readable from the message thread:

StateMeaning
LicensedValid tamper-proof lease on disk
TrialWithin the trial window
ExpiredLease expired, renewal needed
InvalidNo valid lease — show activation UI

You typically read state() in your editor’s constructor to show the correct UI on open, and update the UI (and the audio-thread atomic) whenever the state changes.

Check entitlements

If your plugin has multiple tiers — a free version that becomes pro — use hasFeature:

// Message thread, e.g. in your editor constructor.
// hasFeature() is safe on any thread (including the audio thread).
bool isPro = processor.licensing.hasFeature("pro");
showProFeatures(isPro);

Entitlement labels are strings you define when you set up your product in the Keylight dashboard.

One key across DAW hosts

The license is issued per-customer and tracked against device activations — not against a specific host application. Whether the plugin loads in Ableton, FL Studio, Logic, or Reaper, it reads the same cached lease from disk and performs the same local signature verification against the same public key.

If a customer upgrades their main DAW or switches hosts, nothing changes on the licensing side. Device management — resetting activations, adding slots — lives in the Keylight dashboard, which your customers can reach through a self-service portal.

What Keylight handles

Keylight sits between Stripe and your plugin. Stripe handles the payment; Keylight handles everything after:

  • Minting a signed license key when a purchase completes via Stripe webhook
  • Serving the activation endpoint and tracking per-device activations
  • Storing the signed Ed25519 lease and returning it on activation and revalidation
  • The dashboard for looking up licenses, resetting activations, and revoking keys
  • A customer-facing self-service portal for activation management

You configure your product in the Keylight dashboard, connect your Stripe account, and paste the SDK key into your plugin’s code. The purchase flow, key delivery, and webhook handling run without any server you maintain.

Getting started

The setup is minimal on the Keylight side. Create an account, add a product, connect Stripe, and grab the SDK key and public key for your tenant and product. Drop keylight_single.hpp into your JUCE project, initialize keylight::Client in your processor constructor, call activate() from your editor, and read the atomic from processBlock.

Keylight plans start at $19/month — see the pricing page. The free tier allows up to five licenses, which is enough to validate the full flow end to end: purchase, activation, offline verification, revocation.

The operational surface around licensing — key delivery, dashboard, portal, webhook handling — tends to cost more developer time than the audio-thread integration itself. Using a licensing layer means you skip that entirely and focus on the plugin.

Frequently asked

Can I license a VST or AU plugin today?+

Yes. Drop keylight_single.hpp into your JUCE project, call activate() from your plugin editor, and gate features in processBlock via the lock-free atomic. The adapter verifies tamper-proof Ed25519 leases offline — no backend required.

Will one license work across different DAWs?+

Yes. The license is tied to the customer and their device activations, not to a specific host, so the same key works whether the plugin loads in Ableton, FL Studio, or Logic.

Is the license check safe to call on the audio thread?+

The check you put in processBlock reads a lock-free atomic bool — a single load, no locking, no allocation. All network and disk I/O happens on a background thread managed by the adapter.

Start licensing your app today

Drop in the Swift SDK, point it at your dashboard, and sell paid apps in under a minute. Free forever tier included.

Start Free