From 3143b08bd13b1496918745d41c1d5ec3c5e99858 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 00:33:52 +0000 Subject: [PATCH 1/4] Add end-to-end example, CI workflow, and README improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - examples/end_to_end.py: runnable walkthrough (generate → sign → verify → tamper detection) - .github/workflows/ci.yml: pytest matrix on Python 3.9 / 3.11 / 3.13 - pyproject.toml: add [dev] optional deps (pytest) and explicit testpaths - README: CI badge + "End-to-end example" section linking to examples/ https://claude.ai/code/session_01Fu1FmfopF3rCE9ohvmn9zn --- .github/workflows/ci.yml | 28 ++++++++++++++++++++ README.md | 37 +++++++++++++++++++++++++++ examples/end_to_end.py | 55 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 6 +++++ 4 files changed, 126 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 examples/end_to_end.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..30a209d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.11", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: pip install -e ".[dev]" + + - name: Run tests + run: python -m pytest tests/ -v diff --git a/README.md b/README.md index d42872b..2023a11 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ > **Protocol specification:** [draft-litzki-sovp-01](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — IETF Internet-Draft +[![CI](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/Python-3.9+-blue.svg)](https://www.python.org/downloads/) [![IETF Draft](https://img.shields.io/badge/IETF-draft--litzki--sovp--05-lightgrey.svg)](https://datatracker.ietf.org/doc/draft-litzki-sovp/) @@ -51,6 +52,42 @@ This installs the `sovp` CLI and the `sovp.core` library. Dependencies (`cryptog --- +## End-to-end example + +The fastest way to see SOVP in action — generate a keypair, sign an identity document, verify it, and watch tamper detection fire: + +```python +from sovp.core import generate_keypair, generate_identity_document, verify_identity + +# 1. Generate keys — publish public_key_b64 in your DNS TXT record +private_key_b64, public_key_b64 = generate_keypair() + +# 2. Build and sign — serve this JSON at /.well-known/sovp-identity.json +document = generate_identity_document( + private_key_b64=private_key_b64, + entity_uid="urn:sovp:example-entity", + canonical_url="https://example.com", +) + +# 3. Verify (Psi_core) +signature = document["integrity_proof"]["signature"] +psi_core = verify_identity(document, signature, public_key_b64) +print("Psi_core =", 1 if psi_core else 0) # → 1 + +# 4. Tamper detection +document["entity"]["canonical_url"] = "https://attacker.com" +psi_core = verify_identity(document, signature, public_key_b64) +print("Psi_core =", 1 if psi_core else 0) # → 0 (blocked) +``` + +A runnable version with annotated output is in [`examples/end_to_end.py`](examples/end_to_end.py): + +```bash +python examples/end_to_end.py +``` + +--- + ## Usage ### Python API diff --git a/examples/end_to_end.py b/examples/end_to_end.py new file mode 100644 index 0000000..738ef32 --- /dev/null +++ b/examples/end_to_end.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 Litzki Systems LLC +# SPDX-License-Identifier: Apache-2.0 +# +# End-to-end SOVP walkthrough: generate keys, sign an identity document, +# verify it, and confirm that tampering is detected. +# +# Run: python examples/end_to_end.py + +import json +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from sovp.core import generate_keypair, generate_identity_document, verify_identity + +print("=== SOVP End-to-End Example ===\n") + +# ── Step 1: Generate an Ed25519 keypair ──────────────────────────────────────── +private_key_b64, public_key_b64 = generate_keypair() +print(f"Public key : {public_key_b64}") +print(f"Private key : {private_key_b64[:20]}... (keep secret)\n") + +# Publish public_key_b64 in DNS: +# _sovp.yourdomain.tld IN TXT "v=SOVP1; k=" + +# ── Step 2: Build and sign a sovp-identity.json document ────────────────────── +document = generate_identity_document( + private_key_b64=private_key_b64, + entity_uid="urn:sovp:example-entity", + canonical_url="https://example.com", +) +print("Signed sovp-identity.json:") +print(json.dumps(document, indent=2)) +print() + +# Serve this at: https://example.com/.well-known/sovp-identity.json + +# ── Step 3: Verify — Psi_core resonance check ───────────────────────────────── +signature = document["integrity_proof"]["signature"] + +psi_core = verify_identity(document, signature, public_key_b64) +print(f"Psi_core = {'1 ✓ Verified — ingestion may proceed.' if psi_core else '0 ✗ Blocked.'}") +assert psi_core, "Verification should pass for an unmodified document." + +# ── Step 4: Tamper detection ─────────────────────────────────────────────────── +tampered = json.loads(json.dumps(document)) +tampered["entity"]["canonical_url"] = "https://attacker.com" + +psi_tampered = verify_identity(tampered, signature, public_key_b64) +print(f"Psi_core = {'1' if psi_tampered else '0 ✗ Tamper detected — ingestion blocked.'}") +assert not psi_tampered, "Verification should fail after tampering." + +print("\n=== Done ===") diff --git a/pyproject.toml b/pyproject.toml index 5612718..99dd34b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,5 +13,11 @@ dependencies = [ "jcs", ] +[project.optional-dependencies] +dev = ["pytest"] + [project.scripts] sovp = "sovp.cli:main" + +[tool.pytest.ini_options] +testpaths = ["tests"] From ab56bffaceb40bb35801ff851c9a24fa12abc15a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 00:35:10 +0000 Subject: [PATCH 2/4] Fix Python 3.9 compatibility: add __future__ annotations import dict | None and str | None union syntax (PEP 604) requires Python 3.10+. from __future__ import annotations defers evaluation and makes it valid on 3.9. https://claude.ai/code/session_01Fu1FmfopF3rCE9ohvmn9zn --- sovp/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sovp/core.py b/sovp/core.py index 9d5c86d..f9ac9a6 100644 --- a/sovp/core.py +++ b/sovp/core.py @@ -1,6 +1,8 @@ # Copyright (c) 2026 Litzki Systems LLC # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + import base64 import jcs import uuid From ebf00eac86996fb5cc4919cf8e4b04565a28d5c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 00:37:14 +0000 Subject: [PATCH 3/4] Fix @context inconsistency and clean up Roadmap - README: align IETF Draft version to -05 everywhere (was -01 in intro and roadmap) - README Roadmap: add generate_identity_document (implemented but missing), replace "Not yet implemented" with "Planned", group replay protection rows - CHANGELOG-v02.md: mark @context v1.4 as resolved canonical version https://claude.ai/code/session_01Fu1FmfopF3rCE9ohvmn9zn --- CHANGELOG-v02.md | 4 ++-- README.md | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG-v02.md b/CHANGELOG-v02.md index fa7aaf5..689d113 100644 --- a/CHANGELOG-v02.md +++ b/CHANGELOG-v02.md @@ -8,8 +8,8 @@ Problem: Timestamp validation is now implemented via check_timestamp=True in ver Fix: Remove the note. Replace with: "Timestamp validation is implemented in the reference implementation via the check_timestamp parameter of verify_identity(). Nonce-based deduplication remains a future work item." ## 2. Section 4 — Resolve @context version (v1.4 vs v1.5) -Problem: Draft shows v1.4; reference implementation used v1.5. Tests realigned to v1.4 for now. -Fix: Confirm canonical version. Update draft and implementation to single consistent value. Define versioning policy (what a context version bump implies). +Status: **Resolved.** v1.4 is confirmed canonical. All code, tests, and documentation now consistently use `https://litzki-systems.com/protocol/v1.4`. The v1.5 reference was a transient inconsistency; the draft and implementation are aligned. +Remaining: Define a versioning policy (what a context version bump implies) — deferred to draft-litzki-sovp-03. ## 3. Section 6 / New sub-section — DNS TXT Record Format (normative) Problem: Section 9.1 registers the _sovp DNS label but specifies no TXT record value format. README uses v=SOVP1; k= non-normatively. diff --git a/README.md b/README.md index 2023a11..b673620 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **sovp-python** is the reference implementation of the [Sovereign Validation Protocol (SOVP)](https://litzki-systems.com/sovp) — a pre-ingestion verification protocol that lets LLMs and autonomous agents cryptographically confirm the identity and integrity of a data source before parsing it. It exists because existing mechanisms (DANE, DIDs, TLS) operate at the wrong layer for agentic pipelines: SOVP sits at Layer 0, before the body is read. To get started: clone the repo and run `pip install -e .` — this exposes both a `sovp.core` Python API and a `sovp` CLI with three commands. -> **Protocol specification:** [draft-litzki-sovp-01](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — IETF Internet-Draft +> **Protocol specification:** [draft-litzki-sovp-05](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — IETF Internet-Draft [![CI](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) @@ -224,14 +224,15 @@ Recommended TTL: 300 seconds (per draft Section 6.1). DNSSEC recommended for the | Feature | Status | |---|---| -| `sovp.core` library (`generate_keypair`, `sign_identity`, `verify_identity`) | Implemented | +| `sovp.core` primitives (`generate_keypair`, `sign_identity`, `verify_identity`) | Implemented | +| `sovp.core` document builder (`generate_identity_document`) | Implemented | | CLI (`generate-keypair`, `sign`, `verify`) | Implemented | -| `SOVPIdentity` / `SOVPSigner` / `SOVPValidator` class API | Not yet implemented | -| DNS + HTTP resolution in `SOVPValidator` | Not yet implemented | -| Replay protection (timestamp validation, `check_timestamp=True`) | Implemented (`verify_identity`) | -| Replay protection (nonce deduplication) | Not yet implemented | -| RFC conformance test vectors | Not yet implemented | -| IETF Internet-Draft | [draft-litzki-sovp-01](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — submitted | +| Replay protection — timestamp validation (`check_timestamp=True`) | Implemented | +| Replay protection — nonce deduplication | Planned | +| `SOVPIdentity` / `SOVPSigner` / `SOVPValidator` class API | Planned | +| DNS + HTTP resolution in `SOVPValidator` | Planned | +| RFC conformance test vectors | Planned | +| IETF Internet-Draft | [draft-litzki-sovp-05](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — submitted | | U.S. Provisional Patent | Filed — No. 64/005,737 | --- From 3e880fc9d53576745aa40cac3b82006e5a093ed2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 00:38:31 +0000 Subject: [PATCH 4/4] Revert IETF draft version to -01 (correct public designation) -05 was an internal version number; draft-litzki-sovp-01 is the correct IETF identifier for the submitted Internet-Draft. https://claude.ai/code/session_01Fu1FmfopF3rCE9ohvmn9zn --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b673620..716aca5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **sovp-python** is the reference implementation of the [Sovereign Validation Protocol (SOVP)](https://litzki-systems.com/sovp) — a pre-ingestion verification protocol that lets LLMs and autonomous agents cryptographically confirm the identity and integrity of a data source before parsing it. It exists because existing mechanisms (DANE, DIDs, TLS) operate at the wrong layer for agentic pipelines: SOVP sits at Layer 0, before the body is read. To get started: clone the repo and run `pip install -e .` — this exposes both a `sovp.core` Python API and a `sovp` CLI with three commands. -> **Protocol specification:** [draft-litzki-sovp-05](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — IETF Internet-Draft +> **Protocol specification:** [draft-litzki-sovp-01](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — IETF Internet-Draft [![CI](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/litzki-systems/sovp-python/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) @@ -232,7 +232,7 @@ Recommended TTL: 300 seconds (per draft Section 6.1). DNSSEC recommended for the | `SOVPIdentity` / `SOVPSigner` / `SOVPValidator` class API | Planned | | DNS + HTTP resolution in `SOVPValidator` | Planned | | RFC conformance test vectors | Planned | -| IETF Internet-Draft | [draft-litzki-sovp-05](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — submitted | +| IETF Internet-Draft | [draft-litzki-sovp-01](https://datatracker.ietf.org/doc/draft-litzki-sovp/) — submitted | | U.S. Provisional Patent | Filed — No. 64/005,737 | ---