Files
..

CleanCopy

CleanCopy is a lightweight, headless Windows utility that sits silently in the system tray, automatically sanitizing your clipboard text on trigger. Designed with a strict focus on privacy, developer ergonomics, and performance, CleanCopy operates with a minimal footprint and compiles to a native installer.

Features

  • Automatically removes marketing/analytics trackers (e.g., utm_*, gclid, fbclid, si, msclkid) from copied URLs while keeping functional query arguments. Users can also define custom tracking parameters via a comma-separated input in the Rules tab.
  • Converts curly quotes ("/"/'/'), en-dashes (), and em-dashes () to standard straight quotes (", ') and double hyphens (--).
  • Joins single-newline wraps (frequently introduced by chat apps like WhatsApp or Messenger) into continuous lines while preserving double-newline paragraphs and Markdown layout structure. Also handles hyphenated line breaks (e.g., "inno-\nvation""innovation").
  • Removes zero-width spaces, null bytes, and other invisible Unicode characters that can cause issues when pasting.
  • Plays a system beep and shows a Windows notification each time the clipboard is sanitized.
  • Optional autostart toggle via tauri-plugin-autostart.
  • A dark-themed settings panel styled around the VS Code Lavender Dimmed color scheme, using the local CaskaydiaCove Nerd Font.
  • Integrates secure Ed25519-signature-based offline licensing tied directly to a hardware-fingerprinted machine ID (HWID).

Getting Started

Prerequisites

  1. Node.js (v18+)
  2. Rust & Cargo (latest stable release)
  3. Windows C++ Build Tools (Visual Studio Build Tools with C++ desktop workload enabled)

Installation

Clone the repository and install npm packages from the app directory:

cd app
npm install

Running in Development

Start the development server (starts Vite and launches the Tauri background tray app):

npm run tauri dev

Testing & Code Quality

Running Frontend Tests

Run the Vitest test suite covering React components, state management, and UI behavior:

npm run test

Linting Frontend Code

Run ESLint across all frontend TypeScript/TSX files:

npm run lint

To auto-fix fixable issues:

npm run lint:fix

Formatting Frontend Code

Run Prettier to format all frontend files:

npm run format

To check formatting without writing (useful for CI):

npm run format:check

Running Rust Tests

Run unit tests for clipboard cleansing rules and cryptographic license validation:

cargo test --workspace

Running Rust Linter

Verify code quality and check for warnings using Cargo Clippy:

cargo clippy --workspace -- -D warnings

Building Frontend

Verify that the TypeScript compilation and Vite assets packager complete without warning:

npm run build

Architecture Notes

Clipboard Simulation

When the global hotkey is pressed (Alt + Shift + C by default), CleanCopy uses the Windows keybd_event API to simulate a Ctrl+C keystroke. It releases the Alt/Shift modifiers first to avoid polluting the copied selection, then waits 150ms for the OS clipboard to update before running the sanitization pipeline.

Profile-Based Licensing

The file licensing_profiles.json is compiled directly into the Rust binary via include_str!. Profile selection at runtime:

Build mode Profile Activation URL
cargo test testing http://localhost:3000/api/activate
cargo build (debug) development http://localhost:3000/api/activate
cargo build (release) production https://cleancopy.stevanfreeborn.com/api/activate

License Enforcement

When no valid license is found, CleanCopy locks the UI to the License tab only — the General and Rules tabs are disabled. If the global hotkey is pressed while unlicensed, it opens the settings window to the License tab instead of cleaning the clipboard.

Licensing System

CleanCopy utilizes a unified, database-free Stripe licensing proxy designed to support a suite of multiple desktop applications.

The Activation Flow

  1. The user buys CleanCopy on your landing page via Stripe Checkout.
  2. The user copies their Stripe Payment ID (e.g. pi_3MtgK...) or Charge ID (e.g. ch_3MtgK...) from their Stripe receipt.
  3. The user pastes the Stripe ID into CleanCopy's License tab.
  4. CleanCopy pings server. The server:
    • Verifies the purchase with Stripe's API.
    • Checks that the payment is for the correct product_id.
    • Signs the user's Hardware ID (HWID) using a private Ed25519 signing key.
    • Returns a signed activation token.
  5. CleanCopy verifies the signature using the embedded public key, saving it to license.lic. On subsequent startups, CleanCopy validates this file offline with zero network requests.

Local Development Testing

To test the activation flow locally without deploying a live server:

  1. Start the mock licensing server:
cd app/src-tauri
cargo run --bin mock_license_server
  • CleanCopy detects debug mode and automatically redirects its requests to http://localhost:3000.
  • In the Settings -> License UI, enter any key (e.g. pi_test_12345) and click Activate.
    • To test purchase failures, enter a key starting with pi_invalid (e.g. pi_invalid_999).

Option B: Offline Sign & Bypass

  • Copy the HWID displayed in Settings -> License.
  • Run the license tool to sign it manually with the private key:
cd app/src-tauri
cargo run --bin license_tool issue 04a0664dfa85f71119e9f4fc971a953ad49f82b752087deab6bee816c1d37d6d <YOUR_HWID>
  • Paste the returned token into the activation UI.
  • Delete the license.lic file from your local AppData directory:
    • Path: C:\Users\<user>\AppData\Roaming\com.cleancopy.app\license.lic

The CLI License Tool (license_tool)

CleanCopy includes a built-in Rust command-line utility license_tool.rs for key generation and offline license issuance.

Generating Keypairs (generate-keys)

Generate fresh, cryptographically secure Ed25519 public/private key pairs. The tool uses getrandom to extract secure entropy from the host OS (Windows, macOS, or Linux).

Run the tool with an optional profile name (prefix) and optional target folder:

cd app/src-tauri
cargo run --bin license_tool generate-keys [profile_name] [output_dir]

Example:

cargo run --bin license_tool generate-keys development
  • Generates keys and automatically persists them as licensing/development.priv and licensing/development.pub under your .gitignored project root folder.
  • Outputs the key hex values directly to the console.

Issuing Offline Activation Keys (issue)

Issue signed license tokens manually for air-gapped users, secure enterprise environments, or local automated virtual-machine testing.

Run the tool specifying the profile's private key and the user's Hardware ID (HWID):

cd app/src-tauri
cargo run --bin license_tool issue <private-key-hex> <device-hwid> [product_id] [license_key]

Example:

cargo run --bin license_tool issue 04a0664dfa85f71119e9f4fc971a953ad49f82b752087deab6bee816c1d37d6d 9a0b396c42da0860b246aaa3b8e0cafb86789e062a8947957bb88a46f08477b6
  • Outputs a period-separated activation token: PAYLOAD_B64.SIGNATURE_B64. The customer pastes this token into their Settings panel, which is verified offline against the compiled public key.

Purging License Status (deactivate)

Easily reset your development machine back to the "Unregistered" state to verify and test the activation UI/flow repeatedly without manually locating files.

Run the tool with the deactivate subcommand:

cd app/src-tauri
cargo run --bin license_tool deactivate
  • Locates the local AppData directory, checks if license.lic is present, and deletes it.

Diagnostics: Manually Querying a User's HWID

If a customer is offline and cannot run the app to copy their Hardware ID from the License tab, can instruct them to run this single line in PowerShell:

$guid = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Cryptography').MachineGuid; 
$serial = (Get-CimInstance -ClassName Win32_BaseBoard).SerialNumber; 
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('SHA256'); 
$bytes = [System.Text.Encoding]::UTF8.GetBytes("$guid$serial"); 
$hash = $hasher.ComputeHash($bytes); 
-join ($hash | ForEach-Object { "{0:x2}" -f $_ })

This queries the registry and bios serial, SHA-256 hashes them, and prints the matching HWID.

Key Rotation

If your signing key is compromised or you need to rotate for any reason, CleanCopy supports seamless key rotation via the deprecated_public_keys field in licensing_profiles.json.

How It Works

Each profile contains:

  • public_key_hex: The current signing key (used for all new activations)
  • deprecated_public_keys: Array of previous keys that remain valid for existing users

When verifying a license, CleanCopy tries the current key first, then falls back to each deprecated key. Users with tokens signed by an old key continue to work without re-activation.

Rotation Steps

  • Generate a new keypair:
cd app/src-tauri
cargo run --bin license_tool generate-keys production
  • Update licensing_profiles.json:
{
  "production": {
    "activation_url": "https://cleancopy.stevanfreeborn.com/api/activate",
    "public_key_hex": "<new key>",
    "deprecated_public_keys": ["<old key>"]
  }
}
  • Update the signing key on your activation server to use the new private key.
  • Rebuild and distribute the updated app. Existing users' licenses validate against the deprecated key. New activations use the current key.
  • After the migration period, remove the old key from deprecated_public_keys and rebuild.

Logging and Diagnostics

CleanCopy uses the tauri-plugin-log crate for structured logging at the Info level.

Log Output

Logs are written to two destinations simultaneously:

Destination Purpose
Stdout Visible in the terminal when running npm run tauri dev
Log file Persistent file for production builds

Log File Location

The log file is named app.log and stored in the Tauri log directory:

Platform Path
Windows %APPDATA%/com.cleancopy.app/logs/app.log

What Gets Logged

  • Startup: Config loading, license validation, hotkey registration
  • Clipboard events: Each clean operation with input/output text
  • License activation: Proxy requests, token verification results
  • Errors: File I/O failures, signature verification failures, network errors

Viewing Logs

Real-time (development):

npm run tauri dev
# Logs appear in the terminal

Log file (production):

# Windows PowerShell
Get-Content "$env:APPDATA\com.cleancopy.app\logs\app.log" -Tail 50 -Wait

Log Level

The log level is hardcoded to Info. To change it for debugging, modify the .level() call in app/src-tauri/src/lib.rs:

.level(log::LevelFilter::Debug)
.level(log::LevelFilter::Warn)

Production Distribution

To bundle a production-ready MSI installer and release binary:

npm run tauri build

The compiled assets will be bundled in:

app/src-tauri/target/release/bundle/msi/