Metadata-Version: 2.4
Name: qhermes-kernel
Version: 26.0.0
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Security :: Cryptography
Summary: Post-quantum ML-DSA-65 delegation chain primitives
Author-email: Copertino <hello@copertino.world>
License: LicenseRef-Copertino-1.0
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# qhermes-kernel

Python bindings for `qhermes-kernel`.

Part of **QHermes 26** by Copertino.

Thank you for choosing QHermes 26.

ML-DSA-65 identity derivation, credential issuance, delegation chain
verification, and wire serialization for Python applications.

Wheels target the stable ABI (abi3) and are compatible with Python 3.9
and later.

## Installation

```
pip install qhermes-kernel
```

## Design

**Two layers for two audiences.** `qhermes.kernel` is the Python API for application code — named arguments, `NamedTuple` types, time helpers, and human-readable inspection functions. `qhermes_kernel` is the low-level extension module with direct bindings to the Rust primitives. The high-level layer is appropriate for most application code. The low-level layer provides direct access to TLV encoding and raw wire bytes.

**`make_policy` time arguments are keyword-only.** Unix timestamps are large integers. Accepting them positionally risks passing a timestamp where a duration was expected, or the reverse, without any error at the call site. All time parameters — `not_before`, `not_after`, `hours_valid`, `minutes_valid`, `now` — are keyword-only.

**`Policy` and `Credential` are immutable.** Both are `NamedTuple`. They cannot be modified after construction. Chain assembly via `delegate` returns a new tuple without modifying the originals.

**`decode_chain` does not verify.** It deserializes wire bytes into `Credential` objects without checking signatures or scope containment. It is provided for inspection and debugging. Call `verify_chain` before trusting any chain received from an external source.

**Timestamps throughout are Unix epoch seconds.** Passing milliseconds to any function that accepts a timestamp produces incorrect caveat evaluation without error. This matches the behavior of the Rust kernel.

___

## High-level API — `qhermes.kernel`

### Policy construction

```python
from qhermes.kernel import make_policy

policy = make_policy(
    resources=[b"/tasks", b"/logs"],
    actions=[b"write", b"read"],
    hours_valid=4,
)
```

For asymmetric permissions — different actions on different resources:

```python
policy = make_policy(
    permissions=[
        (b"/source",    b"GET"),
        (b"/artifacts", b"PUT"),
    ],
    hours_valid=2,
)
```

For fine-grained control at the TLV level:

```python
from qhermes.kernel import allow, caveat_not_before, caveat_not_after, Policy
import time

scope   = allow(b"", b"/repos", b"push")
caveats = caveat_not_after(b"", int(time.time()) + 7200)
policy  = Policy(scope_tlv=scope, caveats=caveats)
```

### Identity derivation

```python
from qhermes.kernel import make_identity, derive_public_key

identity = make_identity(master=seed_32, deployment=b"prod", context=b"root")
pk       = derive_public_key(identity)  # 1952 bytes
```

### Credential issuance

```python
from qhermes.kernel import issue_credential, delegate, build_chain

cred  = issue_credential(identity, child_pk=child_pk, policy=policy, depth=1)
chain = delegate(identity, child_pk=child_pk, policy=policy)
wire  = build_chain(chain)
```

### Chain verification and inspection

```python
from qhermes.kernel import verify_chain, decode_chain, explain_credential, parse_payload
import time

n     = verify_chain(root_pk=pk, wire=wire, now=int(time.time()))
chain = decode_chain(wire)

for cred in chain:
    print(explain_credential(cred))

    p = parse_payload(cred)
    # p["child_pk"]   — bytes, PK_SIZE
    # p["role"]       — "leaf" or "node"
    # p["depth"]      — int
    # p["perms"]      — list of (resource, verb) byte tuples
    # p["not_before"] — int | None
    # p["not_after"]  — int | None
```

___

## Low-level API — `qhermes_kernel`

### Constants

| Name | Value | Description |
|---|---|---|
| `PK_SIZE` | 1952 | ML-DSA-65 public key, bytes |
| `SIG_SIZE` | 3309 | ML-DSA-65 signature, bytes |
| `SEED_SIZE` | 32 | Master seed, bytes |

### `make_policy(resources, actions, *, permissions, ...)`

Standard form — cartesian product of resources × actions:

```python
policy = make_policy(
    resources=[b"/data", b"/logs"],
    actions=[b"GET"],
    hours_valid=1,
)
```

Asymmetric form — explicit pairs:

```python
policy = make_policy(
    permissions=[(b"/source", b"GET"), (b"/artifacts", b"PUT")],
    hours_valid=2,
)
```

`resources`/`actions` and `permissions` are mutually exclusive.

### `IdentityIsland`

```python
import qhermes_kernel as k

island = k.IdentityIsland(master=seed_32, deployment=b"prod", context=b"agent-0")
pk     = island.public_key()

scope   = k.perm_tlv(resource=b"/tasks", verb=b"write")
caveats = k.not_after(ts=1800000000)

payload, sig = island.issue_credential(
    child_pk  = child_pk_bytes,
    role      = "leaf",
    depth     = 1,
    scope_tlv = scope,
    caveats   = caveats,
)
```

### Free functions

`perm_tlv(resource, verb)` — encode one permission in TLV format, returns bytes.

`not_before(ts)` / `not_after(ts)` — 9-byte caveat records, ts in Unix seconds.

`encode_chain(credentials)` — serialize `[(issuer_pk, sig, payload)]` to wire bytes.

`decode_chain(wire)` — deserialize without verifying signatures.

`parse_payload(payload)` — deserialize a credential payload into named fields.

`verify_delegation(root_pk, wire, now)` — verify full chain, returns credential count.

___

## License

Copertino Source License 1.0. Change Date: 2036-01-01. Change License: Apache-2.0.
See [LICENSE](../LICENSE).

Copyright © 2026 Copertino. All rights reserved.

