diff --git a/.forgejo/workflows/cd.yaml b/.forgejo/workflows/cd.yaml index 0d804b19..da97a19d 100644 --- a/.forgejo/workflows/cd.yaml +++ b/.forgejo/workflows/cd.yaml @@ -126,6 +126,35 @@ jobs: echo "No mkdocs.yml found. Skipping Pages deployment." fi + - name: Level 9 Automation (Physical Substrate) + run: | + echo "Installing Archival Dependencies..." + sudo apt-get update && sudo apt-get install -y pandoc texlive-latex-base genisoimage + pip install qrcode[pil] + + echo "Generating Archival Master Tome..." + python3 scripts/compile_tome.py + pandoc MASTER_TOME.md -o SOVEREIGN_CANON_MASTER_TOME.pdf --pdf-engine=pdflatex + + echo "Generating Machine-Readable QR Archive..." + python3 scripts/generate_qr_archive.py CANONICAL_VAULT.tar.gz qr_archive/ + + echo "Bundling M-DISC ISO..." + mkisofs -o SOVEREIGN_CANON_MDISC.iso -R -J CANONICAL_VAULT.tar.gz SOVEREIGN_CANON_MASTER_TOME.pdf qr_archive/ + + echo "Level 9 Archival Prep Complete." + + - name: Pin M-DISC ISO to IPFS Sanctum + run: | + POD_NAME=$(kubectl get pods -l app=ipfs-node -o jsonpath="{.items[0].metadata.name}") + kubectl cp SOVEREIGN_CANON_MDISC.iso $POD_NAME:/tmp/SOVEREIGN_CANON_MDISC.iso + CID=$(kubectl exec $POD_NAME -- ipfs add -Q /tmp/SOVEREIGN_CANON_MDISC.iso) + echo "=========================================" + echo "Level 9 M-DISC ISO Anchored to IPFS!" + echo "CID: $CID" + echo "=========================================" + kubectl exec $POD_NAME -- rm /tmp/SOVEREIGN_CANON_MDISC.iso + - name: Sync to Radicle Network run: | RAD_POD=$(kubectl get pods -n radicle -l app=radicle -o jsonpath="{.items[0].metadata.name}") diff --git a/scripts/compile_tome.py b/scripts/compile_tome.py new file mode 100644 index 00000000..b51d3e32 --- /dev/null +++ b/scripts/compile_tome.py @@ -0,0 +1,34 @@ +import os + +def compile_tome(output_file="MASTER_TOME.md"): + print("[*] Compiling the Archival Master Tome...") + + # Files to include in the physical book (in order) + source_files = [ + "README.md", + "papers/The_Computability_of_Recursive_Coherence_v1.7.md" + # We can add more files here as the canon expands + ] + + with open(output_file, 'w', encoding='utf-8') as outfile: + outfile.write("# THE SOVEREIGN CANON: MASTER TOME\n\n") + outfile.write("> Generated for Physical Substrate Archival (Level 9)\n\n") + outfile.write("---\n\n") + + for filename in source_files: + if os.path.exists(filename): + print(f" -> Adding {filename}") + with open(filename, 'r', encoding='utf-8') as infile: + # Write filename as a separator header if it's not the README + if filename != "README.md": + outfile.write(f"\n\n\\pagebreak\n\n") + outfile.write(f"# Document: {os.path.basename(filename)}\n\n") + outfile.write(infile.read()) + outfile.write("\n\n---\n\n") + else: + print(f" [!] Warning: {filename} not found.") + + print(f"[+] Master Tome compiled to {output_file}") + +if __name__ == "__main__": + compile_tome() diff --git a/scripts/generate_qr_archive.py b/scripts/generate_qr_archive.py new file mode 100644 index 00000000..179b0226 --- /dev/null +++ b/scripts/generate_qr_archive.py @@ -0,0 +1,55 @@ +import sys +import os +import math +import qrcode + +def generate_qr_archive(payload_path, output_dir, chunk_size=2000): + print(f"[*] Generating Machine-Readable QR Archive from {payload_path}...") + + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + with open(payload_path, 'rb') as f: + payload_bytes = f.read() + + total_bytes = len(payload_bytes) + total_chunks = math.ceil(total_bytes / chunk_size) + print(f"[*] Payload size: {total_bytes} bytes. Splitting into {total_chunks} chunks of {chunk_size} bytes.") + + for i in range(total_chunks): + start_idx = i * chunk_size + end_idx = min(start_idx + chunk_size, total_bytes) + chunk_data = payload_bytes[start_idx:end_idx] + + # We need to encode the binary data using Base64 or ISO-8859-1 for QR code compatibility + import base64 + chunk_b64 = base64.b64encode(chunk_data).decode('utf-8') + + header = f"[CHUNK_{i+1:04d}_OF_{total_chunks:04d}]\n" + qr_data = header + chunk_b64 + + qr = qrcode.QRCode( + version=None, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=10, + border=4, + ) + qr.add_data(qr_data) + qr.make(fit=True) + + img = qr.make_image(fill_color="black", back_color="white") + + filename = f"chunk_{i+1:04d}.png" + img.save(os.path.join(output_dir, filename)) + + if (i+1) % 100 == 0 or (i+1) == total_chunks: + print(f" -> Generated {i+1}/{total_chunks} QR codes...") + + print(f"[+] QR Archive successfully generated in {output_dir}") + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python3 generate_qr_archive.py ") + sys.exit(1) + + generate_qr_archive(sys.argv[1], sys.argv[2])