{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lecture 6a: THE PROVIDER'S SIDE - Building the Authenticated Structure\n", "\n", "> **DOMAIN BANNER - read this first.** In this notebook YOU ARE THE\n", "> PROVIDER. There is exactly **one** of you per log. You hold the\n", "> signing key. You own a Lean toolchain and hours of compute. You\n", "> carry the append-only obligations. Nothing in this notebook is\n", "> ever executed by an agent - and that asymmetry is not an\n", "> implementation detail, it is the entire design (see the\n", "> justification at the end).\n", "\n", "The provider's job, end to end: **verify -> leaf -> tree -> sign ->\n", "self-check**. Only the first step involves Lean; everything after\n", "is hashing and one signature.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Learning Objectives\n", "\n", "- Build the full authenticated data structure from real attestations: leaves, tree, Signed Tree Head.\n", "- Place the Lean verification correctly: it is the LEAF-MAKING step, the only expensive one, and it never travels to the agent.\n", "- Sign the root with the merkleized library and run the provider's own inclusion self-check (\"the provider eats its own dogfood\").\n", "- Justify the singleton/many split as a design decision.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1 - Verify (the expensive step, done ONCE)\n", "\n", "The leaf content is a signed **attestation**: the outcome of replaying\n", "every Lean proof of one repository under lean-guard (~30 minutes of\n", "kernel re-checking per fork on the reference machine). This notebook\n", "does NOT re-run that - the shipped `evidence/` attestations ARE that\n", "step's output. What matters architecturally: **the Lean cost lives\n", "here and only here.** No agent ever pays it again.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "import sys\n", "\n", "repo_root = Path.cwd()\n", "if not (repo_root / \"src\" / \"pacta\").exists():\n", " repo_root = repo_root.parent\n", "sys.path.insert(0, str(repo_root / \"src\"))\n", "sys.path.insert(0, str(repo_root / \"provider\" / \"src\"))\n", "\n", "from pacta.yamlio import load_data\n", "\n", "attestations = {\n", " fork: load_data(repo_root / \"evidence\" / f\"{fork}-ed25519.attestation.yaml\")\n", " for fork in [\"dalek\", \"anza\", \"risc0\", \"betrusted\"]\n", "}\n", "for fork, att in attestations.items():\n", " certs = att[\"certificates\"]\n", " clean = sum(1 for c in certs if c[\"status\"] == \"proven\" and c[\"axiom_status\"] == \"clean\")\n", " print(f\"{fork}: {clean}/{len(certs)} proven | commit {att['subject']['repo_commit'][:8]} | guard: {att['machine_protection']['lean_guard'].rsplit('/',1)[-1]}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Steps 2+3 - Leaf and tree (cheap, mechanical)\n", "\n", "Each attestation is wrapped, canonically serialized, and hashed with\n", "the RFC 9162 leaf prefix `0x00`; pairs of nodes hash with prefix\n", "`0x01`. Build a REAL provider log in a scratch directory - you are\n", "the provider, so mint your own key first:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import tempfile\n", "from pacta.signing import generate_ed25519_keypair\n", "from pacta_provider.transparency_log import TransparencyLog\n", "\n", "state = Path(tempfile.mkdtemp(prefix=\"provider-lecture-\"))\n", "generate_ed25519_keypair(state / \"provider.key\", state / \"provider.pub\")\n", "log = TransparencyLog(state / \"log\")\n", "log.init(\"lecture-provider\", state / \"provider.pub\")\n", "\n", "receipts = {}\n", "for fork, att in attestations.items():\n", " att_path = state / f\"{fork}.attestation.yaml\"\n", " from pacta.yamlio import dump_data\n", " dump_data(att, att_path)\n", " receipts[fork] = log.append_attestation(att_path, state / \"provider.key\", state / \"provider.pub\")\n", "print(\"tree size:\", receipts[\"betrusted\"][\"tree_size\"])\n", "print(\"root:\", receipts[\"betrusted\"][\"sth\"][\"root_hash\"][:32], \"\u2026\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Steps 4+5 - Sign the root, then CHECK YOURSELF\n", "\n", "The tree head is signed with the **merkleized library itself** (when\n", "the dogfood binary is built): the Ed25519 code that signs this root\n", "is the same pinned dalek source whose proof attestation is a leaf of\n", "this very tree. Before signing, the provider runs the SAME Merkle\n", "inclusion verification an agent would run - on its own signing\n", "library's leaf, against the tree it is about to sign - and embeds\n", "the verdict in the signature block. A root signature that names the\n", "leaf vouching for the code that produced it:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "sth = receipts[\"betrusted\"][\"sth\"]\n", "ed = sth[\"signatures\"][\"ed25519\"]\n", "print(\"signing backend:\", ed.get(\"signing_backend\"))\n", "print(json.dumps(ed.get(\"signing_provenance\", {\"note\": \"dogfood binary not built on this host - OpenSSL fallback, provenance omitted\"}), indent=1))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The structure you just built, drawn from your own log\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pacta.transparency import leaf_hash, merkle_root, node_hash\n", "\n", "def svg_merkle(leaf_hashes, highlight=None, title=\"\", domain=\"PROVIDER: builds every box below\", color=\"#1e7f4f\"):\n", " n = len(leaf_hashes)\n", " width, lh, lv = 980, 108, 92\n", " levels = []\n", " level = [bytes.fromhex(h) if isinstance(h, str) else h for h in leaf_hashes]\n", " levels.append(level)\n", " while len(level) > 1:\n", " nxt = []\n", " for i in range(0, len(level) - 1, 2):\n", " nxt.append(node_hash(level[i], level[i + 1]))\n", " if len(level) % 2:\n", " nxt.append(level[-1])\n", " levels.append(nxt)\n", " level = nxt\n", " height = 130 + lv * len(levels) + 60\n", " out = [f'']\n", " out.append(f'')\n", " out.append(f'{domain}')\n", " out.append(f'{title}')\n", " pos = {}\n", " for li, lvl in enumerate(levels):\n", " y = height - 70 - li * lv\n", " span = width / (len(lvl) + 1)\n", " for i, node in enumerate(lvl):\n", " x = span * (i + 1)\n", " pos[(li, i)] = (x, y)\n", " hl = highlight and li == 0 and i == highlight[0]\n", " sib = highlight and (li, i) in highlight[1]\n", " fill = \"#fdf0da\" if sib else (\"#e2f2e9\" if hl else \"#f4f4f6\")\n", " stroke = \"#a86a10\" if sib else (\"#1e7f4f\" if hl else \"#999\")\n", " out.append(f'')\n", " label = (\"leaf %d\" % i) if li == 0 else (\"root\" if li == len(levels) - 1 else \"node\")\n", " out.append(f'{label}')\n", " out.append(f'{node.hex()[:10]}\u2026')\n", " if li > 0:\n", " for ci in (2 * i, 2 * i + 1):\n", " if (li - 1, ci) in pos:\n", " cx, cy = pos[(li - 1, ci)]\n", " out.append(f'')\n", " rx, ry = pos[(len(levels) - 1, 0)]\n", " out.append(f'')\n", " out.append(f'Signed Tree Head: Ed25519(root) via merkleized library')\n", " out.append(f'')\n", " out.append(\"\")\n", " return \"\".join(out)\n", "\n", "entries = log.entries()\n", "leaf_hexes = [leaf_hash(e.leaf_bytes()).hex() for e in entries]\n", "svg = svg_merkle(leaf_hexes, title=f\"your lecture log: {len(entries)} attestation leaves, root {merkle_root([e.leaf_bytes() for e in entries]).hex()[:16]}\u2026\")\n", "try:\n", " from IPython.display import SVG, display\n", " display(SVG(svg))\n", "except Exception:\n", " print(svg[:200], \"\u2026 (open in a notebook to render)\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Why a singleton? The design justification\n", "\n", "| | Provider (this notebook) | Agent (next notebook) |\n", "|---|---|---|\n", "| How many | **exactly one** per log | unbounded |\n", "| Owns | the signing key, the Lean toolchain, the full log | the provider's PUBLIC key, a pin file |\n", "| Pays | hours of kernel time per repo, ONCE | milliseconds per check, forever |\n", "| Obligations | append-only, sign every head, serve proofs, self-verify | pin every head, demand consistency |\n", "| Can be wrong? | detectably: signatures + pins make lies attributable | fails closed |\n", "\n", "The asymmetry is the product. If agents had to run Lean, the service\n", "would add nothing; if the provider's claims weren't pinned and\n", "signed, trust would be a rumor. Every artifact in this course lives\n", "on exactly one side of this table - and the split between this\n", "notebook and the next MIRRORS it on purpose: if you cannot say\n", "which notebook a step belongs to, you have not understood the step.\n", "\n", "## Exercises\n", "\n", "- Append a fifth attestation (edit one field of a copy) and watch the root change; which internal nodes changed and which did not? Explain from the tree shape.\n", "- The self-inclusion check ran against the tree BEFORE your key existed in any leaf. What does `signing_provenance.self_inclusion` say, and why is recording that honest?\n", "- Cost accounting: with 4 repos x 30 minutes of Lean and N agents x 5 ms of verification, at what N does the provider model beat every-agent-verifies-locally? (Hint: N=1.)\n", "- Design question: what breaks if there are TWO providers with one key? With two keys and one log?\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 5 }