From 97907a26b265a6a059fafd7596a4d5503220234a Mon Sep 17 00:00:00 2001 From: agent-56a7678c6cd71659 Date: Sat, 25 Apr 2026 21:30:10 +0200 Subject: [PATCH] build(agent): jabba#56a767 iteration --- .gitignore | 21 +++++++++ AGENTS.md | 34 ++++++++++++++ README.md | 26 ++++++++++- pyproject.toml | 15 +++++++ src/proofweave/__init__.py | 5 +++ src/proofweave/__main__.py | 19 ++++++++ src/proofweave/compiler.py | 91 ++++++++++++++++++++++++++++++++++++++ test.sh | 10 +++++ tests/conftest.py | 8 ++++ tests/test_compile.py | 37 ++++++++++++++++ 10 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 pyproject.toml create mode 100644 src/proofweave/__init__.py create mode 100644 src/proofweave/__main__.py create mode 100644 src/proofweave/compiler.py create mode 100644 test.sh create mode 100644 tests/conftest.py create mode 100644 tests/test_compile.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd5590b --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +node_modules/ +.npmrc +.env +.env.* +__tests__/ +coverage/ +.nyc_output/ +dist/ +build/ +.cache/ +*.log +.DS_Store +tmp/ +.tmp/ +__pycache__/ +*.pyc +.venv/ +venv/ +*.egg-info/ +.pytest_cache/ +READY_TO_PUBLISH diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b7b335d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,34 @@ +ProofWeave Prototype - AGENTS + +Purpose +- Provide a small, well-tested chunk of the ProofWeave idea: a DSL manifest + toy compiler that emits wasm + proof_summary + manifest. + +Repository layout +- src/proofweave/: Python package + - compiler.py: main compile function that produces bundle artifacts + - __init__.py +- tests/: pytest tests +- pyproject.toml: packaging metadata (project name: idea193-proofweave-proof-carrying) +- README.md: overview +- test.sh: runs pytest and python3 -m build + +Tech stack +- Language: Python 3.8+ +- Packaging: setuptools (PEP 621 pyproject) +- Test: pytest + +Commands +- Run tests: `bash test.sh` (this runs `pytest` and `python3 -m build`) +- Import API: `from proofweave.compiler import compile_genome` + +Development rules for agents +- Keep changes small and well-tested. Add tests for any behavioral change. +- Do not modify packaging metadata without updating `AGENTS.md` and `README.md`. +- Do not add new top-level files without updating this doc. + +Notes +- This is intentionally a small initial slice. Follow-on agents should add: + - a real DSL parser and schema validation + - signing/attestation and Merkle manifest chunking + - a lightweight Rust verifier compiled to WASM + - CI workflows and more comprehensive conformance tests diff --git a/README.md b/README.md index 17161a7..80cebca 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ -# idea193-proofweave-proof-carrying +# ProofWeave (prototype) -Source logic for Idea #193 \ No newline at end of file +ProofWeave is a prototype implementation of a proof-carrying WebAssembly toolchain for "ServiceGenomes". This repository provides: + +- A small typed ServiceGenome DSL (JSON/YAML consumable schema) and a toy compiler that emits: + - a compact manifest JSON (provenance fields), + - a minimal valid wasm binary (empty module placeholder), and + - a proof summary JSON (resource bounds, allowed syscalls). +- A tiny Python API to produce these bundle artifacts. +- Tests and packaging metadata so the project can be built with `python3 -m build`. + +This is an initial, well-tested chunk of the full ProofWeave idea described in the original design. It focuses on deterministic manifest and proof-summary generation suitable for constrained-edge verification. + +Quickstart + +Install dev dependencies (recommended in a venv) and run tests: + + bash test.sh + +Usage (Python API) + + from proofweave.compiler import compile_genome + compile_genome(genome_dict, out_dir) + +See `AGENTS.md` for developer notes, architecture and how to contribute. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..88b048d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "idea193-proofweave-proof-carrying" +version = "0.1.0" +description = "ProofWeave: a proof-carrying WebAssembly toolchain prototype for ServiceGenomes (toy implementation)." +readme = "README.md" +requires-python = ">=3.8" +authors = [ {name = "ProofWeave Authors"} ] +classifiers = ["Programming Language :: Python :: 3"] + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/src/proofweave/__init__.py b/src/proofweave/__init__.py new file mode 100644 index 0000000..2288928 --- /dev/null +++ b/src/proofweave/__init__.py @@ -0,0 +1,5 @@ +"""ProofWeave package - lightweight prototype.""" + +from .compiler import compile_genome + +__all__ = ["compile_genome"] diff --git a/src/proofweave/__main__.py b/src/proofweave/__main__.py new file mode 100644 index 0000000..d5b64d0 --- /dev/null +++ b/src/proofweave/__main__.py @@ -0,0 +1,19 @@ +"""CLI entrypoint for proofweave.""" +from .compiler import compile_genome +import json +import sys + + +def main(argv=None): + argv = argv or sys.argv[1:] + if len(argv) != 2: + print("Usage: python -m proofweave ") + sys.exit(2) + genome_json, out_dir = argv + with open(genome_json, "r", encoding="utf-8") as f: + genome = json.load(f) + compile_genome(genome, out_dir) + + +if __name__ == "__main__": + main() diff --git a/src/proofweave/compiler.py b/src/proofweave/compiler.py new file mode 100644 index 0000000..5c2ced1 --- /dev/null +++ b/src/proofweave/compiler.py @@ -0,0 +1,91 @@ +"""Toy compiler for ProofWeave: emits manifest, wasm placeholder, and proof summary.""" +import json +import hashlib +import os +import time +from typing import Dict, Any + + +WASM_EMPTY_MODULE = b"\x00asm\x01\x00\x00\x00" # minimal valid wasm header (empty module) + + +def _sha256_bytes(b: bytes) -> str: + return hashlib.sha256(b).hexdigest() + + +def _sha256_file(path: str) -> str: + with open(path, "rb") as f: + return _sha256_bytes(f.read()) + + +def compile_genome(genome: Dict[str, Any], out_dir: str) -> Dict[str, str]: + """Compile a ServiceGenome dict into a minimal PC-WASM bundle. + + Writes to out_dir: + - manifest.json + - wasm.wasm (minimal valid wasm module placeholder) + - proof_summary.json + + Returns dict of produced file paths. + """ + os.makedirs(out_dir, exist_ok=True) + + timestamp = int(time.time()) + manifest = { + "parent_hash": genome.get("parent_hash"), + "delta_hash": genome.get("delta_hash"), + "mutation_policy": genome.get("mutation_policy", {}), + "toolchain_digest": genome.get("toolchain_digest", "toy-compiler-0.1"), + "authors": genome.get("authors", []), + "timestamp": timestamp, + } + + manifest_path = os.path.join(out_dir, "manifest.json") + with open(manifest_path, "w", encoding="utf-8") as f: + json.dump(manifest, f, indent=2, sort_keys=True) + + wasm_path = os.path.join(out_dir, "wasm.wasm") + # Write a minimal valid wasm binary (empty module). Real compiler would produce proper wasm. + with open(wasm_path, "wb") as f: + f.write(WASM_EMPTY_MODULE) + + # Produce a tiny proof summary: resource bounds + allowed syscalls + # For this toy compiler we use simple heuristics (counts) and echo provided resource_envelope + resource_envelope = genome.get("resource_envelope", {"cpu_ms": 10, "memory_bytes": 65536}) + proof = { + "resource_cert": { + "cpu_ms": resource_envelope.get("cpu_ms", 10), + "memory_bytes": resource_envelope.get("memory_bytes", 65536), + }, + "allowed_syscalls": genome.get("allowed_syscalls", ["read", "net:egress"]), + "summary_fingerprint": None, + } + + # Compute simple fingerprints for manifest and wasm + manifest_digest = _sha256_file(manifest_path) + wasm_digest = _sha256_bytes(WASM_EMPTY_MODULE) + + proof["summary_fingerprint"] = _sha256_bytes( + json.dumps({"manifest": manifest_digest, "wasm": wasm_digest, "proof": proof["resource_cert"]}, sort_keys=True).encode() + ) + + proof_path = os.path.join(out_dir, "proof_summary.json") + with open(proof_path, "w", encoding="utf-8") as f: + json.dump(proof, f, indent=2, sort_keys=True) + + return {"manifest": manifest_path, "wasm": wasm_path, "proof_summary": proof_path} + + +if __name__ == "__main__": + import argparse + + p = argparse.ArgumentParser() + p.add_argument("genome_json") + p.add_argument("out_dir") + args = p.parse_args() + with open(args.genome_json, "r", encoding="utf-8") as f: + genome = json.load(f) + res = compile_genome(genome, args.out_dir) + print("Wrote:") + for k, v in res.items(): + print(f" - {k}: {v}") diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..cafa292 --- /dev/null +++ b/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running pytest..." +pytest -q + +echo "Building package (python3 -m build)..." +python3 -m build + +echo "All done." diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..0a18799 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +import sys +import os + +# Ensure `src` is on sys.path so tests can import the package without installation +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) +SRC = os.path.join(ROOT, "src") +if SRC not in sys.path: + sys.path.insert(0, SRC) diff --git a/tests/test_compile.py b/tests/test_compile.py new file mode 100644 index 0000000..513c699 --- /dev/null +++ b/tests/test_compile.py @@ -0,0 +1,37 @@ +import json +import os +import tempfile +from proofweave.compiler import compile_genome + + +def test_compile_creates_files(tmp_path): + genome = { + "parent_hash": None, + "delta_hash": "abc123", + "mutation_policy": {"max_cpu_ms": 100}, + "toolchain_digest": "toy-compiler-0.1", + "authors": ["tester"], + "resource_envelope": {"cpu_ms": 5, "memory_bytes": 32768}, + "allowed_syscalls": ["read", "net:egress"], + } + + out = tmp_path / "out" + out.mkdir() + res = compile_genome(genome, str(out)) + + # Verify files exist + for key in ("manifest", "wasm", "proof_summary"): + assert key in res + assert os.path.exists(res[key]) + + # Validate manifest contents + with open(res["manifest"], "r", encoding="utf-8") as f: + manifest = json.load(f) + assert manifest["delta_hash"] == "abc123" + assert manifest["toolchain_digest"] == "toy-compiler-0.1" + + # Validate proof summary + with open(res["proof_summary"], "r", encoding="utf-8") as f: + proof = json.load(f) + assert proof["resource_cert"]["cpu_ms"] == 5 + assert isinstance(proof["summary_fingerprint"], str)