Technical Article

Securing Velocaption's Offline-First License System

Written by Francesco Di Donato 4 minutes reading
Securing Velocaption's Offline-First License System

The V3 Bypass: How I Secured Velocaption’s Offline-First License System

Velocaption is a local video editor with an offline-first license system, allowing use without a constant server connection.

During a pre-launch audit for V3, I found a vulnerability: users could bypass license enforcement using the app’s built-in Developer Tools. This post outlines the bypass, the frontend’s security failures, and the rebuilt license lifecycle.


The V3 Scare: Discovering the IPC Bypass

Velocaption is built with Tauri, meaning the frontend is a web view communicating with a Rust backend via Inter-Process Communication (IPC).

In the pre-V3 architecture, the frontend managed the application’s lock state. This exposed an attack surface through DevTools.

Because Tauri exposes commands via IPC, a user could open DevTools and trigger state changes or call backend commands like invoke("download_and_install_command") after license revocation. Hiding the LicenseModal via CSS or JavaScript granted full access to the application.

Tauri IPC

invoke('update')

FATAL FLAW

Frontend (JS)

• DevTools Open

• Modal Hidden

• CSS Altered

Rust Backend

Begins Download

(No OS check)

The Lesson: Frontend JavaScript logic is inherently manipulable and cannot act as a security barrier.


The Fix: Moving the Source of Truth to the OS

I removed licensing authority from the frontend, integrated Lemon Squeezy as the licensing backend, and established the macOS Keychain as the local source of truth.

Any operation requiring a license now checks the keychain directly within the Rust process. This prevents JavaScript spoofing.

The updated lifecycle operates as follows.


The License Lifecycle (How it Works Now)

The system is offline-first. Network access is required only for initial activation, background heartbeat validation, and downloading assets.

1. Activation (The Only Blocking Network Call)

When a user enters a key, the app posts to the Lemon Squeezy API (/v1/licenses/activate). Upon success, Rust saves a LicenseData object to the macOS Keychain containing the raw license key, the Lemon Squeezy instance ID, and an ISO-8601 validation timestamp.

valid: true

valid: false

User Enters Key

Lemon Squeezy API

macOS Keychain

Show Error UI

2. App Startup & The Background Heartbeat

On launch, Rust checks the macOS Keychain. If a valid entry exists, the UI is synchronously unlocked. This local check lets the application boot without an internet connection.

Once unlocked, Rust spawns a detached, asynchronous task to validate the license against Lemon Squeezy (/v1/licenses/validate) without blocking the user.

Key Found

No Key

Offline

Online

Yes

No

App Launches

Check OS Keychain

Unlock UI Synchronously

Render LicenseModal

Spawn Async Heartbeat

Network Status?

Ignore Silently / App Stays Unlocked

LS API Valid?

Update Keychain Timestamp

Revoke / Delete Key / Lock UI

  • If Offline/Network Error: The error is ignored; the application remains unlocked.
  • If Invalid/Revoked: Rust deletes the keychain entry and emits a LICENSE_REVOKED event to the frontend, which renders the LicenseModal over the entire application, locking the interface.

Securing the Final Loop: Update Gating

To stop revoked users from downloading updates via direct IPC calls, I added backend checks.

I secured tauri_plugin_updater by gating its entry points (check_for_updates_command and download_and_install_command) behind a Rust function: has_valid_license_key().

if !has_valid_license_key() {
    return Err("A valid license is required to install updates.");
}

If invoke("download_and_install_command") fires from DevTools while the license modal is active, Rust checks the OS keychain. Finding it empty, it returns an error to the frontend, blocking the bypass.

invoke update

invoke update

Yes (Reads OS Keychain)

No (Empty/Invalid)

Normal App UI

Rust: has_valid_license_key?

Fetch Update from CDN

Return Err to Frontend

What about manual tampering?

A user could manually create a fake keychain entry to pass the has_valid_license_key() check for a single session. The subsequent background heartbeat will then check the Lemon Squeezy API, identify the fake key, and clear the keychain.


The Takeaway: Trust the System, Not the Screen

The frontend cannot guarantee application security. Relying on JavaScript state, hidden UI modals, or frontend routing permits bypasses.

Shifting the source of truth to the macOS Keychain and enforcing it in Rust eliminates the DevTools vulnerability while preserving offline functionality.

Applications must tolerate network instability without exposing update endpoints to exploitation.

Verify licenses at the system level.

Local Mac workflow

Keep the edit on your Mac.

Choose a license to record, clean, caption, and export without moving private footage into a browser editor.