keymint 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {keymint-2.1.0/keymint.egg-info → keymint-2.2.0}/PKG-INFO +35 -24
- {keymint-2.1.0 → keymint-2.2.0}/README.md +33 -22
- {keymint-2.1.0 → keymint-2.2.0}/keymint/__init__.py +86 -5
- {keymint-2.1.0 → keymint-2.2.0}/keymint/_version.py +2 -2
- {keymint-2.1.0 → keymint-2.2.0}/keymint/identity.py +23 -2
- {keymint-2.1.0 → keymint-2.2.0}/keymint/types.py +48 -0
- {keymint-2.1.0 → keymint-2.2.0/keymint.egg-info}/PKG-INFO +35 -24
- {keymint-2.1.0 → keymint-2.2.0}/setup.py +2 -2
- {keymint-2.1.0 → keymint-2.2.0}/LICENSE +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/MANIFEST.in +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/keymint.egg-info/SOURCES.txt +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/keymint.egg-info/dependency_links.txt +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/keymint.egg-info/requires.txt +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/keymint.egg-info/top_level.txt +0 -0
- {keymint-2.1.0 → keymint-2.2.0}/setup.cfg +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keymint
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Official Python SDK for KeyMint license management with comprehensive API coverage.
|
|
5
5
|
Home-page: https://github.com/keymint-dev/keymint-python
|
|
6
6
|
Author: KeyMint
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: cliff@keymint.dev
|
|
8
8
|
Project-URL: Bug Reports, https://github.com/keymint-dev/keymint-python/issues
|
|
9
9
|
Project-URL: Source, https://github.com/keymint-dev/keymint-python
|
|
10
10
|
Project-URL: Documentation, https://docs.keymint.dev/sdks/python
|
|
@@ -47,15 +47,15 @@ Dynamic: requires-dist
|
|
|
47
47
|
Dynamic: requires-python
|
|
48
48
|
Dynamic: summary
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# Keymint Python
|
|
51
51
|
|
|
52
|
-
A professional, production-ready SDK for integrating with the
|
|
52
|
+
A professional, production-ready SDK for integrating with the Keymint API in Python. Provides robust access to all major Keymint features, with type hints and modern error handling.
|
|
53
53
|
|
|
54
54
|
## Features
|
|
55
55
|
- **Type hints**: Full type hint support for better IDE integration and code safety.
|
|
56
|
-
- **Comprehensive**: Complete API coverage for all
|
|
56
|
+
- **Comprehensive**: Complete API coverage for all Keymint endpoints.
|
|
57
57
|
- **Consistent error handling**: All API errors are returned as structured objects or exceptions.
|
|
58
|
-
- **
|
|
58
|
+
- **Machine Identity**: Built-in utilities for hardware fingerprinting and stable installation IDs.
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
Add the SDK to your project:
|
|
@@ -68,32 +68,36 @@ pip install keymint
|
|
|
68
68
|
|
|
69
69
|
```python
|
|
70
70
|
import os
|
|
71
|
-
import
|
|
71
|
+
from keymint import KeyMint, identity
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
api_key = os.environ.get('KEYMINT_API_KEY')
|
|
74
74
|
product_id = os.environ.get('KEYMINT_PRODUCT_ID')
|
|
75
75
|
|
|
76
|
-
if not
|
|
77
|
-
raise ValueError('Please set the
|
|
76
|
+
if not api_key or not product_id:
|
|
77
|
+
raise ValueError('Please set the KEYMINT_API_KEY and KEYMINT_PRODUCT_ID environment variables.')
|
|
78
78
|
|
|
79
|
-
sdk =
|
|
79
|
+
sdk = KeyMint(api_key)
|
|
80
|
+
|
|
81
|
+
# 1. Get a stable, unique ID for this machine
|
|
82
|
+
host_id = identity.get_or_create_installation_id()
|
|
83
|
+
|
|
84
|
+
# 2. Create a key authorized only for this machine
|
|
85
|
+
result = sdk.create_key({
|
|
86
|
+
'productId': product_id,
|
|
87
|
+
'allowedHosts': [host_id]
|
|
88
|
+
})
|
|
80
89
|
|
|
81
|
-
# Example: Create a key
|
|
82
|
-
result = sdk.create_key({ 'productId': product_id })
|
|
83
90
|
if result and 'key' in result:
|
|
84
|
-
|
|
85
|
-
# ...
|
|
86
|
-
else:
|
|
87
|
-
# Handle error
|
|
88
|
-
pass
|
|
91
|
+
print(f"Created Key: {result['key']}")
|
|
89
92
|
```
|
|
90
93
|
|
|
91
|
-
##
|
|
92
|
-
|
|
94
|
+
## Machine Identity
|
|
95
|
+
Keymint provides utilities to uniquely identify machines for node-locking:
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
- `identity.get_or_create_installation_id()`: **Recommended.** Generates a stable UUID anchored to hardware and persists it to `~/.keymint/installation-id`.
|
|
98
|
+
- `identity.get_machine_id()`: Generates a SHA-256 fingerprint based on BIOS UUID, OS machine ID, and MAC address.
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
## API Methods
|
|
97
101
|
|
|
98
102
|
### License Key Management
|
|
99
103
|
|
|
@@ -105,6 +109,9 @@ All methods return a dictionary.
|
|
|
105
109
|
| `get_key` | Retrieves detailed information about a key. |
|
|
106
110
|
| `block_key` | Blocks a license key. |
|
|
107
111
|
| `unblock_key` | Unblocks a previously blocked license key. |
|
|
112
|
+
| `floating_checkout` | Checks out a floating license seat. |
|
|
113
|
+
| `floating_heartbeat`| Sends a heartbeat to keep a session alive. |
|
|
114
|
+
| `floating_checkin` | Checks in a session, releasing the seat. |
|
|
108
115
|
|
|
109
116
|
### Customer Management
|
|
110
117
|
|
|
@@ -118,10 +125,14 @@ All methods return a dictionary.
|
|
|
118
125
|
| `toggle_customer_status`| Toggles customer active status. |
|
|
119
126
|
| `delete_customer` | Permanently deletes a customer and their keys. |
|
|
120
127
|
|
|
121
|
-
|
|
128
|
+
### Webhook Verification
|
|
129
|
+
|
|
130
|
+
| Method | Description |
|
|
131
|
+
|-------------------------|--------------------------------------------------|
|
|
132
|
+
| `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
|
|
122
133
|
|
|
123
134
|
## License
|
|
124
135
|
MIT
|
|
125
136
|
|
|
126
137
|
## Support
|
|
127
|
-
For help, see [
|
|
138
|
+
For help, see [Keymint API docs](https://docs.keymint.dev) or open an issue.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Keymint Python
|
|
2
2
|
|
|
3
|
-
A professional, production-ready SDK for integrating with the
|
|
3
|
+
A professional, production-ready SDK for integrating with the Keymint API in Python. Provides robust access to all major Keymint features, with type hints and modern error handling.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
- **Type hints**: Full type hint support for better IDE integration and code safety.
|
|
7
|
-
- **Comprehensive**: Complete API coverage for all
|
|
7
|
+
- **Comprehensive**: Complete API coverage for all Keymint endpoints.
|
|
8
8
|
- **Consistent error handling**: All API errors are returned as structured objects or exceptions.
|
|
9
|
-
- **
|
|
9
|
+
- **Machine Identity**: Built-in utilities for hardware fingerprinting and stable installation IDs.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
Add the SDK to your project:
|
|
@@ -19,32 +19,36 @@ pip install keymint
|
|
|
19
19
|
|
|
20
20
|
```python
|
|
21
21
|
import os
|
|
22
|
-
import
|
|
22
|
+
from keymint import KeyMint, identity
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
api_key = os.environ.get('KEYMINT_API_KEY')
|
|
25
25
|
product_id = os.environ.get('KEYMINT_PRODUCT_ID')
|
|
26
26
|
|
|
27
|
-
if not
|
|
28
|
-
raise ValueError('Please set the
|
|
27
|
+
if not api_key or not product_id:
|
|
28
|
+
raise ValueError('Please set the KEYMINT_API_KEY and KEYMINT_PRODUCT_ID environment variables.')
|
|
29
29
|
|
|
30
|
-
sdk =
|
|
30
|
+
sdk = KeyMint(api_key)
|
|
31
|
+
|
|
32
|
+
# 1. Get a stable, unique ID for this machine
|
|
33
|
+
host_id = identity.get_or_create_installation_id()
|
|
34
|
+
|
|
35
|
+
# 2. Create a key authorized only for this machine
|
|
36
|
+
result = sdk.create_key({
|
|
37
|
+
'productId': product_id,
|
|
38
|
+
'allowedHosts': [host_id]
|
|
39
|
+
})
|
|
31
40
|
|
|
32
|
-
# Example: Create a key
|
|
33
|
-
result = sdk.create_key({ 'productId': product_id })
|
|
34
41
|
if result and 'key' in result:
|
|
35
|
-
|
|
36
|
-
# ...
|
|
37
|
-
else:
|
|
38
|
-
# Handle error
|
|
39
|
-
pass
|
|
42
|
+
print(f"Created Key: {result['key']}")
|
|
40
43
|
```
|
|
41
44
|
|
|
42
|
-
##
|
|
43
|
-
|
|
45
|
+
## Machine Identity
|
|
46
|
+
Keymint provides utilities to uniquely identify machines for node-locking:
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
- `identity.get_or_create_installation_id()`: **Recommended.** Generates a stable UUID anchored to hardware and persists it to `~/.keymint/installation-id`.
|
|
49
|
+
- `identity.get_machine_id()`: Generates a SHA-256 fingerprint based on BIOS UUID, OS machine ID, and MAC address.
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
## API Methods
|
|
48
52
|
|
|
49
53
|
### License Key Management
|
|
50
54
|
|
|
@@ -56,6 +60,9 @@ All methods return a dictionary.
|
|
|
56
60
|
| `get_key` | Retrieves detailed information about a key. |
|
|
57
61
|
| `block_key` | Blocks a license key. |
|
|
58
62
|
| `unblock_key` | Unblocks a previously blocked license key. |
|
|
63
|
+
| `floating_checkout` | Checks out a floating license seat. |
|
|
64
|
+
| `floating_heartbeat`| Sends a heartbeat to keep a session alive. |
|
|
65
|
+
| `floating_checkin` | Checks in a session, releasing the seat. |
|
|
59
66
|
|
|
60
67
|
### Customer Management
|
|
61
68
|
|
|
@@ -69,10 +76,14 @@ All methods return a dictionary.
|
|
|
69
76
|
| `toggle_customer_status`| Toggles customer active status. |
|
|
70
77
|
| `delete_customer` | Permanently deletes a customer and their keys. |
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
### Webhook Verification
|
|
80
|
+
|
|
81
|
+
| Method | Description |
|
|
82
|
+
|-------------------------|--------------------------------------------------|
|
|
83
|
+
| `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
|
|
73
84
|
|
|
74
85
|
## License
|
|
75
86
|
MIT
|
|
76
87
|
|
|
77
88
|
## Support
|
|
78
|
-
For help, see [
|
|
89
|
+
For help, see [Keymint API docs](https://docs.keymint.dev) or open an issue.
|
|
@@ -5,14 +5,14 @@ from ._version import __version__
|
|
|
5
5
|
__all__ = ['KeyMint', 'KeyMintApiError', '__version__']
|
|
6
6
|
|
|
7
7
|
class KeyMint:
|
|
8
|
-
def __init__(self,
|
|
9
|
-
if not
|
|
10
|
-
raise ValueError("
|
|
8
|
+
def __init__(self, api_key: str, base_url: str = "https://api.keymint.dev"):
|
|
9
|
+
if not api_key:
|
|
10
|
+
raise ValueError("API key is required to initialize the SDK.")
|
|
11
11
|
|
|
12
|
-
self.
|
|
12
|
+
self.api_key = api_key
|
|
13
13
|
self.base_url = base_url
|
|
14
14
|
self.headers = {
|
|
15
|
-
'Authorization': f'Bearer {self.
|
|
15
|
+
'Authorization': f'Bearer {self.api_key}',
|
|
16
16
|
'Content-Type': 'application/json'
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -79,6 +79,30 @@ class KeyMint:
|
|
|
79
79
|
"""
|
|
80
80
|
return self._handle_request('POST', '/key/deactivate', params)
|
|
81
81
|
|
|
82
|
+
def floating_checkout(self, params: FloatingCheckoutParams) -> FloatingCheckoutResponse:
|
|
83
|
+
"""
|
|
84
|
+
Checks out a floating license seat.
|
|
85
|
+
:param params: Parameters for checking out the license.
|
|
86
|
+
:returns: The checkout response containing sessionId and sessionSecret.
|
|
87
|
+
"""
|
|
88
|
+
return self._handle_request('POST', '/key/checkout', params)
|
|
89
|
+
|
|
90
|
+
def floating_heartbeat(self, params: FloatingHeartbeatParams) -> FloatingHeartbeatResponse:
|
|
91
|
+
"""
|
|
92
|
+
Sends a heartbeat to keep a floating license session alive.
|
|
93
|
+
:param params: Parameters for the heartbeat (includes rotating signature).
|
|
94
|
+
:returns: The heartbeat response with extended expiry and new nonce.
|
|
95
|
+
"""
|
|
96
|
+
return self._handle_request('POST', '/key/heartbeat', params)
|
|
97
|
+
|
|
98
|
+
def floating_checkin(self, params: FloatingCheckinParams) -> FloatingCheckinResponse:
|
|
99
|
+
"""
|
|
100
|
+
Checks in a floating license session, releasing the seat.
|
|
101
|
+
:param params: Parameters for checking in the license (includes rotating signature).
|
|
102
|
+
:returns: The checkin confirmation.
|
|
103
|
+
"""
|
|
104
|
+
return self._handle_request('POST', '/key/checkin', params)
|
|
105
|
+
|
|
82
106
|
def get_key(self, params: GetKeyParams) -> GetKeyResponse:
|
|
83
107
|
"""
|
|
84
108
|
Retrieves detailed information about a specific license key.
|
|
@@ -169,3 +193,60 @@ class KeyMint:
|
|
|
169
193
|
query_params = {'customerId': params['customerId']}
|
|
170
194
|
return self._handle_request('POST', '/customer/disable', params=None, query_params=query_params)
|
|
171
195
|
|
|
196
|
+
@staticmethod
|
|
197
|
+
def verify_webhook_signature(payload: str, header: str, secret: str, tolerance_seconds: int = 300) -> bool:
|
|
198
|
+
"""
|
|
199
|
+
Verifies a webhook payload signature received from Keymint.
|
|
200
|
+
:param payload: The raw request body as a string.
|
|
201
|
+
:param header: The value of the "Keymint-Signature" header.
|
|
202
|
+
:param secret: The webhook endpoint's signing secret.
|
|
203
|
+
:param tolerance_seconds: Time tolerance in seconds to prevent replay attacks. Defaults to 300 (5 minutes).
|
|
204
|
+
:returns: True if the signature is valid, False otherwise.
|
|
205
|
+
"""
|
|
206
|
+
import hmac
|
|
207
|
+
import hashlib
|
|
208
|
+
import time
|
|
209
|
+
|
|
210
|
+
if not header or not secret:
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
# Parse header (e.g. t=1719374021,v1=signature)
|
|
215
|
+
timestamp_str = ""
|
|
216
|
+
signature = ""
|
|
217
|
+
parts = header.split(",")
|
|
218
|
+
for part in parts:
|
|
219
|
+
kv = part.strip().split("=", 1)
|
|
220
|
+
if len(kv) == 2:
|
|
221
|
+
if kv[0] == "t":
|
|
222
|
+
timestamp_str = kv[1]
|
|
223
|
+
elif kv[0] == "v1":
|
|
224
|
+
signature = kv[1]
|
|
225
|
+
|
|
226
|
+
if not timestamp_str or not signature:
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
# Check timestamp validity
|
|
230
|
+
try:
|
|
231
|
+
timestamp_int = int(timestamp_str)
|
|
232
|
+
except ValueError:
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
now = int(time.time())
|
|
236
|
+
if abs(now - timestamp_int) > tolerance_seconds:
|
|
237
|
+
return False
|
|
238
|
+
|
|
239
|
+
# Verify HMAC signature
|
|
240
|
+
signable_content = f"{timestamp_str}.{payload}".encode("utf-8")
|
|
241
|
+
expected_signature = hmac.new(
|
|
242
|
+
secret.encode("utf-8"),
|
|
243
|
+
signable_content,
|
|
244
|
+
hashlib.sha256
|
|
245
|
+
).hexdigest()
|
|
246
|
+
|
|
247
|
+
# Constant-time comparison to prevent timing attacks
|
|
248
|
+
return hmac.compare_digest(expected_signature, signature)
|
|
249
|
+
except Exception:
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
|
|
@@ -7,6 +7,7 @@ Provides two methods for identifying machines:
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import hashlib
|
|
10
|
+
import hmac
|
|
10
11
|
import os
|
|
11
12
|
import platform
|
|
12
13
|
import subprocess
|
|
@@ -195,7 +196,27 @@ def get_or_create_installation_id(storage_path: Optional[str] = None) -> str:
|
|
|
195
196
|
composite_id = f'{new_uuid}:{hardware_anchor}:{int(time.time() * 1000)}'
|
|
196
197
|
|
|
197
198
|
# 3. Persist it
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
try:
|
|
200
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
201
|
+
file_path.write_text(composite_id, encoding='utf-8')
|
|
202
|
+
except Exception:
|
|
203
|
+
pass # Silently fallback to in-memory ID if filesystem is read-only
|
|
200
204
|
|
|
201
205
|
return hashlib.sha256(composite_id.encode('utf-8')).hexdigest()
|
|
206
|
+
|
|
207
|
+
def generate_session_signature(session_id: str, nonce: str, session_secret: str) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Generates a cryptographic signature for a heartbeat or checkin request
|
|
210
|
+
using the session_secret and the rotating nextNonce (passed as the timestamp).
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
session_id: The 22-character unique session ID.
|
|
214
|
+
nonce: The rotating nonce string (nextNonce) received from the previous response.
|
|
215
|
+
session_secret: The temporary session secret key received during checkout.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
A 64-character hexadecimal signature string.
|
|
219
|
+
"""
|
|
220
|
+
key_bytes = session_secret.encode('utf-8')
|
|
221
|
+
msg_bytes = f"{session_id}:{nonce}".encode('utf-8')
|
|
222
|
+
return hmac.new(key_bytes, msg_bytes, hashlib.sha256).hexdigest()
|
|
@@ -187,3 +187,51 @@ class GetCustomerWithKeysResponse(TypedDict):
|
|
|
187
187
|
status: bool
|
|
188
188
|
data: Dict[str, Any] # Contains customer and licenseKeys
|
|
189
189
|
code: int
|
|
190
|
+
|
|
191
|
+
class FloatingCheckoutParams(TypedDict):
|
|
192
|
+
productId: str
|
|
193
|
+
licenseKey: str
|
|
194
|
+
hostId: str
|
|
195
|
+
deviceTag: Optional[str]
|
|
196
|
+
userIdentifier: Optional[str]
|
|
197
|
+
apiKey: Optional[str]
|
|
198
|
+
|
|
199
|
+
class FloatingCheckoutResponse(TypedDict):
|
|
200
|
+
code: int
|
|
201
|
+
message: str
|
|
202
|
+
sessionId: str
|
|
203
|
+
sessionSecret: str
|
|
204
|
+
nextNonce: str
|
|
205
|
+
expiresAt: str
|
|
206
|
+
heartbeatInterval: int
|
|
207
|
+
metadata: Optional[Dict[str, Any]]
|
|
208
|
+
currentSessions: Optional[int]
|
|
209
|
+
maxSessions: Optional[int]
|
|
210
|
+
licenseeName: Optional[str]
|
|
211
|
+
licenseeEmail: Optional[str]
|
|
212
|
+
|
|
213
|
+
class FloatingHeartbeatParams(TypedDict):
|
|
214
|
+
productId: str
|
|
215
|
+
licenseKey: str
|
|
216
|
+
sessionId: str
|
|
217
|
+
timestamp: Any # rotating nonce (nextNonce) received from previous response
|
|
218
|
+
signature: str
|
|
219
|
+
apiKey: Optional[str]
|
|
220
|
+
|
|
221
|
+
class FloatingHeartbeatResponse(TypedDict):
|
|
222
|
+
code: int
|
|
223
|
+
message: str
|
|
224
|
+
expiresAt: str
|
|
225
|
+
nextNonce: str
|
|
226
|
+
|
|
227
|
+
class FloatingCheckinParams(TypedDict):
|
|
228
|
+
productId: str
|
|
229
|
+
licenseKey: str
|
|
230
|
+
sessionId: str
|
|
231
|
+
timestamp: Any # rotating nonce (nextNonce) received from previous response
|
|
232
|
+
signature: str
|
|
233
|
+
apiKey: Optional[str]
|
|
234
|
+
|
|
235
|
+
class FloatingCheckinResponse(TypedDict):
|
|
236
|
+
code: int
|
|
237
|
+
message: str
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keymint
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Official Python SDK for KeyMint license management with comprehensive API coverage.
|
|
5
5
|
Home-page: https://github.com/keymint-dev/keymint-python
|
|
6
6
|
Author: KeyMint
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: cliff@keymint.dev
|
|
8
8
|
Project-URL: Bug Reports, https://github.com/keymint-dev/keymint-python/issues
|
|
9
9
|
Project-URL: Source, https://github.com/keymint-dev/keymint-python
|
|
10
10
|
Project-URL: Documentation, https://docs.keymint.dev/sdks/python
|
|
@@ -47,15 +47,15 @@ Dynamic: requires-dist
|
|
|
47
47
|
Dynamic: requires-python
|
|
48
48
|
Dynamic: summary
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# Keymint Python
|
|
51
51
|
|
|
52
|
-
A professional, production-ready SDK for integrating with the
|
|
52
|
+
A professional, production-ready SDK for integrating with the Keymint API in Python. Provides robust access to all major Keymint features, with type hints and modern error handling.
|
|
53
53
|
|
|
54
54
|
## Features
|
|
55
55
|
- **Type hints**: Full type hint support for better IDE integration and code safety.
|
|
56
|
-
- **Comprehensive**: Complete API coverage for all
|
|
56
|
+
- **Comprehensive**: Complete API coverage for all Keymint endpoints.
|
|
57
57
|
- **Consistent error handling**: All API errors are returned as structured objects or exceptions.
|
|
58
|
-
- **
|
|
58
|
+
- **Machine Identity**: Built-in utilities for hardware fingerprinting and stable installation IDs.
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
Add the SDK to your project:
|
|
@@ -68,32 +68,36 @@ pip install keymint
|
|
|
68
68
|
|
|
69
69
|
```python
|
|
70
70
|
import os
|
|
71
|
-
import
|
|
71
|
+
from keymint import KeyMint, identity
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
api_key = os.environ.get('KEYMINT_API_KEY')
|
|
74
74
|
product_id = os.environ.get('KEYMINT_PRODUCT_ID')
|
|
75
75
|
|
|
76
|
-
if not
|
|
77
|
-
raise ValueError('Please set the
|
|
76
|
+
if not api_key or not product_id:
|
|
77
|
+
raise ValueError('Please set the KEYMINT_API_KEY and KEYMINT_PRODUCT_ID environment variables.')
|
|
78
78
|
|
|
79
|
-
sdk =
|
|
79
|
+
sdk = KeyMint(api_key)
|
|
80
|
+
|
|
81
|
+
# 1. Get a stable, unique ID for this machine
|
|
82
|
+
host_id = identity.get_or_create_installation_id()
|
|
83
|
+
|
|
84
|
+
# 2. Create a key authorized only for this machine
|
|
85
|
+
result = sdk.create_key({
|
|
86
|
+
'productId': product_id,
|
|
87
|
+
'allowedHosts': [host_id]
|
|
88
|
+
})
|
|
80
89
|
|
|
81
|
-
# Example: Create a key
|
|
82
|
-
result = sdk.create_key({ 'productId': product_id })
|
|
83
90
|
if result and 'key' in result:
|
|
84
|
-
|
|
85
|
-
# ...
|
|
86
|
-
else:
|
|
87
|
-
# Handle error
|
|
88
|
-
pass
|
|
91
|
+
print(f"Created Key: {result['key']}")
|
|
89
92
|
```
|
|
90
93
|
|
|
91
|
-
##
|
|
92
|
-
|
|
94
|
+
## Machine Identity
|
|
95
|
+
Keymint provides utilities to uniquely identify machines for node-locking:
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
- `identity.get_or_create_installation_id()`: **Recommended.** Generates a stable UUID anchored to hardware and persists it to `~/.keymint/installation-id`.
|
|
98
|
+
- `identity.get_machine_id()`: Generates a SHA-256 fingerprint based on BIOS UUID, OS machine ID, and MAC address.
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
## API Methods
|
|
97
101
|
|
|
98
102
|
### License Key Management
|
|
99
103
|
|
|
@@ -105,6 +109,9 @@ All methods return a dictionary.
|
|
|
105
109
|
| `get_key` | Retrieves detailed information about a key. |
|
|
106
110
|
| `block_key` | Blocks a license key. |
|
|
107
111
|
| `unblock_key` | Unblocks a previously blocked license key. |
|
|
112
|
+
| `floating_checkout` | Checks out a floating license seat. |
|
|
113
|
+
| `floating_heartbeat`| Sends a heartbeat to keep a session alive. |
|
|
114
|
+
| `floating_checkin` | Checks in a session, releasing the seat. |
|
|
108
115
|
|
|
109
116
|
### Customer Management
|
|
110
117
|
|
|
@@ -118,10 +125,14 @@ All methods return a dictionary.
|
|
|
118
125
|
| `toggle_customer_status`| Toggles customer active status. |
|
|
119
126
|
| `delete_customer` | Permanently deletes a customer and their keys. |
|
|
120
127
|
|
|
121
|
-
|
|
128
|
+
### Webhook Verification
|
|
129
|
+
|
|
130
|
+
| Method | Description |
|
|
131
|
+
|-------------------------|--------------------------------------------------|
|
|
132
|
+
| `verify_webhook_signature`| Verifies the signature of a webhook request payload. |
|
|
122
133
|
|
|
123
134
|
## License
|
|
124
135
|
MIT
|
|
125
136
|
|
|
126
137
|
## Support
|
|
127
|
-
For help, see [
|
|
138
|
+
For help, see [Keymint API docs](https://docs.keymint.dev) or open an issue.
|
|
@@ -5,9 +5,9 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="keymint",
|
|
8
|
-
version="2.
|
|
8
|
+
version="2.2.0",
|
|
9
9
|
author="KeyMint",
|
|
10
|
-
author_email="
|
|
10
|
+
author_email="cliff@keymint.dev",
|
|
11
11
|
description="Official Python SDK for KeyMint license management with comprehensive API coverage.",
|
|
12
12
|
long_description=long_description,
|
|
13
13
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|