Metadata-Version: 2.4
Name: astraguard
Version: 1.0.0
Summary: AstraGuard license management SDK for Python
License: MIT
Project-URL: Homepage, https://astraguard.io
Project-URL: Documentation, https://docs.astraguard.io
Project-URL: Repository, https://github.com/astraguard/sdk
Keywords: license,drm,protection,astraguard
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.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: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-mock>=3.10; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"

# AstraGuard Python SDK

Official Python SDK for [AstraGuard](https://astraguard.io) license management.

## Installation

```bash
pip install astraguard
```

Requires Python 3.8+ and `requests>=2.28.0`.

## Quick Start

```python
from astraguard import AstraGuard

guard = AstraGuard(
    api_url="https://api.astraguard.io",
    product_id="your-product-uuid",
)

# Validate a license key
result = guard.validate("XXXX-XXXX-XXXX-XXXX")

if result.valid:
    print("License accepted!")
    print(f"Expires: {result.expires_at or 'never (lifetime)'}")
else:
    print(f"License rejected: {result.reason}")
```

## Activate (first use)

```python
# Binds the hardware ID on first activation
result = guard.activate("XXXX-XXXX-XXXX-XXXX")

if result.valid:
    print("Activation successful!")
```

## Validate or Exit

Terminate the process automatically when the license is invalid - ideal for CLI tools and desktop apps:

```python
result = guard.validate_or_exit(
    "XXXX-XXXX-XXXX-XXXX",
    message="Invalid license. Purchase at https://astraguard.io",
)
# Execution continues only when the license is valid
print(f"Welcome! {result.expires_at}")
```

## Features and Variables

```python
result = guard.validate("XXXX-XXXX-XXXX-XXXX")

# Check a feature flag
if result.has_feature("premium_mode"):
    enable_premium()

# Or use the shortcut on the client (uses last result)
if guard.has_feature("premium_mode"):
    enable_premium()

# Read a remote variable
api_endpoint = result.get_variable("api_url", default="https://default.example.com")
```

## Anti-Debug Protection

```python
from astraguard import AstraGuard

guard = AstraGuard(api_url="https://api.astraguard.io", product_id="your-uuid")

# Exit immediately when a debugger is detected
guard.check_debug(exit_on_detect=True)

# Or check manually
from astraguard import is_debugger_attached
if is_debugger_attached():
    sys.exit(1)
```

## Custom HWID

```python
from astraguard import get_hwid

hwid = get_hwid()
print(f"This machine's HWID: {hwid}")  # e.g. A1B2-C3D4-E5F6-7890

# Pass a custom HWID to validate/activate
result = guard.validate("XXXX-XXXX-XXXX-XXXX", hwid=hwid)
```

## Error Handling

```python
from astraguard import AstraGuard, AstraGuardError

guard = AstraGuard(api_url="https://api.astraguard.io", product_id="your-uuid")

try:
    result = guard.validate("XXXX-XXXX-XXXX-XXXX")
except AstraGuardError as e:
    print(f"Error [{e.code}]: {e}")
    # e.code values: NETWORK_ERROR, MAINTENANCE_MODE, RATE_LIMITED, SERVER_ERROR, ...
```

## Debug Logging

```python
guard = AstraGuard(
    api_url="https://api.astraguard.io",
    product_id="your-uuid",
    debug=True,   # prints request/response details to stderr
)
```

## Version Enforcement

```python
guard = AstraGuard(
    api_url="https://api.astraguard.io",
    product_id="your-uuid",
    version="1.2.0",  # sent with every validate call
)

result = guard.validate("XXXX-XXXX-XXXX-XXXX")
if result.needs_update:
    print("Please update the application.")
    sys.exit(1)
```

## LicenseResult Reference

| Attribute | Type | Description |
|-----------|------|-------------|
| `valid` | `bool` | Whether the license is accepted |
| `reason` | `str or None` | Rejection reason (e.g. `expired`, `hwid_mismatch`) |
| `message` | `str or None` | Human-readable message from the server |
| `expires_at` | `str or None` | ISO 8601 expiry date, or `None` for lifetime |
| `remaining_days` | `int or None` | Days until expiry |
| `features` | `list[str]` | Enabled feature flag names |
| `variables` | `dict[str, str]` | Remote key-value configuration |
| `license` | `LicenseInfo or None` | Full license object |
