Move the log service to ltl.zkdefi.org (root path); keep path-mount support

- serve/webdocs/cli default to base_path='' (own subdomain, root serving)
- webdocs link builder handles the empty base like web.py already did
- all docs, paper, notebook 06b, and published-mirror README point at
  https://ltl.zkdefi.org; DEPLOY.md rewritten for subdomain + redirect
- tests: root mount is the production shape; one test keeps exercising
  the path-mounted variant

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mrwulf 2026-07-06 19:14:52 +02:00
parent aa16f6d530
commit 47dfb61a7f
12 changed files with 54 additions and 32 deletions

View file

@ -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 <prefix>` 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 <provider.ed25519.pub from the published repo> \
--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 <pubkey>
git clone https://github.com/saymrwulf/lean-transparency-log && cd lean-transparency-log && python3 verify.py --all
```

View file

@ -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 <mirror clone> --public-key <pub>
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 <pub>
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 <pub>
pacta witness-audit --published-dir <clone of lean-transparency-log> --log-public-key <pub>
```

View file

@ -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",

Binary file not shown.

View file

@ -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

View file

@ -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:

View file

@ -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**

View file

@ -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,

View file

@ -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 {}

View file

@ -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

View file

@ -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)

View file

@ -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"