A small open-source library that signs and verifies license keys entirely on-device. No backend, no accounts, no phone-home. You generate the keys, we hand them to your buyers.
Ed25519Signed and verified on-device0Servers to runMITOpen source, yours to forkOfflineIncluding first activation
Why this exists
Licensing should not become infrastructure.
You wrote a menu bar utility. Now you are being asked to run an activation server, keep it online forever, store customer records, and pay a monthly fee to a licensing SaaS that takes a cut of every sale. All of it so a text field can say “valid”.
A signature check does the same job, on the customer’s machine, for free, forever.
×An activation server you must keep alive
×A monthly bill that scales with your sales
×Customer records you never wanted to hold
A signed string, checked on the device
01
No server to run.
Keys are signed once, on your machine, and verified on the customer’s Mac. There is nothing to host, nothing to pay for monthly, and nothing that can go down at 3am and lock out your customers.
02
It works offline.
Activation is a signature check, not a network request. Your app works on a plane, behind a corporate firewall, and in ten years when you have moved on to something else.
03
No customer data leaves the Mac.
No accounts, no device fingerprints, no analytics, no activation limits. You cannot leak what you never collected — and privacy-minded Mac users notice.
04
You keep the keys. Literally.
You generate the keypair. Your private key never touches this store or anyone else’s infrastructure. If we vanished tomorrow, your licensing keeps working exactly as it does today.
How it works
Four steps, once.
1
Create your minting key
indielicense init — makes an Ed25519 keypair in a secure directory outside your repo. The public key goes in the app; the private key never leaves your machine.
2
Generate the Swift plumbing
indielicense integrate swift writes plain Swift files into your project — verifier, config, manager, and optional SwiftUI key entry and status badge. No SDK, no dependency to keep updated.
3
Mint a batch of keys
indielicense generate --count 500 writes keys.csv. Key ids continue across batches, so you can top up whenever you run low.
4
Upload the CSV
Drop it into your listing here as the key pool. We hand one key to each buyer at checkout and tell you when you are running low.
Key modes
Three ways to sell.
Chosen when you mint the batch, not baked into your app. You can mint a different mode tomorrow without shipping a new build.
Lifetime--mode lifetime
The app unlocks forever. No expiry, no update cutoff. The “$9, yours forever” key.
Updates--mode updates --updates-duration 365d
Unlocks forever, but only versions released within 365 days of activation are covered. After that they keep their current version or buy a renewal. This is the buy-once-with-12-months-of-updates model.
Trial--mode trial --expires 14d
Stops working 14 days after activation. For press, beta testers and “extend my trial” support requests.
The clock starts at activation, never at generation. A key holds a duration, not a date. Mint 500 trial keys today and they can sit unsold for six months — the countdown begins the first time a customer pastes one in.
Let your agent do it
Paste this into your coding agent.
It knows the library, the constraints, and the mistakes to avoid. Most apps are wired up in a single pass.
Paste into Claude Code from your project root — it will run the licensing interview with you.Paste into Codex. Pre-filled with sensible defaults for a single pass.Paste into Cursor’s composer. Includes .cursorrules entries to add.
Add IndieLicense to this macOS app so I can sell it with offline license keys.
Start here
- Repo: https://github.com/tarasowski/indielicence
- Read AI_INTEGRATION.md first — it is the copy-paste integration playbook.
- Read AGENTS.md — it contains mandatory security and repository rules. Follow them.
- SPEC.md describes the key format if you need it.
Run the licensing interview from AI_INTEGRATION.md with me before writing code.
Do not guess my answers. I need to decide: key mode (lifetime / updates /
trial), update window length, keyless trial days, and purchase URL.
Then do the work
1. Install the CLI: brew install tarasowski/tap/indielicense
2. indielicense init --product <id> --key-dir ~/Licensing/<id>
The private key never enters the repo or the app bundle. Only the public
key is embedded. Remind me to back up the key directory.
3. indielicense integrate swift --product <id> --key-dir ~/Licensing/<id> \
--build-date <today> --output ./<App>/License --ui swiftui \
--denylist bundled --trial <N>d --purchase-url <my store URL>
This generates app-owned Swift files — LicenseVerifier, LicenseConfig,
LicenseManager, and the SwiftUI activation/badge views. There is no SDK.
4. Wire LicenseManager into app launch. Gate paid features on
license.hasFullAccess (licensed OR in keyless trial), not isLicensed.
5. Add LicenseBadgeView to the toolbar so trial state is always visible.
6. Embed the release build date as a source constant. Never read the
executable's modification time — it is mutable after code signing.
7. Handle LicenseStore throw as an explicit storage-failure state. Never
silently start a new activation window on a storage error.
8. Work through the LICENSE_INTEGRATION.md checklist it generates.
Constraints
- No networking, telemetry, or device fingerprinting. Validation is offline.
- Never overwrite existing files in the output directory.
- Treat any note text from a key as display data — SwiftUI text APIs only.
When done, show me the command to mint 500 keys and confirm where my private
key lives.
Integrate IndieLicense (github.com/tarasowski/indielicence) into this
macOS Swift app for offline license-key validation. Follow AI_INTEGRATION.md
in that repo; AGENTS.md has the security rules.
Assume: product id "<id>", key mode "updates" with a 365-day window, a 7-day
keyless trial, output directory ./<App>/License. Ask me only if one of these
cannot be inferred from the project.
Steps
1. brew install tarasowski/tap/indielicense
2. indielicense init --product <id> --key-dir ~/Licensing/<id>
3. indielicense integrate swift --product <id> --key-dir ~/Licensing/<id> \
--build-date <today> --output ./<App>/License --ui swiftui \
--denylist bundled --trial 7d --purchase-url <store URL>
4. Call LicenseManager at launch; gate features on hasFullAccess.
5. Embed the build date as a compile-time constant, not file metadata.
6. Surface storage errors as an explicit failure state.
Rules
- Private key stays in ~/Licensing/<id>, never in the repo or bundle.
- Only the public key is embedded.
- No network calls, no telemetry, no fingerprinting, no activation limits.
- Do not overwrite existing files.
Output the diff, then the command to mint 500 keys into keys.csv.
@Codebase Add offline license keys to this macOS app using IndieLicense.
Reference: github.com/tarasowski/indielicence — read AI_INTEGRATION.md for the
integration playbook and AGENTS.md for the security rules before editing.
The CLI generates plain Swift files into the project; there is no runtime SDK
to add to Package.swift unless I ask for the SPM route.
brew install tarasowski/tap/indielicense
indielicense init --product <id> --key-dir ~/Licensing/<id>
indielicense integrate swift --product <id> --key-dir ~/Licensing/<id> \
--build-date <today> --output ./<App>/License --ui swiftui \
--denylist bundled --trial 7d --purchase-url <store URL>
Then, in the app:
- Instantiate LicenseManager once and pass it through the environment.
- Gate paid features on hasFullAccess (licensed or in trial), not isLicensed.
- Put LicenseBadgeView in the main toolbar.
- Embed the build date as a source constant, never file metadata.
- Treat storage throws as an explicit failure state, not a fresh trial.
Add to .cursorrules so future edits respect it:
- Never commit or print private key material from ~/Licensing.
- Only the public key may be embedded in the app.
- License validation is offline; do not add network calls or telemetry.
- Do not overwrite generated files in the License directory.
Ask me for the product id, trial length and purchase URL before running
anything.
Free, MIT-licensed, and yours to fork.
We built it because we needed it, and we open-sourced it because licensing should not be a business model. Use it with this store, use it on your own site, use it with a competitor. It is yours either way.