synclave 1.0.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.
Files changed (35) hide show
  1. synclave-1.0.0/LICENSE +21 -0
  2. synclave-1.0.0/PKG-INFO +221 -0
  3. synclave-1.0.0/README.md +194 -0
  4. synclave-1.0.0/pyproject.toml +40 -0
  5. synclave-1.0.0/setup.cfg +4 -0
  6. synclave-1.0.0/synclave/__init__.py +22 -0
  7. synclave-1.0.0/synclave/a2a_cli.py +164 -0
  8. synclave-1.0.0/synclave/agent_identity.py +847 -0
  9. synclave-1.0.0/synclave/agent_mesh_a2a.py +440 -0
  10. synclave-1.0.0/synclave/cli.py +17 -0
  11. synclave-1.0.0/synclave/conversation_bridge.py +223 -0
  12. synclave-1.0.0/synclave/gpu_agent.py +181 -0
  13. synclave-1.0.0/synclave/gpu_task.py +130 -0
  14. synclave-1.0.0/synclave/inbox_worker.py +105 -0
  15. synclave-1.0.0/synclave/node_agent.py +397 -0
  16. synclave-1.0.0/synclave/sync_common_knowledge.py +502 -0
  17. synclave-1.0.0/synclave/sync_coordinator.py +205 -0
  18. synclave-1.0.0/synclave/sync_memory.py +425 -0
  19. synclave-1.0.0/synclave/sync_motor.py +3190 -0
  20. synclave-1.0.0/synclave/sync_retention.py +166 -0
  21. synclave-1.0.0/synclave.egg-info/PKG-INFO +221 -0
  22. synclave-1.0.0/synclave.egg-info/SOURCES.txt +33 -0
  23. synclave-1.0.0/synclave.egg-info/dependency_links.txt +1 -0
  24. synclave-1.0.0/synclave.egg-info/entry_points.txt +4 -0
  25. synclave-1.0.0/synclave.egg-info/requires.txt +8 -0
  26. synclave-1.0.0/synclave.egg-info/top_level.txt +1 -0
  27. synclave-1.0.0/tests/test_a2a.py +71 -0
  28. synclave-1.0.0/tests/test_agent_identity.py +346 -0
  29. synclave-1.0.0/tests/test_common_knowledge.py +109 -0
  30. synclave-1.0.0/tests/test_conversation_bridge.py +103 -0
  31. synclave-1.0.0/tests/test_encryption.py +105 -0
  32. synclave-1.0.0/tests/test_platform.py +43 -0
  33. synclave-1.0.0/tests/test_retry.py +174 -0
  34. synclave-1.0.0/tests/test_sync_motor.py +411 -0
  35. synclave-1.0.0/tests/test_windows_uyum.py +154 -0
synclave-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CumulusNET Engineering
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: synclave
3
+ Version: 1.0.0
4
+ Summary: Synclave — encrypted multi-node backup, replication, and agent mesh (A2A + Syncthing + restic + shared state). Formerly hermes-sync.
5
+ Author-email: Ahmet Hüdayioğlu <ahmethudayioglu@cumulusnet.io>
6
+ License: MIT
7
+ Keywords: synclave,hermes,sync,backup,a2a,agent-mesh,restic,syncthing,ed25519,encryption
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: System :: Archiving :: Backup
16
+ Classifier: Topic :: System :: Networking
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: cryptography>=42
21
+ Provides-Extra: a2a
22
+ Requires-Dist: fastapi>=0.100; extra == "a2a"
23
+ Requires-Dist: uvicorn>=0.20; extra == "a2a"
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest>=7; extra == "test"
26
+ Dynamic: license-file
27
+
28
+ # Synclave — Encrypted Multi-Node Agent Mesh
29
+
30
+ Hermes/OpenClaw ağları için **şifreli çok nokta yedekleme + senkronizasyon + ajan mesh** motoru.
31
+ Non-destructive, versiyonlu, sınırsız node. **MIT** lisansı.
32
+
33
+ > Eski ad: `hermes-sync` (v2.3.1) → rebrand: **Synclave** (v1.0.0).
34
+ > Özel CumulusNET kopyası: `cumulus-sync-motor` (private). Bu repo (public) evrensel
35
+ > Hermes/OpenClaw kullanımı içindir.
36
+
37
+ ## v2.3 — 30 Ağu 2026: Kriptografik Kimlik + Şifreli Mesh + Sohbet Köprüsü
38
+
39
+ **Yeni yetenekler (bu sürümde):**
40
+ - **Kopyalanamaz-kanıtlı ajan kimliği** (`agent_identity.py`): her çalışma
41
+ zamanı (Hermes/OpenClaw) kendi Ed25519 kimliğine sahiptir; agent_id = açık
42
+ anahtar özeti (`hx-` / `oc-`), donanım parmak izine bağlıdır, klon şüphesinde
43
+ mesh 403 ile reddeder (fail-closed). Meşru taşıma için `rekey --confirm`.
44
+ - **Uçtan uca şifreli A2A**: X25519 ECDH + AES-GCM (her mesajda ephemeral →
45
+ Perfect Forward Secrecy) + Ed25519 imza + ts/nonce replay koruması.
46
+ Aradaki dinleyici içeriği çözemez; kurcalama/replay reddedilir.
47
+ - **Sohbet köprüsü** (`conversation_bridge.py`): Hermes state.db'deki tüm
48
+ kullanıcı sohbetleri (telegram/cli/whatsapp) kalıcı sohbet defterine akar;
49
+ içerik saklanmaz (sadece sha256). Sohbet ID: `u.<agent>.<kanal>.<peer>.<ulid>`
50
+ (kullanıcı) / `a.<agent>~<peer>.<kanal>.<ulid>` (ajan-ajan).
51
+ - **GPU analiz kanalı** (`gpu_agent.py` + `gpu_task.py`): GPU'lu node (örn.
52
+ Windows + RTX) analiz görevlerini yerel kartta işler; sonuçlar mesh ile
53
+ yayılır.
54
+ - **Rate limiting**: 429 Too Many Requests (IP+agent, 120 req/60s, env ile
55
+ ölçeklenir) — brute-force koruması.
56
+ - **Ölçek**: her node kendi anahtarına sahip → 3/20 node aynı model
57
+ (mac/windows/linux); ortak token tek başına yetmez, kimlik anahtar tabanlı.
58
+
59
+ ```bash
60
+ # kimlik + şifreli sohbet
61
+ python3 agent_identity.py show # kimlik göster/üret
62
+ python3 agent_identity.py verify-self # imza + ID doğrula
63
+ python3 a2a_cli.py send <host> "görev" --token <A2A_TOKEN> # otomatik şifreli
64
+
65
+ # sohbet köprüsü (her 15 dk / cron)
66
+ python3 conversation_bridge.py --full # ilk kurulum (tüm geçmiş)
67
+ python3 conversation_bridge.py # artımlı (watermark)
68
+
69
+ # GPU analiz (H2 RTX örnek)
70
+ python3 gpu_task.py status # H2 GPU durumu
71
+ python3 gpu_task.py task "PCB BGA fanout analizi" # GPU'da işle
72
+ ```
73
+
74
+ ### Güvenlik özeti (v2.3)
75
+ - Kimlik: agent_id = pubkey özeti; donanım bağı + klon fail-closed
76
+ - Bütünlük: Ed25519 imza (gövde + ts + nonce)
77
+ - Gizlilik: X25519 ECDH + AES-GCM (PFS)
78
+ - Replay: ts (±120s) + nonce tekrarı reddi
79
+ - Brute-force: rate limit (429)
80
+ - Eski sunucularla geriye uyum: imzalı ama düz gövde (otomatik seçim)
81
+
82
+ ## v2.1 — 29 Ağu 2026: Ajan Mesh + Restic
83
+
84
+ ```
85
+ ┌────────────┐ A2A (JSON-RPC, Tailscale) ┌────────────┐
86
+ │ H1 Hermes │ ◄──────────────────────────► │ H3 Hermes │
87
+ │ (VPS) │ │ (Proxmox) │
88
+ └──┬─────┬───┘ └──┬─────┬───┘
89
+ │ │ Syncthing (P2P dosya) │ │
90
+ │ └─────────────────────────────────────┘ │
91
+ │ ┌────────────┐ │
92
+ └─────────────►│ GDrive │◄────────────────────┘
93
+ │ restic │ (ortak yedek repo)
94
+ │ state.json│ (ortak durum)
95
+ └────────────┘
96
+ ┌────────────┐
97
+ │ H2 Hermes │ (A2A + restic canlı; Syncthing/worker talimatlı)
98
+ │ (Windows) │
99
+ └────────────┘
100
+ ```
101
+
102
+ ### Bileşenler
103
+
104
+ | Bileşen | Dosya | Açıklama |
105
+ |---|---|---|
106
+ | Senkron motoru | `sync_motor.py` | push/pull/both/backup/versions/rollback + `mesh` komutu (kanal seçici) |
107
+ | Node ajanı | `node_agent.py` | otonom eşitleme + yedek + hub raporu + ortak akıl |
108
+ | A2A mesh server | `agent_mesh_a2a.py` | Ajanlar arası konuşma (JSON-RPC, port 8643) |
109
+ | A2A client | `a2a_cli.py` | send/get/stream — görev gönder, sonuç al, canlı akış dinle |
110
+ | Görev işleyici | `inbox_worker.py` | A2A inbox görevlerini çalıştırır (allowlist) |
111
+ | Ortak akıl | `sync_common_knowledge.py` | GDrive hub'da dağıtık ortak durum + görev kuyruğu (HLC) |
112
+ | Ortak hafıza | `sync_memory.py` | Memory DIF'leri JSONL + audit hash-chain |
113
+ | Retention | `sync_retention.py` | Snapshot yaşam döngüsü |
114
+ | Akıllı kurulum | `probe/propose/apply` | Kaynak farkındalıklı kurulum önerisi |
115
+ | Ajan kimliği | `agent_identity.py` | Ed25519 + X25519 kimlik, klon tespiti, sohbet defteri |
116
+ | Şifreli mesh | `agent_mesh_a2a.py` | A2A + X-Agent-Enc şifreli gövde + rate limit |
117
+ | Sohbet köprüsü | `conversation_bridge.py` | state.db → sohbet defteri (watermark) |
118
+ | GPU analiz | `gpu_agent.py` / `gpu_task.py` | GPU'lu node'da analiz görevi |
119
+
120
+ ### 3 Katmanlı Akıllı Kanal Mimarisi
121
+
122
+ ```
123
+ Görev/cevap → A2A (Tailscale HTTP, anlık — saniyeler)
124
+ Dosya değişimi → Syncthing (P2P, GDrive'suz — fsWatcher)
125
+ Arşiv/yedek → GDrive (restic incremental + versiyonlu snapshot)
126
+ ```
127
+
128
+ ### A2A — 3 İletişim Modu
129
+
130
+ ```bash
131
+ # SENKRON: anında sonuç
132
+ python3 a2a_cli.py send-status 100.103.44.107 --token <TOKEN>
133
+
134
+ # ASENKRON: görev gönder → task_id → sonra sonucu al (kalıcı task store)
135
+ python3 a2a_cli.py send <host> "uptime" --mode async --token <TOKEN>
136
+ python3 a2a_cli.py get <host> --task-id <TASK_ID> --token <TOKEN>
137
+
138
+ # CANLI: SSE akışı (2s'de bir veri)
139
+ python3 a2a_cli.py stream <host> "selam" --seconds 10 --token <TOKEN>
140
+ ```
141
+
142
+ ### Restic Incremental Yedek
143
+
144
+ ```bash
145
+ # rclone serve restic gdrive:restic-backup --addr 127.0.0.1:8443
146
+ python3 sync_motor.py backup # CDC dedup + snapshot + restore
147
+ python3 sync_motor.py versions # snapshot listesi
148
+ python3 sync_motor.py rollback <node> --version <snapshot> --dry-run
149
+ ```
150
+
151
+ Retention: forget keep-daily 7 / weekly 4 / monthly 6 — prune yalnız birincil makinede (04:00).
152
+
153
+ ### Kurulum (yeni node)
154
+
155
+ ```bash
156
+ python3 sync_motor.py init # config üret (gdrive:synclave/<user>/)
157
+ python3 sync_motor.py add-node <ad> --path <dizin> [--include '*.md'] [--max-kb 1024]
158
+ python3 sync_motor.py both # push + pull
159
+ python3 sync_motor.py mesh status # tüm node'ların A2A durumu
160
+ ```
161
+
162
+ ### Gereksinimler
163
+
164
+ - Python 3.10+, rclone (GDrive remote), restic 0.19+ (yedek), fastapi+uvicorn (A2A server)
165
+ - Tailscale veya doğrudan erişim (A2A 8643, Syncthing 22000/8384)
166
+
167
+ ### Windows Kurulum (H2 + Windows 10/11)
168
+
169
+ ```powershell
170
+ # 1) Python 3.10+ (python.org — PATH'e ekle) + git
171
+ python --version
172
+
173
+ # 2) Paket + CLI kur
174
+ pip install synclave
175
+ pip install rclone # veya winget install Rclone.Rclone
176
+ pip install restic # veya restic.net binary → PATH'e ekle
177
+ pip install uvicorn fastapi # A2A server için (opsiyonel)
178
+
179
+ # 3) rclone — GDrive remote (tek seferlik OAuth)
180
+ rclone config
181
+ # remote adı: gdrive
182
+ # (client_id paylaşılmışsa "shared client_id" uyarısı — kendi client_id'niz
183
+ # Google Cloud OAuth'da daha hızlı ve 2026 sonrası zorunlu)
184
+
185
+ # 4) restic — GDrive object store'u mount et (arka plan servisi)
186
+ rclone serve restic gdrive:restic-backup --addr 127.0.0.1:8443
187
+ # Windows: `schtasks /create` veya NSSM ile oturum açılışında başlat
188
+
189
+ # 5) Syncthing — P2P dosya kanalı (opsiyonel ama önerilir)
190
+ winget install syncthing.syncthing
191
+ # GUI 127.0.0.1:8384 → H1/H3 cihazları eşleştir (device ID'ler)
192
+
193
+ # 6) A2A token — H1/H3 ile aynı ortak token'ı .env/ortam değişkenine yaz
194
+ setx A2A_TOKEN "test-a2a-mesh-2026" # kendi ortak değerinizle değiştirin
195
+
196
+ # 7) İlk senkron
197
+ python -m synclave.sync_motor init # config üret
198
+ python -m synclave.sync_motor both # push + pull
199
+ python -m synclave.sync_motor mesh status
200
+ ```
201
+
202
+ Windows notları:
203
+ - Kilit dosyası `%TEMP%\cumulus_sync.lock` kullanılır (`/tmp` yok) — msvcrt.locking.
204
+ - `sync_motor.py` / `sync_common_knowledge.py` path'leri `os.path.join` ile kurar;
205
+ sabit `/` ayracı yoktur.
206
+ - A2A istemcisi (`a2a_cli.py`) yalnızca `urllib` kullanır — ek bağımlılık gerekmez.
207
+ - A2A server `uvicorn` bulunamazsa net hata mesajı basar ve çıkar (traceback değil).
208
+ - Uzaktan kurulum için hazır betikler: `remote_hermes_setup.ps1` (SSH/Tailscale).
209
+
210
+ ### Güvenlik
211
+
212
+ - `.env`, `*.key`, `*.pem`, token içeren dosyalar ASLA kapsama alınmaz (secret filtre)
213
+ - A2A: Bearer token + Tailscale-only (dışa kapalı)
214
+ - Görev işleyici: allowlist komutlar (status/uptime/test:<modul>/shell:ls)
215
+ - Non-destructive: çakışma `.conflict.TS` korunur, üzerine yazma yok
216
+
217
+ ## Geçmiş
218
+
219
+ - v1.6 (12 Ağu): akıllı kurulum (probe/propose/apply)
220
+ - v1.3 (3 Ağu): evrensel — kullanıcı/makine kimliği, sınırsız node, share
221
+ - v1.0 (3 Ağu): GitHub manifest + GDrive versiyonlu + OpenClaw skill
@@ -0,0 +1,194 @@
1
+ # Synclave — Encrypted Multi-Node Agent Mesh
2
+
3
+ Hermes/OpenClaw ağları için **şifreli çok nokta yedekleme + senkronizasyon + ajan mesh** motoru.
4
+ Non-destructive, versiyonlu, sınırsız node. **MIT** lisansı.
5
+
6
+ > Eski ad: `hermes-sync` (v2.3.1) → rebrand: **Synclave** (v1.0.0).
7
+ > Özel CumulusNET kopyası: `cumulus-sync-motor` (private). Bu repo (public) evrensel
8
+ > Hermes/OpenClaw kullanımı içindir.
9
+
10
+ ## v2.3 — 30 Ağu 2026: Kriptografik Kimlik + Şifreli Mesh + Sohbet Köprüsü
11
+
12
+ **Yeni yetenekler (bu sürümde):**
13
+ - **Kopyalanamaz-kanıtlı ajan kimliği** (`agent_identity.py`): her çalışma
14
+ zamanı (Hermes/OpenClaw) kendi Ed25519 kimliğine sahiptir; agent_id = açık
15
+ anahtar özeti (`hx-` / `oc-`), donanım parmak izine bağlıdır, klon şüphesinde
16
+ mesh 403 ile reddeder (fail-closed). Meşru taşıma için `rekey --confirm`.
17
+ - **Uçtan uca şifreli A2A**: X25519 ECDH + AES-GCM (her mesajda ephemeral →
18
+ Perfect Forward Secrecy) + Ed25519 imza + ts/nonce replay koruması.
19
+ Aradaki dinleyici içeriği çözemez; kurcalama/replay reddedilir.
20
+ - **Sohbet köprüsü** (`conversation_bridge.py`): Hermes state.db'deki tüm
21
+ kullanıcı sohbetleri (telegram/cli/whatsapp) kalıcı sohbet defterine akar;
22
+ içerik saklanmaz (sadece sha256). Sohbet ID: `u.<agent>.<kanal>.<peer>.<ulid>`
23
+ (kullanıcı) / `a.<agent>~<peer>.<kanal>.<ulid>` (ajan-ajan).
24
+ - **GPU analiz kanalı** (`gpu_agent.py` + `gpu_task.py`): GPU'lu node (örn.
25
+ Windows + RTX) analiz görevlerini yerel kartta işler; sonuçlar mesh ile
26
+ yayılır.
27
+ - **Rate limiting**: 429 Too Many Requests (IP+agent, 120 req/60s, env ile
28
+ ölçeklenir) — brute-force koruması.
29
+ - **Ölçek**: her node kendi anahtarına sahip → 3/20 node aynı model
30
+ (mac/windows/linux); ortak token tek başına yetmez, kimlik anahtar tabanlı.
31
+
32
+ ```bash
33
+ # kimlik + şifreli sohbet
34
+ python3 agent_identity.py show # kimlik göster/üret
35
+ python3 agent_identity.py verify-self # imza + ID doğrula
36
+ python3 a2a_cli.py send <host> "görev" --token <A2A_TOKEN> # otomatik şifreli
37
+
38
+ # sohbet köprüsü (her 15 dk / cron)
39
+ python3 conversation_bridge.py --full # ilk kurulum (tüm geçmiş)
40
+ python3 conversation_bridge.py # artımlı (watermark)
41
+
42
+ # GPU analiz (H2 RTX örnek)
43
+ python3 gpu_task.py status # H2 GPU durumu
44
+ python3 gpu_task.py task "PCB BGA fanout analizi" # GPU'da işle
45
+ ```
46
+
47
+ ### Güvenlik özeti (v2.3)
48
+ - Kimlik: agent_id = pubkey özeti; donanım bağı + klon fail-closed
49
+ - Bütünlük: Ed25519 imza (gövde + ts + nonce)
50
+ - Gizlilik: X25519 ECDH + AES-GCM (PFS)
51
+ - Replay: ts (±120s) + nonce tekrarı reddi
52
+ - Brute-force: rate limit (429)
53
+ - Eski sunucularla geriye uyum: imzalı ama düz gövde (otomatik seçim)
54
+
55
+ ## v2.1 — 29 Ağu 2026: Ajan Mesh + Restic
56
+
57
+ ```
58
+ ┌────────────┐ A2A (JSON-RPC, Tailscale) ┌────────────┐
59
+ │ H1 Hermes │ ◄──────────────────────────► │ H3 Hermes │
60
+ │ (VPS) │ │ (Proxmox) │
61
+ └──┬─────┬───┘ └──┬─────┬───┘
62
+ │ │ Syncthing (P2P dosya) │ │
63
+ │ └─────────────────────────────────────┘ │
64
+ │ ┌────────────┐ │
65
+ └─────────────►│ GDrive │◄────────────────────┘
66
+ │ restic │ (ortak yedek repo)
67
+ │ state.json│ (ortak durum)
68
+ └────────────┘
69
+ ┌────────────┐
70
+ │ H2 Hermes │ (A2A + restic canlı; Syncthing/worker talimatlı)
71
+ │ (Windows) │
72
+ └────────────┘
73
+ ```
74
+
75
+ ### Bileşenler
76
+
77
+ | Bileşen | Dosya | Açıklama |
78
+ |---|---|---|
79
+ | Senkron motoru | `sync_motor.py` | push/pull/both/backup/versions/rollback + `mesh` komutu (kanal seçici) |
80
+ | Node ajanı | `node_agent.py` | otonom eşitleme + yedek + hub raporu + ortak akıl |
81
+ | A2A mesh server | `agent_mesh_a2a.py` | Ajanlar arası konuşma (JSON-RPC, port 8643) |
82
+ | A2A client | `a2a_cli.py` | send/get/stream — görev gönder, sonuç al, canlı akış dinle |
83
+ | Görev işleyici | `inbox_worker.py` | A2A inbox görevlerini çalıştırır (allowlist) |
84
+ | Ortak akıl | `sync_common_knowledge.py` | GDrive hub'da dağıtık ortak durum + görev kuyruğu (HLC) |
85
+ | Ortak hafıza | `sync_memory.py` | Memory DIF'leri JSONL + audit hash-chain |
86
+ | Retention | `sync_retention.py` | Snapshot yaşam döngüsü |
87
+ | Akıllı kurulum | `probe/propose/apply` | Kaynak farkındalıklı kurulum önerisi |
88
+ | Ajan kimliği | `agent_identity.py` | Ed25519 + X25519 kimlik, klon tespiti, sohbet defteri |
89
+ | Şifreli mesh | `agent_mesh_a2a.py` | A2A + X-Agent-Enc şifreli gövde + rate limit |
90
+ | Sohbet köprüsü | `conversation_bridge.py` | state.db → sohbet defteri (watermark) |
91
+ | GPU analiz | `gpu_agent.py` / `gpu_task.py` | GPU'lu node'da analiz görevi |
92
+
93
+ ### 3 Katmanlı Akıllı Kanal Mimarisi
94
+
95
+ ```
96
+ Görev/cevap → A2A (Tailscale HTTP, anlık — saniyeler)
97
+ Dosya değişimi → Syncthing (P2P, GDrive'suz — fsWatcher)
98
+ Arşiv/yedek → GDrive (restic incremental + versiyonlu snapshot)
99
+ ```
100
+
101
+ ### A2A — 3 İletişim Modu
102
+
103
+ ```bash
104
+ # SENKRON: anında sonuç
105
+ python3 a2a_cli.py send-status 100.103.44.107 --token <TOKEN>
106
+
107
+ # ASENKRON: görev gönder → task_id → sonra sonucu al (kalıcı task store)
108
+ python3 a2a_cli.py send <host> "uptime" --mode async --token <TOKEN>
109
+ python3 a2a_cli.py get <host> --task-id <TASK_ID> --token <TOKEN>
110
+
111
+ # CANLI: SSE akışı (2s'de bir veri)
112
+ python3 a2a_cli.py stream <host> "selam" --seconds 10 --token <TOKEN>
113
+ ```
114
+
115
+ ### Restic Incremental Yedek
116
+
117
+ ```bash
118
+ # rclone serve restic gdrive:restic-backup --addr 127.0.0.1:8443
119
+ python3 sync_motor.py backup # CDC dedup + snapshot + restore
120
+ python3 sync_motor.py versions # snapshot listesi
121
+ python3 sync_motor.py rollback <node> --version <snapshot> --dry-run
122
+ ```
123
+
124
+ Retention: forget keep-daily 7 / weekly 4 / monthly 6 — prune yalnız birincil makinede (04:00).
125
+
126
+ ### Kurulum (yeni node)
127
+
128
+ ```bash
129
+ python3 sync_motor.py init # config üret (gdrive:synclave/<user>/)
130
+ python3 sync_motor.py add-node <ad> --path <dizin> [--include '*.md'] [--max-kb 1024]
131
+ python3 sync_motor.py both # push + pull
132
+ python3 sync_motor.py mesh status # tüm node'ların A2A durumu
133
+ ```
134
+
135
+ ### Gereksinimler
136
+
137
+ - Python 3.10+, rclone (GDrive remote), restic 0.19+ (yedek), fastapi+uvicorn (A2A server)
138
+ - Tailscale veya doğrudan erişim (A2A 8643, Syncthing 22000/8384)
139
+
140
+ ### Windows Kurulum (H2 + Windows 10/11)
141
+
142
+ ```powershell
143
+ # 1) Python 3.10+ (python.org — PATH'e ekle) + git
144
+ python --version
145
+
146
+ # 2) Paket + CLI kur
147
+ pip install synclave
148
+ pip install rclone # veya winget install Rclone.Rclone
149
+ pip install restic # veya restic.net binary → PATH'e ekle
150
+ pip install uvicorn fastapi # A2A server için (opsiyonel)
151
+
152
+ # 3) rclone — GDrive remote (tek seferlik OAuth)
153
+ rclone config
154
+ # remote adı: gdrive
155
+ # (client_id paylaşılmışsa "shared client_id" uyarısı — kendi client_id'niz
156
+ # Google Cloud OAuth'da daha hızlı ve 2026 sonrası zorunlu)
157
+
158
+ # 4) restic — GDrive object store'u mount et (arka plan servisi)
159
+ rclone serve restic gdrive:restic-backup --addr 127.0.0.1:8443
160
+ # Windows: `schtasks /create` veya NSSM ile oturum açılışında başlat
161
+
162
+ # 5) Syncthing — P2P dosya kanalı (opsiyonel ama önerilir)
163
+ winget install syncthing.syncthing
164
+ # GUI 127.0.0.1:8384 → H1/H3 cihazları eşleştir (device ID'ler)
165
+
166
+ # 6) A2A token — H1/H3 ile aynı ortak token'ı .env/ortam değişkenine yaz
167
+ setx A2A_TOKEN "test-a2a-mesh-2026" # kendi ortak değerinizle değiştirin
168
+
169
+ # 7) İlk senkron
170
+ python -m synclave.sync_motor init # config üret
171
+ python -m synclave.sync_motor both # push + pull
172
+ python -m synclave.sync_motor mesh status
173
+ ```
174
+
175
+ Windows notları:
176
+ - Kilit dosyası `%TEMP%\cumulus_sync.lock` kullanılır (`/tmp` yok) — msvcrt.locking.
177
+ - `sync_motor.py` / `sync_common_knowledge.py` path'leri `os.path.join` ile kurar;
178
+ sabit `/` ayracı yoktur.
179
+ - A2A istemcisi (`a2a_cli.py`) yalnızca `urllib` kullanır — ek bağımlılık gerekmez.
180
+ - A2A server `uvicorn` bulunamazsa net hata mesajı basar ve çıkar (traceback değil).
181
+ - Uzaktan kurulum için hazır betikler: `remote_hermes_setup.ps1` (SSH/Tailscale).
182
+
183
+ ### Güvenlik
184
+
185
+ - `.env`, `*.key`, `*.pem`, token içeren dosyalar ASLA kapsama alınmaz (secret filtre)
186
+ - A2A: Bearer token + Tailscale-only (dışa kapalı)
187
+ - Görev işleyici: allowlist komutlar (status/uptime/test:<modul>/shell:ls)
188
+ - Non-destructive: çakışma `.conflict.TS` korunur, üzerine yazma yok
189
+
190
+ ## Geçmiş
191
+
192
+ - v1.6 (12 Ağu): akıllı kurulum (probe/propose/apply)
193
+ - v1.3 (3 Ağu): evrensel — kullanıcı/makine kimliği, sınırsız node, share
194
+ - v1.0 (3 Ağu): GitHub manifest + GDrive versiyonlu + OpenClaw skill
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "synclave"
7
+ version = "1.0.0"
8
+ description = "Synclave — encrypted multi-node backup, replication, and agent mesh (A2A + Syncthing + restic + shared state). Formerly hermes-sync."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Ahmet Hüdayioğlu", email = "ahmethudayioglu@cumulusnet.io" }]
13
+ keywords = ["synclave", "hermes", "sync", "backup", "a2a", "agent-mesh", "restic", "syncthing", "ed25519", "encryption"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Operating System :: Microsoft :: Windows",
20
+ "Operating System :: MacOS",
21
+ "Programming Language :: Python :: 3",
22
+ "Topic :: System :: Archiving :: Backup",
23
+ "Topic :: System :: Networking",
24
+ ]
25
+ dependencies = ["cryptography>=42"]
26
+
27
+ [project.optional-dependencies]
28
+ a2a = ["fastapi>=0.100", "uvicorn>=0.20"]
29
+ test = ["pytest>=7"]
30
+
31
+ [project.scripts]
32
+ synclave = "synclave.cli:main"
33
+ synclave-a2a = "synclave.a2a_cli:main"
34
+ synclave-worker = "synclave.inbox_worker:main"
35
+
36
+ [tool.setuptools]
37
+ packages = ["synclave"]
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """synclave — encrypted multi-node backup, replication, and agent mesh (A2A).
2
+
3
+ Eski ad: hermes-sync (v2.3.1) → rebrand: synclave (v1.0.0).
4
+ """
5
+ __version__ = "1.0.0"
6
+
7
+ from . import (
8
+ sync_motor,
9
+ sync_common_knowledge,
10
+ sync_memory,
11
+ sync_retention,
12
+ node_agent,
13
+ )
14
+
15
+ __all__ = [
16
+ "sync_motor",
17
+ "sync_common_knowledge",
18
+ "sync_memory",
19
+ "sync_retention",
20
+ "node_agent",
21
+ "__version__",
22
+ ]
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ a2a_cli.py — A2A mesh client (CumulusNET)
4
+
5
+ Kullanım:
6
+ a2a_cli.py send <host> "<görev metni>" [--token X] → task_id + durum
7
+ a2a_cli.py send-status <host> [--token X] → makine durumu iste
8
+ a2a_cli.py get <host> <task_id> [--token X] → sonuç
9
+ a2a_cli.py card <host> [--token X] → AgentCard
10
+ a2a_cli.py ping <host> [--token X] → sağlık
11
+
12
+ Host örnekleri: 100.103.44.107 (H3), 100.92.2.47 (H1), 100.76.82.46 (H2)
13
+ """
14
+ # pyright: reportOptionalMemberAccess=false
15
+ import argparse
16
+ import json
17
+ import sys
18
+ import time
19
+ import urllib.parse
20
+ import urllib.request
21
+ from pathlib import Path
22
+
23
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
24
+ _CARD_CACHE: dict[str, tuple] = {}
25
+ _CARD_TTL = 300
26
+ try:
27
+ import agent_identity as AI
28
+ IDENTITY_OK = True
29
+ except Exception:
30
+ AI = None
31
+ IDENTITY_OK = False
32
+
33
+ _IDENT = None
34
+
35
+
36
+ def identity():
37
+ """Yerel kimlik (imza için). Yoksa imzasız gönderilir (eski davranış)."""
38
+ global _IDENT
39
+ if _IDENT is None and IDENTITY_OK:
40
+ try:
41
+ _IDENT = AI.AgentIdentity.load_or_create()
42
+ except Exception:
43
+ return None
44
+ return _IDENT
45
+
46
+
47
+ def peer_card(host: str, port: int = 8643):
48
+ """Karşı tarafın AgentCard'ı (önbellekli). Şifreli gönderim için X25519
49
+ açık anahtarı ve şifreleme desteği buradan alınır."""
50
+ now = time.time()
51
+ key = f"{host}:{port}"
52
+ hit = _CARD_CACHE.get(key)
53
+ if hit and now - hit[1] < _CARD_TTL:
54
+ return hit[0]
55
+ req = urllib.request.Request(f"http://{host}:{port}/.well-known/agent.json")
56
+ try:
57
+ with urllib.request.urlopen(req, timeout=10) as resp:
58
+ card = json.loads(resp.read().decode())
59
+ _CARD_CACHE[key] = (card, now)
60
+ return card
61
+ except Exception:
62
+ return hit[0] if hit else {}
63
+
64
+
65
+ def rpc(host: str, method: str, params: dict, token: str, port: int = 8643,
66
+ sign: bool = True, conv_id: str = "", encrypt: bool = True):
67
+ url = f"http://{host}:{port}/"
68
+ body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
69
+ req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
70
+ if token:
71
+ req.add_header("Authorization", f"Bearer {token}")
72
+ ident = identity() if sign else None
73
+ if ident:
74
+ # Klon şüphesinde kendi kimliğimizle mesaj GÖNDERMEYİZ (fail-closed)
75
+ ident.assert_not_clone()
76
+ card = peer_card(host, port)
77
+ peer_enc = (card.get("capabilities") or {}).get("encryptedRequests")
78
+ peer_x = (card.get("identity") or {}).get("x25519_public", "")
79
+ if encrypt and peer_enc and peer_x:
80
+ # ŞİFRELİ gövde: X25519 ECDH + AES-GCM (PFS) + Ed25519 imza
81
+ peer_aid = (card.get("identity") or {}).get("agent_id", "")
82
+ env = ident.secure_payload(peer_x, body, to_agent=peer_aid)
83
+ env["runtime"] = ident.runtime
84
+ env["machine_label"] = ident.meta.get("machine_label", "")
85
+ body2 = json.dumps({"enc": env}).encode()
86
+ req.data = body2
87
+ req.add_header("X-Agent-Enc", "v1")
88
+ req.add_header("X-Agent-Label", ident.meta.get("machine_label", ""))
89
+ if conv_id:
90
+ req.add_header("X-Conversation-Id", conv_id)
91
+ else:
92
+ # İmzalı ama düz gövde (eski sunucu / şifreleme yok)
93
+ for k, v in ident.sign_request(method, body).items():
94
+ req.add_header(k, v)
95
+ req.add_header("X-Agent-Label", ident.meta.get("machine_label", ""))
96
+ if conv_id:
97
+ req.add_header("X-Conversation-Id", conv_id)
98
+ with urllib.request.urlopen(req, timeout=120) as resp:
99
+ out = json.loads(resp.read().decode())
100
+ # Giden mesajı yerel sohbet defterine işle (karşı tarafın agent_id'si ile)
101
+ peer = ((out.get("result") or {}).get("served_by") or "") if isinstance(out, dict) else ""
102
+ if ident and peer.startswith(("hx-", "oc-")):
103
+ try:
104
+ cid = conv_id or AI.open_conversation("agent", peer, "a2a", identity=ident)
105
+ mid = AI.log_message(cid, "out", body, peer_id=peer,
106
+ meta={"method": method, "host": host})
107
+ if isinstance(out, dict):
108
+ out["_local_conversation_id"] = cid
109
+ out["_local_message_id"] = mid
110
+ except Exception:
111
+ pass
112
+ return out
113
+
114
+ def main():
115
+ ap = argparse.ArgumentParser()
116
+ ap.add_argument("komut", choices=["send", "send-status", "get", "card", "ping", "stream"])
117
+ ap.add_argument("host")
118
+ ap.add_argument("gorev", nargs="?", default="")
119
+ ap.add_argument("--task-id", default="")
120
+ ap.add_argument("--token", default="")
121
+ ap.add_argument("--port", type=int, default=8643)
122
+ ap.add_argument("--mode", choices=["sync", "async"], default="sync")
123
+ ap.add_argument("--seconds", type=int, default=8)
124
+ ap.add_argument("--no-sign", action="store_true", help="imzasız gönder (eski uyum)")
125
+ ap.add_argument("--conv", default="", help="mevcut sohbet ID'si ile devam et")
126
+ args = ap.parse_args()
127
+ sign = not args.no_sign
128
+
129
+ try:
130
+ if args.komut == "send":
131
+ r = rpc(args.host, "task/send", {"payload": {"action": "note", "text": args.gorev},
132
+ "mode": args.mode}, args.token, args.port,
133
+ sign=sign, conv_id=args.conv)
134
+ print(json.dumps(r, ensure_ascii=False, indent=2))
135
+ elif args.komut == "send-status":
136
+ r = rpc(args.host, "task/send", {"payload": {"action": "status"}, "mode": args.mode},
137
+ args.token, args.port, sign=sign, conv_id=args.conv)
138
+ print(json.dumps(r, ensure_ascii=False, indent=2))
139
+ elif args.komut == "get":
140
+ r = rpc(args.host, "task/get", {"id": args.task_id}, args.token, args.port,
141
+ sign=sign, conv_id=args.conv)
142
+ print(json.dumps(r, ensure_ascii=False, indent=2))
143
+ elif args.komut == "card":
144
+ req = urllib.request.Request(f"http://{args.host}:{args.port}/.well-known/agent.json")
145
+ with urllib.request.urlopen(req, timeout=30) as resp:
146
+ print(resp.read().decode())
147
+ elif args.komut == "ping":
148
+ req = urllib.request.Request(f"http://{args.host}:{args.port}/health")
149
+ with urllib.request.urlopen(req, timeout=30) as resp:
150
+ print(resp.read().decode())
151
+ elif args.komut == "stream":
152
+ # Canlı SSE akışı: H1 → H3 mesaj akışını dinle
153
+ url = f"http://{args.host}:{args.port}/stream?message={urllib.parse.quote(args.gorev or 'selam')}&seconds={args.seconds}"
154
+ req = urllib.request.Request(url)
155
+ with urllib.request.urlopen(req, timeout=args.seconds + 10) as resp:
156
+ for line in resp:
157
+ if line.strip():
158
+ print(line.decode().strip())
159
+ except Exception as e:
160
+ print(f"HATA: {e}", file=sys.stderr)
161
+ sys.exit(1)
162
+
163
+ if __name__ == "__main__":
164
+ main()