Metadata-Version: 2.5
Name: shipwright-license
Version: 0.2.0
Summary: Offline-verifiable, Ed25519-signed license files for Shipwright products.
Author: Shipwright AI Labs
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: cryptography>=44
Requires-Dist: pyjwt[crypto]<3,>=2.10
Description-Content-Type: text/markdown

# shipwright-license

Offline-verifiable license files for Shipwright products (Shipwright, Harbor, Engine Room).
Licensed under Apache-2.0, so the open-source Engine Room core can verify licenses without
pulling in proprietary code.

- **Format:** compact JWS, `alg: EdDSA` (Ed25519), `typ: shipwright-license+jwt`, wrapped
  in `-----BEGIN SHIPWRIGHT LICENSE-----` armor.
- **Trust:** verification uses only the trust-anchor key set compiled into the product.
  It never fetches keys.
- **Fail-safe:** `verify_license` never raises for a bad license. It returns `INVALID`,
  and the product falls back to community features. A license problem never stops a
  product from serving and never touches data.

```python
from shipwright_license import KeySet, verify_license

TRUST_ANCHORS = KeySet.from_mapping(
    {
        "lic-2026-01": "<base64url raw Ed25519 public key>",  # active
        "lic-2027-01": "<base64url raw Ed25519 public key>",  # next (pre-shipped for rotation)
    }
)

result = verify_license(
    open("/etc/shipwright/license.lic").read(),
    keyset=TRUST_ANCHORS,
    product="engine-room",
    # One id, or several while migrating ids (any match counts):
    appliance_id=[appliance_uuid, legacy_appliance_id],
)
if result.has_feature("ee.pool"):
    enable_pool(max_nodes=result.limit("max_nodes"))
```

## States

| State | Meaning | Licensed features |
|---|---|---|
| `valid` | Signature good, within term | on |
| `expiring` | 30 days or fewer left | on (show banner) |
| `grace` | Past `exp`, within `grace_days` | on (loud warning) |
| `expired` | Past grace | off |
| `invalid` | Bad signature, key, audience, binding, header or claims | off |

## Rejected header parameters

Only `alg`, `kid` and `typ` are accepted in the header. Any other parameter (for example
`jku`, `jwk`, `x5u`, `x5c` or `crit`) makes the license invalid. `alg` must be exactly
`EdDSA`.

The full specification is `docs/license-format.md` in the Admiralty repository.
