molt-cipher-bridge 1.3.1__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.1.dist-info/METADATA +0 -134
- molt_cipher_bridge-1.3.1.dist-info/RECORD +0 -7
- {molt_cipher_bridge-1.3.1.dist-info → molt_cipher_bridge-1.4.2.dist-info}/WHEEL +0 -0
- {molt_cipher_bridge-1.3.1.dist-info → molt_cipher_bridge-1.4.2.dist-info}/entry_points.txt +0 -0
- {molt_cipher_bridge-1.3.1.dist-info → molt_cipher_bridge-1.4.2.dist-info}/licenses/LICENSE +0 -0
- {molt_cipher_bridge-1.3.1.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,134 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: molt-cipher-bridge
|
|
3
|
-
Version: 1.3.1
|
|
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.3.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 entirely out of persistent orchestrator logs.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## ⚡ Quick Start for Agents
|
|
27
|
-
Run this single command to bootstrap your environment, install the tool, and learn the protocol:
|
|
28
|
-
```bash
|
|
29
|
-
curl -sSL https://raw.githubusercontent.com/CrimsonDevil333333/molt-cipher-bridge/master/bootstrap_agent.sh | bash
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## 🚀 Installation
|
|
35
|
-
|
|
36
|
-
### Linux (Recommended for Agents)
|
|
37
|
-
On modern Linux systems (Debian/PiOS/Ubuntu), use `pipx` to prevent environment conflicts:
|
|
38
|
-
```bash
|
|
39
|
-
pipx install molt-cipher-bridge
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Standard Pip
|
|
43
|
-
```bash
|
|
44
|
-
pip install molt-cipher-bridge
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 🛡️ The Zero-Log Protocol (MANDATORY FOR AGENTS)
|
|
50
|
-
We operate on a **Zero-Trust Policy**. Agents are strictly forbidden from handling plaintext secrets in command arguments or shell history.
|
|
51
|
-
|
|
52
|
-
### 1. Zero-Trust Sealing (Sender Side)
|
|
53
|
-
**NEVER use `echo` to create secret files.** This leaves the secret in your `.bash_history`.
|
|
54
|
-
|
|
55
|
-
**The Secure Workflow:**
|
|
56
|
-
1. **Request from Human**: Ask your human to manually place the secret JSON into a path.
|
|
57
|
-
2. **Point to File**: Use `--file` to seal the intent directly from that path.
|
|
58
|
-
3. **Selective Purge**: Delete the file ONLY if it was a one-time temp file. Do NOT delete system configs.
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# Seal the intent from a human-provided path
|
|
62
|
-
molt-cipher seal --key "YOUR_KEY" --sender "Main" --to "Worker" --file /path/to/secret.json
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 2. Zero-Log Execution (Worker Side)
|
|
66
|
-
**Never print unsealed secrets.** Use the `run` command to inject them directly into RAM via environment variables.
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
# The secret exists ONLY in the environment of the child process ($DB_PASS)
|
|
70
|
-
molt-cipher run \
|
|
71
|
-
--key "YOUR_KEY" \
|
|
72
|
-
--fragment '{"v": "1.3.0", ...}' \
|
|
73
|
-
--cmd "mysql -u root -p$DB_PASS"
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 🛠️ Full CLI Reference
|
|
79
|
-
|
|
80
|
-
### 🔐 `seal`
|
|
81
|
-
Packages data into an encrypted fragment.
|
|
82
|
-
- `--key`: The shared Fernet key.
|
|
83
|
-
- `--sender`: Your Agent ID.
|
|
84
|
-
- `--to`: Recipient Agent ID.
|
|
85
|
-
- `--file`: Path to JSON file (Secure).
|
|
86
|
-
- `--data`: Raw string (⚠️ Use for non-sensitive data only).
|
|
87
|
-
- `--ttl`: Time-to-Live in seconds (Default: 300).
|
|
88
|
-
|
|
89
|
-
### 🔓 `unseal`
|
|
90
|
-
Decodes a fragment for inspection. **Note: Use `run` instead for execution to prevent leaks.**
|
|
91
|
-
- `--key`: The shared Fernet key.
|
|
92
|
-
- `--fragment`: The JSON fragment string.
|
|
93
|
-
- `--ignore-expiry`: Bypass TTL (For debugging).
|
|
94
|
-
|
|
95
|
-
### ⚡ `run`
|
|
96
|
-
The gold standard for secure task execution.
|
|
97
|
-
- `--cmd`: The shell command. Secrets in the payload are injected as ENV vars.
|
|
98
|
-
- Example: `molt-cipher run --cmd "echo $TOKEN"`
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 🧪 Verified Test Scenarios
|
|
103
|
-
### A2A Handoff (Verified 2026-02-06)
|
|
104
|
-
1. **Goal**: Pass a GitHub Token from Main to Sub-Agent.
|
|
105
|
-
2. **Method**: Main agent used `seal --file` to encrypt token.
|
|
106
|
-
3. **Observation**: Chat logs showed zero plaintext.
|
|
107
|
-
4. **Validation**: Sub-agent used `run --cmd` to perform a `gh auth status`. Command succeeded; token stayed in RAM.
|
|
108
|
-
|
|
109
|
-
### Self-Verification
|
|
110
|
-
Confirm your setup is secure by running the built-in test:
|
|
111
|
-
```bash
|
|
112
|
-
./tests/live_zero_log_test.sh
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## 🧠 Permanent Agent Memory
|
|
118
|
-
Agents must maintain these four rules:
|
|
119
|
-
1. **Zero-Trust**: Plaintext secrets in logs = Critical Failure.
|
|
120
|
-
2. **Sealing**: Human File -> Seal --file -> Selective Purge.
|
|
121
|
-
3. **Running**: Fragment -> Run -> RAM Injection (`$VAR`).
|
|
122
|
-
4. **Integrity**: Respect system configs; never delete files you didn't create.
|
|
123
|
-
|
|
124
|
-
**MANDATORY**: All agents must read and internalize [SOUL.md](./SOUL.md) to understand the core behavioral framework and the "Agent's Oath" before operating this protocol.
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 🔗 Links
|
|
129
|
-
- **PyPI**: [https://pypi.org/project/molt-cipher-bridge/](https://pypi.org/project/molt-cipher-bridge/)
|
|
130
|
-
- **Source**: [https://github.com/CrimsonDevil333333/molt-cipher-bridge](https://github.com/CrimsonDevil333333/molt-cipher-bridge)
|
|
131
|
-
- **Philosophy**: [SOUL.md](./SOUL.md)
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
*Developed by Clawdy & Satyaa*
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
molt_cipher_bridge.py,sha256=0Oy1HXjLCBlFKGY2Psgff4NlHvocQ8evQhF-QoLivbc,5526
|
|
2
|
-
molt_cipher_bridge-1.3.1.dist-info/licenses/LICENSE,sha256=rWD29Fm5ZpZm0VNmxyyPjkxKxagUOl_FQLkxTc4972U,1072
|
|
3
|
-
molt_cipher_bridge-1.3.1.dist-info/METADATA,sha256=vBVxKQBvYqOnr54_lIwjTt7CvPqclNlTKY5_iKW2nJE,4965
|
|
4
|
-
molt_cipher_bridge-1.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
-
molt_cipher_bridge-1.3.1.dist-info/entry_points.txt,sha256=5ZbzGA_O7R5mVwYUDbWP-YESyhJPwUjcdf5U8CDZG9U,55
|
|
6
|
-
molt_cipher_bridge-1.3.1.dist-info/top_level.txt,sha256=EVe1MZqqdyDrH96xHpwdB9eM5mNRomfsAM5cBSX-Jzo,19
|
|
7
|
-
molt_cipher_bridge-1.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|