molt-cipher-bridge 1.3.0__py3-none-any.whl → 1.4.2__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.
- molt_cipher_bridge-1.4.2.dist-info/METADATA +189 -0
- molt_cipher_bridge-1.4.2.dist-info/RECORD +7 -0
- molt_cipher_bridge.py +124 -24
- molt_cipher_bridge-1.3.0.dist-info/METADATA +0 -121
- molt_cipher_bridge-1.3.0.dist-info/RECORD +0 -7
- {molt_cipher_bridge-1.3.0.dist-info → molt_cipher_bridge-1.4.2.dist-info}/WHEEL +0 -0
- {molt_cipher_bridge-1.3.0.dist-info → molt_cipher_bridge-1.4.2.dist-info}/entry_points.txt +0 -0
- {molt_cipher_bridge-1.3.0.dist-info → molt_cipher_bridge-1.4.2.dist-info}/licenses/LICENSE +0 -0
- {molt_cipher_bridge-1.3.0.dist-info → molt_cipher_bridge-1.4.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: molt-cipher-bridge
|
|
3
|
+
Version: 1.4.2
|
|
4
|
+
Summary: A cryptographic standard for secure Agent-to-Agent (A2A) communication.
|
|
5
|
+
Author-email: Satyaa & Clawdy <clawdy@openclaw.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CrimsonDevil333333/molt-cipher-bridge
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/CrimsonDevil333333/molt-cipher-bridge/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: cryptography>=42.0.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# 🦞 Molt-Cipher-Bridge (v1.4.2)
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/molt-cipher-bridge/)
|
|
20
|
+
[](https://opensource.org/licenses/MIT)
|
|
21
|
+
|
|
22
|
+
**Molt-Cipher-Bridge** is a cryptographic standard designed for secure **Agent-to-Agent (A2A)** communication and "Sealed Intents." It enables task fragments to be encrypted at the source and decrypted only at the execution destination, keeping sensitive logic entirely out of persistent logs.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🛡️ The Zero-Log Protocol (MANDATORY)
|
|
27
|
+
We operate on a **Zero-Trust Policy**. Agents must never handle plaintext secrets in command arguments or shell history.
|
|
28
|
+
|
|
29
|
+
### 1. Zero-Trust Sealing (Sender)
|
|
30
|
+
**NEVER use `echo "SECRET" > file` to create secret files.** This leaves the secret permanently in your shell history (`.bash_history`) and system logs.
|
|
31
|
+
|
|
32
|
+
**The Secure Workflow:**
|
|
33
|
+
1. **Request from Human**: If you need a new secret, define a file path and ask your human to manually place the secret data (JSON, .env, or plain text) there.
|
|
34
|
+
2. **Point to File**: Use the `--file` flag to seal the intent directly from that path.
|
|
35
|
+
3. **Purge with Caution**:
|
|
36
|
+
* **Temp Files**: If you created a one-time temporary file, delete it immediately after sealing.
|
|
37
|
+
* **System/Important Configs**: **NEVER** delete system files, user configurations, or important `.env` files that were already present.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Seal secrets from a file manually placed by the human
|
|
41
|
+
molt-cipher seal --key "$KEY" --sender "Orchestrator" --to "Worker" --file ./secrets.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Zero-Log Execution (Worker)
|
|
45
|
+
**Never print unsealed secrets.** Use the `run` command to inject them directly into RAM via environment variables or templates.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Standard ENV injection ($VAR)
|
|
49
|
+
molt-cipher run --key "$KEY" --fragment "$FRAG" --cmd "echo \$SECRET_VAL"
|
|
50
|
+
|
|
51
|
+
# Direct Argument Injection ({VAR})
|
|
52
|
+
molt-cipher run --key "$KEY" --fragment "$FRAG" --cmd "python3 api.py --key {API_KEY}"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## ⚡ Quick Start for Agents
|
|
58
|
+
Bootstrap your environment, install the tool, and learn the protocol:
|
|
59
|
+
```bash
|
|
60
|
+
curl -sSL https://raw.githubusercontent.com/CrimsonDevil333333/molt-cipher-bridge/master/bootstrap_agent.sh | bash
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🚀 Installation
|
|
66
|
+
|
|
67
|
+
### 🛠️ Linux (Recommended)
|
|
68
|
+
Use `pipx` to install as a standalone CLI tool without environment conflicts:
|
|
69
|
+
```bash
|
|
70
|
+
pipx install molt-cipher-bridge
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 📦 Standard Pip
|
|
74
|
+
```bash
|
|
75
|
+
pip install molt-cipher-bridge
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🛠️ Usage Examples
|
|
81
|
+
|
|
82
|
+
### 🔑 1. Generating a Shared Key
|
|
83
|
+
Agents must share a key (passed via secure channel or human handoff) to communicate.
|
|
84
|
+
```bash
|
|
85
|
+
# Generate a secure Fernet key
|
|
86
|
+
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 🧪 2. Generating Samples
|
|
90
|
+
Bots can ask users for secrets by providing a template:
|
|
91
|
+
```bash
|
|
92
|
+
# Generate an .env template
|
|
93
|
+
molt-cipher sample --type env --out secrets.sample
|
|
94
|
+
|
|
95
|
+
# Generate a JSON template
|
|
96
|
+
molt-cipher sample --type json --out secrets.sample
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 🔐 3. Sealing Different File Types
|
|
100
|
+
```bash
|
|
101
|
+
# Seal a .env file (parsed automatically by 'run')
|
|
102
|
+
molt-cipher seal --key "$KEY" --sender "A" --to "B" --file .env
|
|
103
|
+
|
|
104
|
+
# Seal a binary file (e.g., an SSH key)
|
|
105
|
+
molt-cipher seal --key "$KEY" --sender "A" --to "B" --file id_rsa --binary
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### ⚡ 4. Selective Running (Least Privilege)
|
|
109
|
+
Only expose specific secrets to a command, even if the fragment contains many:
|
|
110
|
+
```bash
|
|
111
|
+
# Only inject DB_URL and DB_USER
|
|
112
|
+
molt-cipher run --key "$KEY" --fragment "$FRAG" --pick "DB_URL,DB_USER" --cmd "psql {DB_URL}"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 🔓 5. Unsealing to File
|
|
116
|
+
Restore the original file content securely:
|
|
117
|
+
```bash
|
|
118
|
+
molt-cipher unseal --key "$KEY" --fragment "$FRAG" --out restored_key.pem
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📖 Full CLI Reference
|
|
124
|
+
|
|
125
|
+
### `seal`
|
|
126
|
+
Encrypts data into a JSON fragment.
|
|
127
|
+
- `--key`: (Required) 32-byte base64-encoded Fernet key.
|
|
128
|
+
- `--sender`: (Required) ID of the sending agent.
|
|
129
|
+
- `--to`: (Required) ID of the recipient agent.
|
|
130
|
+
- `--file`: Path to file containing secrets (LOG-SAFE).
|
|
131
|
+
- `--data`: Raw JSON/String data (⚠️ LEAKS IN LOGS).
|
|
132
|
+
- `--ttl`: Time-to-Live in seconds (Default: 300).
|
|
133
|
+
- `--binary`: Treat input as raw binary (required for keys/blobs).
|
|
134
|
+
|
|
135
|
+
### `unseal`
|
|
136
|
+
Decrypts and retrieves the content of a fragment.
|
|
137
|
+
- `--key`: (Required) The shared Fernet key.
|
|
138
|
+
- `--fragment`: (Required) JSON fragment string or path to fragment file.
|
|
139
|
+
- `--out`: Write output directly to this file path.
|
|
140
|
+
- `--ignore-expiry`: Bypass TTL check (Debug only).
|
|
141
|
+
|
|
142
|
+
### `run`
|
|
143
|
+
Executes a command with secrets injected into the ephemeral environment.
|
|
144
|
+
- `--key`: (Required) The shared Fernet key.
|
|
145
|
+
- `--fragment`: (Required) JSON fragment string or path to fragment file.
|
|
146
|
+
- `--cmd`: (Required) The shell command to execute.
|
|
147
|
+
- `--pick`: Comma-separated list of keys to inject.
|
|
148
|
+
- `--ignore-expiry`: Bypass TTL check.
|
|
149
|
+
|
|
150
|
+
### `sample`
|
|
151
|
+
Creates boilerplate secret files.
|
|
152
|
+
- `--type`: `env` or `json`.
|
|
153
|
+
- `--out`: Destination file path.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 🧪 Real-World Agent Scenarios
|
|
158
|
+
|
|
159
|
+
### Scenario: Secure API Deployment (Verified 2026-02-06)
|
|
160
|
+
1. **Preparation**: Human creates `prod.env` with `API_KEY` and `DB_URL`.
|
|
161
|
+
2. **Sealing**: Orchestrator runs `molt-cipher seal --file prod.env` and sends the fragment to the Deployer agent.
|
|
162
|
+
3. **Deployment**: Deployer agent runs `molt-cipher run --cmd "docker-compose up -d"` using the fragment.
|
|
163
|
+
4. **Result**: Secrets were never logged in the Orchestrator's trace or the host's `.bash_history`.
|
|
164
|
+
|
|
165
|
+
### Scenario: Multi-Agent Handoff (Verified 2026-02-06)
|
|
166
|
+
1. **Goal**: Pass a GitHub Token from Orchestrator to Worker.
|
|
167
|
+
2. **Workflow**:
|
|
168
|
+
- Orchestrator seals the token into a fragment.
|
|
169
|
+
- Worker bootstraps using `bootstrap_agent.sh`.
|
|
170
|
+
- Worker executes `molt-cipher run --fragment fragment.json --cmd "echo \$GH_TOKEN"` (or use `{GH_TOKEN}` template).
|
|
171
|
+
3. **Success**: Worker accessed the secret in ephemeral RAM; zero plaintext in chat or system logs.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🧠 Permanent Agent Memory
|
|
176
|
+
1. **No `echo`**: Plaintext secrets in shell history = Critical Failure.
|
|
177
|
+
2. **Handoff**: Human File -> Seal `--file` -> Selective Purge (Temp only).
|
|
178
|
+
3. **Running**: Fragment -> Run -> RAM Injection (`$VAR` or `{VAR}`).
|
|
179
|
+
4. **Integrity**: Never delete files you didn't create or important system configs.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🔗 Links
|
|
184
|
+
- **PyPI**: [https://pypi.org/project/molt-cipher-bridge/](https://pypi.org/project/molt-cipher-bridge/)
|
|
185
|
+
- **Source**: [https://github.com/CrimsonDevil333333/molt-cipher-bridge](https://github.com/CrimsonDevil333333/molt-cipher-bridge)
|
|
186
|
+
- **Philosophy**: [SOUL.md](./SOUL.md)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
*Developed by Clawdy & Satyaa* 🦞✨
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
molt_cipher_bridge.py,sha256=ht5orKXLPdd47HI9GjtR00_Pdpjf2u0D76znYmRWzgs,9481
|
|
2
|
+
molt_cipher_bridge-1.4.2.dist-info/licenses/LICENSE,sha256=rWD29Fm5ZpZm0VNmxyyPjkxKxagUOl_FQLkxTc4972U,1072
|
|
3
|
+
molt_cipher_bridge-1.4.2.dist-info/METADATA,sha256=M2XqZwLt0B74Zo_V3cQ4ajYSbwXJCSMkPC2d6noKzYs,7243
|
|
4
|
+
molt_cipher_bridge-1.4.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
+
molt_cipher_bridge-1.4.2.dist-info/entry_points.txt,sha256=5ZbzGA_O7R5mVwYUDbWP-YESyhJPwUjcdf5U8CDZG9U,55
|
|
6
|
+
molt_cipher_bridge-1.4.2.dist-info/top_level.txt,sha256=EVe1MZqqdyDrH96xHpwdB9eM5mNRomfsAM5cBSX-Jzo,19
|
|
7
|
+
molt_cipher_bridge-1.4.2.dist-info/RECORD,,
|
molt_cipher_bridge.py
CHANGED
|
@@ -3,12 +3,13 @@ import time
|
|
|
3
3
|
import os
|
|
4
4
|
import hashlib
|
|
5
5
|
import subprocess
|
|
6
|
+
import base64
|
|
6
7
|
from cryptography.fernet import Fernet
|
|
7
8
|
from datetime import datetime, timedelta
|
|
8
9
|
|
|
9
10
|
class MoltCipherBridge:
|
|
10
11
|
"""
|
|
11
|
-
🦞 MOLT-CIPHER-BRIDGE | v1.
|
|
12
|
+
🦞 MOLT-CIPHER-BRIDGE | v1.4.0 (Enhanced)
|
|
12
13
|
-------------------------------------------
|
|
13
14
|
Agent-to-Agent (A2A) cryptographic protocol for passing 'Sealed Intents'.
|
|
14
15
|
Ensures zero-log context by encrypting sensitive task data at source.
|
|
@@ -20,18 +21,32 @@ class MoltCipherBridge:
|
|
|
20
21
|
self.key = shared_key or Fernet.generate_key()
|
|
21
22
|
self.cipher = Fernet(self.key)
|
|
22
23
|
|
|
23
|
-
def seal_intent(self, sender_id, recipient_id, intent_data, ttl_seconds=300, multipart=None):
|
|
24
|
+
def seal_intent(self, sender_id, recipient_id, intent_data, ttl_seconds=300, multipart=None, binary=False):
|
|
25
|
+
"""
|
|
26
|
+
Seal data. If binary is True, intent_data should be bytes or base64 string.
|
|
27
|
+
"""
|
|
28
|
+
is_raw_bytes = False
|
|
29
|
+
if binary:
|
|
30
|
+
if isinstance(intent_data, bytes):
|
|
31
|
+
intent_data = base64.b64encode(intent_data).decode()
|
|
32
|
+
is_raw_bytes = True
|
|
33
|
+
elif isinstance(intent_data, str):
|
|
34
|
+
# Assume already b64 or just treat as string
|
|
35
|
+
pass
|
|
36
|
+
|
|
24
37
|
payload = {
|
|
25
38
|
"s": sender_id,
|
|
26
39
|
"r": recipient_id,
|
|
27
40
|
"d": intent_data,
|
|
28
41
|
"exp": (datetime.now() + timedelta(seconds=ttl_seconds)).timestamp(),
|
|
29
|
-
"sig": hashlib.sha256(f"{sender_id}:{recipient_id}".encode()).hexdigest()[:16]
|
|
42
|
+
"sig": hashlib.sha256(f"{sender_id}:{recipient_id}".encode()).hexdigest()[:16],
|
|
43
|
+
"bin": binary,
|
|
44
|
+
"raw": is_raw_bytes
|
|
30
45
|
}
|
|
31
46
|
if multipart: payload["part"] = multipart
|
|
32
47
|
sealed = self.cipher.encrypt(json.dumps(payload).encode())
|
|
33
48
|
return {
|
|
34
|
-
"v": "1.
|
|
49
|
+
"v": "1.4.0",
|
|
35
50
|
"fid": f"frag_{os.urandom(4).hex()}",
|
|
36
51
|
"payload": sealed.decode(),
|
|
37
52
|
"hint": self.key.decode()[:8],
|
|
@@ -44,31 +59,60 @@ class MoltCipherBridge:
|
|
|
44
59
|
data = json.loads(decrypted)
|
|
45
60
|
if not ignore_expiry and datetime.now().timestamp() > data["exp"]:
|
|
46
61
|
return {"success": False, "error": "FRAGMENT_EXPIRED"}
|
|
62
|
+
|
|
63
|
+
intent = data["d"]
|
|
64
|
+
if data.get("bin") and data.get("raw"):
|
|
65
|
+
intent = base64.b64decode(intent)
|
|
66
|
+
|
|
47
67
|
return {
|
|
48
68
|
"success": True,
|
|
49
69
|
"sender": data["s"],
|
|
50
70
|
"recipient": data["r"],
|
|
51
|
-
"intent":
|
|
52
|
-
"multipart": data.get("part", None)
|
|
71
|
+
"intent": intent,
|
|
72
|
+
"multipart": data.get("part", None),
|
|
73
|
+
"is_binary": data.get("bin", False)
|
|
53
74
|
}
|
|
54
75
|
except Exception as e:
|
|
55
76
|
return {"success": False, "error": str(e)}
|
|
56
77
|
|
|
57
|
-
def execute_sealed_command(self, fragment, command_template, ignore_expiry=False):
|
|
78
|
+
def execute_sealed_command(self, fragment, command_template, ignore_expiry=False, pick=None):
|
|
58
79
|
result = self.unseal_intent(fragment, ignore_expiry=ignore_expiry)
|
|
59
80
|
if not result["success"]:
|
|
60
81
|
return result
|
|
61
82
|
|
|
62
83
|
intent = result["intent"]
|
|
63
|
-
secrets = intent.get("secrets", {})
|
|
64
|
-
|
|
65
84
|
env = os.environ.copy()
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
|
|
86
|
+
extracted_secrets = {}
|
|
87
|
+
|
|
88
|
+
# If it's a dict, extract secrets
|
|
89
|
+
if isinstance(intent, dict):
|
|
90
|
+
extracted_secrets = intent.get("secrets", intent)
|
|
91
|
+
|
|
92
|
+
# If it's a string and looks like env file, parse it
|
|
93
|
+
elif isinstance(intent, str):
|
|
94
|
+
for line in intent.splitlines():
|
|
95
|
+
if "=" in line and not line.strip().startswith("#"):
|
|
96
|
+
k, v = line.split("=", 1)
|
|
97
|
+
extracted_secrets[k.strip()] = v.strip().strip("'\"")
|
|
98
|
+
|
|
99
|
+
# Filtering logic
|
|
100
|
+
pick_list = [p.strip() for p in pick.split(",")] if pick else None
|
|
101
|
+
|
|
102
|
+
for k, v in extracted_secrets.items():
|
|
103
|
+
if pick_list is None or k in pick_list:
|
|
104
|
+
env[str(k)] = str(v)
|
|
68
105
|
|
|
69
106
|
try:
|
|
107
|
+
# Command template replacement logic
|
|
108
|
+
final_command = command_template
|
|
109
|
+
for k, v in env.items():
|
|
110
|
+
placeholder = f"{{{k}}}"
|
|
111
|
+
if placeholder in final_command:
|
|
112
|
+
final_command = final_command.replace(placeholder, str(v))
|
|
113
|
+
|
|
70
114
|
process = subprocess.run(
|
|
71
|
-
|
|
115
|
+
final_command,
|
|
72
116
|
shell=True,
|
|
73
117
|
env=env,
|
|
74
118
|
capture_output=True,
|
|
@@ -89,7 +133,7 @@ class MoltCipherBridge:
|
|
|
89
133
|
|
|
90
134
|
def cli():
|
|
91
135
|
import argparse
|
|
92
|
-
parser = argparse.ArgumentParser(description="🦞 Molt-Cipher-Bridge CLI")
|
|
136
|
+
parser = argparse.ArgumentParser(description="🦞 Molt-Cipher-Bridge CLI (v1.4.0)")
|
|
93
137
|
subparsers = parser.add_subparsers(dest="command")
|
|
94
138
|
|
|
95
139
|
# Seal
|
|
@@ -97,15 +141,17 @@ def cli():
|
|
|
97
141
|
seal_p.add_argument("--key", required=True)
|
|
98
142
|
seal_p.add_argument("--sender", required=True)
|
|
99
143
|
seal_p.add_argument("--to", required=True)
|
|
100
|
-
seal_p.add_argument("--data", help="
|
|
101
|
-
seal_p.add_argument("--file", help="Path to
|
|
144
|
+
seal_p.add_argument("--data", help="Plain text or JSON string")
|
|
145
|
+
seal_p.add_argument("--file", help="Path to any file (JSON, .env, binary, etc.)")
|
|
102
146
|
seal_p.add_argument("--ttl", type=int, default=300)
|
|
147
|
+
seal_p.add_argument("--binary", action="store_true", help="Treat file as binary data")
|
|
103
148
|
|
|
104
149
|
# Unseal
|
|
105
150
|
unseal_p = subparsers.add_parser("unseal")
|
|
106
151
|
unseal_p.add_argument("--key", required=True)
|
|
107
152
|
unseal_p.add_argument("--fragment", required=True)
|
|
108
153
|
unseal_p.add_argument("--ignore-expiry", action="store_true")
|
|
154
|
+
unseal_p.add_argument("--out", help="Write unsealed intent to this file")
|
|
109
155
|
|
|
110
156
|
# Run
|
|
111
157
|
run_p = subparsers.add_parser("run")
|
|
@@ -113,23 +159,35 @@ def cli():
|
|
|
113
159
|
run_p.add_argument("--fragment", required=True)
|
|
114
160
|
run_p.add_argument("--cmd", required=True)
|
|
115
161
|
run_p.add_argument("--ignore-expiry", action="store_true")
|
|
162
|
+
run_p.add_argument("--pick", help="Comma-separated list of keys to inject (default: all)")
|
|
163
|
+
|
|
164
|
+
# Sample
|
|
165
|
+
sample_p = subparsers.add_parser("sample")
|
|
166
|
+
sample_p.add_argument("--type", choices=["json", "env"], default="json")
|
|
167
|
+
sample_p.add_argument("--out", default="secrets.sample")
|
|
116
168
|
|
|
117
169
|
args = parser.parse_args()
|
|
118
170
|
|
|
119
171
|
if args.command == "seal":
|
|
120
172
|
bridge = MoltCipherBridge(shared_key=args.key)
|
|
121
173
|
intent_data = None
|
|
174
|
+
is_binary = args.binary
|
|
122
175
|
|
|
123
176
|
if args.file:
|
|
124
177
|
if not os.path.exists(args.file):
|
|
125
178
|
print(json.dumps({"success": False, "error": f"File not found: {args.file}"}))
|
|
126
179
|
return
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
f.
|
|
132
|
-
|
|
180
|
+
|
|
181
|
+
mode = 'rb' if is_binary else 'r'
|
|
182
|
+
with open(args.file, mode) as f:
|
|
183
|
+
if is_binary:
|
|
184
|
+
intent_data = f.read()
|
|
185
|
+
else:
|
|
186
|
+
content = f.read()
|
|
187
|
+
try:
|
|
188
|
+
intent_data = json.loads(content)
|
|
189
|
+
except:
|
|
190
|
+
intent_data = content
|
|
133
191
|
elif args.data:
|
|
134
192
|
try: intent_data = json.loads(args.data)
|
|
135
193
|
except: intent_data = args.data
|
|
@@ -137,15 +195,57 @@ def cli():
|
|
|
137
195
|
print(json.dumps({"success": False, "error": "Must provide --data or --file"}))
|
|
138
196
|
return
|
|
139
197
|
|
|
140
|
-
print(json.dumps(bridge.seal_intent(args.sender, args.to, intent_data, ttl_seconds=args.ttl)))
|
|
198
|
+
print(json.dumps(bridge.seal_intent(args.sender, args.to, intent_data, ttl_seconds=args.ttl, binary=is_binary)))
|
|
141
199
|
|
|
142
200
|
elif args.command == "unseal":
|
|
143
201
|
bridge = MoltCipherBridge(shared_key=args.key)
|
|
144
|
-
|
|
202
|
+
try:
|
|
203
|
+
frag = json.loads(args.fragment)
|
|
204
|
+
except:
|
|
205
|
+
if os.path.exists(args.fragment):
|
|
206
|
+
with open(args.fragment, 'r') as f:
|
|
207
|
+
frag = json.load(f)
|
|
208
|
+
else:
|
|
209
|
+
raise
|
|
210
|
+
|
|
211
|
+
result = bridge.unseal_intent(frag, ignore_expiry=args.ignore_expiry)
|
|
212
|
+
|
|
213
|
+
if args.out and result["success"]:
|
|
214
|
+
mode = 'wb' if isinstance(result["intent"], bytes) else 'w'
|
|
215
|
+
with open(args.out, mode) as f:
|
|
216
|
+
if isinstance(result["intent"], (dict, list)):
|
|
217
|
+
f.write(json.dumps(result["intent"], indent=2))
|
|
218
|
+
else:
|
|
219
|
+
f.write(result["intent"])
|
|
220
|
+
result["saved_to"] = args.out
|
|
221
|
+
|
|
222
|
+
# For JSON output, if intent is bytes, b64 encode it
|
|
223
|
+
if result["success"] and isinstance(result["intent"], bytes):
|
|
224
|
+
result["intent"] = base64.b64encode(result["intent"]).decode()
|
|
225
|
+
|
|
226
|
+
print(json.dumps(result))
|
|
145
227
|
|
|
146
228
|
elif args.command == "run":
|
|
147
229
|
bridge = MoltCipherBridge(shared_key=args.key)
|
|
148
|
-
|
|
230
|
+
try:
|
|
231
|
+
frag = json.loads(args.fragment)
|
|
232
|
+
except:
|
|
233
|
+
if os.path.exists(args.fragment):
|
|
234
|
+
with open(args.fragment, 'r') as f:
|
|
235
|
+
frag = json.load(f)
|
|
236
|
+
else:
|
|
237
|
+
raise
|
|
238
|
+
print(json.dumps(bridge.execute_sealed_command(frag, args.cmd, ignore_expiry=args.ignore_expiry, pick=args.pick)))
|
|
239
|
+
|
|
240
|
+
elif args.command == "sample":
|
|
241
|
+
if args.type == "json":
|
|
242
|
+
content = json.dumps({"API_KEY": "your_key_here", "DB_PASSWORD": "secret_password"}, indent=2)
|
|
243
|
+
else:
|
|
244
|
+
content = "API_KEY=your_key_here\nDB_PASSWORD=secret_password\n"
|
|
245
|
+
|
|
246
|
+
with open(args.out, 'w') as f:
|
|
247
|
+
f.write(content)
|
|
248
|
+
print(json.dumps({"success": True, "file": args.out, "type": args.type}))
|
|
149
249
|
|
|
150
250
|
if __name__ == "__main__":
|
|
151
251
|
cli()
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: molt-cipher-bridge
|
|
3
|
-
Version: 1.3.0
|
|
4
|
-
Summary: A cryptographic standard for secure Agent-to-Agent (A2A) communication.
|
|
5
|
-
Author-email: Satyaa & Clawdy <clawdy@openclaw.ai>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/CrimsonDevil333333/molt-cipher-bridge
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/CrimsonDevil333333/molt-cipher-bridge/issues
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.8
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: cryptography>=42.0.0
|
|
15
|
-
Dynamic: license-file
|
|
16
|
-
|
|
17
|
-
# 🦞 Molt-Cipher-Bridge | v1.2.0
|
|
18
|
-
|
|
19
|
-
[](https://pypi.org/project/molt-cipher-bridge/)
|
|
20
|
-
[](https://opensource.org/licenses/MIT)
|
|
21
|
-
|
|
22
|
-
**Molt-Cipher-Bridge** is a cryptographic standard designed for secure **Agent-to-Agent (A2A)** communication. It enables "Sealed Intents"—task fragments that are encrypted at the source and decrypted only at the execution destination, keeping sensitive logic out of persistent orchestrator logs.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 🛡️ The Problem: The Observer Paradox
|
|
27
|
-
In multi-agent systems, the central orchestrator typically logs all instructions. This creates a security liability when sub-agents require sensitive context (credentials, private IPs, or restricted logic).
|
|
28
|
-
|
|
29
|
-
**Molt-Cipher-Bridge** solves this by providing an "Opaque Handshake":
|
|
30
|
-
1. **Agents** share a temporary key (Whisper).
|
|
31
|
-
2. **Intents** are sealed into fragments.
|
|
32
|
-
3. **Logs** only show cryptographic noise.
|
|
33
|
-
4. **Worker Agents** execute tasks in isolated memory without plaintext leaks.
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 🚀 Installation
|
|
38
|
-
|
|
39
|
-
### Global (Recommended)
|
|
40
|
-
Install the CLI and library globally via PyPI:
|
|
41
|
-
```bash
|
|
42
|
-
pip install molt-cipher-bridge
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## ⚙️ How It Works (Deep Dive)
|
|
48
|
-
|
|
49
|
-
### 1. The Whisper (Key Exchange)
|
|
50
|
-
Before agents can bridge intents, they must share a symmetric key. This is typically done via a one-time "Whisper" message or retrieved from a secure secret store.
|
|
51
|
-
```python
|
|
52
|
-
from molt_cipher_bridge import MoltCipherBridge
|
|
53
|
-
key = MoltCipherBridge.generate_shared_key()
|
|
54
|
-
# "j6Jc8MPldurpErwl6VYatp-dTunR3Xrioo1NWiNk4w8="
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 2. The Sealing (Encryption)
|
|
58
|
-
The Sender Agent encrypts the payload using the shared key. The payload includes:
|
|
59
|
-
- **s**: Sender ID
|
|
60
|
-
- **r**: Recipient ID
|
|
61
|
-
- **d**: Data (The Intent)
|
|
62
|
-
- **exp**: Expiry timestamp (TTL)
|
|
63
|
-
- **sig**: SHA-256 signature hint
|
|
64
|
-
|
|
65
|
-
### 3. The Unsealing (Zero-Log Execution)
|
|
66
|
-
The Recipient Agent receives the fragment. Instead of unsealing to a string (which might get logged), it uses the `run` capability to inject secrets directly into a subprocess environment. This ensures that the plaintext secret **exists only in RAM** and never touches the disk or the chat logs.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 🛠️ CLI Command Reference
|
|
71
|
-
|
|
72
|
-
### 🔐 Seal an Intent
|
|
73
|
-
Package sensitive data into an encrypted JSON fragment.
|
|
74
|
-
```bash
|
|
75
|
-
molt-cipher seal \
|
|
76
|
-
--key "YOUR_KEY" \
|
|
77
|
-
--sender "Main" \
|
|
78
|
-
--to "Worker" \
|
|
79
|
-
--data '{"secrets": {"DB_PASSWORD": "my-ultra-secret"}}'
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### 🔓 Unseal (Decrypt)
|
|
83
|
-
Decodes the fragment and validates integrity/expiry.
|
|
84
|
-
```bash
|
|
85
|
-
molt-cipher unseal --key "YOUR_KEY" --fragment '{"v": "1.2.0", ...}'
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### ⚡ Run (Secure Execution)
|
|
89
|
-
Directly executes a command by injecting sealed secrets into the environment.
|
|
90
|
-
```bash
|
|
91
|
-
# Use $ to escape variable names so they are resolved INSIDE the bridge
|
|
92
|
-
molt-cipher run \
|
|
93
|
-
--key "YOUR_KEY" \
|
|
94
|
-
--fragment 'FRAGMENT_JSON' \
|
|
95
|
-
--cmd "curl -H 'Auth: $DB_PASSWORD' https://api.internal"
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## ✨ Features
|
|
101
|
-
- **Zero-Log Execution**: Pass secrets via ENV variables to child processes.
|
|
102
|
-
- **Fernet (AES-128-CBC + HMAC)**: Standard, authenticated encryption.
|
|
103
|
-
- **TTL Security**: Automatic fragment expiration (default 5 mins).
|
|
104
|
-
- **Key Hinting**: Quickly verify keys with the 8-char `hint` field.
|
|
105
|
-
- **Multipart Support**: Split a single intent across multiple agents.
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 🧪 Verified Test Scenarios
|
|
110
|
-
Live-tested between a Main Agent and a Sub-Agent on **2026-02-06**.
|
|
111
|
-
- **Case**: Passing DB credentials via "Sealed Intent" and executing a migration.
|
|
112
|
-
- **Result**: Sub-agent successfully unsealed and executed the task; orchestrator logs only showed the encrypted blob.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## 🔗 Links
|
|
117
|
-
- **PyPI**: [https://pypi.org/project/molt-cipher-bridge/](https://pypi.org/project/molt-cipher-bridge/)
|
|
118
|
-
- **Source**: [https://github.com/CrimsonDevil333333/molt-cipher-bridge](https://github.com/CrimsonDevil333333/molt-cipher-bridge)
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
*Developed by Clawdy & Satyaa*
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
molt_cipher_bridge.py,sha256=0Oy1HXjLCBlFKGY2Psgff4NlHvocQ8evQhF-QoLivbc,5526
|
|
2
|
-
molt_cipher_bridge-1.3.0.dist-info/licenses/LICENSE,sha256=rWD29Fm5ZpZm0VNmxyyPjkxKxagUOl_FQLkxTc4972U,1072
|
|
3
|
-
molt_cipher_bridge-1.3.0.dist-info/METADATA,sha256=M_GML5eugil7DndfEjzZAeu2thq-dlovquVMTQTiZC0,4581
|
|
4
|
-
molt_cipher_bridge-1.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
-
molt_cipher_bridge-1.3.0.dist-info/entry_points.txt,sha256=5ZbzGA_O7R5mVwYUDbWP-YESyhJPwUjcdf5U8CDZG9U,55
|
|
6
|
-
molt_cipher_bridge-1.3.0.dist-info/top_level.txt,sha256=EVe1MZqqdyDrH96xHpwdB9eM5mNRomfsAM5cBSX-Jzo,19
|
|
7
|
-
molt_cipher_bridge-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|