Metadata-Version: 2.4
Name: sar-envelope
Version: 0.1.1
Summary: Portable SAR (Settlement Attestation Receipt) v0.1 envelope primitives: JCS canonicalization, receipt ID derivation, Ed25519 signature-input construction, and .well-known/sar-keys.json key discovery.
License: MIT
Project-URL: Homepage, https://defaultverifier.com
Project-URL: Fixtures, https://github.com/nutstrut/sar-envelope/tree/main/py/fixtures
Keywords: sar,jcs,rfc8785,canonicalization,ed25519,receipts
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: jcs>=0.2.1

# sar-envelope (Python)

Envelope primitives for the **portable SAR v0.1 signed core** — the
six-field `task_id_hash, verdict, confidence, reason_code, ts,
verifier_kid` contract used across SAR (Settlement Attestation Receipt)
implementations, including independent implementations on non-EVM/non-
wallet rails.

This package implements exactly four things:

1. **JCS (RFC 8785) canonicalization** — `canonicalize(value) -> bytes`.
2. **Deterministic receipt/claim ID derivation** —
   `receipt_id(core) = "sha256:" + hex(sha256(JCS(core)))`.
3. **Ed25519 signature-input construction** — `signing_input(core)`
   returns the exact 32 bytes an Ed25519 key signs (the sha256 digest of
   the JCS-canonicalized core, not the raw canonical JSON bytes).
4. **Key discovery** against a published `.well-known/sar-keys.json`
   registry (JWK format) — `parse_key_registry`, `resolve_key`,
   `fetch_key_registry`, including `kid` resolution and algorithm binding
   (only `kty=OKP, crv=Ed25519` is recognized).

It does **not** verify signatures, run a trust registry, implement a CLI,
or encode any issuer-specific extension (e.g. a wallet-bound counterparty
binding). `counterparty` and `_ext` are never part of the six-field
signed core this package computes over.

## Install

```bash
pip install sar-envelope
```

## Example

This reproduces fixture `01_valid_portable` from the published conformance
corpus (see [Fixtures](#fixtures) below) byte-for-byte:

```python
from sar_envelope import extract_core, receipt_id, signing_input

receipt = {
    "receipt_version": "0.1",
    "task_id_hash": "sha256:fixture-portable-task-0001",
    "verdict": "PASS",
    "confidence": 1,
    "reason_code": "SPEC_MATCH",
    "ts": "2026-07-23T00:00:00Z",
    "verifier_kid": "fixture-portable-kid-01",
    "sig_alg": "Ed25519",
}

core = extract_core(receipt)
rid = receipt_id(core)
assert rid == "sha256:be6c760480f4127a842d3321774680531caa4a9acf16684c067086f83a8d8b6f"

# The bytes an Ed25519 private key signs for this receipt:
digest = signing_input(core)  # 32 bytes; receipt_id hex-encodes this same value
```

Key discovery against the real published registry format:

```python
from sar_envelope import fetch_key_registry, resolve_key

registry = fetch_key_registry("https://defaultverifier.com/.well-known/sar-keys.json")
entry = resolve_key(registry, "sar-prod-ed25519-01")
entry.pubkey  # raw 32-byte Ed25519 public key
entry.alg     # "Ed25519" — only OKP/Ed25519 registry entries are returned
```

## Fixtures

The test suite in this package runs against the canonical Portable SAR
v0.1 conformance corpus, bundled at `fixtures/portable-sar-fixtures.json`
and `fixtures/portable-sar-fixture-keys.json`:
https://github.com/nutstrut/sar-envelope/tree/main/py/fixtures

## Scope and versioning

Implements **SAR v0.1**, portable six-field signed-core profile only.
Pre-1.0 (`0.1.1`): the API may change before a 1.0 release. This package
computes canonical bytes, receipt IDs, and signature input deterministically
from data you supply — it does not itself authenticate a producer's
identity, prove human authority over an action, or provide any
end-to-end verification guarantee. Signature verification and trust-registry
policy (pinning, revocation, refresh) are the caller's responsibility and
are out of scope for this package.
