proof-aware-crypto-tooling-.../provider/src/pacta_provider/web.py

166 lines
7 KiB
Python
Raw Normal View History

The log goes public: git-published mirror, online service, witnesses Three synchronized faces of one log - transport orthogonal to trust: - PUBLISHED GIT MIRROR: log-publish exports the public face (one file per leaf so git history mirrors log history; the FULL STH history as the witness channel; per-component attestations + receipts; the provider public key; a standalone stdlib-only verify.py and customer README). Live at github.com/saymrwulf/lean-transparency-log (genesis: 8 leaves incl. the honest failed-run entries, dogfood-signed head). - ONLINE SERVICE (pacta_provider serve): read-only, zero-dependency HTTP with CT-style endpoints under a base path for zkdefi.org/lean-transparency-log - /v1/sth, /v1/sth-history, /v1/sth-consistency?first=N, /v1/proof, /v1/attestation, /v1/entries, /v1/metadata, /healthz - plus self-contained customer documentation at /docs (current state, attested components, API, the verify- without-trusting-this-site path, and the means/does-NOT-mean boundary). The process never loads private keys: heads are signed offline; a compromised server can withhold or replay (pinning + freshness detect both) but never forge. STH history now recorded append-only by the provider (with a backfill head signed for the existing log). - AGENT ONLINE CLIENT: pacta log-fetch (download evidence; explicitly UNVERIFIED until receipt-verify runs - transport is not trust) and pacta sth-refresh (fetch head, verify signature, advance the pin via an online consistency proof from the pinned size; fail closed). - WITNESSES: pacta witness-audit over a clone of the published mirror recomputes every prefix root from the public leaves and checks every historical head + signature - no consistency proofs needed when the leaves are public. Tampering one published entry trips both the leaf-hash check and the prefix-root check (tested). verify.py gives customers the same audit with zero installation. - DEPLOY.md: the complete server-session checklist for zkdefi.org - reconstruct the servable log FROM the published mirror (the server stays in witness trust-position), hardened systemd unit, nginx/Caddy path routing, Forgejo mirror setup, the provider->world update cycle, and remote smoke tests. Validated end-to-end on the REAL log: all 10 endpoints, online-fetched proof re-verified locally through the dogfood verifier with pinning, online pin refresh, publish + witness audit green, tamper caught, standalone verify.py green in the published clone. 54/54 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:05:20 +00:00
"""The online face of the transparency log: a READ-ONLY, zero-dependency
HTTP service exposing CT-style endpoints under a base path (deployed at
ltl.zkdefi.org behind a reverse proxy).
The log goes public: git-published mirror, online service, witnesses Three synchronized faces of one log - transport orthogonal to trust: - PUBLISHED GIT MIRROR: log-publish exports the public face (one file per leaf so git history mirrors log history; the FULL STH history as the witness channel; per-component attestations + receipts; the provider public key; a standalone stdlib-only verify.py and customer README). Live at github.com/saymrwulf/lean-transparency-log (genesis: 8 leaves incl. the honest failed-run entries, dogfood-signed head). - ONLINE SERVICE (pacta_provider serve): read-only, zero-dependency HTTP with CT-style endpoints under a base path for zkdefi.org/lean-transparency-log - /v1/sth, /v1/sth-history, /v1/sth-consistency?first=N, /v1/proof, /v1/attestation, /v1/entries, /v1/metadata, /healthz - plus self-contained customer documentation at /docs (current state, attested components, API, the verify- without-trusting-this-site path, and the means/does-NOT-mean boundary). The process never loads private keys: heads are signed offline; a compromised server can withhold or replay (pinning + freshness detect both) but never forge. STH history now recorded append-only by the provider (with a backfill head signed for the existing log). - AGENT ONLINE CLIENT: pacta log-fetch (download evidence; explicitly UNVERIFIED until receipt-verify runs - transport is not trust) and pacta sth-refresh (fetch head, verify signature, advance the pin via an online consistency proof from the pinned size; fail closed). - WITNESSES: pacta witness-audit over a clone of the published mirror recomputes every prefix root from the public leaves and checks every historical head + signature - no consistency proofs needed when the leaves are public. Tampering one published entry trips both the leaf-hash check and the prefix-root check (tested). verify.py gives customers the same audit with zero installation. - DEPLOY.md: the complete server-session checklist for zkdefi.org - reconstruct the servable log FROM the published mirror (the server stays in witness trust-position), hardened systemd unit, nginx/Caddy path routing, Forgejo mirror setup, the provider->world update cycle, and remote smoke tests. Validated end-to-end on the REAL log: all 10 endpoints, online-fetched proof re-verified locally through the dogfood verifier with pinning, online pin refresh, publish + witness audit green, tamper caught, standalone verify.py green in the published clone. 54/54 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:05:20 +00:00
Security posture: this process never loads a private key. Tree heads are
signed OFFLINE by the provider CLI (log-append / log-sth); the service
serves stored, already-signed material. Compromise of the web process can
therefore withhold or replay data (which agents detect via pinning and
freshness policies) but can never forge a signature.
"""
from __future__ import annotations
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Any
from urllib.parse import parse_qs, urlparse
from .transparency_log import TransparencyLog
API_VERSION = "v1"
def make_handler(log: TransparencyLog, base_path: str, docs_html: str):
base = "/" + base_path.strip("/") if base_path.strip("/") else ""
class Handler(BaseHTTPRequestHandler):
server_version = "pacta-log/1"
def do_GET(self) -> None: # noqa: N802 (stdlib API)
try:
self._route()
except Exception as exc: # noqa: BLE001 - the service must not die on a bad request
self._send(500, {"error": f"internal error: {type(exc).__name__}"})
def _route(self) -> None:
parsed = urlparse(self.path)
path = parsed.path.rstrip("/")
query = {key: values[0] for key, values in parse_qs(parsed.query).items()}
if not path.startswith(base):
self._send(404, {"error": "unknown path", "base_path": base or "/"})
return
route = path[len(base):] or "/"
if route in ("/", "/docs"):
self._send_html(docs_html)
elif route == "/healthz":
self._send(200, {"ok": True, "tree_size": len(log.entries())})
elif route == f"/{API_VERSION}/metadata":
self._send(200, log.metadata())
elif route == f"/{API_VERSION}/sth":
history = log.sth_history()
if history:
self._send(200, history[-1])
else:
from pacta.yamlio import load_data
if log.sth_path.exists():
self._send(200, load_data(log.sth_path))
else:
self._send(404, {"error": "no signed tree head yet"})
elif route == f"/{API_VERSION}/sth-history":
self._send(200, {"sth_history": log.sth_history()})
elif route == f"/{API_VERSION}/sth-consistency":
first = int(query.get("first", "-1"))
if first < 0:
self._send(400, {"error": "missing or invalid ?first=<old tree size>"})
return
try:
self._send(200, log.consistency_from(first))
except ValueError as exc:
self._send(400, {"error": str(exc)})
elif route == f"/{API_VERSION}/proof":
leaf_hash = query.get("leaf_hash")
component = query.get("component")
if component and not leaf_hash:
entry = log.newest_entry_for_component(component)
leaf_hash = entry.leaf_hash if entry else None
if not leaf_hash:
self._send(400, {"error": "supply ?leaf_hash=<hex> or ?component=<name>"})
return
proof = log.proof_for_leaf_hash(leaf_hash)
if proof is None:
self._send(404, {"error": f"no leaf with hash {leaf_hash}"})
return
self._send(200, proof)
elif route == f"/{API_VERSION}/attestation":
component = query.get("component")
if not component:
self._send(400, {"error": "supply ?component=<name>"})
return
entry = log.newest_entry_for_component(component)
if entry is None:
self._send(404, {"error": f"no attestation for component {component}"})
return
self._send(200, {
"leaf_index": entry.index,
"leaf_hash": entry.leaf_hash,
"attestation": entry.leaf.get("attestation"),
})
elif route == f"/{API_VERSION}/entries":
start = int(query.get("start", "0"))
end = int(query.get("end", str(len(log.entries()))))
entries = log.entries()[start:end]
self._send(200, {
"entries": [
{"index": entry.index, "leaf_hash": entry.leaf_hash, "leaf": entry.leaf}
for entry in entries
]
})
else:
self._send(404, {
"error": "unknown endpoint",
"endpoints": [
f"{base}/docs",
f"{base}/healthz",
f"{base}/{API_VERSION}/metadata",
f"{base}/{API_VERSION}/sth",
f"{base}/{API_VERSION}/sth-history",
f"{base}/{API_VERSION}/sth-consistency?first=N",
f"{base}/{API_VERSION}/proof?component=NAME | ?leaf_hash=HEX",
f"{base}/{API_VERSION}/attestation?component=NAME",
f"{base}/{API_VERSION}/entries?start=N&end=M",
],
})
def _send(self, code: int, payload: dict[str, Any], code_if_error: int | None = None) -> None:
if code_if_error and "error" in payload:
code = code_if_error
body = json.dumps(payload, indent=2, sort_keys=True).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(body)
def _send_html(self, html: str) -> None:
body = html.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, fmt: str, *args: Any) -> None: # quiet by default
pass
return Handler
def serve(
log_dir: str,
base_path: str = "",
The log goes public: git-published mirror, online service, witnesses Three synchronized faces of one log - transport orthogonal to trust: - PUBLISHED GIT MIRROR: log-publish exports the public face (one file per leaf so git history mirrors log history; the FULL STH history as the witness channel; per-component attestations + receipts; the provider public key; a standalone stdlib-only verify.py and customer README). Live at github.com/saymrwulf/lean-transparency-log (genesis: 8 leaves incl. the honest failed-run entries, dogfood-signed head). - ONLINE SERVICE (pacta_provider serve): read-only, zero-dependency HTTP with CT-style endpoints under a base path for zkdefi.org/lean-transparency-log - /v1/sth, /v1/sth-history, /v1/sth-consistency?first=N, /v1/proof, /v1/attestation, /v1/entries, /v1/metadata, /healthz - plus self-contained customer documentation at /docs (current state, attested components, API, the verify- without-trusting-this-site path, and the means/does-NOT-mean boundary). The process never loads private keys: heads are signed offline; a compromised server can withhold or replay (pinning + freshness detect both) but never forge. STH history now recorded append-only by the provider (with a backfill head signed for the existing log). - AGENT ONLINE CLIENT: pacta log-fetch (download evidence; explicitly UNVERIFIED until receipt-verify runs - transport is not trust) and pacta sth-refresh (fetch head, verify signature, advance the pin via an online consistency proof from the pinned size; fail closed). - WITNESSES: pacta witness-audit over a clone of the published mirror recomputes every prefix root from the public leaves and checks every historical head + signature - no consistency proofs needed when the leaves are public. Tampering one published entry trips both the leaf-hash check and the prefix-root check (tested). verify.py gives customers the same audit with zero installation. - DEPLOY.md: the complete server-session checklist for zkdefi.org - reconstruct the servable log FROM the published mirror (the server stays in witness trust-position), hardened systemd unit, nginx/Caddy path routing, Forgejo mirror setup, the provider->world update cycle, and remote smoke tests. Validated end-to-end on the REAL log: all 10 endpoints, online-fetched proof re-verified locally through the dogfood verifier with pinning, online pin refresh, publish + witness audit green, tamper caught, standalone verify.py green in the published clone. 54/54 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:05:20 +00:00
host: str = "127.0.0.1",
port: int = 8461,
docs_html: str | None = None,
) -> ThreadingHTTPServer:
log = TransparencyLog(log_dir)
log.metadata() # fail fast if the log is not initialized
if docs_html is None:
from .webdocs import render_docs
docs_html = render_docs(log, base_path)
handler = make_handler(log, base_path, docs_html)
return ThreadingHTTPServer((host, port), handler)