Metadata-Version: 2.4
Name: shifra-api
Version: 2.2.1
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 run

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

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 run

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

That single call verifies the license and device, resolves the linked Python
product, downloads and verifies its published release, prints the Console
Identity configured by the workspace owner, runs the release under heartbeat
supervision, applies server-authorized updates, and closes the session on exit.

The protected program inherits stdin, stdout and stderr. `print()`, logging,
tracebacks, Unicode output and child-process output therefore appear in real
time just as they do for a normal Python program. `quiet=True` suppresses only
Shifra's informational banner; it never discards application output.

There is no application execution timeout. Long-running loops, asyncio event
loops, workers and servers continue until they exit, the user interrupts them,
or an authenticated heartbeat requires revocation/update. Heartbeats run in the
supervisor process and remain independent of application CPU usage or event
loops.

### 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 a supervised subprocess without a temporary
  plaintext source file, so heartbeat revocation can stop it. Anyone with
  control of the machine can still inspect process memory — this package
  protects authorization and integrity, not absolute source secrecy.
- Release-local pure-Python modules are imported directly from the in-memory
  archive. Native extensions and code that requires a real on-disk `__file__`
  must be installed normally as dependencies.
- `multiprocessing` with `fork` works on platforms that provide it. Windows and
  other spawn-only modes cannot re-import the in-memory `__main__` archive in a
  fresh interpreter; put multiprocessing worker code in an installed package
  when spawn support is required.
