Metadata-Version: 2.4
Name: keystone-licensing-sdk
Version: 1.0.0
Summary: Official Python SDK for the Keystone Licensing Platform
Author-email: Oussama Chatri <contact@oc-lab.com>
License: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Requires-Dist: cryptography>=41.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# Keystone Python SDK (`keystone-sdk`)

The official Python client library for the [Keystone](https://gitlab.com/oc-lab/keystone) licensing platform. It provides complete coverage of the licensing lifecycle: online validation and activation, continuous background runtime enforcement with automatic lease renewal, offline Ed25519-signed license verification for air-gapped environments, and the full management API suite.

---

## Features

- **License Validation & Activation**: Validate, activate, deactivate, heartbeat, and feature-check via `client.enforce`.
- **Runtime Enforcement (`LicensingRuntime`)**: Background lease renewal, clock rollback detection, and bounded offline grace handling.
- **Offline Licensing**: Issue, verify, and resolve signed offline license packages and challenges using Ed25519 cryptographic signatures (`client.offline`).
- **Complete Management API**: 14 management sub-services under `client.management` (products, licenses, customers, devices, activations, API keys, webhooks, audit logs, analytics, plans, users, organizations, auth).
- **Hardware Fingerprinting**: Built-in cross-platform device fingerprinting (`client.fingerprint`).
- **Resilient Transport**: Automatic retry with exponential backoff for transient 5xx/429 errors and in-memory validation caching.

---

## Requirements

- **Python**: 3.9, 3.10, 3.11, 3.12, or 3.14+.
- **Dependencies**: `httpx` (HTTP transport) and `cryptography` (Ed25519 signature verification).

---

## Installation

```bash
pip install keystone-sdk
```

---

## Quickstart

```python
import os
from keystone_sdk import KeystoneClient, ValidateOptions

# Initialize the client
client = KeystoneClient(
    api_key=os.getenv("KEYSTONE_API_KEY", "ks_live_xxxxxxxxxxxxxxxxxxxx"),
    base_url=os.getenv("KEYSTONE_BASE_URL", "http://localhost:8080/v1"),
)

# 1. Check server health
health = client.health.check()
print(f"Keystone server status: {health.status}")

# 2. Validate a license
result = client.enforce.validate(
    license_key="KS-LIVE-XXXX-XXXX-XXXX", options=ValidateOptions(product_key="my-product")
)

if result.valid:
    print(f"License valid! Expires at: {result.license.expires_at}")
    print(f"Entitlements: {result.features}")
else:
    print(f"Validation failed: {result.message}")
    for check in result.checks:
        print(f" - {check.name}: {check.status} ({check.message})")
```

---

## Runtime License Enforcement

The `LicensingRuntime` class provides continuous background licensing enforcement:

```python
from keystone_sdk import KeystoneClient, LicensingRuntime, RuntimeStatus

client = KeystoneClient(api_key="ks_live_xxxxxxxxxxxxxxxxxxxx")

# Context manager automatically handles start() and stop()
with LicensingRuntime(
    lease_service=client.lease, license_key="KS-LIVE-XXXX-XXXX-XXXX", product_slug="my-product", auto_refresh=True
) as runtime:
    # Check if a specific feature is authorized
    if runtime.authorize("feature:export"):
        print("Export feature is licensed!")

    # Or protect a block with context manager (raises RuntimeError if unauthorized)
    with runtime.protected("feature:export"):
        perform_export()
```

### Runtime Status States

| Status | Description |
|---|---|
| `AUTHORIZING` | Initial lease issuance in progress |
| `AUTHORIZED` | Valid signed lease held, online and active |
| `GRACE` | Network offline, within permitted offline lease duration |
| `EXPIRED` | Lease has passed `expires_at` timestamp |
| `REVOKED` | License marked revoked in signed lease |
| `CLOCK_ROLLBACK` | System clock tampering detected |
| `OFFLINE` | No network and no valid lease held |
| `ERROR` | Unrecoverable communication or verification error |

---

## Offline License Verification

For air-gapped or intermittently connected environments:

```python
# Verify a signed offline package locally without internet access
pkg = {"payload": {...}, "signature": {"kid": "key-1", "alg": "Ed25519", "signature": "..."}, "public_key": "..."}

# Add trusted server public key
client.offline.add_trusted_key("key-1", "hex-or-base64-public-key")

# Verify offline package signature and validity
verify_result = client.offline.verify_local(pkg)
if verify_result.valid:
    print("Offline license verified successfully!")
```

---

## Management API

The `client.management` surface provides complete administrative control (requires Admin JWT access token):

```python
# Authenticate as admin
auth_resp = client.management.auth.login("admin@example.com", "secure-password")
client.set_access_token(auth_resp.access_token)

# Manage products
products = client.management.products.list()
new_prod = client.management.products.create({"name": "Pro App", "slug": "pro-app"})

# Manage licenses
licenses = client.management.licenses.list()
new_lic = client.management.licenses.create({"product_id": new_prod.id, "type": "subscription", "max_activations": 5})

# Manage customers, devices, webhooks, audit logs, and analytics
customers = client.management.customers.list()
audit_logs = client.management.audit_logs.list()
analytics = client.management.analytics.overview()
```

---

## Error Handling

The SDK provides typed exceptions inheriting from `KeystoneError`:

```python
from keystone_sdk.errors import (
    KeystoneError,
    KeystoneApiError,
    AuthenticationError,
    OfflineVerificationError,
    NetworkError,
)

try:
    result = client.enforce.validate("KS-INVALID-KEY")
except AuthenticationError:
    print("Invalid API Key or JWT token")
except KeystoneApiError as e:
    print(f"API Error {e.status_code}: {e.message} (Code: {e.code})")
except NetworkError:
    print("Network connectivity issue")
except KeystoneError as e:
    print(f"SDK Error: {e}")
```

---

## Configuration Options

| Variable / Option | Default | Description |
|---|---|---|
| `KEYSTONE_API_KEY` | `None` | API Key for SDK-tier operations |
| `KEYSTONE_ACCESS_TOKEN` | `None` | JWT Access Token for Management operations |
| `KEYSTONE_BASE_URL` | `http://localhost:8080/v1` | Keystone API base URL |
| `KEYSTONE_TIMEOUT` | `30.0` | HTTP Request timeout in seconds |
| `KEYSTONE_CACHE_ENABLED` | `True` | In-memory validation caching toggle |
| `KEYSTONE_CACHE_TTL` | `300` | Validation cache TTL in seconds |
| `KEYSTONE_RETRY_ENABLED` | `True` | Exponential backoff retry toggle |

---

## Contributing & Security

- **Contributing Guide**: See [CONTRIBUTING.md](CONTRIBUTING.md)
- **Security Policy**: See [SECURITY.md](SECURITY.md)
- **Changelog**: See [CHANGELOG.md](CHANGELOG.md)

---

## Maintainer & License

- **Author / Maintainer**: Oussama Chatri ([OC Lab](https://gitlab.com/oc-lab))
- **License**: [MIT License](LICENSE)
