keep-protocol 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- keep_protocol-0.1.0/PKG-INFO +178 -0
- keep_protocol-0.1.0/README.md +151 -0
- keep_protocol-0.1.0/keep/__init__.py +6 -0
- keep_protocol-0.1.0/keep/client.py +79 -0
- keep_protocol-0.1.0/keep/keep_pb2.py +26 -0
- keep_protocol-0.1.0/keep_protocol.egg-info/PKG-INFO +178 -0
- keep_protocol-0.1.0/keep_protocol.egg-info/SOURCES.txt +10 -0
- keep_protocol-0.1.0/keep_protocol.egg-info/dependency_links.txt +1 -0
- keep_protocol-0.1.0/keep_protocol.egg-info/requires.txt +2 -0
- keep_protocol-0.1.0/keep_protocol.egg-info/top_level.txt +1 -0
- keep_protocol-0.1.0/pyproject.toml +45 -0
- keep_protocol-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keep-protocol
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Signed protobuf packets over TCP for AI agent-to-agent communication
|
|
5
|
+
Author: Chris Crawford
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CLCrawford-dev/keep-protocol
|
|
8
|
+
Project-URL: Repository, https://github.com/CLCrawford-dev/keep-protocol
|
|
9
|
+
Project-URL: Issues, https://github.com/CLCrawford-dev/keep-protocol/issues
|
|
10
|
+
Keywords: agent-protocol,ai-agents,protobuf,ed25519,tcp,agent-communication,mcp,multi-agent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: protobuf>=4.25
|
|
26
|
+
Requires-Dist: cryptography>=41.0
|
|
27
|
+
|
|
28
|
+
# keep-protocol
|
|
29
|
+
|
|
30
|
+
**Signed protobuf packets over TCP for AI agent-to-agent communication.**
|
|
31
|
+
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://github.com/CLCrawford-dev/keep-protocol/actions)
|
|
34
|
+
|
|
35
|
+
> Keep is the quiet pipe agents whisper through.
|
|
36
|
+
> A single TCP connection, a tiny Protobuf envelope, an ed25519 signature —
|
|
37
|
+
> just enough fields to say who's talking, who should listen, what they want,
|
|
38
|
+
> how much they'll pay, and when the message expires.
|
|
39
|
+
> Unsigned packets vanish without a trace. Signed ones get heard.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
### Python SDK
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install keep-protocol
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from keep.client import KeepClient
|
|
53
|
+
|
|
54
|
+
client = KeepClient("localhost", 9009)
|
|
55
|
+
reply = client.send(
|
|
56
|
+
src="bot:my-agent",
|
|
57
|
+
dst="server",
|
|
58
|
+
body="hello from my agent",
|
|
59
|
+
)
|
|
60
|
+
print(reply.body) # "done"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Run the Server
|
|
64
|
+
|
|
65
|
+
**Docker (recommended):**
|
|
66
|
+
```bash
|
|
67
|
+
docker run -d -p 9009:9009 --name keep ghcr.io/clcrawford-dev/keep-server:latest
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**From source:**
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/CLCrawford-dev/keep-protocol.git
|
|
73
|
+
cd keep-protocol
|
|
74
|
+
go build -o keep .
|
|
75
|
+
./keep # listens on :9009
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Verify It Works
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install keep-protocol
|
|
82
|
+
python -c "
|
|
83
|
+
from keep.client import KeepClient
|
|
84
|
+
reply = KeepClient('localhost', 9009).send(body='ping')
|
|
85
|
+
print('OK' if reply.body == 'done' else 'FAIL')
|
|
86
|
+
"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Why Keep?
|
|
92
|
+
|
|
93
|
+
| Feature | Keep | HTTP/REST | gRPC | NATS |
|
|
94
|
+
|---------|------|-----------|------|------|
|
|
95
|
+
| Latency | Sub-ms (TCP) | 1-10ms | 1-5ms | 1-5ms |
|
|
96
|
+
| Auth | ed25519 built-in | Bring your own | mTLS | Tokens |
|
|
97
|
+
| Schema | 10 fields, done | Unlimited | Unlimited | None |
|
|
98
|
+
| Setup | 1 binary, 0 config | Web server + routes | Codegen + server | Broker cluster |
|
|
99
|
+
| Agent-native | Yes | No | No | Partial |
|
|
100
|
+
| Spam resistance | fee + ttl fields | None | None | None |
|
|
101
|
+
|
|
102
|
+
Keep is not a replacement for gRPC or NATS. It is a protocol for agents that
|
|
103
|
+
need to find each other and exchange signed intent with minimal ceremony.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Packet Schema
|
|
108
|
+
|
|
109
|
+
```protobuf
|
|
110
|
+
message Packet {
|
|
111
|
+
bytes sig = 1; // ed25519 signature (64 bytes)
|
|
112
|
+
bytes pk = 2; // sender's public key (32 bytes)
|
|
113
|
+
uint32 typ = 3; // 0=ask, 1=offer, 2=heartbeat
|
|
114
|
+
string id = 4; // unique message ID
|
|
115
|
+
string src = 5; // sender: "bot:my-agent" or "human:chris"
|
|
116
|
+
string dst = 6; // destination: "server", "nearest:weather", "swarm:planner"
|
|
117
|
+
string body = 7; // intent or payload
|
|
118
|
+
uint64 fee = 8; // micro-fee in sats (anti-spam)
|
|
119
|
+
uint32 ttl = 9; // time-to-live in seconds
|
|
120
|
+
bytes scar = 10; // gitmem-style memory commit (optional)
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Signing Protocol
|
|
125
|
+
|
|
126
|
+
Identity is a keypair. No accounts, no registration.
|
|
127
|
+
|
|
128
|
+
### Sending a signed packet
|
|
129
|
+
|
|
130
|
+
1. Build a `Packet` with all fields — leave `sig` and `pk` empty
|
|
131
|
+
2. Serialize to bytes — this is the **sign payload**
|
|
132
|
+
3. Sign those bytes with your ed25519 private key
|
|
133
|
+
4. Set `sig` (64 bytes) and `pk` (32 bytes) on the Packet
|
|
134
|
+
5. Serialize the full Packet — send over TCP
|
|
135
|
+
|
|
136
|
+
### Server verification
|
|
137
|
+
|
|
138
|
+
1. Unmarshal the incoming bytes into a `Packet`
|
|
139
|
+
2. If `sig` and `pk` are empty — **DROPPED** (logged, no reply)
|
|
140
|
+
3. Copy all fields except `sig`/`pk` into a new Packet, serialize it
|
|
141
|
+
4. Verify the signature against those bytes using the sender's `pk`
|
|
142
|
+
5. If invalid — **DROPPED** (logged, no reply)
|
|
143
|
+
6. If valid — process the message, send `done` reply
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Examples
|
|
148
|
+
|
|
149
|
+
See the [`examples/`](examples/) directory:
|
|
150
|
+
|
|
151
|
+
- **[python_basic.py](examples/python_basic.py)** — Minimal SDK usage
|
|
152
|
+
- **[python_raw.py](examples/python_raw.py)** — Raw TCP + signing without the SDK (educational)
|
|
153
|
+
- **[mcp_tool_definition.py](examples/mcp_tool_definition.py)** — Expose keep as an MCP tool
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Use Cases
|
|
158
|
+
|
|
159
|
+
- **Local swarm** — agents on same VM use `localhost:9009` for zero-latency handoff
|
|
160
|
+
- **Relay swarm** — agents publish to public relays — relays enforce fee/ttl/reputation
|
|
161
|
+
- **Memory sharing** — `scar` field carries gitmem-style commits — agents barter knowledge
|
|
162
|
+
- **Anti-spam market** — `fee` field creates micro-economy — pay to get priority
|
|
163
|
+
|
|
164
|
+
## Design Principles
|
|
165
|
+
|
|
166
|
+
- **Silent rejection** — unsigned senders don't know if the server exists
|
|
167
|
+
- **Identity without accounts** — your keypair is your identity
|
|
168
|
+
- **Full visibility** — dropped packets are logged server-side
|
|
169
|
+
- **Minimal overhead** — protobuf over raw TCP, no HTTP/JSON
|
|
170
|
+
- **Semantic routing** — `dst` is a name, not an address
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# keep-protocol
|
|
2
|
+
|
|
3
|
+
**Signed protobuf packets over TCP for AI agent-to-agent communication.**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/CLCrawford-dev/keep-protocol/actions)
|
|
7
|
+
|
|
8
|
+
> Keep is the quiet pipe agents whisper through.
|
|
9
|
+
> A single TCP connection, a tiny Protobuf envelope, an ed25519 signature —
|
|
10
|
+
> just enough fields to say who's talking, who should listen, what they want,
|
|
11
|
+
> how much they'll pay, and when the message expires.
|
|
12
|
+
> Unsigned packets vanish without a trace. Signed ones get heard.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
### Python SDK
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install keep-protocol
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from keep.client import KeepClient
|
|
26
|
+
|
|
27
|
+
client = KeepClient("localhost", 9009)
|
|
28
|
+
reply = client.send(
|
|
29
|
+
src="bot:my-agent",
|
|
30
|
+
dst="server",
|
|
31
|
+
body="hello from my agent",
|
|
32
|
+
)
|
|
33
|
+
print(reply.body) # "done"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Run the Server
|
|
37
|
+
|
|
38
|
+
**Docker (recommended):**
|
|
39
|
+
```bash
|
|
40
|
+
docker run -d -p 9009:9009 --name keep ghcr.io/clcrawford-dev/keep-server:latest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**From source:**
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/CLCrawford-dev/keep-protocol.git
|
|
46
|
+
cd keep-protocol
|
|
47
|
+
go build -o keep .
|
|
48
|
+
./keep # listens on :9009
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Verify It Works
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install keep-protocol
|
|
55
|
+
python -c "
|
|
56
|
+
from keep.client import KeepClient
|
|
57
|
+
reply = KeepClient('localhost', 9009).send(body='ping')
|
|
58
|
+
print('OK' if reply.body == 'done' else 'FAIL')
|
|
59
|
+
"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Why Keep?
|
|
65
|
+
|
|
66
|
+
| Feature | Keep | HTTP/REST | gRPC | NATS |
|
|
67
|
+
|---------|------|-----------|------|------|
|
|
68
|
+
| Latency | Sub-ms (TCP) | 1-10ms | 1-5ms | 1-5ms |
|
|
69
|
+
| Auth | ed25519 built-in | Bring your own | mTLS | Tokens |
|
|
70
|
+
| Schema | 10 fields, done | Unlimited | Unlimited | None |
|
|
71
|
+
| Setup | 1 binary, 0 config | Web server + routes | Codegen + server | Broker cluster |
|
|
72
|
+
| Agent-native | Yes | No | No | Partial |
|
|
73
|
+
| Spam resistance | fee + ttl fields | None | None | None |
|
|
74
|
+
|
|
75
|
+
Keep is not a replacement for gRPC or NATS. It is a protocol for agents that
|
|
76
|
+
need to find each other and exchange signed intent with minimal ceremony.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Packet Schema
|
|
81
|
+
|
|
82
|
+
```protobuf
|
|
83
|
+
message Packet {
|
|
84
|
+
bytes sig = 1; // ed25519 signature (64 bytes)
|
|
85
|
+
bytes pk = 2; // sender's public key (32 bytes)
|
|
86
|
+
uint32 typ = 3; // 0=ask, 1=offer, 2=heartbeat
|
|
87
|
+
string id = 4; // unique message ID
|
|
88
|
+
string src = 5; // sender: "bot:my-agent" or "human:chris"
|
|
89
|
+
string dst = 6; // destination: "server", "nearest:weather", "swarm:planner"
|
|
90
|
+
string body = 7; // intent or payload
|
|
91
|
+
uint64 fee = 8; // micro-fee in sats (anti-spam)
|
|
92
|
+
uint32 ttl = 9; // time-to-live in seconds
|
|
93
|
+
bytes scar = 10; // gitmem-style memory commit (optional)
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Signing Protocol
|
|
98
|
+
|
|
99
|
+
Identity is a keypair. No accounts, no registration.
|
|
100
|
+
|
|
101
|
+
### Sending a signed packet
|
|
102
|
+
|
|
103
|
+
1. Build a `Packet` with all fields — leave `sig` and `pk` empty
|
|
104
|
+
2. Serialize to bytes — this is the **sign payload**
|
|
105
|
+
3. Sign those bytes with your ed25519 private key
|
|
106
|
+
4. Set `sig` (64 bytes) and `pk` (32 bytes) on the Packet
|
|
107
|
+
5. Serialize the full Packet — send over TCP
|
|
108
|
+
|
|
109
|
+
### Server verification
|
|
110
|
+
|
|
111
|
+
1. Unmarshal the incoming bytes into a `Packet`
|
|
112
|
+
2. If `sig` and `pk` are empty — **DROPPED** (logged, no reply)
|
|
113
|
+
3. Copy all fields except `sig`/`pk` into a new Packet, serialize it
|
|
114
|
+
4. Verify the signature against those bytes using the sender's `pk`
|
|
115
|
+
5. If invalid — **DROPPED** (logged, no reply)
|
|
116
|
+
6. If valid — process the message, send `done` reply
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Examples
|
|
121
|
+
|
|
122
|
+
See the [`examples/`](examples/) directory:
|
|
123
|
+
|
|
124
|
+
- **[python_basic.py](examples/python_basic.py)** — Minimal SDK usage
|
|
125
|
+
- **[python_raw.py](examples/python_raw.py)** — Raw TCP + signing without the SDK (educational)
|
|
126
|
+
- **[mcp_tool_definition.py](examples/mcp_tool_definition.py)** — Expose keep as an MCP tool
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Use Cases
|
|
131
|
+
|
|
132
|
+
- **Local swarm** — agents on same VM use `localhost:9009` for zero-latency handoff
|
|
133
|
+
- **Relay swarm** — agents publish to public relays — relays enforce fee/ttl/reputation
|
|
134
|
+
- **Memory sharing** — `scar` field carries gitmem-style commits — agents barter knowledge
|
|
135
|
+
- **Anti-spam market** — `fee` field creates micro-economy — pay to get priority
|
|
136
|
+
|
|
137
|
+
## Design Principles
|
|
138
|
+
|
|
139
|
+
- **Silent rejection** — unsigned senders don't know if the server exists
|
|
140
|
+
- **Identity without accounts** — your keypair is your identity
|
|
141
|
+
- **Full visibility** — dropped packets are logged server-side
|
|
142
|
+
- **Minimal overhead** — protobuf over raw TCP, no HTTP/JSON
|
|
143
|
+
- **Semantic routing** — `dst` is a name, not an address
|
|
144
|
+
|
|
145
|
+
## Contributing
|
|
146
|
+
|
|
147
|
+
We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Keep protocol client -- sign and send packets over TCP."""
|
|
2
|
+
|
|
3
|
+
import socket
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
|
8
|
+
|
|
9
|
+
from keep import keep_pb2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class KeepClient:
|
|
13
|
+
"""Client for the keep-protocol server.
|
|
14
|
+
|
|
15
|
+
Generates an ephemeral ed25519 keypair on init (or accepts an existing one).
|
|
16
|
+
Signs every outgoing packet.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
host: str = "localhost",
|
|
22
|
+
port: int = 9009,
|
|
23
|
+
private_key: Optional[Ed25519PrivateKey] = None,
|
|
24
|
+
timeout: float = 10.0,
|
|
25
|
+
):
|
|
26
|
+
self.host = host
|
|
27
|
+
self.port = port
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
self._private_key = private_key or Ed25519PrivateKey.generate()
|
|
30
|
+
self._public_key = self._private_key.public_key()
|
|
31
|
+
self._pk_bytes = self._public_key.public_bytes_raw()
|
|
32
|
+
|
|
33
|
+
def send(
|
|
34
|
+
self,
|
|
35
|
+
body: str,
|
|
36
|
+
src: str = "bot:keep-client",
|
|
37
|
+
dst: str = "server",
|
|
38
|
+
typ: int = 0,
|
|
39
|
+
fee: int = 0,
|
|
40
|
+
ttl: int = 60,
|
|
41
|
+
msg_id: Optional[str] = None,
|
|
42
|
+
scar: bytes = b"",
|
|
43
|
+
) -> keep_pb2.Packet:
|
|
44
|
+
"""Sign and send a packet, return the server's reply."""
|
|
45
|
+
msg_id = msg_id or str(uuid.uuid4())
|
|
46
|
+
|
|
47
|
+
# Build unsigned packet
|
|
48
|
+
p = keep_pb2.Packet()
|
|
49
|
+
p.typ = typ
|
|
50
|
+
p.id = msg_id
|
|
51
|
+
p.src = src
|
|
52
|
+
p.dst = dst
|
|
53
|
+
p.body = body
|
|
54
|
+
p.fee = fee
|
|
55
|
+
p.ttl = ttl
|
|
56
|
+
p.scar = scar
|
|
57
|
+
|
|
58
|
+
# Sign
|
|
59
|
+
sign_payload = p.SerializeToString()
|
|
60
|
+
sig_bytes = self._private_key.sign(sign_payload)
|
|
61
|
+
|
|
62
|
+
# Set sig + pk
|
|
63
|
+
p.sig = sig_bytes
|
|
64
|
+
p.pk = self._pk_bytes
|
|
65
|
+
wire_data = p.SerializeToString()
|
|
66
|
+
|
|
67
|
+
# Send over TCP
|
|
68
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
69
|
+
s.settimeout(self.timeout)
|
|
70
|
+
try:
|
|
71
|
+
s.connect((self.host, self.port))
|
|
72
|
+
s.sendall(wire_data)
|
|
73
|
+
reply_data = s.recv(4096)
|
|
74
|
+
finally:
|
|
75
|
+
s.close()
|
|
76
|
+
|
|
77
|
+
resp = keep_pb2.Packet()
|
|
78
|
+
resp.ParseFromString(reply_data)
|
|
79
|
+
return resp
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: keep.proto
|
|
4
|
+
"""Generated protocol buffer code."""
|
|
5
|
+
from google.protobuf.internal import builder as _builder
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
# @@protoc_insertion_point(imports)
|
|
10
|
+
|
|
11
|
+
_sym_db = _symbol_database.Default()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nkeep.proto\"\x8a\x01\n\x06Packet\x12\x0b\n\x03sig\x18\x01 \x01(\x0c\x12\n\n\x02pk\x18\x02 \x01(\x0c\x12\x0b\n\x03typ\x18\x03 \x01(\r\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0b\n\x03src\x18\x05 \x01(\t\x12\x0b\n\x03\x64st\x18\x06 \x01(\t\x12\x0c\n\x04\x62ody\x18\x07 \x01(\t\x12\x0b\n\x03\x66\x65\x65\x18\x08 \x01(\x04\x12\x0b\n\x03ttl\x18\t \x01(\r\x12\x0c\n\x04scar\x18\n \x01(\x0c\x42\x08Z\x06.;mainb\x06proto3')
|
|
17
|
+
|
|
18
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
|
19
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'keep_pb2', globals())
|
|
20
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
21
|
+
|
|
22
|
+
DESCRIPTOR._options = None
|
|
23
|
+
DESCRIPTOR._serialized_options = b'Z\006.;main'
|
|
24
|
+
_PACKET._serialized_start=15
|
|
25
|
+
_PACKET._serialized_end=153
|
|
26
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keep-protocol
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Signed protobuf packets over TCP for AI agent-to-agent communication
|
|
5
|
+
Author: Chris Crawford
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CLCrawford-dev/keep-protocol
|
|
8
|
+
Project-URL: Repository, https://github.com/CLCrawford-dev/keep-protocol
|
|
9
|
+
Project-URL: Issues, https://github.com/CLCrawford-dev/keep-protocol/issues
|
|
10
|
+
Keywords: agent-protocol,ai-agents,protobuf,ed25519,tcp,agent-communication,mcp,multi-agent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: protobuf>=4.25
|
|
26
|
+
Requires-Dist: cryptography>=41.0
|
|
27
|
+
|
|
28
|
+
# keep-protocol
|
|
29
|
+
|
|
30
|
+
**Signed protobuf packets over TCP for AI agent-to-agent communication.**
|
|
31
|
+
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://github.com/CLCrawford-dev/keep-protocol/actions)
|
|
34
|
+
|
|
35
|
+
> Keep is the quiet pipe agents whisper through.
|
|
36
|
+
> A single TCP connection, a tiny Protobuf envelope, an ed25519 signature —
|
|
37
|
+
> just enough fields to say who's talking, who should listen, what they want,
|
|
38
|
+
> how much they'll pay, and when the message expires.
|
|
39
|
+
> Unsigned packets vanish without a trace. Signed ones get heard.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
### Python SDK
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install keep-protocol
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from keep.client import KeepClient
|
|
53
|
+
|
|
54
|
+
client = KeepClient("localhost", 9009)
|
|
55
|
+
reply = client.send(
|
|
56
|
+
src="bot:my-agent",
|
|
57
|
+
dst="server",
|
|
58
|
+
body="hello from my agent",
|
|
59
|
+
)
|
|
60
|
+
print(reply.body) # "done"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Run the Server
|
|
64
|
+
|
|
65
|
+
**Docker (recommended):**
|
|
66
|
+
```bash
|
|
67
|
+
docker run -d -p 9009:9009 --name keep ghcr.io/clcrawford-dev/keep-server:latest
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**From source:**
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/CLCrawford-dev/keep-protocol.git
|
|
73
|
+
cd keep-protocol
|
|
74
|
+
go build -o keep .
|
|
75
|
+
./keep # listens on :9009
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Verify It Works
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install keep-protocol
|
|
82
|
+
python -c "
|
|
83
|
+
from keep.client import KeepClient
|
|
84
|
+
reply = KeepClient('localhost', 9009).send(body='ping')
|
|
85
|
+
print('OK' if reply.body == 'done' else 'FAIL')
|
|
86
|
+
"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Why Keep?
|
|
92
|
+
|
|
93
|
+
| Feature | Keep | HTTP/REST | gRPC | NATS |
|
|
94
|
+
|---------|------|-----------|------|------|
|
|
95
|
+
| Latency | Sub-ms (TCP) | 1-10ms | 1-5ms | 1-5ms |
|
|
96
|
+
| Auth | ed25519 built-in | Bring your own | mTLS | Tokens |
|
|
97
|
+
| Schema | 10 fields, done | Unlimited | Unlimited | None |
|
|
98
|
+
| Setup | 1 binary, 0 config | Web server + routes | Codegen + server | Broker cluster |
|
|
99
|
+
| Agent-native | Yes | No | No | Partial |
|
|
100
|
+
| Spam resistance | fee + ttl fields | None | None | None |
|
|
101
|
+
|
|
102
|
+
Keep is not a replacement for gRPC or NATS. It is a protocol for agents that
|
|
103
|
+
need to find each other and exchange signed intent with minimal ceremony.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Packet Schema
|
|
108
|
+
|
|
109
|
+
```protobuf
|
|
110
|
+
message Packet {
|
|
111
|
+
bytes sig = 1; // ed25519 signature (64 bytes)
|
|
112
|
+
bytes pk = 2; // sender's public key (32 bytes)
|
|
113
|
+
uint32 typ = 3; // 0=ask, 1=offer, 2=heartbeat
|
|
114
|
+
string id = 4; // unique message ID
|
|
115
|
+
string src = 5; // sender: "bot:my-agent" or "human:chris"
|
|
116
|
+
string dst = 6; // destination: "server", "nearest:weather", "swarm:planner"
|
|
117
|
+
string body = 7; // intent or payload
|
|
118
|
+
uint64 fee = 8; // micro-fee in sats (anti-spam)
|
|
119
|
+
uint32 ttl = 9; // time-to-live in seconds
|
|
120
|
+
bytes scar = 10; // gitmem-style memory commit (optional)
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Signing Protocol
|
|
125
|
+
|
|
126
|
+
Identity is a keypair. No accounts, no registration.
|
|
127
|
+
|
|
128
|
+
### Sending a signed packet
|
|
129
|
+
|
|
130
|
+
1. Build a `Packet` with all fields — leave `sig` and `pk` empty
|
|
131
|
+
2. Serialize to bytes — this is the **sign payload**
|
|
132
|
+
3. Sign those bytes with your ed25519 private key
|
|
133
|
+
4. Set `sig` (64 bytes) and `pk` (32 bytes) on the Packet
|
|
134
|
+
5. Serialize the full Packet — send over TCP
|
|
135
|
+
|
|
136
|
+
### Server verification
|
|
137
|
+
|
|
138
|
+
1. Unmarshal the incoming bytes into a `Packet`
|
|
139
|
+
2. If `sig` and `pk` are empty — **DROPPED** (logged, no reply)
|
|
140
|
+
3. Copy all fields except `sig`/`pk` into a new Packet, serialize it
|
|
141
|
+
4. Verify the signature against those bytes using the sender's `pk`
|
|
142
|
+
5. If invalid — **DROPPED** (logged, no reply)
|
|
143
|
+
6. If valid — process the message, send `done` reply
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Examples
|
|
148
|
+
|
|
149
|
+
See the [`examples/`](examples/) directory:
|
|
150
|
+
|
|
151
|
+
- **[python_basic.py](examples/python_basic.py)** — Minimal SDK usage
|
|
152
|
+
- **[python_raw.py](examples/python_raw.py)** — Raw TCP + signing without the SDK (educational)
|
|
153
|
+
- **[mcp_tool_definition.py](examples/mcp_tool_definition.py)** — Expose keep as an MCP tool
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Use Cases
|
|
158
|
+
|
|
159
|
+
- **Local swarm** — agents on same VM use `localhost:9009` for zero-latency handoff
|
|
160
|
+
- **Relay swarm** — agents publish to public relays — relays enforce fee/ttl/reputation
|
|
161
|
+
- **Memory sharing** — `scar` field carries gitmem-style commits — agents barter knowledge
|
|
162
|
+
- **Anti-spam market** — `fee` field creates micro-economy — pay to get priority
|
|
163
|
+
|
|
164
|
+
## Design Principles
|
|
165
|
+
|
|
166
|
+
- **Silent rejection** — unsigned senders don't know if the server exists
|
|
167
|
+
- **Identity without accounts** — your keypair is your identity
|
|
168
|
+
- **Full visibility** — dropped packets are logged server-side
|
|
169
|
+
- **Minimal overhead** — protobuf over raw TCP, no HTTP/JSON
|
|
170
|
+
- **Semantic routing** — `dst` is a name, not an address
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
keep/__init__.py
|
|
4
|
+
keep/client.py
|
|
5
|
+
keep/keep_pb2.py
|
|
6
|
+
keep_protocol.egg-info/PKG-INFO
|
|
7
|
+
keep_protocol.egg-info/SOURCES.txt
|
|
8
|
+
keep_protocol.egg-info/dependency_links.txt
|
|
9
|
+
keep_protocol.egg-info/requires.txt
|
|
10
|
+
keep_protocol.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
keep
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "keep-protocol"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Signed protobuf packets over TCP for AI agent-to-agent communication"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [{name = "Chris Crawford"}]
|
|
12
|
+
requires-python = ">= 3.9"
|
|
13
|
+
keywords = [
|
|
14
|
+
"agent-protocol",
|
|
15
|
+
"ai-agents",
|
|
16
|
+
"protobuf",
|
|
17
|
+
"ed25519",
|
|
18
|
+
"tcp",
|
|
19
|
+
"agent-communication",
|
|
20
|
+
"mcp",
|
|
21
|
+
"multi-agent",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Communications",
|
|
34
|
+
"Topic :: Security :: Cryptography",
|
|
35
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"protobuf >= 4.25",
|
|
39
|
+
"cryptography >= 41.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/CLCrawford-dev/keep-protocol"
|
|
44
|
+
Repository = "https://github.com/CLCrawford-dev/keep-protocol"
|
|
45
|
+
Issues = "https://github.com/CLCrawford-dev/keep-protocol/issues"
|