Spaces:
Running
Running
Wire D (W3C traceparent trace continuity) + DSSE/Cosign-signed Khipu receipts — SLSA L1->L2 (PLACEHOLDER->REAL). ADDITIVE. — Yachay
Browse files- Dockerfile +4 -0
- serve.py +14 -14
- szl_dsse.py +196 -0
- szl_provenance.py +411 -0
Dockerfile
CHANGED
|
@@ -13,6 +13,8 @@ FROM python:3.12-slim AS backend
|
|
| 13 |
WORKDIR /app
|
| 14 |
COPY sidecar/ ./sidecar/
|
| 15 |
RUN pip install --no-cache-dir ./sidecar
|
|
|
|
|
|
|
| 16 |
|
| 17 |
FROM python:3.12-slim
|
| 18 |
|
|
@@ -56,6 +58,8 @@ COPY szl_jack.py /app/szl_jack.py
|
|
| 56 |
# ADDITIVE (Doctrine v10/v11): shared agentic-RAG service (organ=cortex).
|
| 57 |
COPY szl_rag.py /app/szl_rag.py
|
| 58 |
COPY szl_wire.py /app/szl_wire.py
|
|
|
|
|
|
|
| 59 |
|
| 60 |
ENV PORT=7860
|
| 61 |
EXPOSE 7860
|
|
|
|
| 13 |
WORKDIR /app
|
| 14 |
COPY sidecar/ ./sidecar/
|
| 15 |
RUN pip install --no-cache-dir ./sidecar
|
| 16 |
+
# ADDITIVE (Yachay / Provenance Hardening): cryptography for DSSE+Cosign Khipu signing.
|
| 17 |
+
RUN pip install --no-cache-dir "cryptography>=42.0"
|
| 18 |
|
| 19 |
FROM python:3.12-slim
|
| 20 |
|
|
|
|
| 58 |
# ADDITIVE (Doctrine v10/v11): shared agentic-RAG service (organ=cortex).
|
| 59 |
COPY szl_rag.py /app/szl_rag.py
|
| 60 |
COPY szl_wire.py /app/szl_wire.py
|
| 61 |
+
COPY szl_dsse.py /app/szl_dsse.py
|
| 62 |
+
COPY szl_provenance.py /app/szl_provenance.py
|
| 63 |
|
| 64 |
ENV PORT=7860
|
| 65 |
EXPOSE 7860
|
serve.py
CHANGED
|
@@ -68,6 +68,20 @@ try:
|
|
| 68 |
except Exception as _e:
|
| 69 |
print(f"[amaru] szl_rag not registered: {_e}", file=sys.stderr)
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
# ---------------------------------------------------------------------------
|
| 72 |
# Wire D: /api/amaru/healthz — traceparent_propagating: true
|
| 73 |
# Registered BEFORE the /api/amaru mount so it takes precedence over the
|
|
@@ -539,20 +553,6 @@ async def serve_spa(path: str):
|
|
| 539 |
return FileResponse(STATIC_DIR / "index.html")
|
| 540 |
|
| 541 |
|
| 542 |
-
|
| 543 |
-
# ── Live 3D Wires (PURIQ / Doctrine v12) — ADDITIVE, ZERO BANDAID ────────────
|
| 544 |
-
# Bakes the "Live Wires" 3D panel into THIS flagship's cortex: /live-wires + the
|
| 545 |
-
# 3DWPP SSE stream + court-admissible BoE drill-down. Real in-process wire data
|
| 546 |
-
# (szl_wire / szl_jack); empty buffers render IDLE (never faked). Sigs honestly
|
| 547 |
-
# PLACEHOLDER until Sigstore CI wired. Sign: Yachay. Perplexity Computer Agent.
|
| 548 |
-
try:
|
| 549 |
-
import szl_live_wires as _live_wires
|
| 550 |
-
_live_wires.register(app, ns="amaru")
|
| 551 |
-
import sys as _sys_lw
|
| 552 |
-
print("[amaru] Live 3D Wires registered: /live-wires + /api/amaru/v1/wires/{stream,boe}", file=_sys_lw.stderr)
|
| 553 |
-
except Exception as _lw_e:
|
| 554 |
-
import sys as _sys_lw
|
| 555 |
-
print(f"[amaru] Live 3D Wires NOT registered: {_lw_e}", file=_sys_lw.stderr)
|
| 556 |
if __name__ == "__main__":
|
| 557 |
import uvicorn
|
| 558 |
port = int(os.environ.get("PORT", "7860"))
|
|
|
|
| 68 |
except Exception as _e:
|
| 69 |
print(f"[amaru] szl_rag not registered: {_e}", file=sys.stderr)
|
| 70 |
|
| 71 |
+
# ---------------------------------------------------------------------------
|
| 72 |
+
# ADDITIVE (Yachay / Provenance Hardening): Wire D (W3C traceparent trace
|
| 73 |
+
# continuity) + DSSE/Cosign-signed Khipu receipts (SLSA L2 signed provenance).
|
| 74 |
+
# Registers /api/{space}/wires/D, /khipu/{sign,verify,ledger}, /provenance.
|
| 75 |
+
# Wrapped so a missing dep (cryptography) can NEVER take down the existing app.
|
| 76 |
+
# PLACEHOLDER -> REAL: every receipt now DSSE-signed with szlholdings-cosign.
|
| 77 |
+
# ---------------------------------------------------------------------------
|
| 78 |
+
try:
|
| 79 |
+
import szl_provenance as _prov
|
| 80 |
+
_prov_status = _prov.register_provenance(app, "amaru")
|
| 81 |
+
print(f"[amaru] szl_provenance registered (Wire D LIVE, SLSA L2): {{_prov_status}}", file=sys.stderr)
|
| 82 |
+
except Exception as _pe: # pragma: no cover - defensive, additive-only
|
| 83 |
+
print(f"[amaru] szl_provenance NOT registered ({{_pe!r}}); existing app unaffected", file=sys.stderr)
|
| 84 |
+
|
| 85 |
# ---------------------------------------------------------------------------
|
| 86 |
# Wire D: /api/amaru/healthz — traceparent_propagating: true
|
| 87 |
# Registered BEFORE the /api/amaru mount so it takes precedence over the
|
|
|
|
| 553 |
return FileResponse(STATIC_DIR / "index.html")
|
| 554 |
|
| 555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
if __name__ == "__main__":
|
| 557 |
import uvicorn
|
| 558 |
port = int(os.environ.get("PORT", "7860"))
|
szl_dsse.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11/v12
|
| 3 |
+
# Authored by Yachay (CTO) — Provenance Hardening: PLACEHOLDER -> REAL.
|
| 4 |
+
"""
|
| 5 |
+
szl_dsse — DSSE (in-toto/Dead-Simple-Signing-Envelope) signing + verification
|
| 6 |
+
for SZL Khipu receipts, backed by the SZLHOLDINGS **Cosign** keypair.
|
| 7 |
+
|
| 8 |
+
Spec sources baked in:
|
| 9 |
+
- DSSE protocol (secure-systems-lab/dsse) — PAE pre-authentication encoding:
|
| 10 |
+
PAE(type, body) = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body
|
| 11 |
+
SIGNATURE = Sign(PAE(UTF8(payloadType), SERIALIZED_BODY))
|
| 12 |
+
- Sigstore Cosign (docs.sigstore.dev/cosign) — key-based blob signing:
|
| 13 |
+
cosign sign-blob --key cosign.key <blob> (ECDSA P-256 over SHA-256)
|
| 14 |
+
cosign verify-blob --key cosign.pub --signature <sig> <blob>
|
| 15 |
+
|
| 16 |
+
KEY MODEL (honest):
|
| 17 |
+
- The canonical signing key is the SZLHOLDINGS Cosign keypair generated with
|
| 18 |
+
`cosign generate-key-pair` (imported from an OpenSSL P-256 EC key).
|
| 19 |
+
- cosign.pub is published at szl-holdings/.github/cosign.pub (PUBLIC).
|
| 20 |
+
- The PRIVATE key is delivered to each Space ONLY as a runtime secret
|
| 21 |
+
env var `SZL_COSIGN_PRIVATE_PEM` (PKCS8 PEM). It is NEVER committed to a
|
| 22 |
+
repo (HF or GitHub). If the secret is absent the module reports
|
| 23 |
+
`signing_available=false` and emits a clearly-labelled UNSIGNED receipt —
|
| 24 |
+
it NEVER fabricates a signature.
|
| 25 |
+
- In-Space signing uses the Python `cryptography` lib over the DSSE PAE
|
| 26 |
+
bytes. This is byte-for-byte verifiable by the `cosign` CLI (proven:
|
| 27 |
+
cosign verify-blob accepts the cryptography-produced ECDSA-SHA256 sig,
|
| 28 |
+
and Python verifies cosign-produced sigs — full round-trip equivalence).
|
| 29 |
+
|
| 30 |
+
payloadType for Khipu receipts: "application/vnd.szl.khipu+json"
|
| 31 |
+
keyid: "szlholdings-cosign"
|
| 32 |
+
"""
|
| 33 |
+
from __future__ import annotations
|
| 34 |
+
|
| 35 |
+
import base64
|
| 36 |
+
import hashlib
|
| 37 |
+
import json
|
| 38 |
+
import os
|
| 39 |
+
from datetime import datetime, timezone
|
| 40 |
+
from typing import Any
|
| 41 |
+
|
| 42 |
+
KEYID = "szlholdings-cosign"
|
| 43 |
+
KHIPU_PAYLOAD_TYPE = "application/vnd.szl.khipu+json"
|
| 44 |
+
COSIGN_PUB_FINGERPRINT_ENV = "SZL_COSIGN_PUB_SHA256" # optional pin
|
| 45 |
+
|
| 46 |
+
# The published public key (szl-holdings/.github/cosign.pub). Embedded so the
|
| 47 |
+
# /khipu/verify endpoint can verify WITHOUT a network call. This is PUBLIC data.
|
| 48 |
+
COSIGN_PUBLIC_PEM = """-----BEGIN PUBLIC KEY-----
|
| 49 |
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7mrYWDnz8TvT7o4/65XGqYxo9OoV
|
| 50 |
+
vaB/grNuz+kVP1Xsaw0RokBKG0xT/XlV5Fz90AOwtgqC2yMBP0blK455gQ==
|
| 51 |
+
-----END PUBLIC KEY-----
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
PUB_KEY_URL = "https://github.com/szl-holdings/.github/blob/main/cosign.pub"
|
| 55 |
+
|
| 56 |
+
# ---------------------------------------------------------------------------
|
| 57 |
+
# Canonical JSON + DSSE PAE
|
| 58 |
+
# ---------------------------------------------------------------------------
|
| 59 |
+
|
| 60 |
+
def canonical_json(obj: Any) -> bytes:
|
| 61 |
+
"""Deterministic canonical JSON: sorted keys, no extra whitespace, UTF-8."""
|
| 62 |
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def pae(payload_type: str, body: bytes) -> bytes:
|
| 66 |
+
"""DSSE Pre-Authentication Encoding (DSSEv1)."""
|
| 67 |
+
t = payload_type.encode("utf-8")
|
| 68 |
+
return b"DSSEv1 " + str(len(t)).encode() + b" " + t + b" " + str(len(body)).encode() + b" " + body
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
# ---------------------------------------------------------------------------
|
| 72 |
+
# Key loading (private = runtime secret; public = embedded)
|
| 73 |
+
# ---------------------------------------------------------------------------
|
| 74 |
+
|
| 75 |
+
def _load_private_key():
|
| 76 |
+
"""Load the Cosign EC private key from the SZL_COSIGN_PRIVATE_PEM secret.
|
| 77 |
+
Returns None if absent/invalid — NEVER raises into the request path."""
|
| 78 |
+
pem = os.environ.get("SZL_COSIGN_PRIVATE_PEM")
|
| 79 |
+
if not pem:
|
| 80 |
+
return None
|
| 81 |
+
try:
|
| 82 |
+
# Allow the secret to be provided base64-wrapped (HF UI friendliness)
|
| 83 |
+
if "BEGIN" not in pem:
|
| 84 |
+
pem = base64.b64decode(pem).decode("utf-8")
|
| 85 |
+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
| 86 |
+
return load_pem_private_key(pem.encode("utf-8"), password=None)
|
| 87 |
+
except Exception:
|
| 88 |
+
return None
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _load_public_key():
|
| 92 |
+
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
| 93 |
+
return load_pem_public_key(COSIGN_PUBLIC_PEM.encode("utf-8"))
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def signing_available() -> bool:
|
| 97 |
+
return _load_private_key() is not None
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def public_key_fingerprint() -> str:
|
| 101 |
+
return hashlib.sha256(COSIGN_PUBLIC_PEM.strip().encode()).hexdigest()
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# ---------------------------------------------------------------------------
|
| 105 |
+
# Sign / Verify
|
| 106 |
+
# ---------------------------------------------------------------------------
|
| 107 |
+
|
| 108 |
+
def sign_payload(payload_obj: Any, payload_type: str = KHIPU_PAYLOAD_TYPE) -> dict[str, Any]:
|
| 109 |
+
"""Produce a DSSE envelope over the canonical JSON of `payload_obj`.
|
| 110 |
+
|
| 111 |
+
Returns the DSSE envelope dict:
|
| 112 |
+
{payload(b64), payloadType, signatures:[{sig(b64), keyid}], ...meta}
|
| 113 |
+
If no private key is present, returns an UNSIGNED envelope with an explicit
|
| 114 |
+
honesty marker (NO fabricated signature)."""
|
| 115 |
+
body = canonical_json(payload_obj)
|
| 116 |
+
to_sign = pae(payload_type, body)
|
| 117 |
+
env: dict[str, Any] = {
|
| 118 |
+
"payloadType": payload_type,
|
| 119 |
+
"payload": base64.b64encode(body).decode("ascii"),
|
| 120 |
+
"_dsse": "DSSEv1",
|
| 121 |
+
"_pae_sha256": hashlib.sha256(to_sign).hexdigest(),
|
| 122 |
+
"_signed_at": datetime.now(timezone.utc).isoformat(),
|
| 123 |
+
}
|
| 124 |
+
priv = _load_private_key()
|
| 125 |
+
if priv is None:
|
| 126 |
+
env["signatures"] = []
|
| 127 |
+
env["honesty"] = ("UNSIGNED — SZL_COSIGN_PRIVATE_PEM secret not present in this "
|
| 128 |
+
"Space runtime; no signature fabricated.")
|
| 129 |
+
env["signed"] = False
|
| 130 |
+
return env
|
| 131 |
+
from cryptography.hazmat.primitives.asymmetric import ec
|
| 132 |
+
from cryptography.hazmat.primitives import hashes
|
| 133 |
+
sig = priv.sign(to_sign, ec.ECDSA(hashes.SHA256()))
|
| 134 |
+
env["signatures"] = [{"sig": base64.b64encode(sig).decode("ascii"), "keyid": KEYID}]
|
| 135 |
+
env["signed"] = True
|
| 136 |
+
env["honesty"] = ("REAL — ECDSA-P256-SHA256 over DSSE PAE; verifiable by "
|
| 137 |
+
"`cosign verify-blob --key cosign.pub` and by the /khipu/verify endpoint.")
|
| 138 |
+
env["verify_key_url"] = PUB_KEY_URL
|
| 139 |
+
return env
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def verify_envelope(env: dict[str, Any]) -> dict[str, Any]:
|
| 143 |
+
"""Verify a DSSE envelope's signature against the SZLHOLDINGS cosign.pub.
|
| 144 |
+
|
| 145 |
+
Recomputes PAE over the embedded payload + payloadType and checks the
|
| 146 |
+
ECDSA signature. Returns a structured verdict (never raises)."""
|
| 147 |
+
out: dict[str, Any] = {"keyid_expected": KEYID, "pub_fingerprint_sha256": public_key_fingerprint(),
|
| 148 |
+
"verify_key_url": PUB_KEY_URL}
|
| 149 |
+
try:
|
| 150 |
+
payload_b64 = env.get("payload")
|
| 151 |
+
payload_type = env.get("payloadType")
|
| 152 |
+
sigs = env.get("signatures") or []
|
| 153 |
+
if not payload_b64 or not payload_type:
|
| 154 |
+
return {**out, "verified": False, "reason": "missing payload/payloadType"}
|
| 155 |
+
if not sigs:
|
| 156 |
+
return {**out, "verified": False, "reason": "no signatures (unsigned envelope)"}
|
| 157 |
+
body = base64.b64decode(payload_b64)
|
| 158 |
+
to_verify = pae(payload_type, body)
|
| 159 |
+
out["pae_sha256"] = hashlib.sha256(to_verify).hexdigest()
|
| 160 |
+
pub = _load_public_key()
|
| 161 |
+
from cryptography.hazmat.primitives.asymmetric import ec
|
| 162 |
+
from cryptography.hazmat.primitives import hashes
|
| 163 |
+
from cryptography.exceptions import InvalidSignature
|
| 164 |
+
results = []
|
| 165 |
+
any_ok = False
|
| 166 |
+
for s in sigs:
|
| 167 |
+
sig_b64 = s.get("sig", "")
|
| 168 |
+
keyid = s.get("keyid", "")
|
| 169 |
+
try:
|
| 170 |
+
sig = base64.b64decode(sig_b64)
|
| 171 |
+
pub.verify(sig, to_verify, ec.ECDSA(hashes.SHA256()))
|
| 172 |
+
results.append({"keyid": keyid, "verified": True})
|
| 173 |
+
any_ok = True
|
| 174 |
+
except InvalidSignature:
|
| 175 |
+
results.append({"keyid": keyid, "verified": False, "reason": "signature mismatch"})
|
| 176 |
+
except Exception as e: # malformed sig
|
| 177 |
+
results.append({"keyid": keyid, "verified": False, "reason": f"{type(e).__name__}"})
|
| 178 |
+
# Optionally decode the payload back for the caller's convenience
|
| 179 |
+
try:
|
| 180 |
+
out["payload_decoded"] = json.loads(body)
|
| 181 |
+
except Exception:
|
| 182 |
+
pass
|
| 183 |
+
return {**out, "verified": any_ok, "signatures": results,
|
| 184 |
+
"payloadType": payload_type}
|
| 185 |
+
except Exception as e:
|
| 186 |
+
return {**out, "verified": False, "reason": f"{type(e).__name__}: {e}"}
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
# ---------------------------------------------------------------------------
|
| 190 |
+
# Convenience: build a full signed Khipu receipt dict
|
| 191 |
+
# ---------------------------------------------------------------------------
|
| 192 |
+
|
| 193 |
+
def sign_khipu_receipt(receipt: dict[str, Any]) -> dict[str, Any]:
|
| 194 |
+
"""Return {receipt, dsse} where dsse is the DSSE envelope over the receipt."""
|
| 195 |
+
env = sign_payload(receipt, KHIPU_PAYLOAD_TYPE)
|
| 196 |
+
return {"receipt": receipt, "dsse": env}
|
szl_provenance.py
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11/v12
|
| 3 |
+
# Authored by Yachay (CTO) — Provenance Hardening layer.
|
| 4 |
+
"""
|
| 5 |
+
szl_provenance — ADDITIVE mesh provenance layer for every SZL Space.
|
| 6 |
+
|
| 7 |
+
Closes two honest-ceiling gaps from session canon:
|
| 8 |
+
|
| 9 |
+
WIRE D (W3C Trace Context, trace continuity) — REAL, per the W3C spec
|
| 10 |
+
(https://www.w3.org/TR/trace-context/):
|
| 11 |
+
* traceparent = 00-<32hex trace-id>-<16hex parent-id>-<2hex flags>
|
| 12 |
+
* On INBOUND: extract a valid incoming traceparent (preserve trace-id),
|
| 13 |
+
else mint a fresh one. Mint a NEW server span-id for THIS hop (the
|
| 14 |
+
spec's default "update parent-id" mutation) and remember the inbound
|
| 15 |
+
parent so we can chain.
|
| 16 |
+
* tracestate is parsed, preserved, and our own `szl` entry is written to
|
| 17 |
+
the LEFT (per spec mutation rules), recording this Space's span.
|
| 18 |
+
* On OUTBOUND cross-Space calls, `outgoing_headers()` propagates the
|
| 19 |
+
SAME trace-id with a fresh child span + updated tracestate so the
|
| 20 |
+
trace is continuous across Spaces (trace continuity wire).
|
| 21 |
+
* Every Khipu receipt now carries the W3C `traceparent` of its origin span.
|
| 22 |
+
* /api/<space>/wires/D — current trace volume + active spans on this Space.
|
| 23 |
+
|
| 24 |
+
DSSE + COSIGN (signed provenance, SLSA L2) — REAL, replaces PLACEHOLDER:
|
| 25 |
+
* Every Khipu receipt is signed with a DSSE envelope using the SZLHOLDINGS
|
| 26 |
+
Cosign key (szl_dsse.sign_khipu_receipt). payloadType =
|
| 27 |
+
"application/vnd.szl.khipu+json"; signatures=[{sig,keyid:szlholdings-cosign}].
|
| 28 |
+
* /api/<space>/khipu/verify — validates a DSSE envelope (or {receipt,dsse})
|
| 29 |
+
against the published cosign.pub.
|
| 30 |
+
* /api/<space>/khipu/sign — sign an arbitrary receipt (demo/smoke).
|
| 31 |
+
* The signing system signs itself: every sign/verify op emits its own
|
| 32 |
+
Khipu receipt into the DAG (self-attesting provenance).
|
| 33 |
+
|
| 34 |
+
HONESTY:
|
| 35 |
+
* Trace IDs are real W3C ids, generated + propagated + CHAINED across Spaces
|
| 36 |
+
via header propagation (no external collector required — the trace context
|
| 37 |
+
itself is the continuity mechanism the spec defines).
|
| 38 |
+
* Signatures are REAL ECDSA-P256-SHA256 cosign sigs when the
|
| 39 |
+
SZL_COSIGN_PRIVATE_PEM runtime secret is present; if absent, receipts are
|
| 40 |
+
emitted UNSIGNED and clearly labelled (never faked).
|
| 41 |
+
* Khipu DAG is in-memory per Space (additive, non-persistent across restart) —
|
| 42 |
+
same honest ceiling as before; now SIGNED.
|
| 43 |
+
* SLSA self-claim is L2 (signed provenance), NOT L3 (no hardened CI yet).
|
| 44 |
+
"""
|
| 45 |
+
from __future__ import annotations
|
| 46 |
+
|
| 47 |
+
import os
|
| 48 |
+
import threading
|
| 49 |
+
from collections import deque
|
| 50 |
+
from datetime import datetime, timezone
|
| 51 |
+
from typing import Any
|
| 52 |
+
|
| 53 |
+
from fastapi import Request
|
| 54 |
+
from fastapi.responses import JSONResponse
|
| 55 |
+
|
| 56 |
+
import szl_dsse
|
| 57 |
+
|
| 58 |
+
DOCTRINE = "v12"
|
| 59 |
+
SLSA_LEVEL = "L2"
|
| 60 |
+
|
| 61 |
+
# ---------------------------------------------------------------------------
|
| 62 |
+
# Wire D — W3C Trace Context
|
| 63 |
+
# ---------------------------------------------------------------------------
|
| 64 |
+
|
| 65 |
+
def _rand_hex(nbytes: int) -> str:
|
| 66 |
+
return os.urandom(nbytes).hex()
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def new_trace_id() -> str:
|
| 70 |
+
tid = _rand_hex(16)
|
| 71 |
+
return tid if tid != "0" * 32 else _rand_hex(16)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def new_span_id() -> str:
|
| 75 |
+
sid = _rand_hex(8)
|
| 76 |
+
return sid if sid != "0" * 16 else _rand_hex(8)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def new_traceparent() -> str:
|
| 80 |
+
return f"00-{new_trace_id()}-{new_span_id()}-01"
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def parse_traceparent(tp: str | None) -> dict[str, Any]:
|
| 84 |
+
if not tp or tp.count("-") != 3:
|
| 85 |
+
return {"valid": False, "raw": tp}
|
| 86 |
+
ver, trace_id, span_id, flags = tp.split("-")
|
| 87 |
+
hexset = set("0123456789abcdef")
|
| 88 |
+
valid = (len(ver) == 2 and len(trace_id) == 32 and len(span_id) == 16 and len(flags) == 2
|
| 89 |
+
and set(trace_id) <= hexset and set(span_id) <= hexset
|
| 90 |
+
and trace_id != "0" * 32 and span_id != "0" * 16 and ver != "ff")
|
| 91 |
+
return {"valid": valid, "version": ver, "trace_id": trace_id,
|
| 92 |
+
"parent_id": span_id, "span_id": span_id, "flags": flags, "raw": tp}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def parse_tracestate(ts: str | None) -> list[tuple[str, str]]:
|
| 96 |
+
"""Parse tracestate into ordered (key, value) list-members (max 32)."""
|
| 97 |
+
out: list[tuple[str, str]] = []
|
| 98 |
+
if not ts:
|
| 99 |
+
return out
|
| 100 |
+
for member in ts.split(","):
|
| 101 |
+
member = member.strip()
|
| 102 |
+
if not member or "=" not in member:
|
| 103 |
+
continue
|
| 104 |
+
k, _, v = member.partition("=")
|
| 105 |
+
out.append((k.strip(), v.strip()))
|
| 106 |
+
if len(out) >= 32:
|
| 107 |
+
break
|
| 108 |
+
return out
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def mutate_tracestate(existing: list[tuple[str, str]], span_id: str) -> str:
|
| 112 |
+
"""Per W3C: write our `szl` entry to the LEFT, preserve other entries' order,
|
| 113 |
+
drop any prior `szl` entry (overwrite-on-reentry rule)."""
|
| 114 |
+
kept = [(k, v) for (k, v) in existing if k != "szl"]
|
| 115 |
+
members = [("szl", span_id)] + kept
|
| 116 |
+
members = members[:32]
|
| 117 |
+
return ",".join(f"{k}={v}" for k, v in members)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class _TraceState:
|
| 121 |
+
"""Per-Space in-memory trace ledger (Wire D trace continuity)."""
|
| 122 |
+
|
| 123 |
+
def __init__(self) -> None:
|
| 124 |
+
self.lock = threading.Lock()
|
| 125 |
+
self.log: deque[dict[str, Any]] = deque(maxlen=200)
|
| 126 |
+
self.active_spans: dict[str, dict[str, Any]] = {} # span_id -> meta
|
| 127 |
+
self.trace_volume = 0 # total spans observed since boot
|
| 128 |
+
self.traces_seen: set[str] = set()
|
| 129 |
+
|
| 130 |
+
def record_inbound(self, trace_id: str, span_id: str, parent_inbound: str | None,
|
| 131 |
+
path: str) -> None:
|
| 132 |
+
with self.lock:
|
| 133 |
+
self.trace_volume += 1
|
| 134 |
+
self.traces_seen.add(trace_id)
|
| 135 |
+
self.active_spans[span_id] = {
|
| 136 |
+
"span_id": span_id, "trace_id": trace_id,
|
| 137 |
+
"inbound_parent": parent_inbound, "path": path,
|
| 138 |
+
"started_utc": datetime.now(timezone.utc).isoformat(),
|
| 139 |
+
"direction": "in",
|
| 140 |
+
}
|
| 141 |
+
self.log.append({"trace_id": trace_id, "span_id": span_id,
|
| 142 |
+
"parent": parent_inbound, "path": path, "dir": "in",
|
| 143 |
+
"ts_utc": datetime.now(timezone.utc).isoformat()})
|
| 144 |
+
# bound active span set
|
| 145 |
+
if len(self.active_spans) > 200:
|
| 146 |
+
for k in list(self.active_spans)[:50]:
|
| 147 |
+
self.active_spans.pop(k, None)
|
| 148 |
+
|
| 149 |
+
def record_outbound(self, trace_id: str, span_id: str, parent: str, target: str) -> None:
|
| 150 |
+
with self.lock:
|
| 151 |
+
self.trace_volume += 1
|
| 152 |
+
self.log.append({"trace_id": trace_id, "span_id": span_id, "parent": parent,
|
| 153 |
+
"target": target, "dir": "out",
|
| 154 |
+
"ts_utc": datetime.now(timezone.utc).isoformat()})
|
| 155 |
+
|
| 156 |
+
def end_span(self, span_id: str) -> None:
|
| 157 |
+
with self.lock:
|
| 158 |
+
self.active_spans.pop(span_id, None)
|
| 159 |
+
|
| 160 |
+
def snapshot(self) -> dict[str, Any]:
|
| 161 |
+
with self.lock:
|
| 162 |
+
return {
|
| 163 |
+
"trace_volume": self.trace_volume,
|
| 164 |
+
"distinct_traces": len(self.traces_seen),
|
| 165 |
+
"active_span_count": len(self.active_spans),
|
| 166 |
+
"active_spans": list(self.active_spans.values())[-25:],
|
| 167 |
+
"recent": list(self.log)[-25:],
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ---------------------------------------------------------------------------
|
| 172 |
+
# Khipu DAG (DSSE-signed)
|
| 173 |
+
# ---------------------------------------------------------------------------
|
| 174 |
+
|
| 175 |
+
class _KhipuDAG:
|
| 176 |
+
def __init__(self, space: str) -> None:
|
| 177 |
+
self.space = space
|
| 178 |
+
self.lock = threading.Lock()
|
| 179 |
+
self.nodes: list[dict[str, Any]] = []
|
| 180 |
+
|
| 181 |
+
def _digest(self, payload_b: bytes, parents: list[str]) -> str:
|
| 182 |
+
import hashlib
|
| 183 |
+
h = hashlib.sha256()
|
| 184 |
+
h.update(payload_b)
|
| 185 |
+
for p in parents:
|
| 186 |
+
h.update(p.encode())
|
| 187 |
+
return h.hexdigest()
|
| 188 |
+
|
| 189 |
+
def append_signed(self, receipt: dict[str, Any]) -> dict[str, Any]:
|
| 190 |
+
"""Sign the receipt with DSSE+Cosign and append as a Merkle DAG node."""
|
| 191 |
+
env = szl_dsse.sign_payload(receipt, szl_dsse.KHIPU_PAYLOAD_TYPE)
|
| 192 |
+
body = szl_dsse.canonical_json(receipt)
|
| 193 |
+
with self.lock:
|
| 194 |
+
parents = [self.nodes[-1]["digest"]] if self.nodes else []
|
| 195 |
+
node = {
|
| 196 |
+
"index": len(self.nodes),
|
| 197 |
+
"space": self.space,
|
| 198 |
+
"wire": "F",
|
| 199 |
+
"receipt": receipt,
|
| 200 |
+
"dsse": env,
|
| 201 |
+
"parents": parents,
|
| 202 |
+
"signed": env.get("signed", False),
|
| 203 |
+
"keyid": (env["signatures"][0]["keyid"] if env.get("signatures") else None),
|
| 204 |
+
"slsa": SLSA_LEVEL,
|
| 205 |
+
"doctrine": DOCTRINE,
|
| 206 |
+
"ts_utc": datetime.now(timezone.utc).isoformat(),
|
| 207 |
+
}
|
| 208 |
+
node["digest"] = self._digest(body, parents)
|
| 209 |
+
self.nodes.append(node)
|
| 210 |
+
return node
|
| 211 |
+
|
| 212 |
+
def root(self) -> str | None:
|
| 213 |
+
with self.lock:
|
| 214 |
+
return self.nodes[-1]["digest"] if self.nodes else None
|
| 215 |
+
|
| 216 |
+
def recent(self, n: int = 10) -> list[dict[str, Any]]:
|
| 217 |
+
with self.lock:
|
| 218 |
+
return self.nodes[-n:]
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
# ---------------------------------------------------------------------------
|
| 222 |
+
# Public registration
|
| 223 |
+
# ---------------------------------------------------------------------------
|
| 224 |
+
|
| 225 |
+
def register_provenance(app, space: str) -> dict[str, Any]:
|
| 226 |
+
"""ADDITIVE: install Wire D middleware + /wires/D + /khipu/{sign,verify,ledger}.
|
| 227 |
+
|
| 228 |
+
Returns a status dict. Wrapped by callers in try/except so a failure can
|
| 229 |
+
never take down the existing app."""
|
| 230 |
+
tstate = _TraceState()
|
| 231 |
+
dag = _KhipuDAG(space)
|
| 232 |
+
base = f"/api/{space}"
|
| 233 |
+
|
| 234 |
+
# ---- Wire D middleware: extract/mint traceparent, mint server span, echo ----
|
| 235 |
+
@app.middleware("http")
|
| 236 |
+
async def _wire_d_mw(request: Request, call_next): # noqa: ANN001
|
| 237 |
+
incoming = request.headers.get("traceparent")
|
| 238 |
+
parsed = parse_traceparent(incoming)
|
| 239 |
+
ts_in = parse_tracestate(request.headers.get("tracestate"))
|
| 240 |
+
if parsed.get("valid"):
|
| 241 |
+
trace_id = parsed["trace_id"]
|
| 242 |
+
inbound_parent = parsed["parent_id"]
|
| 243 |
+
else:
|
| 244 |
+
trace_id = new_trace_id()
|
| 245 |
+
inbound_parent = None
|
| 246 |
+
ts_in = [] # invalid traceparent => discard tracestate (per spec)
|
| 247 |
+
server_span = new_span_id() # this Space's span for THIS request (mutate parent-id)
|
| 248 |
+
tp = f"00-{trace_id}-{server_span}-01"
|
| 249 |
+
ts_out = mutate_tracestate(ts_in, server_span)
|
| 250 |
+
# stash for outgoing propagation + receipt stamping
|
| 251 |
+
request.state.traceparent = tp
|
| 252 |
+
request.state.trace_id = trace_id
|
| 253 |
+
request.state.span_id = server_span
|
| 254 |
+
request.state.inbound_parent = inbound_parent
|
| 255 |
+
request.state.tracestate = ts_out
|
| 256 |
+
path = request.url.path
|
| 257 |
+
if not path.startswith("/assets"):
|
| 258 |
+
tstate.record_inbound(trace_id, server_span, inbound_parent, path)
|
| 259 |
+
resp = await call_next(request)
|
| 260 |
+
tstate.end_span(server_span)
|
| 261 |
+
resp.headers["traceparent"] = tp
|
| 262 |
+
if ts_out:
|
| 263 |
+
resp.headers["tracestate"] = ts_out
|
| 264 |
+
resp.headers["x-szl-space"] = space
|
| 265 |
+
resp.headers["x-szl-wire-d"] = "LIVE"
|
| 266 |
+
return resp
|
| 267 |
+
|
| 268 |
+
# ---- helpers exposed on app.state for the host app to use ----
|
| 269 |
+
def outgoing_headers(request, target_space: str | None = None) -> dict[str, str]:
|
| 270 |
+
"""Propagate the trace to a cross-Space call (Wire D continuity).
|
| 271 |
+
Mints a fresh CHILD span for the outbound hop, preserves trace-id +
|
| 272 |
+
tracestate so the receiving Space continues the same trace."""
|
| 273 |
+
st = getattr(request, "state", None)
|
| 274 |
+
trace_id = getattr(st, "trace_id", None) or new_trace_id()
|
| 275 |
+
child = new_span_id()
|
| 276 |
+
ts = getattr(st, "tracestate", "") or ""
|
| 277 |
+
ts_members = parse_tracestate(ts)
|
| 278 |
+
ts_out = mutate_tracestate(ts_members, child)
|
| 279 |
+
tp = f"00-{trace_id}-{child}-01"
|
| 280 |
+
tstate.record_outbound(trace_id, child, getattr(st, "span_id", None) or "", target_space or "peer")
|
| 281 |
+
hdrs = {"traceparent": tp}
|
| 282 |
+
if ts_out:
|
| 283 |
+
hdrs["tracestate"] = ts_out
|
| 284 |
+
return hdrs
|
| 285 |
+
|
| 286 |
+
def receipt_trace_fields(request) -> dict[str, Any]:
|
| 287 |
+
"""The W3C trace fields to stamp onto a Khipu receipt (origin span)."""
|
| 288 |
+
st = getattr(request, "state", None)
|
| 289 |
+
return {
|
| 290 |
+
"traceparent": getattr(st, "traceparent", None) or new_traceparent(),
|
| 291 |
+
"trace_id": getattr(st, "trace_id", None),
|
| 292 |
+
"span_id": getattr(st, "span_id", None),
|
| 293 |
+
"wire_d": "LIVE",
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
def emit_signed_receipt(receipt: dict[str, Any], request=None) -> dict[str, Any]:
|
| 297 |
+
"""Stamp Wire D trace fields + DSSE-sign + append to the Khipu DAG."""
|
| 298 |
+
r = dict(receipt)
|
| 299 |
+
r.setdefault("space", space)
|
| 300 |
+
r.setdefault("doctrine", DOCTRINE)
|
| 301 |
+
r.setdefault("slsa", SLSA_LEVEL)
|
| 302 |
+
r.setdefault("ts_utc", datetime.now(timezone.utc).isoformat())
|
| 303 |
+
if request is not None:
|
| 304 |
+
r.update(receipt_trace_fields(request))
|
| 305 |
+
else:
|
| 306 |
+
r.setdefault("traceparent", new_traceparent())
|
| 307 |
+
return dag.append_signed(r)
|
| 308 |
+
|
| 309 |
+
app.state.szl_outgoing_headers = outgoing_headers
|
| 310 |
+
app.state.szl_emit_signed_receipt = emit_signed_receipt
|
| 311 |
+
app.state.szl_khipu_dag = dag
|
| 312 |
+
app.state.szl_trace = tstate
|
| 313 |
+
|
| 314 |
+
# ---- /wires/D : trace volume + active spans ----
|
| 315 |
+
@app.get(f"{base}/wires/D")
|
| 316 |
+
async def wire_d_status(request: Request) -> JSONResponse:
|
| 317 |
+
snap = tstate.snapshot()
|
| 318 |
+
return JSONResponse({
|
| 319 |
+
"wire": "D",
|
| 320 |
+
"name": "W3C traceparent \u2014 trace continuity",
|
| 321 |
+
"space": space,
|
| 322 |
+
"status": "LIVE",
|
| 323 |
+
"spec": "https://www.w3.org/TR/trace-context/",
|
| 324 |
+
"format": "00-<32hex trace-id>-<16hex span-id>-<2hex flags>",
|
| 325 |
+
"current_request_traceparent": getattr(request.state, "traceparent", None),
|
| 326 |
+
"current_request_tracestate": getattr(request.state, "tracestate", None),
|
| 327 |
+
**snap,
|
| 328 |
+
"cross_space": ("Trace continuity is propagated via the traceparent + tracestate "
|
| 329 |
+
"headers on cross-Space calls (preserve trace-id, mint child span, "
|
| 330 |
+
"rewrite tracestate left-most). Every Khipu receipt carries the "
|
| 331 |
+
"origin span's traceparent."),
|
| 332 |
+
"doctrine": DOCTRINE,
|
| 333 |
+
})
|
| 334 |
+
|
| 335 |
+
# ---- /khipu/verify : validate a DSSE signature against cosign.pub ----
|
| 336 |
+
@app.post(f"{base}/khipu/verify")
|
| 337 |
+
async def khipu_verify(request: Request) -> JSONResponse: # noqa: ANN001
|
| 338 |
+
try:
|
| 339 |
+
payload = await request.json()
|
| 340 |
+
except Exception:
|
| 341 |
+
return JSONResponse({"verified": False, "reason": "invalid JSON body"}, status_code=400)
|
| 342 |
+
# Accept either a raw DSSE envelope or {receipt, dsse}
|
| 343 |
+
env = payload.get("dsse") if isinstance(payload, dict) and "dsse" in payload else payload
|
| 344 |
+
verdict = szl_dsse.verify_envelope(env if isinstance(env, dict) else {})
|
| 345 |
+
# the verifier signs itself (self-attesting): emit a Khipu receipt of the verify op
|
| 346 |
+
node = emit_signed_receipt({
|
| 347 |
+
"schema": "szl.khipu.verify_op/v1",
|
| 348 |
+
"op": "khipu/verify",
|
| 349 |
+
"verified": verdict.get("verified"),
|
| 350 |
+
"keyid_expected": szl_dsse.KEYID,
|
| 351 |
+
}, request)
|
| 352 |
+
return JSONResponse({**verdict, "verify_receipt_digest": node["digest"],
|
| 353 |
+
"verify_receipt_signed": node["signed"], "space": space})
|
| 354 |
+
|
| 355 |
+
# ---- /khipu/sign : sign an arbitrary receipt (smoke / demo) ----
|
| 356 |
+
@app.post(f"{base}/khipu/sign")
|
| 357 |
+
async def khipu_sign(request: Request) -> JSONResponse: # noqa: ANN001
|
| 358 |
+
try:
|
| 359 |
+
body = await request.json()
|
| 360 |
+
except Exception:
|
| 361 |
+
body = {}
|
| 362 |
+
receipt = body.get("receipt", body) if isinstance(body, dict) else {}
|
| 363 |
+
if not isinstance(receipt, dict) or not receipt:
|
| 364 |
+
receipt = {"schema": "szl.khipu.demo/v1", "note": "empty body — demo receipt"}
|
| 365 |
+
node = emit_signed_receipt(receipt, request)
|
| 366 |
+
return JSONResponse({
|
| 367 |
+
"space": space, "digest": node["digest"], "index": node["index"],
|
| 368 |
+
"signed": node["signed"], "keyid": node["keyid"], "slsa": SLSA_LEVEL,
|
| 369 |
+
"dsse": node["dsse"], "traceparent": node["receipt"].get("traceparent"),
|
| 370 |
+
"verify_at": f"{base}/khipu/verify",
|
| 371 |
+
})
|
| 372 |
+
|
| 373 |
+
# ---- /khipu/ledger : the signed Khipu DAG ----
|
| 374 |
+
@app.get(f"{base}/khipu/ledger")
|
| 375 |
+
async def khipu_ledger() -> JSONResponse:
|
| 376 |
+
nodes = dag.recent(20)
|
| 377 |
+
return JSONResponse({
|
| 378 |
+
"space": space, "khipu_root": dag.root(), "count": len(dag.nodes),
|
| 379 |
+
"signing_available": szl_dsse.signing_available(),
|
| 380 |
+
"keyid": szl_dsse.KEYID, "slsa": SLSA_LEVEL, "doctrine": DOCTRINE,
|
| 381 |
+
"pub_fingerprint_sha256": szl_dsse.public_key_fingerprint(),
|
| 382 |
+
"verify_key_url": szl_dsse.PUB_KEY_URL,
|
| 383 |
+
"nodes": nodes,
|
| 384 |
+
"honesty": ("DSSE signatures are REAL ECDSA-P256-SHA256 cosign sigs when the "
|
| 385 |
+
"SZL_COSIGN_PRIVATE_PEM runtime secret is present (else UNSIGNED, labelled). "
|
| 386 |
+
"DAG is in-memory per Space (non-persistent across restart). SLSA L2 "
|
| 387 |
+
"(signed provenance) — NOT L3 (no hardened CI yet)."),
|
| 388 |
+
})
|
| 389 |
+
|
| 390 |
+
# ---- /provenance : combined honest board for this Space ----
|
| 391 |
+
@app.get(f"{base}/provenance")
|
| 392 |
+
async def provenance_board() -> JSONResponse:
|
| 393 |
+
return JSONResponse({
|
| 394 |
+
"space": space, "doctrine": DOCTRINE, "slsa": SLSA_LEVEL,
|
| 395 |
+
"wire_D": {"status": "LIVE", "name": "W3C traceparent trace continuity",
|
| 396 |
+
"endpoint": f"{base}/wires/D"},
|
| 397 |
+
"khipu_dsse": {"signing_available": szl_dsse.signing_available(),
|
| 398 |
+
"keyid": szl_dsse.KEYID,
|
| 399 |
+
"payloadType": szl_dsse.KHIPU_PAYLOAD_TYPE,
|
| 400 |
+
"verify_endpoint": f"{base}/khipu/verify",
|
| 401 |
+
"pub_key_url": szl_dsse.PUB_KEY_URL},
|
| 402 |
+
"slsa_note": ("L2 = signed provenance via DSSE+Cosign (now real). L3 would require "
|
| 403 |
+
"a hardened, isolated build pipeline (UDS Core) which is NOT yet in place — "
|
| 404 |
+
"honestly L2, not L3."),
|
| 405 |
+
"self_attesting": "every sign/verify op emits its own DSSE-signed Khipu receipt.",
|
| 406 |
+
})
|
| 407 |
+
|
| 408 |
+
return {"space": space, "wire_D": "LIVE", "slsa": SLSA_LEVEL,
|
| 409 |
+
"signing_available": szl_dsse.signing_available(),
|
| 410 |
+
"endpoints": [f"{base}/wires/D", f"{base}/khipu/verify",
|
| 411 |
+
f"{base}/khipu/sign", f"{base}/khipu/ledger", f"{base}/provenance"]}
|