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.
Files changed (50) hide show
  1. connectonion/__init__.py +1 -1
  2. connectonion/cli/co_ai/agent.py +3 -3
  3. connectonion/cli/co_ai/main.py +2 -2
  4. connectonion/cli/co_ai/plugins/__init__.py +2 -3
  5. connectonion/cli/co_ai/plugins/system_reminder.py +154 -0
  6. connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +166 -208
  7. connectonion/cli/co_ai/prompts/system-reminders/agent.md +23 -0
  8. connectonion/cli/co_ai/prompts/system-reminders/plan_mode.md +13 -0
  9. connectonion/cli/co_ai/prompts/system-reminders/security.md +14 -0
  10. connectonion/cli/co_ai/prompts/system-reminders/simplicity.md +14 -0
  11. connectonion/cli/co_ai/tools/plan_mode.py +1 -4
  12. connectonion/cli/co_ai/tools/read.py +0 -6
  13. connectonion/cli/commands/copy_commands.py +21 -0
  14. connectonion/cli/commands/trust_commands.py +152 -0
  15. connectonion/cli/main.py +82 -0
  16. connectonion/core/llm.py +2 -2
  17. connectonion/docs/concepts/fast_rules.md +237 -0
  18. connectonion/docs/concepts/onboarding.md +465 -0
  19. connectonion/docs/concepts/plugins.md +2 -1
  20. connectonion/docs/concepts/trust.md +933 -192
  21. connectonion/docs/design-decisions/023-trust-policy-system-design.md +323 -0
  22. connectonion/docs/network/README.md +23 -1
  23. connectonion/docs/network/connect.md +135 -0
  24. connectonion/docs/network/host.md +73 -4
  25. connectonion/docs/useful_plugins/tool_approval.md +139 -0
  26. connectonion/network/__init__.py +7 -6
  27. connectonion/network/asgi/__init__.py +3 -0
  28. connectonion/network/asgi/http.py +125 -19
  29. connectonion/network/asgi/websocket.py +276 -15
  30. connectonion/network/connect.py +145 -29
  31. connectonion/network/host/auth.py +70 -67
  32. connectonion/network/host/routes.py +88 -3
  33. connectonion/network/host/server.py +100 -17
  34. connectonion/network/trust/__init__.py +27 -19
  35. connectonion/network/trust/factory.py +51 -24
  36. connectonion/network/trust/fast_rules.py +100 -0
  37. connectonion/network/trust/tools.py +316 -32
  38. connectonion/network/trust/trust_agent.py +403 -0
  39. connectonion/transcribe.py +1 -1
  40. connectonion/useful_plugins/__init__.py +2 -1
  41. connectonion/useful_plugins/tool_approval.py +233 -0
  42. {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/METADATA +1 -1
  43. {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/RECORD +45 -37
  44. connectonion/cli/co_ai/plugins/reminder.py +0 -76
  45. connectonion/cli/co_ai/plugins/shell_approval.py +0 -105
  46. connectonion/cli/co_ai/prompts/reminders/plan_mode.md +0 -34
  47. connectonion/cli/co_ai/reminders.py +0 -159
  48. connectonion/network/trust/prompts.py +0 -71
  49. {connectonion-0.6.3.dist-info → connectonion-0.6.5.dist-info}/WHEEL +0 -0
  50. {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/, llm_do.py, nacl.signing] | imported by [network/host/routes.py, network/host/server.py] | tested by [tests/network/test_host_auth.py]
5
- Data flow: receives request dict with {payload, from, signature} → extract_and_authenticate() verifies Ed25519 signature via verify_signature() using nacl checks timestamp expiry (5 min window) applies trust policy (open/careful/strict/custom) optionally evaluates via trust agent → returns (prompt, identity, sig_valid, error)
6
- State/Effects: reads trust settings from agent | calls trust agent for evaluation if custom policy | no persistent state
7
- Integration: exposes verify_signature(payload, signature, public_key) → bool, extract_and_authenticate(data, trust, blacklist, whitelist, agent_address) → (prompt, identity, sig_valid, error), get_agent_address(agent) → str, is_custom_trust(trust) → bool | used by host() to enforce authentication on all requests
8
- Performance: signature verification uses nacl (fast Ed25519) | 5 minute timestamp window prevents replay | whitelist bypass for trusted callers
9
- Errors: returns error strings: "unauthorized: ...", "forbidden: ...", "rejected: ..." | does NOT raise exceptions (caller checks error)
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 create_trust_agent, TRUST_LEVELS
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
- Trust levels control additional policies AFTER signature verification:
68
- - "open": Any valid signer allowed
69
- - "careful": Warnings for unknown signers (default)
70
- - "strict": Whitelist only
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, whitelist=whitelist, agent_address=agent_address
105
+ data, blacklist=blacklist, agent_address=agent_address
82
106
  )
83
107
  if error:
84
108
  return prompt, identity, False, error
85
109
 
86
- # Trust level: additional policies AFTER signature verification
87
- if trust == "strict" and whitelist and identity not in whitelist:
88
- return None, identity, True, "forbidden: not in whitelist"
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
- def _authenticate_signed(data: dict, *, blacklist=None, whitelist=None, agent_address=None):
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 - no agent creation needed.
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
- return {
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 - Submit prompt, get result
201
- GET /sessions/{id} - Get session by ID
202
- GET /sessions - List all sessions
203
- GET /health - Health check
204
- GET /info - Agent info
205
- WS /ws - WebSocket
206
- GET /logs - Activity log (requires OPENONION_API_KEY)
207
- GET /logs/sessions - Activity sessions (requires OPENONION_API_KEY)
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
- route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl)
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
- route_handlers = _create_route_handlers(create_agent, agent_metadata, result_ttl)
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
- This module contains:
13
- - factory: Trust agent creation and configuration
14
- - prompts: Pre-configured trust prompts for different levels
15
- - tools: Verification tools for trust agents
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 .factory import create_trust_agent, get_default_trust_level, validate_trust_level, TRUST_LEVELS
19
- from .prompts import TRUST_PROMPTS, get_trust_prompt
20
- from .tools import get_trust_verification_tools
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
- "create_trust_agent",
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
- "TRUST_PROMPTS",
28
- "get_trust_prompt",
29
- "get_trust_verification_tools",
37
+ "parse_policy",
30
38
  ]