proofchain-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: proofchain-cli
3
+ Version: 0.1.0
4
+ Summary: Hash-chained, signed, independently-verifiable audit proofs for any Postgres table of real events.
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: psycopg2-binary
7
+ Requires-Dist: cryptography
@@ -0,0 +1,44 @@
1
+ # proofchain
2
+
3
+ Point it at any Postgres table of real events. Get a hash-chained,
4
+ Ed25519-signed, append-only proof log. Anyone can independently verify
5
+ the chain — no trust in the operator required.
6
+
7
+ ## Why
8
+
9
+ Most "proof of X" claims in finance and crypto are just numbers an
10
+ operator generated internally, with no way for an outside party to
11
+ check them. proofchain fixes that generically: point it at any table,
12
+ any columns, and get a cryptographic chain that's tamper-evident and
13
+ independently re-verifiable by anyone, using only the public key.
14
+
15
+ ## Install
16
+
17
+ pip install proofchain
18
+
19
+ ## Example: a small business proving payout history to a lender
20
+
21
+ export PROOFCHAIN_DSN="postgresql://user@host:port/business_db"
22
+ export PROOFCHAIN_KEYFILE="/path/to/signing_key.json"
23
+
24
+ proofchain build \
25
+ --table vendor_payouts \
26
+ --id-col id \
27
+ --columns vendor_name,amount_usd,paid_at \
28
+ --where "status = 'completed'" \
29
+ --order-by paid_at \
30
+ --log ./payout_proof.jsonl
31
+
32
+ proofchain verify --log ./payout_proof.jsonl
33
+
34
+ The lender (or anyone) can independently re-run `verify` against the
35
+ same log file and confirm nothing was altered — without ever needing
36
+ API access to the business's database or having to trust the business
37
+ at all.
38
+
39
+ ## How it works
40
+
41
+ Each `build` run snapshots the specified rows, signs them with your
42
+ Ed25519 key, and chains the entry to the previous one via SHA-256.
43
+ Any edit to historical data breaks the hash chain on the next verify.
44
+ No table or column names are hardcoded — this works on any schema.
File without changes
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ proofchain.py — generic hash-chained, signed audit-proof CLI.
4
+
5
+ Point it at ANY Postgres table of real events (trades, sales, burns,
6
+ whatever) and get a tamper-evident, independently-verifiable proof log.
7
+ Generalized from omega_revenue_proof.py — same mechanism, config-driven
8
+ instead of hardcoded to one schema.
9
+
10
+ Usage:
11
+ export PROOFCHAIN_DSN="postgresql://user@host:port/db"
12
+ export PROOFCHAIN_KEYFILE="/path/to/key.json" # {"secret":[...]} or [...]
13
+
14
+ python3 proofchain.py build \
15
+ --table my_trades \
16
+ --id-col id \
17
+ --columns symbol,pnl_usdt,closed_at \
18
+ --where "status IN ('closed_profit','closed_stop') AND closed_at IS NOT NULL" \
19
+ --order-by closed_at \
20
+ --log ./my_proof.jsonl
21
+
22
+ python3 proofchain.py verify --log ./my_proof.jsonl
23
+
24
+ No table/column names are hardcoded. Nothing here reads or writes
25
+ omega_bank, omega_ledger, or any Omega-specific table unless you pass
26
+ those names explicitly via --table.
27
+ """
28
+ import os, sys, json, base64, argparse, hashlib
29
+ from pathlib import Path
30
+ from datetime import datetime, timezone
31
+
32
+ import psycopg2
33
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import (
34
+ Ed25519PrivateKey, Ed25519PublicKey
35
+ )
36
+ from cryptography.exceptions import InvalidSignature
37
+
38
+
39
+ def load_signer():
40
+ keyfile = os.environ.get("PROOFCHAIN_KEYFILE")
41
+ if not keyfile:
42
+ sys.exit("PROOFCHAIN_KEYFILE env var not set — no placeholder key will be used.")
43
+ data = json.loads(Path(keyfile).read_text())
44
+ raw = bytes(data["secret"][:32]) if isinstance(data, dict) else bytes(data[:32])
45
+ priv = Ed25519PrivateKey.from_private_bytes(raw)
46
+ pub = priv.public_key()
47
+ return priv, pub
48
+
49
+
50
+ def build(args):
51
+ dsn = os.environ.get("PROOFCHAIN_DSN")
52
+ if not dsn:
53
+ sys.exit("PROOFCHAIN_DSN env var not set.")
54
+
55
+ priv, pub = load_signer()
56
+ cols = [c.strip() for c in args.columns.split(",")]
57
+ select_cols = ", ".join([args.id_col] + cols)
58
+
59
+ query = f"SELECT {select_cols} FROM {args.table}"
60
+ if args.where:
61
+ query += f" WHERE {args.where}"
62
+ if args.order_by:
63
+ query += f" ORDER BY {args.order_by} ASC"
64
+
65
+ conn = psycopg2.connect(dsn)
66
+ with conn.cursor() as cur:
67
+ cur.execute(query)
68
+ rows = cur.fetchall()
69
+ conn.close()
70
+
71
+ records = []
72
+ for row in rows:
73
+ rec = {"id": str(row[0])}
74
+ for i, col in enumerate(cols):
75
+ rec[col] = str(row[i + 1]) if row[i + 1] is not None else None
76
+ records.append(rec)
77
+
78
+ log_path = Path(args.log)
79
+ prev_hash = "genesis"
80
+ if log_path.exists() and log_path.stat().st_size > 0:
81
+ last_line = log_path.read_text().strip().splitlines()[-1]
82
+ prev_hash = json.loads(last_line)["entry_hash"]
83
+
84
+ snapshot = {
85
+ "type": "proofchain_snapshot",
86
+ "table": args.table,
87
+ "record_count": len(records),
88
+ "records": records,
89
+ "prev_hash": prev_hash,
90
+ "built_at": datetime.now(timezone.utc).isoformat(),
91
+ }
92
+ msg = json.dumps(snapshot, sort_keys=True, default=str).encode()
93
+ sig = base64.b64encode(priv.sign(msg)).decode()
94
+ entry_hash = hashlib.sha256(msg + sig.encode()).hexdigest()
95
+ snapshot["signature"] = sig
96
+ snapshot["entry_hash"] = entry_hash
97
+
98
+ with open(log_path, "a") as f:
99
+ f.write(json.dumps(snapshot, default=str) + "\n")
100
+
101
+ print(f"Snapshot written. {len(records)} record(s) from '{args.table}'. "
102
+ f"entry_hash={entry_hash[:16]}...")
103
+ print(f"Log: {log_path}")
104
+
105
+
106
+ def verify(args):
107
+ priv, pub = load_signer()
108
+ log_path = Path(args.log)
109
+ if not log_path.exists():
110
+ print("No log yet.")
111
+ return
112
+
113
+ lines = log_path.read_text().strip().splitlines()
114
+ prev_hash = "genesis"
115
+ ok = True
116
+ for i, line in enumerate(lines):
117
+ record = json.loads(line)
118
+ sig = record.pop("signature")
119
+ claimed_hash = record.pop("entry_hash")
120
+ if record.get("prev_hash") != prev_hash:
121
+ print(f" [BROKEN CHAIN] entry {i}: expected prev_hash={prev_hash}, "
122
+ f"got {record.get('prev_hash')}")
123
+ ok = False
124
+ msg = json.dumps(record, sort_keys=True, default=str).encode()
125
+ try:
126
+ pub.verify(base64.b64decode(sig), msg)
127
+ except InvalidSignature:
128
+ print(f" [BAD SIGNATURE] entry {i}")
129
+ ok = False
130
+ recomputed = hashlib.sha256(msg + sig.encode()).hexdigest()
131
+ if recomputed != claimed_hash:
132
+ print(f" [HASH MISMATCH] entry {i}")
133
+ ok = False
134
+ prev_hash = claimed_hash
135
+
136
+ print("VERIFIED: chain intact, all signatures valid" if ok
137
+ else "FAILED: tampering or corruption detected")
138
+
139
+
140
+ def main():
141
+ parser = argparse.ArgumentParser()
142
+ sub = parser.add_subparsers(dest="cmd", required=True)
143
+
144
+ b = sub.add_parser("build")
145
+ b.add_argument("--table", required=True)
146
+ b.add_argument("--id-col", default="id")
147
+ b.add_argument("--columns", required=True, help="comma-separated column list")
148
+ b.add_argument("--where", default="")
149
+ b.add_argument("--order-by", default="")
150
+ b.add_argument("--log", required=True)
151
+
152
+ v = sub.add_parser("verify")
153
+ v.add_argument("--log", required=True)
154
+
155
+ args = parser.parse_args()
156
+ if args.cmd == "build":
157
+ build(args)
158
+ elif args.cmd == "verify":
159
+ verify(args)
160
+
161
+
162
+ if __name__ == "__main__":
163
+ main()
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: proofchain-cli
3
+ Version: 0.1.0
4
+ Summary: Hash-chained, signed, independently-verifiable audit proofs for any Postgres table of real events.
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: psycopg2-binary
7
+ Requires-Dist: cryptography
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ proofchain/__init__.py
4
+ proofchain/cli.py
5
+ proofchain_cli.egg-info/PKG-INFO
6
+ proofchain_cli.egg-info/SOURCES.txt
7
+ proofchain_cli.egg-info/dependency_links.txt
8
+ proofchain_cli.egg-info/entry_points.txt
9
+ proofchain_cli.egg-info/requires.txt
10
+ proofchain_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ proofchain = proofchain.cli:main
@@ -0,0 +1,2 @@
1
+ psycopg2-binary
2
+ cryptography
@@ -0,0 +1 @@
1
+ proofchain
@@ -0,0 +1,13 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "proofchain-cli"
7
+ version = "0.1.0"
8
+ description = "Hash-chained, signed, independently-verifiable audit proofs for any Postgres table of real events."
9
+ requires-python = ">=3.9"
10
+ dependencies = ["psycopg2-binary", "cryptography"]
11
+
12
+ [project.scripts]
13
+ proofchain = "proofchain.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+