Metadata-Version: 2.4
Name: grebkey
Version: 0.1.4
Summary: Python SDK for GrebKey license key validation, see grebkey.efraim.us
Author-email: GrebCo <efraimgrebnevp@gmail.com>
License: MIT
Project-URL: Homepage, https://grebkey.com
Project-URL: Documentation, https://docs.grebkey.com
Project-URL: Repository, https://github.com/yourname/grebkey-python
Project-URL: Issues, https://github.com/yourname/grebkey-python/issues
Keywords: license,key,validation,activation,software-licensing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.20.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: responses>=0.20.0; extra == "dev"
Dynamic: license-file

# GrebKey

Python SDK for validating GrebKey license keys inside your application. It works completely remotely and makes it easy to implement features like machine activation, expiration, and offline grace periods. The SDK handles caching and retries so your users stay licensed even if the API is temporarily unreachable.
This sdk is designed to be simple and secure, with no sensitive information stored on the client side. It identifies machines using stable OS-level identifiers without relying on hardware serial numbers or MAC addresses, making it robust across different environments and user permissions.

## Install

```bash
pip install grebkey
```

## Quickstart

```python
from grebkey import GrebKeyClient, GrebKeyError

client = GrebKeyClient(
    api_url="https://grebkeyapi.efraim.us",
    product_id="prod_abc123",
)

try:
    result = client.validate("GREB-XXXX-XXXX-XXXX-XXXX")
except GrebKeyError:
    print("Could not verify license.")
    exit(1)

if result.valid and result.machine_activated:
    print("Licensed!")
else:
    print(f"Not licensed: {result.reason}")
```

## Configuration

| Parameter | Type | Default | Description |
|---|---|---|---|
| `api_url` | str | required | Base URL of your GrebKey API |
| `product_id` | str | None | Restrict validation to a specific product |
| `cache_ttl` | int | 3600 | Seconds before re-validating over the network |
| `grace_period` | int | 86400 | Seconds the app keeps working if the API is unreachable |
| `cache_dir` | str | `~/.grebkey/cache` | Directory for the local cache file |
| `timeout` | int | 10 | HTTP request timeout in seconds |

## Caching and offline behavior

On every `validate()` call:

1. If a cached result exists and is younger than `cache_ttl`, it's returned immediately (no network call).
2. If the cache is stale, the SDK hits the API and updates the cache.
3. If the API is unreachable and a cached result exists within the `grace_period`, that cached result is returned with `result.from_cache = True`.
4. If the API is unreachable and no valid cache exists (or grace period expired), `GrebKeyNetworkError` is raised.

Cache files are stored at `~/.grebkey/cache/` with permissions `0600` (owner read/write only). The license key itself is never stored — only a hash of it is used as the filename.

## Machine fingerprinting

The SDK automatically identifies the current machine by hashing together stable OS-level identifiers:

- **Linux**: `/etc/machine-id`
- **macOS**: `IOPlatformUUID` from `ioreg`
- **Windows**: `MachineGuid` from the registry

Hostname and username are included as fallback signals. The result is a SHA-256 hex string that stays stable across reboots and app updates without requiring admin privileges.

Access it directly with `client.get_fingerprint()`.

## Error handling

```python
from grebkey import GrebKeyError, GrebKeyNetworkError, GrebKeyAPIError

try:
    result = client.validate(key)
except GrebKeyNetworkError:
    # API unreachable and no valid cache / grace period expired
    pass
except GrebKeyAPIError as e:
    # API returned 4xx or 5xx
    print(e.status_code, e.message)
except GrebKeyError:
    # Catch-all for any other GrebKey error
    pass
```

## Full API reference

### `GrebKeyClient`

#### `client.validate(key) -> ValidationResult`

Check whether the license key is valid and this machine is authorized.

```python
result.valid                  # bool
result.reason                 # str | None — "invalid_key", "expired", "suspended", "revoked", "product_mismatch"
result.key_id                 # str
result.product_id             # str
result.metadata               # dict
result.max_activations        # int
result.current_activations    # int
result.remaining_activations  # int
result.machine_activated      # bool — is THIS machine activated?
result.expires_at             # str | None — ISO timestamp
result.from_cache             # bool — served from local cache?
```

#### `client.activate(key, api_key=None, label=None) -> ActivationResult`

Register this machine against the license key.

> **Warning:** The `api_key` parameter requires a `gk_live_` secret key. Never embed this in client software shipped to end users. Use this method server-side only, or proxy through your own backend.

```python
activation.id           # activation ID
activation.fingerprint  # machine fingerprint used
```

#### `client.deactivate(key, api_key=None)`

Remove this machine's activation. Same auth considerations as `activate()`.

#### `client.get_fingerprint() -> str`

Return the stable machine fingerprint (SHA-256 hex string) for the current machine.

---

[GrebKey docs (Not Done Yet!)](https://docs.grebkey.efraim.us) · [Dashboard](https://grebkey.efraim.us)
