Metadata-Version: 2.4
Name: palynth-provenance
Version: 0.1.0
Summary: Palynth provenance kernel: canonical JSON, SHA-256 hash chains, Merkle roots, Ed25519 writer signatures. Byte-identical with the TypeScript kernel; the golden vectors ship inside the package.
Author: Palynth
License: Apache-2.0
Project-URL: Homepage, https://palynth.com
Project-URL: Repository, https://github.com/harshareddy-lab/palynth-lab
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: signing
Requires-Dist: cryptography>=42; extra == "signing"
Dynamic: license-file

# palynth-provenance

The Palynth provenance kernel, for Python. Palynth is the verifiable memory
layer for everything on record — one system with two halves that prove each
other: a capsule (`.paly`) that proves a document down to the bytes that
state each claim, and an append-only, hash-chained ledger that proves its
history across documents. This package is the ledger's kernel.

It is byte-identical with the TypeScript kernel (`@palynth/provenance`) and
the browser port: the same input produces the same canonical bytes and the
same hash on all three. The golden vectors that pin that agreement ship
inside this package, so an installed copy can prove it:

```
python -m palynth_provenance.selftest
```

Stdlib only. Signing needs `cryptography`:

```
pip install "palynth-provenance[signing]"
```

## Use

```python
from palynth_provenance import Ledger, verify, merkle_root, key_from_seed, verify_signature
import os

signer = key_from_seed(os.urandom(32).hex())      # keep the seed secret
ledger = Ledger("your-project-uuid")
ledger.append(
    asset_uuid="opening-101",
    event_type="ASSET_CREATED",
    timestamp="2026-09-01T00:00:00.000Z",
    reason="Opening 101 extracted from the door schedule.",
    payload={"note": "3'-0\" x 7'-0\" · HM · hardware set 08A"},
    signer=signer,
)

ok, problems = verify(ledger.events)              # hashes and links
all(verify_signature(e) for e in ledger.events)   # signatures under the keys they name
merkle_root([e["hash"] for e in ledger.events])   # what an anchor attests
```

## What is guaranteed

- Zero network. This package never makes a request.
- Three implementations agree on every byte, and the bundled fixture makes
  that a test rather than a promise.
- A signature is derived from the hash and never hashed over: signing
  changes no existing hash, and an unsigned event is one without the field.
- The canonical form is frozen. Changing it would invalidate every chain
  ever written, so the kernel here is a byte-identical copy of the one
  production runs, bound by a test, never edited by hand.
