agmem 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

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.
Files changed (48) hide show
  1. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/METADATA +144 -14
  2. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/RECORD +48 -28
  3. memvcs/cli.py +10 -0
  4. memvcs/commands/add.py +6 -0
  5. memvcs/commands/audit.py +59 -0
  6. memvcs/commands/clone.py +7 -0
  7. memvcs/commands/daemon.py +45 -0
  8. memvcs/commands/distill.py +24 -0
  9. memvcs/commands/federated.py +59 -0
  10. memvcs/commands/fsck.py +31 -0
  11. memvcs/commands/garden.py +22 -0
  12. memvcs/commands/gc.py +66 -0
  13. memvcs/commands/merge.py +55 -1
  14. memvcs/commands/prove.py +66 -0
  15. memvcs/commands/pull.py +27 -0
  16. memvcs/commands/resolve.py +130 -0
  17. memvcs/commands/timeline.py +27 -0
  18. memvcs/commands/verify.py +74 -23
  19. memvcs/commands/when.py +27 -0
  20. memvcs/core/audit.py +124 -0
  21. memvcs/core/compression_pipeline.py +157 -0
  22. memvcs/core/consistency.py +9 -9
  23. memvcs/core/crypto_verify.py +291 -0
  24. memvcs/core/distiller.py +47 -29
  25. memvcs/core/encryption.py +169 -0
  26. memvcs/core/federated.py +147 -0
  27. memvcs/core/gardener.py +47 -29
  28. memvcs/core/ipfs_remote.py +200 -0
  29. memvcs/core/knowledge_graph.py +77 -5
  30. memvcs/core/llm/__init__.py +10 -0
  31. memvcs/core/llm/anthropic_provider.py +50 -0
  32. memvcs/core/llm/base.py +27 -0
  33. memvcs/core/llm/factory.py +30 -0
  34. memvcs/core/llm/openai_provider.py +36 -0
  35. memvcs/core/merge.py +36 -23
  36. memvcs/core/objects.py +39 -19
  37. memvcs/core/pack.py +278 -0
  38. memvcs/core/privacy_budget.py +63 -0
  39. memvcs/core/remote.py +229 -3
  40. memvcs/core/repository.py +82 -2
  41. memvcs/core/temporal_index.py +9 -0
  42. memvcs/core/trust.py +103 -0
  43. memvcs/core/vector_store.py +15 -1
  44. memvcs/core/zk_proofs.py +158 -0
  45. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/WHEEL +0 -0
  46. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/entry_points.txt +0 -0
  47. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/licenses/LICENSE +0 -0
  48. {agmem-0.1.2.dist-info → agmem-0.1.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,158 @@
1
+ """
2
+ Zero-knowledge proof system for agmem.
3
+
4
+ Hash/signature-based proofs: keyword containment (Merkle set membership),
5
+ memory freshness (signed timestamp). Full zk-SNARK backend can be added later.
6
+ """
7
+
8
+ import base64
9
+ import hashlib
10
+ import json
11
+ import os
12
+ from pathlib import Path
13
+ from typing import Optional, List, Tuple, Any, Dict
14
+
15
+ from .crypto_verify import (
16
+ build_merkle_tree,
17
+ merkle_proof,
18
+ verify_merkle_proof,
19
+ load_public_key,
20
+ load_private_key_from_env,
21
+ sign_merkle_root,
22
+ verify_signature,
23
+ ED25519_AVAILABLE,
24
+ )
25
+
26
+
27
+ def _word_hashes(content: str) -> List[str]:
28
+ """Extract words and return sorted list of SHA-256 hashes (hex)."""
29
+ words = set()
30
+ for word in content.split():
31
+ w = word.strip().lower()
32
+ if len(w) >= 1:
33
+ words.add(w)
34
+ return sorted(hashlib.sha256(w.encode()).hexdigest() for w in words)
35
+
36
+
37
+ def prove_keyword_containment(memory_path: Path, keyword: str, output_proof_path: Path) -> bool:
38
+ """
39
+ Prove memory file contains keyword without revealing content.
40
+ Proof: Merkle set membership of H(keyword) over word hashes in file.
41
+ """
42
+ if not memory_path.exists() or not memory_path.is_file():
43
+ return False
44
+ try:
45
+ content = memory_path.read_text(encoding="utf-8", errors="replace")
46
+ except Exception:
47
+ return False
48
+ word_hashes_list = _word_hashes(content)
49
+ keyword_hash = hashlib.sha256(keyword.strip().lower().encode()).hexdigest()
50
+ if keyword_hash not in word_hashes_list:
51
+ return False
52
+ root = build_merkle_tree(word_hashes_list)
53
+ proof_path_list = merkle_proof(word_hashes_list, keyword_hash)
54
+ if proof_path_list is None:
55
+ return False
56
+ proof_data = {
57
+ "statement_type": "keyword",
58
+ "keyword_hash": keyword_hash,
59
+ "root": root,
60
+ "path": proof_path_list,
61
+ }
62
+ output_proof_path.parent.mkdir(parents=True, exist_ok=True)
63
+ output_proof_path.write_text(json.dumps(proof_data, indent=2))
64
+ return True
65
+
66
+
67
+ def prove_memory_freshness(
68
+ memory_path: Path, after_timestamp: str, output_proof_path: Path, mem_dir: Optional[Path] = None
69
+ ) -> bool:
70
+ """
71
+ Prove memory was updated after date without revealing content.
72
+ Proof: signed file mtime (or current time) and optional public key.
73
+ """
74
+ if not memory_path.exists() or not memory_path.is_file():
75
+ return False
76
+ if not ED25519_AVAILABLE:
77
+ return False
78
+ try:
79
+ stat = memory_path.stat()
80
+ ts = stat.st_mtime
81
+ from datetime import datetime, timezone
82
+ iso_ts = datetime.fromtimestamp(ts, tz=timezone.utc).isoformat()
83
+ except Exception:
84
+ return False
85
+ private_pem = load_private_key_from_env() if mem_dir is not None else None
86
+ if private_pem is None:
87
+ return False
88
+ try:
89
+ sig_hex = sign_merkle_root(iso_ts, private_pem)
90
+ except Exception:
91
+ return False
92
+ proof_data = {"statement_type": "freshness", "timestamp": iso_ts, "signature": sig_hex}
93
+ if mem_dir is not None:
94
+ pub_pem = load_public_key(mem_dir)
95
+ if pub_pem is not None:
96
+ proof_data["public_key_pem_b64"] = base64.b64encode(pub_pem).decode()
97
+ output_proof_path.parent.mkdir(parents=True, exist_ok=True)
98
+ output_proof_path.write_text(json.dumps(proof_data, indent=2))
99
+ return True
100
+
101
+
102
+ def verify_proof(proof_path: Path, statement_type: str, **kwargs: Any) -> bool:
103
+ """
104
+ Verify a proof. statement_type in ("keyword", "freshness").
105
+ For keyword: pass keyword=... (the keyword string).
106
+ For freshness: pass after_timestamp=... (ISO date string). Optional mem_dir=... for public key.
107
+ """
108
+ if not proof_path.exists() or not proof_path.is_file():
109
+ return False
110
+ try:
111
+ data = json.loads(proof_path.read_text())
112
+ except Exception:
113
+ return False
114
+ if data.get("statement_type") != statement_type:
115
+ return False
116
+ if statement_type == "keyword":
117
+ keyword = kwargs.get("keyword")
118
+ if keyword is None:
119
+ return False
120
+ keyword_hash = hashlib.sha256(keyword.strip().lower().encode()).hexdigest()
121
+ if data.get("keyword_hash") != keyword_hash:
122
+ return False
123
+ root = data.get("root")
124
+ path_list = data.get("path")
125
+ if not root or path_list is None:
126
+ return False
127
+ return verify_merkle_proof(keyword_hash, path_list, root)
128
+ if statement_type == "freshness":
129
+ after_ts = kwargs.get("after_timestamp")
130
+ if after_ts is None:
131
+ return False
132
+ ts_str = data.get("timestamp")
133
+ sig_hex = data.get("signature")
134
+ if not ts_str or not sig_hex:
135
+ return False
136
+ pub_pem_b64 = data.get("public_key_pem_b64")
137
+ if pub_pem_b64:
138
+ try:
139
+ pub_pem = base64.b64decode(pub_pem_b64)
140
+ except Exception:
141
+ return False
142
+ else:
143
+ mem_dir = kwargs.get("mem_dir")
144
+ if mem_dir is None:
145
+ return False
146
+ pub_pem = load_public_key(Path(mem_dir))
147
+ if pub_pem is None:
148
+ return False
149
+ if not verify_signature(ts_str, sig_hex, pub_pem):
150
+ return False
151
+ try:
152
+ from datetime import datetime
153
+ after_dt = datetime.fromisoformat(after_ts.replace("Z", "+00:00"))
154
+ ts_dt = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
155
+ return ts_dt >= after_dt
156
+ except Exception:
157
+ return False
158
+ return False
File without changes