Loupe
Documentation

Technical

License storage.

Loupe stores your activation license as a flat JSON file in Application Support — not in the macOS Keychain. The Ed25519 signature on the envelope is the integrity boundary, so the file path is bundle-ID keyed and stable across signing-cert rotations, dev rebuilds, and reinstalls.

Where the license lives

The license file is at:

~/Library/Application Support/studio.shyguy.loupe/license.json

It contains the signed activation envelope (Ed25519 over JSON, base64url-encoded), the activation device ID, the original activation timestamp, and a sticky permanently-expired flag. File permissions are 0600 (user read/write only); writes are atomic.

Inspect with cat; delete with rm to deactivate (Loupe re-creates it on the next successful activation).

Why a flat file instead of Keychain

The activation envelope's Ed25519 signature is what proves the license is valid. Loupe verifies it locally against a public key embedded in the binary. Keychain doesn't add meaningful confidentiality on top of that signature — but it does couple the entry to the binary's signing identity.

Coupling caused silent license loss across re-signs: Apple Developer cert rotations, dev rebuilds, app reinstalls. The file path is bundle-ID keyed and stable across all of those events. If you upgrade Loupe across a developer-cert rotation, your trial / paid license survives.

Per-case AES encryption keys are still in Keychain — those genuinely need it. Compromise of a per-case key means the encrypted case file is readable; we want OS-managed access control on those. The license is a different surface: a publicly-distributable signed envelope. Different requirements, different storage.

Migrating between Macs

Two equally valid paths:

  1. Copy the license file to the new Mac at the same path:~/Library/Application Support/studio.shyguy.loupe/license.json
  2. Re-activate from the original loupe://activate?envelope=... URL on the new Mac. Re-activation does not reset the trial clock — the expiry date is signed into the envelope.

Migrating cases between Macs requires both the case files (in Application Support) and the per-case Keychain keys. See the bundled Reference Manual §6 for the full migration recipe.

Reproducibility

Storage code: Sources/Loupe/Services/LicenseStorage.swift. Verify the file exists with ls -la ~/Library/Application\ Support/studio.shyguy.loupe/ after activation. Verify Keychain holds only per-case keys (not the license) with security find-generic-password -s studio.shyguy.loupe -a license — should return "could not be found" on a v1.0+ install. Tests: Tests/LoupeTests/LicenseStorageTests.swift covers the file round-trip + the one-shot Keychain migration for installs that pre-date this storage layout.