Metadata-Version: 2.4
Name: shifra-api
Version: 2.1.0
Summary: SHIFRA server-driven licensing client for Python products
Author: Shifra
License: Proprietary
Project-URL: Documentation, https://github.com/o3rr/licensecloud-backend/blob/main/docs/loader-v2.md
Keywords: licensing,license-key,shifra,protection
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=41

# shifra-api

Server-driven licensing for Python products. You configure **one** thing: your
license key.

```python
from shifra_api import Shafra

Shafra("YOUR-LICENSE-KEY").run()
```

Everything else — which script runs, its version, heartbeat settings, device
and network policy, branding — is decided by the Shifra server from your key.

## Install

```bash
pip install shifra-api
```

Requires Python 3.9+ and the `cryptography` package (installed automatically).

> Distribution builds are produced by Shifra's build process, which embeds the
> API origin and the pinned signing key. A plain source checkout has neither and
> will refuse to start — that is intentional.

## Usage

### Run the protected script

```python
from shifra_api import Shafra

Shafra("YOUR-LICENSE-KEY").run()
```

### Check a license without running anything

```python
from shifra_api import Shafra, ShafraError

try:
    license = Shafra("YOUR-LICENSE-KEY").verify()
except ShafraError as error:
    print(error.code, error.message)
else:
    print(license.product_name, license.product_version)
    print("expires:", license.expires_at)
```

### Hold a verified session while your own code runs

```python
from shifra_api import Shafra

with Shafra("YOUR-LICENSE-KEY") as session:
    app.run()          # heartbeat is active for the whole block
```

The session ends when the block exits. If the license is revoked mid-run, the
heartbeat stops and a `SessionRevoked` error is raised when the block closes.

## Errors

Every failure has a stable `code`. Import the specific class or catch
`ShafraError`:

| Code | Meaning |
|---|---|
| `LICENSE_INVALID` | Key not found |
| `LICENSE_EXPIRED` | License expired |
| `LICENSE_REVOKED` | License banned or revoked |
| `LICENSE_NOT_LINKED` | Key not assigned to a product yet |
| `HWID_MISMATCH` | Bound to a different device |
| `DEVICE_LIMIT_REACHED` | Too many devices for this license |
| `IP_NOT_ALLOWED` | Network not permitted |
| `LOADER_UPDATE_REQUIRED` | Update the client |
| `MAINTENANCE` | Service in maintenance |
| `NETWORK_TIMEOUT` / `NETWORK_ERROR` | Could not reach the server |
| `INVALID_SERVER_SIGNATURE` | Response failed verification |
| `PAYLOAD_INTEGRITY_FAILED` | Downloaded script failed its digest check |
| `SESSION_REVOKED` / `SESSION_EXPIRED` | Runtime session ended |

Connection problems are never reported as signature failures.

## About the hardware identity

The client derives a fingerprint from stable machine identifiers and sends only
a SHA-256 digest. Raw identifiers never leave your machine. This is a signal for
the server to bind licenses to devices, not hardware attestation.

## Security notes

- Responses are verified with an Ed25519 key embedded in the build. A signature
  received over the network is never used as a trust anchor.
- Downloads are checked against a signed digest before anything runs.
- The protected script runs in-process. Anyone with control of the machine can
  read it from memory — this package protects authorization and integrity, not
  source secrecy.
