diff --git a/DEPLOY.md b/DEPLOY.md index e51e99d..f79a186 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,4 +1,4 @@ -# Deploying the online log at zkdefi.org/lean-transparency-log +# Deploying the online log at ltl.zkdefi.org STATUS: DEPLOYED (2026-07-06) and serving. This file is now the as-built record plus the update runbook. Deliberately generic about the host: it @@ -101,7 +101,7 @@ After=network.target User=pacta WorkingDirectory=/srv/pacta/app Environment=PYTHONPATH=/srv/pacta/app/src:/srv/pacta/app/provider/src -ExecStart=/usr/bin/python3 -m pacta_provider serve --log-dir /srv/pacta/log --base-path lean-transparency-log --host 127.0.0.1 --port 8461 +ExecStart=/usr/bin/python3 -m pacta_provider serve --log-dir /srv/pacta/log --host 127.0.0.1 --port 8461 Restart=on-failure # hardening: read-only service, no key material anywhere near it ProtectSystem=strict @@ -115,16 +115,21 @@ WantedBy=multi-user.target ```bash sudo systemctl daemon-reload && sudo systemctl enable --now pacta-log -curl -s http://127.0.0.1:8461/lean-transparency-log/healthz +curl -s http://127.0.0.1:8461/healthz ``` -## 3. Reverse proxy on zkdefi.org +The service serves from the root path by default (it lives on its own +subdomain); pass `--base-path ` only if you must mount it under +a path instead. -Caddy (inside the existing `zkdefi.org` site block): +## 3. Reverse proxy: ltl.zkdefi.org + +DNS: an `A` record for `ltl` pointing at the same host (or a `CNAME` to +the apex). Caddy then gets its own site block and handles the +certificate automatically: ```caddy -redir /lean-transparency-log /lean-transparency-log/docs -route /lean-transparency-log/* { +ltl.zkdefi.org { reverse_proxy 127.0.0.1:8461 { transport http { response_header_timeout 15s @@ -133,21 +138,34 @@ route /lean-transparency-log/* { } ``` +If the log was ever served under a path (the original deployment used +`zkdefi.org/lean-transparency-log`), keep a permanent redirect in the +old site block so published links stay alive: + +```caddy +redir /lean-transparency-log https://ltl.zkdefi.org/ permanent +handle_path /lean-transparency-log/* { + redir https://ltl.zkdefi.org{uri} permanent +} +``` + nginx equivalent, with basic rate limiting (the backend is a stdlib threading server - let the proxy absorb abuse): ```nginx limit_req_zone $binary_remote_addr zone=pactalog:1m rate=20r/s; -location /lean-transparency-log/ { - limit_req zone=pactalog burst=40 nodelay; - proxy_read_timeout 15s; - proxy_pass http://127.0.0.1:8461/lean-transparency-log/; - proxy_set_header Host $host; +server { + server_name ltl.zkdefi.org; + location / { + limit_req zone=pactalog burst=40 nodelay; + proxy_read_timeout 15s; + proxy_pass http://127.0.0.1:8461/; + proxy_set_header Host $host; + } } -location = /lean-transparency-log { return 301 /lean-transparency-log/docs; } ``` -Check: `https://zkdefi.org/lean-transparency-log/docs` renders the customer +Check: `https://ltl.zkdefi.org/docs` renders the customer documentation; `/v1/sth` returns the dogfood-signed head. ## 4. Second mirror (any Forgejo/Gitea/GitLab you operate) @@ -182,12 +200,12 @@ sudo systemctl restart pacta-log ## 6. Smoke tests from anywhere ```bash -pacta log-fetch --url https://zkdefi.org/lean-transparency-log --component dalek-ed25519-verified --out-dir /tmp/e +pacta log-fetch --url https://ltl.zkdefi.org --component dalek-ed25519-verified --out-dir /tmp/e pacta receipt-verify --attestation /tmp/e/dalek-ed25519-verified.attestation.json \ --receipt /tmp/e/dalek-ed25519-verified.receipt.json \ --log-public-key \ --sth-store ~/.pacta-pins.json -pacta sth-refresh --url https://zkdefi.org/lean-transparency-log \ +pacta sth-refresh --url https://ltl.zkdefi.org \ --sth-store ~/.pacta-pins.json --log-public-key git clone https://github.com/saymrwulf/lean-transparency-log && cd lean-transparency-log && python3 verify.py --all ``` diff --git a/README.md b/README.md index 92b36dc..a66bc4a 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ The log has three synchronized faces, transport being orthogonal to trust: every leaf, every signed tree head (the WITNESS CHANNEL - all cloners see the same heads), per-component receipts, the provider public key, and a standalone stdlib-only `verify.py`. Anyone: `python3 verify.py --all`. -3. **HTTP service** (deployed at `zkdefi.org/lean-transparency-log`, see +3. **HTTP service** (deployed at `ltl.zkdefi.org`, see `DEPLOY.md`): read-only CT-style endpoints + customer docs. The web process never touches private keys - heads are signed offline; a compromised server can withhold or replay (pinning + freshness detect @@ -247,8 +247,8 @@ The log has three synchronized faces, transport being orthogonal to trust: ```bash PYTHONPATH=src:provider/src python -m pacta_provider serve --log-dir ... --base-path lean-transparency-log PYTHONPATH=src:provider/src python -m pacta_provider log-publish --log-dir ... --git-dir --public-key -pacta log-fetch --url https://zkdefi.org/lean-transparency-log --component dalek-ed25519-verified --out-dir fetched -pacta sth-refresh --url https://zkdefi.org/lean-transparency-log --sth-store pins.json --log-public-key +pacta log-fetch --url https://ltl.zkdefi.org --component dalek-ed25519-verified --out-dir fetched +pacta sth-refresh --url https://ltl.zkdefi.org --sth-store pins.json --log-public-key pacta witness-audit --published-dir --log-public-key ``` diff --git a/notebooks/06b_agent_verify_inclusion.ipynb b/notebooks/06b_agent_verify_inclusion.ipynb index be4cf70..c4825e9 100644 --- a/notebooks/06b_agent_verify_inclusion.ipynb +++ b/notebooks/06b_agent_verify_inclusion.ipynb @@ -220,7 +220,7 @@ "(`pacta witness-audit` recomputes every prefix root from the\n", "published leaves; run `python3 verify.py --all` in a clone for the\n", "no-install version). The ONLINE SERVICE\n", - "(`zkdefi.org/lean-transparency-log`) adds live endpoints: fetch\n", + "(`ltl.zkdefi.org`) adds live endpoints: fetch\n", "fresh evidence (`pacta log-fetch`), advance your pin with an\n", "online consistency proof (`pacta sth-refresh`). The verification\n", "you do afterwards is IDENTICAL in all three transports - this\n", diff --git a/paper/ltl.pdf b/paper/ltl.pdf index 29dfa8f..42c12a1 100644 Binary files a/paper/ltl.pdf and b/paper/ltl.pdf differ diff --git a/paper/ltl.tex b/paper/ltl.tex index d5e9c5a..fdbfb76 100644 --- a/paper/ltl.tex +++ b/paper/ltl.tex @@ -18,7 +18,7 @@ \title{\textbf{LTL: the Lean Transparency Log}\\[2pt] \large Distributing Machine-Checked Proof Evidence via an Authenticated Data Structure Signed by Its Own Certified Artifact} -\author{saymrwulf\\ \small zkdefi.org --- \url{https://zkdefi.org/lean-transparency-log}} +\author{saymrwulf\\ \small zkdefi.org --- \url{https://ltl.zkdefi.org}} \date{July 2026} \begin{document} @@ -281,7 +281,7 @@ signing library's attestation enters the log record \label{sec:deploy} The LTL is deployed\footnote{Service: -\url{https://zkdefi.org/lean-transparency-log} (read-only HTTP API and +\url{https://ltl.zkdefi.org} (read-only HTTP API and documentation). Mirror: \url{https://github.com/saymrwulf/lean-transparency-log}. Operator/consumer tooling and a twelve-lecture course: \url{https://github.com/saymrwulf/proof-aware-crypto-tooling-agent}. The diff --git a/provider/src/pacta_provider/cli.py b/provider/src/pacta_provider/cli.py index df7474b..25c3a86 100644 --- a/provider/src/pacta_provider/cli.py +++ b/provider/src/pacta_provider/cli.py @@ -72,7 +72,7 @@ def build_parser() -> argparse.ArgumentParser: serve = sub.add_parser("serve", help="Serve the log read-only over HTTP (CT-style endpoints + customer docs). Never touches private keys.") serve.add_argument("--log-dir", required=True) - serve.add_argument("--base-path", default="lean-transparency-log") + serve.add_argument("--base-path", default="") serve.add_argument("--host", default="127.0.0.1") serve.add_argument("--port", type=int, default=8461) serve.set_defaults(func=cmd_serve) @@ -166,7 +166,9 @@ def cmd_serve(args) -> int: from .web import serve as make_server server = make_server(args.log_dir, base_path=args.base_path, host=args.host, port=args.port) - print(f"serving read-only log on http://{args.host}:{args.port}/{args.base_path.strip('/')}/docs") + base = args.base_path.strip("/") + prefix = f"/{base}" if base else "" + print(f"serving read-only log on http://{args.host}:{args.port}{prefix}/docs") try: server.serve_forever() except KeyboardInterrupt: diff --git a/provider/src/pacta_provider/published_assets.py b/provider/src/pacta_provider/published_assets.py index 8f16aab..0a1ddab 100644 --- a/provider/src/pacta_provider/published_assets.py +++ b/provider/src/pacta_provider/published_assets.py @@ -184,7 +184,7 @@ python3 verify.py --receipt receipts/dalek-ed25519-verified.receipt.json ``` The online service (same data, live endpoints + customer documentation): -**https://zkdefi.org/lean-transparency-log** +**https://ltl.zkdefi.org** The provider tooling, agent tooling, and course materials: **https://github.com/saymrwulf/proof-aware-crypto-tooling-agent** diff --git a/provider/src/pacta_provider/web.py b/provider/src/pacta_provider/web.py index 5f5516a..5765707 100644 --- a/provider/src/pacta_provider/web.py +++ b/provider/src/pacta_provider/web.py @@ -1,6 +1,6 @@ """The online face of the transparency log: a READ-ONLY, zero-dependency HTTP service exposing CT-style endpoints under a base path (deployed at -zkdefi.org/lean-transparency-log behind a reverse proxy). +ltl.zkdefi.org behind a reverse proxy). Security posture: this process never loads a private key. Tree heads are signed OFFLINE by the provider CLI (log-append / log-sth); the service @@ -150,7 +150,7 @@ def make_handler(log: TransparencyLog, base_path: str, docs_html: str): def serve( log_dir: str, - base_path: str = "lean-transparency-log", + base_path: str = "", host: str = "127.0.0.1", port: int = 8461, docs_html: str | None = None, diff --git a/provider/src/pacta_provider/webdocs.py b/provider/src/pacta_provider/webdocs.py index 357e565..e1d772b 100644 --- a/provider/src/pacta_provider/webdocs.py +++ b/provider/src/pacta_provider/webdocs.py @@ -102,7 +102,7 @@ def _svg_tree(entries: list[LogEntry], root_hex: str, signing_backend: str) -> s def render_docs(log: TransparencyLog, base_path: str) -> str: - base = "/" + base_path.strip("/") + base = "/" + base_path.strip("/") if base_path.strip("/") else "" metadata = log.metadata() history = log.sth_history() latest: dict[str, Any] = history[-1] if history else {} diff --git a/scripts/build_curriculum_notebooks.py b/scripts/build_curriculum_notebooks.py index 39034b5..80e7bf1 100644 --- a/scripts/build_curriculum_notebooks.py +++ b/scripts/build_curriculum_notebooks.py @@ -1549,7 +1549,7 @@ COURSE = { (`pacta witness-audit` recomputes every prefix root from the published leaves; run `python3 verify.py --all` in a clone for the no-install version). The ONLINE SERVICE - (`zkdefi.org/lean-transparency-log`) adds live endpoints: fetch + (`ltl.zkdefi.org`) adds live endpoints: fetch fresh evidence (`pacta log-fetch`), advance your pin with an online consistency proof (`pacta sth-refresh`). The verification you do afterwards is IDENTICAL in all three transports - this diff --git a/src/pacta/cli.py b/src/pacta/cli.py index cc5d1a6..af4e320 100644 --- a/src/pacta/cli.py +++ b/src/pacta/cli.py @@ -143,7 +143,7 @@ def build_parser() -> argparse.ArgumentParser: receipt_verify.set_defaults(func=cmd_receipt_verify) log_fetch = sub.add_parser("log-fetch", help="Fetch attestation + inclusion proof for a component from an ONLINE log; verify locally afterwards.") - log_fetch.add_argument("--url", required=True, help="Base URL, e.g. https://zkdefi.org/lean-transparency-log") + log_fetch.add_argument("--url", required=True, help="Base URL, e.g. https://ltl.zkdefi.org") log_fetch.add_argument("--component", required=True) log_fetch.add_argument("--out-dir", default="fetched-evidence") log_fetch.set_defaults(func=cmd_log_fetch) diff --git a/tests/test_web_and_witness.py b/tests/test_web_and_witness.py index 9774519..b223cbe 100644 --- a/tests/test_web_and_witness.py +++ b/tests/test_web_and_witness.py @@ -30,11 +30,12 @@ def _make_log(tmp_path, n=3): def test_web_endpoints_and_online_proof_roundtrip(tmp_path): + # root mount: the production shape (ltl.zkdefi.org serves from /) _make_log(tmp_path) server = serve(str(tmp_path / "log"), port=0) port = server.server_address[1] threading.Thread(target=server.serve_forever, daemon=True).start() - base = f"http://127.0.0.1:{port}/lean-transparency-log" + base = f"http://127.0.0.1:{port}" try: def get(path): with urllib.request.urlopen(base + path, timeout=10) as r: @@ -60,8 +61,9 @@ def test_web_endpoints_and_online_proof_roundtrip(tmp_path): def test_logclient_fetch_and_refresh_pin(tmp_path): + # path mount still supported for anyone proxying under a prefix _make_log(tmp_path) - server = serve(str(tmp_path / "log"), port=0) + server = serve(str(tmp_path / "log"), base_path="lean-transparency-log", port=0) port = server.server_address[1] threading.Thread(target=server.serve_forever, daemon=True).start() base = f"http://127.0.0.1:{port}/lean-transparency-log"