connectonion 0.6.3__py3-none-any.whl → 0.6.5__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.
- connectonion/__init__.py +1 -1
- connectonion/cli/co_ai/agent.py +3 -3
- connectonion/cli/co_ai/main.py +2 -2
- connectonion/cli/co_ai/plugins/__init__.py +2 -3
- connectonion/cli/co_ai/plugins/system_reminder.py +154 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +166 -208
- connectonion/cli/co_ai/prompts/system-reminders/agent.md +23 -0
- connectonion/cli/co_ai/prompts/system-reminders/plan_mode.md +13 -0
- connectonion/cli/co_ai/prompts/system-reminders/security.md +14 -0
- connectonion/cli/co_ai/prompts/system-reminders/simplicity.md +14 -0
- connectonion/cli/co_ai/tools/plan_mode.py +1 -4
- connectonion/cli/co_ai/tools/read.py +0 -6
- connectonion/cli/commands/copy_commands.py +21 -0
- connectonion/cli/commands/trust_commands.py +152 -0
- connectonion/cli/main.py +82 -0
- connectonion/core/llm.py +2 -2
- connectonion/docs/concepts/fast_rules.md +237 -0
- connectonion/docs/concepts/onboarding.md +465 -0
- connectonion/docs/concepts/plugins.md +2 -1
- connectonion/docs/concepts/trust.md +933 -192
- connectonion/docs/design-decisions/023-trust-policy-system-design.md +323 -0
- connectonion/docs/network/README.md +23 -1
- connectonion/docs/network/connect.md +135 -0
- connectonion/docs/network/host.md +73 -4
- connectonion/docs/useful_plugins/tool_approval.md +139 -0
- connectonion/network/__init__.py +7 -6
- connectonion/network/asgi/__init__.py +3 -0
- connectonion/network/asgi/http.py +125 -19
- connectonion/network/asgi/websocket.py +276 -15
- connectonion/network/connect.py +145 -29
- connectonion/network/host/auth.py +70 -67
- connectonion/network/host/routes.py +88 -3
- connectonion/network/host/server.py +100 -17
- connectonion/network/trust/__init__.py +27 -19
- connectonion/network/trust/factory.py +51 -24
- connectonion/network/trust/fast_rules.py +100 -0
- connectonion/network/trust/tools.py +316 -32
- connectonion/network/trust/trust_agent.py +403 -0
- connectonion/transcribe.py +1 -1
- connectonion/useful_plugins/__init__.py +2 -1
- connectonion/useful_plugins/tool_approval.py +233 -0
- {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/METADATA +1 -1
- {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/RECORD +45 -37
- connectonion/cli/co_ai/plugins/reminder.py +0 -76
- connectonion/cli/co_ai/plugins/shell_approval.py +0 -105
- connectonion/cli/co_ai/prompts/reminders/plan_mode.md +0 -34
- connectonion/cli/co_ai/reminders.py +0 -159
- connectonion/network/trust/prompts.py +0 -71
- {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/WHEEL +0 -0
- {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Purpose: Ed25519 signature verification and trust-based authentication for hosted agents
|
|
3
3
|
LLM-Note:
|
|
4
|
-
Dependencies: imports from [network/trust
|
|
5
|
-
Data flow: receives request dict with {payload, from, signature} → extract_and_authenticate() verifies Ed25519 signature
|
|
6
|
-
State/Effects:
|
|
7
|
-
Integration: exposes verify_signature(
|
|
8
|
-
Performance:
|
|
9
|
-
Errors: returns error strings: "unauthorized: ...", "forbidden: ..."
|
|
4
|
+
Dependencies: imports from [network/trust/TrustAgent, nacl.signing] | imported by [network/host/routes.py, network/host/server.py] | tested by [tests/unit/test_host_auth.py]
|
|
5
|
+
Data flow: receives request dict with {payload, from, signature} → extract_and_authenticate() verifies Ed25519 signature → uses TrustAgent.should_allow() for trust decisions (fast rules + LLM fallback) → returns (prompt, identity, sig_valid, error)
|
|
6
|
+
State/Effects: TrustAgent handles all trust state (whitelist, contacts, blocklist in ~/.co/)
|
|
7
|
+
Integration: exposes verify_signature(), extract_and_authenticate(), get_agent_address(), is_custom_trust() | used by host() to enforce authentication
|
|
8
|
+
Performance: TrustAgent.should_allow() runs fast rules first (zero tokens), only uses LLM for 'ask' cases
|
|
9
|
+
Errors: returns error strings: "unauthorized: ...", "forbidden: ..." | does NOT raise exceptions
|
|
10
10
|
Authentication and signature verification for hosted agents.
|
|
11
|
+
|
|
12
|
+
Trust evaluation (via TrustAgent.should_allow()):
|
|
13
|
+
1. Parameter whitelist (highest priority, instant allow)
|
|
14
|
+
2. Signature verification (protocol level)
|
|
15
|
+
3. TrustAgent handles fast rules + LLM fallback
|
|
11
16
|
"""
|
|
12
17
|
|
|
13
18
|
import hashlib
|
|
14
19
|
import json
|
|
15
20
|
import time
|
|
16
21
|
|
|
17
|
-
from ..trust import
|
|
22
|
+
from ..trust import TrustAgent, TRUST_LEVELS
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
# Signature expiry window (5 minutes)
|
|
@@ -64,10 +69,29 @@ def extract_and_authenticate(data: dict, trust, *, blacklist=None, whitelist=Non
|
|
|
64
69
|
"signature": "0xEd25519Signature..."
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
Onboarding (in payload):
|
|
73
|
+
{
|
|
74
|
+
"payload": {"prompt": "...", "invite_code": "BETA2024", ...}
|
|
75
|
+
}
|
|
76
|
+
or:
|
|
77
|
+
{
|
|
78
|
+
"payload": {"prompt": "...", "payment": 10, ...}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Authentication flow:
|
|
82
|
+
1. Signature verification (protocol level, always required)
|
|
83
|
+
2. Parameter whitelist check (instant allow if match)
|
|
84
|
+
3. Fast rules from YAML config:
|
|
85
|
+
- allow: [whitelisted, contact] → instant allow
|
|
86
|
+
- deny: [blocked] → instant deny
|
|
87
|
+
- onboard: {invite_code, payment} → promote stranger to contact
|
|
88
|
+
- default: allow | deny | ask → final decision
|
|
89
|
+
4. Trust agent (only if fast rules return None for 'ask' cases)
|
|
90
|
+
|
|
91
|
+
Trust levels (predefined YAML configs):
|
|
92
|
+
- "open": default=allow (development)
|
|
93
|
+
- "careful": default=ask (staging)
|
|
94
|
+
- "strict": allow=[whitelisted], default=deny (production)
|
|
71
95
|
- Custom policy/Agent: LLM evaluation
|
|
72
96
|
|
|
73
97
|
Returns: (prompt, identity, sig_valid, error)
|
|
@@ -76,31 +100,48 @@ def extract_and_authenticate(data: dict, trust, *, blacklist=None, whitelist=Non
|
|
|
76
100
|
if "payload" not in data or "signature" not in data:
|
|
77
101
|
return None, None, False, "unauthorized: signed request required"
|
|
78
102
|
|
|
79
|
-
# Verify signature (protocol level - always required)
|
|
103
|
+
# Verify signature (protocol level - always required, even for whitelisted)
|
|
80
104
|
prompt, identity, error = _authenticate_signed(
|
|
81
|
-
data, blacklist=blacklist,
|
|
105
|
+
data, blacklist=blacklist, agent_address=agent_address
|
|
82
106
|
)
|
|
83
107
|
if error:
|
|
84
108
|
return prompt, identity, False, error
|
|
85
109
|
|
|
86
|
-
#
|
|
87
|
-
if
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
# Custom trust policy/agent evaluation
|
|
91
|
-
if is_custom_trust(trust):
|
|
92
|
-
trust_agent = create_trust_agent(trust)
|
|
93
|
-
accepted, reason = evaluate_with_trust_agent(trust_agent, prompt, identity, True)
|
|
94
|
-
if not accepted:
|
|
95
|
-
return None, identity, True, f"rejected: {reason}"
|
|
96
|
-
|
|
97
|
-
return prompt, identity, True, None
|
|
98
|
-
|
|
110
|
+
# Parameter whitelist bypasses trust POLICY (not signature verification)
|
|
111
|
+
if whitelist and identity in whitelist:
|
|
112
|
+
return prompt, identity, True, None
|
|
99
113
|
|
|
100
|
-
|
|
114
|
+
# Use TrustAgent for all trust decisions (fast rules + LLM fallback)
|
|
115
|
+
payload = data.get("payload", {})
|
|
116
|
+
request_data = {
|
|
117
|
+
"prompt": prompt,
|
|
118
|
+
"invite_code": payload.get("invite_code"),
|
|
119
|
+
"payment": payload.get("payment", 0),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Trust should be TrustAgent (resolved by host/create_app)
|
|
123
|
+
# But handle string for backwards compatibility with direct calls
|
|
124
|
+
if isinstance(trust, TrustAgent):
|
|
125
|
+
trust_agent = trust
|
|
126
|
+
elif isinstance(trust, str):
|
|
127
|
+
trust_agent = TrustAgent(trust)
|
|
128
|
+
else:
|
|
129
|
+
# Unknown type (e.g., Agent) - use default "careful"
|
|
130
|
+
trust_agent = TrustAgent("careful")
|
|
131
|
+
|
|
132
|
+
decision = trust_agent.should_allow(identity, request_data)
|
|
133
|
+
|
|
134
|
+
if decision.allow:
|
|
135
|
+
return prompt, identity, True, None
|
|
136
|
+
else:
|
|
137
|
+
return None, identity, True, f"forbidden: {decision.reason}"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _authenticate_signed(data: dict, *, blacklist=None, agent_address=None):
|
|
101
141
|
"""Authenticate signed request with Ed25519 - ALWAYS REQUIRED.
|
|
102
142
|
|
|
103
143
|
Protocol-level signature verification. All requests must be signed.
|
|
144
|
+
Whitelist is NOT checked here - it bypasses trust policy, not signature.
|
|
104
145
|
|
|
105
146
|
Returns: (prompt, identity, error) - error is None on success
|
|
106
147
|
"""
|
|
@@ -112,14 +153,10 @@ def _authenticate_signed(data: dict, *, blacklist=None, whitelist=None, agent_ad
|
|
|
112
153
|
timestamp = payload.get("timestamp")
|
|
113
154
|
to_address = payload.get("to")
|
|
114
155
|
|
|
115
|
-
# Check blacklist first
|
|
156
|
+
# Check blacklist first (security: even before signature check)
|
|
116
157
|
if blacklist and identity in blacklist:
|
|
117
158
|
return None, identity, "forbidden: blacklisted"
|
|
118
159
|
|
|
119
|
-
# Check whitelist (bypass signature check - trusted caller)
|
|
120
|
-
if whitelist and identity in whitelist:
|
|
121
|
-
return prompt, identity, None
|
|
122
|
-
|
|
123
160
|
# Validate required fields
|
|
124
161
|
if not identity:
|
|
125
162
|
return None, None, "unauthorized: 'from' field required"
|
|
@@ -137,7 +174,7 @@ def _authenticate_signed(data: dict, *, blacklist=None, whitelist=None, agent_ad
|
|
|
137
174
|
if agent_address and to_address and to_address != agent_address:
|
|
138
175
|
return None, identity, "unauthorized: wrong recipient"
|
|
139
176
|
|
|
140
|
-
# Verify signature
|
|
177
|
+
# Verify signature ALWAYS (no whitelist bypass - that's at policy level)
|
|
141
178
|
if not verify_signature(payload, signature, identity):
|
|
142
179
|
return None, identity, "unauthorized: invalid signature"
|
|
143
180
|
|
|
@@ -150,40 +187,6 @@ def get_agent_address(agent) -> str:
|
|
|
150
187
|
return f"0x{h[:40]}"
|
|
151
188
|
|
|
152
189
|
|
|
153
|
-
def evaluate_with_trust_agent(trust_agent, prompt: str, identity: str, sig_valid: bool) -> tuple[bool, str]:
|
|
154
|
-
"""Evaluate request using a custom trust agent (policy or Agent).
|
|
155
|
-
|
|
156
|
-
Only called when trust is a policy string or custom Agent - NOT for simple levels.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
trust_agent: The trust agent created from policy or custom Agent
|
|
160
|
-
prompt: The request prompt
|
|
161
|
-
identity: The requester's identity/address
|
|
162
|
-
sig_valid: Whether the signature is valid
|
|
163
|
-
|
|
164
|
-
Returns:
|
|
165
|
-
(accepted, reason) tuple
|
|
166
|
-
"""
|
|
167
|
-
from pydantic import BaseModel
|
|
168
|
-
from ...llm_do import llm_do
|
|
169
|
-
|
|
170
|
-
class TrustDecision(BaseModel):
|
|
171
|
-
accept: bool
|
|
172
|
-
reason: str
|
|
173
|
-
|
|
174
|
-
request_info = f"""Evaluate this request:
|
|
175
|
-
- prompt: {prompt[:200]}{'...' if len(prompt) > 200 else ''}
|
|
176
|
-
- identity: {identity or 'anonymous'}
|
|
177
|
-
- signature_valid: {sig_valid}"""
|
|
178
|
-
|
|
179
|
-
decision = llm_do(
|
|
180
|
-
request_info,
|
|
181
|
-
output=TrustDecision,
|
|
182
|
-
system_prompt=trust_agent.system_prompt,
|
|
183
|
-
)
|
|
184
|
-
return decision.accept, decision.reason
|
|
185
|
-
|
|
186
|
-
|
|
187
190
|
def is_custom_trust(trust) -> bool:
|
|
188
191
|
"""Check if trust needs a custom agent (policy or Agent, not a level)."""
|
|
189
192
|
if not isinstance(trust, str):
|
|
@@ -88,13 +88,19 @@ def health_handler(agent_name: str, start_time: float) -> dict:
|
|
|
88
88
|
return {"status": "healthy", "agent": agent_name, "uptime": int(time.time() - start_time)}
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def info_handler(agent_metadata: dict, trust: str) -> dict:
|
|
91
|
+
def info_handler(agent_metadata: dict, trust: str, trust_config: dict | None = None) -> dict:
|
|
92
92
|
"""GET /info
|
|
93
93
|
|
|
94
|
-
Returns pre-extracted metadata
|
|
94
|
+
Returns pre-extracted metadata including onboard requirements.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
agent_metadata: Agent name, address, tools
|
|
98
|
+
trust: Trust level string ("open", "careful", "strict", or custom)
|
|
99
|
+
trust_config: Parsed YAML config from trust policy (optional)
|
|
95
100
|
"""
|
|
96
101
|
from ... import __version__
|
|
97
|
-
|
|
102
|
+
|
|
103
|
+
result = {
|
|
98
104
|
"name": agent_metadata["name"],
|
|
99
105
|
"address": agent_metadata["address"],
|
|
100
106
|
"tools": agent_metadata["tools"],
|
|
@@ -102,6 +108,17 @@ def info_handler(agent_metadata: dict, trust: str) -> dict:
|
|
|
102
108
|
"version": __version__,
|
|
103
109
|
}
|
|
104
110
|
|
|
111
|
+
# Add onboard info if available
|
|
112
|
+
if trust_config:
|
|
113
|
+
onboard = trust_config.get("onboard", {})
|
|
114
|
+
if onboard:
|
|
115
|
+
result["onboard"] = {
|
|
116
|
+
"invite_code": "invite_code" in onboard,
|
|
117
|
+
"payment": onboard.get("payment"),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
105
122
|
|
|
106
123
|
def admin_logs_handler(agent_name: str) -> dict:
|
|
107
124
|
"""GET /admin/logs - return plain text activity log file."""
|
|
@@ -133,3 +150,71 @@ def admin_sessions_handler() -> dict:
|
|
|
133
150
|
# Sort by updated date descending (newest first)
|
|
134
151
|
sessions.sort(key=lambda s: s.get("updated", s.get("created", "")), reverse=True)
|
|
135
152
|
return {"sessions": sessions}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# === Admin Trust Routes ===
|
|
156
|
+
# These receive TrustAgent instance from route_handlers
|
|
157
|
+
|
|
158
|
+
def admin_trust_promote_handler(trust_agent, client_id: str) -> dict:
|
|
159
|
+
"""POST /admin/trust/promote - Promote client to next level."""
|
|
160
|
+
level = trust_agent.get_level(client_id)
|
|
161
|
+
if level == "stranger":
|
|
162
|
+
result = trust_agent.promote_to_contact(client_id)
|
|
163
|
+
elif level == "contact":
|
|
164
|
+
result = trust_agent.promote_to_whitelist(client_id)
|
|
165
|
+
elif level == "whitelist":
|
|
166
|
+
return {"error": "Already at highest level", "level": level}
|
|
167
|
+
elif level == "blocked":
|
|
168
|
+
return {"error": "Client is blocked. Unblock first.", "level": level}
|
|
169
|
+
else:
|
|
170
|
+
return {"error": f"Unknown level: {level}"}
|
|
171
|
+
|
|
172
|
+
return {"success": True, "message": result, "level": trust_agent.get_level(client_id)}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def admin_trust_demote_handler(trust_agent, client_id: str) -> dict:
|
|
176
|
+
"""POST /admin/trust/demote - Demote client to previous level."""
|
|
177
|
+
level = trust_agent.get_level(client_id)
|
|
178
|
+
if level == "whitelist":
|
|
179
|
+
result = trust_agent.demote_to_contact(client_id)
|
|
180
|
+
elif level == "contact":
|
|
181
|
+
result = trust_agent.demote_to_stranger(client_id)
|
|
182
|
+
elif level == "stranger":
|
|
183
|
+
return {"error": "Already at lowest level", "level": level}
|
|
184
|
+
elif level == "blocked":
|
|
185
|
+
return {"error": "Client is blocked. Unblock first.", "level": level}
|
|
186
|
+
else:
|
|
187
|
+
return {"error": f"Unknown level: {level}"}
|
|
188
|
+
|
|
189
|
+
return {"success": True, "message": result, "level": trust_agent.get_level(client_id)}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def admin_trust_block_handler(trust_agent, client_id: str, reason: str = "") -> dict:
|
|
193
|
+
"""POST /admin/trust/block - Block a client."""
|
|
194
|
+
result = trust_agent.block(client_id, reason)
|
|
195
|
+
return {"success": True, "message": result, "level": trust_agent.get_level(client_id)}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def admin_trust_unblock_handler(trust_agent, client_id: str) -> dict:
|
|
199
|
+
"""POST /admin/trust/unblock - Unblock a client."""
|
|
200
|
+
result = trust_agent.unblock(client_id)
|
|
201
|
+
return {"success": True, "message": result, "level": trust_agent.get_level(client_id)}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def admin_trust_level_handler(trust_agent, client_id: str) -> dict:
|
|
205
|
+
"""GET /admin/trust/level/{client_id} - Get client's trust level."""
|
|
206
|
+
return {"client_id": client_id, "level": trust_agent.get_level(client_id)}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# === Super Admin Routes (admin management) ===
|
|
210
|
+
|
|
211
|
+
def admin_admins_add_handler(trust_agent, admin_id: str) -> dict:
|
|
212
|
+
"""POST /superadmin/add - Add an admin. Super admin only."""
|
|
213
|
+
result = trust_agent.add_admin(admin_id)
|
|
214
|
+
return {"success": True, "message": result}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def admin_admins_remove_handler(trust_agent, admin_id: str) -> dict:
|
|
218
|
+
"""POST /superadmin/remove - Remove an admin. Super admin only."""
|
|
219
|
+
result = trust_agent.remove_admin(admin_id)
|
|
220
|
+
return {"success": True, "message": result}
|
|
@@ -26,6 +26,7 @@ don't interfere between concurrent requests.
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
import os
|
|
29
|
+
from functools import partial
|
|
29
30
|
from pathlib import Path
|
|
30
31
|
from typing import Callable, Union
|
|
31
32
|
|
|
@@ -33,7 +34,8 @@ from rich.console import Console
|
|
|
33
34
|
from rich.panel import Panel
|
|
34
35
|
|
|
35
36
|
from ..asgi import create_app as asgi_create_app
|
|
36
|
-
from ..trust import get_default_trust_level
|
|
37
|
+
from ..trust import TrustAgent, get_default_trust_level, parse_policy, TRUST_LEVELS
|
|
38
|
+
from ..trust.factory import PROMPTS_DIR
|
|
37
39
|
from .session import SessionStorage
|
|
38
40
|
from .auth import extract_and_authenticate
|
|
39
41
|
from .routes import (
|
|
@@ -44,9 +46,49 @@ from .routes import (
|
|
|
44
46
|
info_handler,
|
|
45
47
|
admin_logs_handler,
|
|
46
48
|
admin_sessions_handler,
|
|
49
|
+
# Admin trust routes
|
|
50
|
+
admin_trust_promote_handler,
|
|
51
|
+
admin_trust_demote_handler,
|
|
52
|
+
admin_trust_block_handler,
|
|
53
|
+
admin_trust_unblock_handler,
|
|
54
|
+
admin_trust_level_handler,
|
|
55
|
+
# Super admin routes
|
|
56
|
+
admin_admins_add_handler,
|
|
57
|
+
admin_admins_remove_handler,
|
|
47
58
|
)
|
|
48
59
|
|
|
49
60
|
|
|
61
|
+
def _parse_trust_config(trust: Union[str, "Agent"]) -> dict | None:
|
|
62
|
+
"""Parse trust config from trust parameter.
|
|
63
|
+
|
|
64
|
+
Returns YAML config dict if trust is a level or file path, None otherwise.
|
|
65
|
+
Used to extract onboard info for /info endpoint.
|
|
66
|
+
"""
|
|
67
|
+
if not isinstance(trust, str):
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
# Check if it's a trust level
|
|
71
|
+
if trust.lower() in TRUST_LEVELS:
|
|
72
|
+
policy_path = PROMPTS_DIR / f"{trust.lower()}.md"
|
|
73
|
+
if policy_path.exists():
|
|
74
|
+
config, _ = parse_policy(policy_path.read_text(encoding='utf-8'))
|
|
75
|
+
return config
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
# Check if it's a file path
|
|
79
|
+
path = Path(trust)
|
|
80
|
+
if path.exists() and path.is_file():
|
|
81
|
+
config, _ = parse_policy(path.read_text(encoding='utf-8'))
|
|
82
|
+
return config
|
|
83
|
+
|
|
84
|
+
# Inline policy text
|
|
85
|
+
if trust.startswith('---'):
|
|
86
|
+
config, _ = parse_policy(trust)
|
|
87
|
+
return config
|
|
88
|
+
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
50
92
|
def get_default_trust() -> str:
|
|
51
93
|
"""Get default trust based on environment.
|
|
52
94
|
|
|
@@ -70,7 +112,7 @@ def _extract_agent_metadata(create_agent: Callable) -> tuple[dict, object]:
|
|
|
70
112
|
return metadata, sample
|
|
71
113
|
|
|
72
114
|
|
|
73
|
-
def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_ttl: int):
|
|
115
|
+
def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_ttl: int, trust_agent):
|
|
74
116
|
"""Create route handler dict for ASGI app.
|
|
75
117
|
|
|
76
118
|
Args:
|
|
@@ -79,6 +121,7 @@ def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_
|
|
|
79
121
|
agent_metadata: Pre-extracted metadata (name, tools, address) - avoids
|
|
80
122
|
creating agents for health/info endpoints.
|
|
81
123
|
result_ttl: How long to keep results on server in seconds
|
|
124
|
+
trust_agent: TrustAgent instance for trust operations
|
|
82
125
|
"""
|
|
83
126
|
agent_name = agent_metadata["name"]
|
|
84
127
|
|
|
@@ -91,8 +134,8 @@ def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_
|
|
|
91
134
|
def handle_health(start_time):
|
|
92
135
|
return health_handler(agent_name, start_time)
|
|
93
136
|
|
|
94
|
-
def handle_info(trust):
|
|
95
|
-
return info_handler(agent_metadata, trust)
|
|
137
|
+
def handle_info(trust, trust_config=None):
|
|
138
|
+
return info_handler(agent_metadata, trust, trust_config)
|
|
96
139
|
|
|
97
140
|
def handle_admin_logs():
|
|
98
141
|
return admin_logs_handler(agent_name)
|
|
@@ -107,6 +150,17 @@ def _create_route_handlers(create_agent: Callable, agent_metadata: dict, result_
|
|
|
107
150
|
"ws_input": handle_ws_input,
|
|
108
151
|
"admin_logs": handle_admin_logs,
|
|
109
152
|
"admin_sessions": admin_sessions_handler,
|
|
153
|
+
# TrustAgent instance for direct access in http.py/websocket.py
|
|
154
|
+
"trust_agent": trust_agent,
|
|
155
|
+
# Admin trust routes (partial injects trust_agent as first arg)
|
|
156
|
+
"admin_trust_promote": partial(admin_trust_promote_handler, trust_agent),
|
|
157
|
+
"admin_trust_demote": partial(admin_trust_demote_handler, trust_agent),
|
|
158
|
+
"admin_trust_block": partial(admin_trust_block_handler, trust_agent),
|
|
159
|
+
"admin_trust_unblock": partial(admin_trust_unblock_handler, trust_agent),
|
|
160
|
+
"admin_trust_level": partial(admin_trust_level_handler, trust_agent),
|
|
161
|
+
# Super admin routes
|
|
162
|
+
"admin_admins_add": partial(admin_admins_add_handler, trust_agent),
|
|
163
|
+
"admin_admins_remove": partial(admin_admins_remove_handler, trust_agent),
|
|
110
164
|
}
|
|
111
165
|
|
|
112
166
|
|
|
@@ -197,14 +251,14 @@ def host(
|
|
|
197
251
|
whitelist: Allowed identities
|
|
198
252
|
|
|
199
253
|
Endpoints:
|
|
200
|
-
POST /input
|
|
201
|
-
GET /sessions/{id}
|
|
202
|
-
GET /sessions
|
|
203
|
-
GET /health
|
|
204
|
-
GET /info
|
|
205
|
-
WS /ws
|
|
206
|
-
GET /logs
|
|
207
|
-
GET /
|
|
254
|
+
POST /input - Submit prompt, get result
|
|
255
|
+
GET /sessions/{id} - Get session by ID
|
|
256
|
+
GET /sessions - List all sessions
|
|
257
|
+
GET /health - Health check
|
|
258
|
+
GET /info - Agent info
|
|
259
|
+
WS /ws - WebSocket
|
|
260
|
+
GET /admin/logs - Activity log (requires OPENONION_API_KEY)
|
|
261
|
+
GET /admin/sessions - Activity sessions (requires OPENONION_API_KEY)
|
|
208
262
|
"""
|
|
209
263
|
import uvicorn
|
|
210
264
|
from ... import address
|
|
@@ -228,11 +282,24 @@ def host(
|
|
|
228
282
|
agent_metadata["address"] = addr_data['address']
|
|
229
283
|
|
|
230
284
|
storage = SessionStorage()
|
|
231
|
-
|
|
285
|
+
|
|
286
|
+
# Create TrustAgent instance - the single interface for all trust operations
|
|
287
|
+
# Users can subclass TrustAgent to customize (e.g., database-backed admin storage)
|
|
288
|
+
if isinstance(trust, TrustAgent):
|
|
289
|
+
trust_agent = trust
|
|
290
|
+
else:
|
|
291
|
+
trust_agent = TrustAgent(trust if isinstance(trust, str) else "careful")
|
|
292
|
+
|
|
293
|
+
route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl, trust_agent)
|
|
294
|
+
|
|
295
|
+
# Parse trust config for /info onboard info
|
|
296
|
+
trust_config = _parse_trust_config(trust)
|
|
297
|
+
|
|
232
298
|
app = asgi_create_app(
|
|
233
299
|
route_handlers=route_handlers,
|
|
234
300
|
storage=storage,
|
|
235
|
-
trust=trust
|
|
301
|
+
trust=trust_agent, # Pass resolved TrustAgent, not raw trust
|
|
302
|
+
trust_config=trust_config,
|
|
236
303
|
blacklist=blacklist,
|
|
237
304
|
whitelist=whitelist,
|
|
238
305
|
)
|
|
@@ -243,13 +310,23 @@ def host(
|
|
|
243
310
|
|
|
244
311
|
# Display startup info
|
|
245
312
|
relay_status = f"[green]✓[/] {relay_url}" if relay_url else "[dim]disabled[/]"
|
|
313
|
+
|
|
314
|
+
# Build trust info for display
|
|
315
|
+
trust_info = ""
|
|
316
|
+
if trust_config and isinstance(trust, str) and trust.lower() in TRUST_LEVELS:
|
|
317
|
+
onboard = trust_config.get("onboard", {})
|
|
318
|
+
invite_codes = onboard.get("invite_code", [])
|
|
319
|
+
if invite_codes:
|
|
320
|
+
codes_str = ", ".join(invite_codes) if isinstance(invite_codes, list) else invite_codes
|
|
321
|
+
trust_info = f"\n[bold]Invite:[/] {codes_str}\n[dim] Run 'co copy trust/{trust}' to customize[/]"
|
|
322
|
+
|
|
246
323
|
Console().print(Panel(
|
|
247
324
|
f"[bold]POST[/] http://localhost:{port}/input\n"
|
|
248
325
|
f"[dim]GET /sessions/{{id}} · /sessions · /health · /info[/]\n"
|
|
249
326
|
f"[dim]WS ws://localhost:{port}/ws\n"
|
|
250
327
|
f"[dim]UI http://localhost:{port}/docs[/]\n\n"
|
|
251
328
|
f"[bold]Address:[/] {agent_metadata['address']}\n"
|
|
252
|
-
f"[bold]Relay:[/] {relay_status}",
|
|
329
|
+
f"[bold]Relay:[/] {relay_status}{trust_info}",
|
|
253
330
|
title=f"[green]Agent '{agent_metadata['name']}'[/]"
|
|
254
331
|
))
|
|
255
332
|
|
|
@@ -279,11 +356,17 @@ def create_app(create_agent: Callable, storage=None, trust="careful", result_ttl
|
|
|
279
356
|
agent_metadata, sample = _extract_agent_metadata(create_agent)
|
|
280
357
|
agent_metadata["address"] = get_agent_address(sample)
|
|
281
358
|
|
|
282
|
-
|
|
359
|
+
# Create TrustAgent instance
|
|
360
|
+
if isinstance(trust, TrustAgent):
|
|
361
|
+
trust_agent = trust
|
|
362
|
+
else:
|
|
363
|
+
trust_agent = TrustAgent(trust if isinstance(trust, str) else "careful")
|
|
364
|
+
|
|
365
|
+
route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl, trust_agent)
|
|
283
366
|
return asgi_create_app(
|
|
284
367
|
route_handlers=route_handlers,
|
|
285
368
|
storage=storage,
|
|
286
|
-
trust=trust
|
|
369
|
+
trust=trust_agent, # Pass resolved TrustAgent, not raw trust
|
|
287
370
|
blacklist=blacklist,
|
|
288
371
|
whitelist=whitelist,
|
|
289
372
|
)
|
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Purpose: Trust verification system module re-exporting factory, prompts, and verification tools
|
|
3
|
-
LLM-Note:
|
|
4
|
-
Dependencies: imports from [factory.py, prompts.py, tools.py] | imported by [network/__init__.py, network/host/auth.py, network/host/server.py] | tested by [tests/network/test_trust.py]
|
|
5
|
-
Data flow: re-exports trust agent creation utilities and trust level prompts
|
|
6
|
-
State/Effects: no state
|
|
7
|
-
Integration: exposes create_trust_agent(trust_spec) → Agent, get_default_trust_level() → str, validate_trust_level(level), TRUST_LEVELS dict, TRUST_PROMPTS dict, get_trust_prompt(level) → str, get_trust_verification_tools() → list | used by host() for access control policies
|
|
8
|
-
Performance: trivial
|
|
9
|
-
Errors: none
|
|
10
2
|
Trust verification system for agent networking.
|
|
11
3
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
4
|
+
Usage:
|
|
5
|
+
from connectonion.network.trust import TrustAgent
|
|
6
|
+
|
|
7
|
+
trust = TrustAgent("careful") # or "open", "strict", or path to policy
|
|
8
|
+
|
|
9
|
+
# Access control
|
|
10
|
+
decision = trust.should_allow("client-123", {"prompt": "hello"})
|
|
11
|
+
|
|
12
|
+
# Trust level operations
|
|
13
|
+
trust.promote_to_contact("client-123")
|
|
14
|
+
trust.block("bad-actor", reason="spam")
|
|
15
|
+
level = trust.get_level("client-123") # "stranger", "contact", "whitelist", "blocked"
|
|
16
|
+
|
|
17
|
+
# Admin operations
|
|
18
|
+
trust.is_admin("client-123")
|
|
19
|
+
trust.add_admin("new-admin")
|
|
20
|
+
|
|
21
|
+
TrustAgent is the single interface for all trust operations.
|
|
22
|
+
Subclass to customize behavior (e.g., database-backed admin storage).
|
|
16
23
|
"""
|
|
17
24
|
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
25
|
+
from .trust_agent import TrustAgent, Decision
|
|
26
|
+
from .factory import get_default_trust_level, validate_trust_level, TRUST_LEVELS
|
|
27
|
+
from .fast_rules import parse_policy
|
|
21
28
|
|
|
22
29
|
__all__ = [
|
|
23
|
-
|
|
30
|
+
# TrustAgent - the single interface
|
|
31
|
+
"TrustAgent",
|
|
32
|
+
"Decision",
|
|
33
|
+
# Helpers
|
|
24
34
|
"get_default_trust_level",
|
|
25
35
|
"validate_trust_level",
|
|
26
36
|
"TRUST_LEVELS",
|
|
27
|
-
"
|
|
28
|
-
"get_trust_prompt",
|
|
29
|
-
"get_trust_verification_tools",
|
|
37
|
+
"parse_policy",
|
|
30
38
|
]
|