Official Python SDK for SublimeKeys — license activation and verification with offline-capable signed leases
Project description
sublimekeys
Official Python SDK for SublimeKeys — license key generation, activation, verification and trials for indie desktop apps, with offline-capable verification: once a license is activated, your app can verify it locally in milliseconds with no network call, for up to 7 days at a time, before quietly re-syncing online.
pip install sublimekeys
Quickstart
This mirrors the lifecycle described in the SublimeKeys docs: first run activates, every later run verifies (offline-first), uninstall deactivates.
from sublimekeys import SublimeKeysClient
client = SublimeKeysClient(product_id="my-app")
# First run — ask the user for their key once.
result = client.activate(license_key=user_entered_key)
if result.valid:
unlock_full_version()
# Every later launch — offline-first, instant, falls back online
# automatically once the cached lease's 7-day trust window lapses.
result = client.verify(license_key=saved_key)
if result.valid:
unlock_full_version()
print(result.source) # "offline_cache" most days, "online" roughly weekly
# Uninstall / sign out.
client.deactivate(license_key=saved_key)
client.get_machine_id() gives you a stable, locally-persisted identifier if
you need to store it yourself — every method above also generates and caches
one automatically the first time it's needed, so passing it explicitly is
optional.
Trials
result = client.start_trial() # get-or-create a 7-day trial; idempotent —
# reinstalling never resets the clock
if result.status == "active":
print(f"{result.days_left} days left")
result = client.trial_status() # read-only check, never starts one
How offline verification works
When activate() or verify() succeeds, the server returns a lease — a
small Ed25519-signed token proving "this license is valid for this machine,
as of now." The SDK caches it locally and, on every later verify() call,
checks the signature against a pinned public key built into this package
— no network call, no dependency on the server being reachable.
The lease itself expires after 7 days (server-controlled). Once it does, the
next verify() call transparently goes online, gets a fresh lease, and the
cycle repeats. This means a revoked or expired license can take up to 7 days
to be caught while a machine stays fully offline — an intentional tradeoff
for instant, network-independent checks the rest of the time, not a bug.
API
| Method | What it does |
|---|---|
activate(license_key, machine_id=None) |
First run. Always online. |
verify(license_key, machine_id=None, allow_offline=True) |
Every later run. Offline-first by default. |
deactivate(license_key, machine_id=None) |
Uninstall/sign-out. Frees the seat, clears the local cache. |
start_trial(machine_id=None) |
Get-or-create a trial. |
trial_status(machine_id=None) |
Read-only trial check. |
get_machine_id() |
Stable per-install identifier (auto-generated, persisted locally). |
All methods return a small dataclass (LicenseResult or TrialResult) —
never raise for a normal "not valid" outcome. Network failures are retried
automatically with exponential backoff (2 retries by default, tunable via
SublimeKeysClient(..., max_retries=, backoff_base=)) before giving up;
verify() then falls back to the offline cache or a
valid=False, source="offline_cache_miss" result rather than raising.
A 5xx response that survives all retries raises ServerError (a
NetworkError subclass) instead of the usual dataclass return — the one
case worth distinguishing from an ordinary "not valid" result.
Command-line interface
Installing the package also installs a sublimekeys command — handy for
testing an integration from a terminal or in a shell script, without
writing a throwaway Python file:
sublimekeys activate --product my-app YOUR-LICENSE-KEY
sublimekeys verify --product my-app YOUR-LICENSE-KEY
sublimekeys verify --product my-app --json YOUR-LICENSE-KEY # machine-readable
sublimekeys trial-status --product my-app
sublimekeys machine-id --product my-app
Exit code is 0 for a valid/active result, 1 for invalid, 2 for a
usage error. Run sublimekeys --help or sublimekeys <command> --help
for the full flag list (--base-url, --cache-dir, --machine-id, --json).
Packaging with PyInstaller
cryptography's compiled backend is the kind of dependency that works fine
under python app.py and only breaks in a frozen build. Before shipping:
pyinstaller-hooks-contribships an officialhook-cryptographyand is usually auto-discovered. If you hitModuleNotFoundError: _cffi_backend, add--collect-all cryptography(or--hidden-import=_cffi_backend) to your PyInstaller command.- Test the frozen executable's
activate()/verify()calls, not just the unfrozen dev script — this is the single highest-value check before shipping to real users. - On macOS, notarizing a PyInstaller bundle that includes OpenSSL/rust-openssl dylibs is a known extra step — sign all bundled dylibs and test the signed, notarized build specifically.
See examples/pyinstaller/ for a minimal buildable example.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sublimekeys-0.2.0.tar.gz.
File metadata
- Download URL: sublimekeys-0.2.0.tar.gz
- Upload date:
- Size: 15.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8485b8e25e0a19ae7aff010098faa5b6bd2d4f26f8ad591c8d5e5743570189fe
|
|
| MD5 |
2e44c93c0ff32a7bee39ecf148ce2433
|
|
| BLAKE2b-256 |
5fd406edffdd5cf678a8a2a6df8549a9c546f2481d2d63702317389009cab1ee
|
Provenance
The following attestation bundles were made for sublimekeys-0.2.0.tar.gz:
Publisher:
publish.yml on Volume00/sublimekeys-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sublimekeys-0.2.0.tar.gz -
Subject digest:
8485b8e25e0a19ae7aff010098faa5b6bd2d4f26f8ad591c8d5e5743570189fe - Sigstore transparency entry: 2130732495
- Sigstore integration time:
-
Permalink:
Volume00/sublimekeys-python@cf38b9b74b74d02dacc15f2cbdbec5074e2cc35d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Volume00
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf38b9b74b74d02dacc15f2cbdbec5074e2cc35d -
Trigger Event:
release
-
Statement type:
File details
Details for the file sublimekeys-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sublimekeys-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2b10eb665d65bf7ed2038a12e2515aa7957826df1bdce1236887d58cf7bb8ef
|
|
| MD5 |
eefb8b99812f92069fddf3e0ff6515ea
|
|
| BLAKE2b-256 |
4e6d31bdd7ef106129763d317f4cce4b0b1be75eae695902bb99afd1d65ebe06
|
Provenance
The following attestation bundles were made for sublimekeys-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on Volume00/sublimekeys-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sublimekeys-0.2.0-py3-none-any.whl -
Subject digest:
c2b10eb665d65bf7ed2038a12e2515aa7957826df1bdce1236887d58cf7bb8ef - Sigstore transparency entry: 2130732579
- Sigstore integration time:
-
Permalink:
Volume00/sublimekeys-python@cf38b9b74b74d02dacc15f2cbdbec5074e2cc35d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Volume00
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf38b9b74b74d02dacc15f2cbdbec5074e2cc35d -
Trigger Event:
release
-
Statement type: