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
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: build
3
+ intent: build
4
+ ---
5
+
6
+ <system-reminder>
7
+ Agent creation detected. Use ConnectOnion framework:
8
+
9
+ **ConnectOnion agents are Python files (.py), NOT markdown files.**
10
+
11
+ ```python
12
+ from connectonion import Agent
13
+
14
+ def list_files(dir: str) -> list[str]: ...
15
+ def get_hash(path: str) -> str: ...
16
+ def delete(path: str) -> str: ...
17
+
18
+ agent = Agent("cleaner", tools=[list_files, get_hash, delete])
19
+ agent.input("Remove duplicate files")
20
+ ```
21
+
22
+ **Give tools, not logic. Let the agent decide strategy. NEVER create standalone scripts with hardcoded logic.**
23
+ </system-reminder>
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: plan-mode
3
+ triggers:
4
+ - tool: enter_plan_mode
5
+ ---
6
+
7
+ <system-reminder>
8
+ Plan mode is active. You MUST NOT make any edits or run non-readonly tools.
9
+
10
+ Only allowed: glob, grep, read_file, write_plan, exit_plan_mode
11
+
12
+ Write your plan to the plan file, then call exit_plan_mode when done.
13
+ </system-reminder>
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: security
3
+ triggers:
4
+ - tool: read_file
5
+ path_pattern: ["*.env*", "*credentials*", "*secrets*", "*password*", "*token*", "*.pem", "*.key"]
6
+ - tool: read
7
+ path_pattern: ["*.env*", "*credentials*", "*secrets*", "*password*", "*token*", "*.pem", "*.key"]
8
+ ---
9
+
10
+ <system-reminder>
11
+ This file may contain sensitive information.
12
+ - Never expose secrets in output
13
+ - Never commit real credentials
14
+ </system-reminder>
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: simplicity
3
+ triggers:
4
+ - tool: edit
5
+ - tool: multi_edit
6
+ - tool: write
7
+ ---
8
+
9
+ <system-reminder>
10
+ Keep it simple:
11
+ - Only change what's directly needed
12
+ - Don't add error handling for scenarios that can't happen
13
+ - Three similar lines > premature abstraction
14
+ </system-reminder>
@@ -6,8 +6,6 @@ from rich.console import Console
6
6
  from rich.panel import Panel
7
7
  from rich.markdown import Markdown
8
8
 
9
- from connectonion.cli.co_ai.reminders import REMINDERS
10
-
11
9
  console = Console()
12
10
 
13
11
  # Plan mode state (module-level for simplicity)
@@ -101,8 +99,7 @@ def enter_plan_mode() -> str:
101
99
  border_style="green"
102
100
  ))
103
101
 
104
- plan_mode_reminder = REMINDERS.get("plan_mode_active", "")
105
- return f"Entered plan mode. Write your plan to {_plan_file_path}, then call exit_plan_mode() when ready for user approval.\n\n{plan_mode_reminder}"
102
+ return f"Entered plan mode. Write your plan to {_plan_file_path}, then call exit_plan_mode() when ready for user approval."
106
103
 
107
104
 
108
105
  def exit_plan_mode() -> str:
@@ -3,8 +3,6 @@
3
3
  from pathlib import Path
4
4
  from typing import Optional
5
5
 
6
- from connectonion.cli.co_ai.reminders import inject_reminder, should_show_security_reminder
7
-
8
6
 
9
7
  def read_file(
10
8
  path: str,
@@ -60,8 +58,4 @@ def read_file(
60
58
  if end < total_lines:
61
59
  result += f"\n\n... ({total_lines - end} more lines)"
62
60
 
63
- # Inject security reminder for sensitive files
64
- if should_show_security_reminder(path):
65
- result = inject_reminder(result, "security")
66
-
67
61
  return result
@@ -63,6 +63,13 @@ PROMPTS = {
63
63
  "coding_agent": "coding_agent",
64
64
  }
65
65
 
66
+ # Registry of copyable trust policies
67
+ TRUST = {
68
+ "open": "open.md",
69
+ "careful": "careful.md",
70
+ "strict": "strict.md",
71
+ }
72
+
66
73
 
67
74
  def handle_copy(
68
75
  names: List[str],
@@ -82,11 +89,14 @@ def handle_copy(
82
89
  import connectonion.useful_plugins as plugins_module
83
90
  import connectonion.tui as tui_module
84
91
  import connectonion.useful_prompts as prompts_module
92
+ import connectonion
85
93
 
86
94
  useful_tools_dir = Path(tools_module.__file__).parent
87
95
  useful_plugins_dir = Path(plugins_module.__file__).parent
88
96
  tui_dir = Path(tui_module.__file__).parent
89
97
  useful_prompts_dir = Path(prompts_module.__file__).parent
98
+ # Trust policies are in the main package's prompts/trust/ directory
99
+ trust_dir = Path(connectonion.__file__).parent.parent / "prompts" / "trust"
90
100
 
91
101
  current_dir = Path.cwd()
92
102
 
@@ -123,6 +133,13 @@ def handle_copy(
123
133
  dest_dir = Path(path) if path else current_dir / "prompts"
124
134
  copy_directory(source, dest_dir, force)
125
135
 
136
+ # Check if it's a trust policy (support both "careful" and "trust/careful")
137
+ elif name_lower in TRUST or (name_lower.startswith("trust/") and name_lower[6:] in TRUST):
138
+ policy_name = name_lower[6:] if name_lower.startswith("trust/") else name_lower
139
+ source = trust_dir / TRUST[policy_name]
140
+ dest_dir = Path(path) if path else current_dir / "prompts" / "trust"
141
+ copy_file(source, dest_dir, force)
142
+
126
143
  else:
127
144
  console.print(f"[red]Unknown: {name}[/red]")
128
145
  console.print("Use [cyan]co copy --list[/cyan] to see available items")
@@ -180,9 +197,13 @@ def show_available_items():
180
197
  for name, dir_name in sorted(PROMPTS.items()):
181
198
  table.add_row(name, "prompt", f"{dir_name}/")
182
199
 
200
+ for name, file in sorted(TRUST.items()):
201
+ table.add_row(f"trust/{name}", "trust", file)
202
+
183
203
  for name, file in sorted(TUI.items()):
184
204
  table.add_row(name, "tui", file)
185
205
 
186
206
  console.print(table)
187
207
  console.print("\n[dim]Usage: co copy <name> [--path ./custom/][/dim]")
188
208
  console.print("[dim]Prompts are copied as directories to ./prompts/<name>/[/dim]")
209
+ console.print("[dim]Trust policies are copied to ./prompts/trust/<name>.md[/dim]")
@@ -0,0 +1,152 @@
1
+ """
2
+ CLI commands for managing trust lists (contacts, whitelist, blocklist, admins).
3
+
4
+ Addresses are shown in full (not truncated) so users can easily copy them
5
+ for use in other commands or configuration files.
6
+
7
+ Usage:
8
+ co trust list # List all trust lists
9
+ co trust level <address> # Check trust level of address
10
+ co trust add <address> # Add to contacts (default)
11
+ co trust add <address> -w # Add to whitelist
12
+ co trust remove <address> # Remove from all lists
13
+ co trust block <address> # Block an address
14
+ co trust unblock <address> # Unblock an address
15
+ co trust admin add <address> # Add admin (super admin only)
16
+ co trust admin remove <address> # Remove admin (super admin only)
17
+ """
18
+
19
+ from pathlib import Path
20
+ from rich.console import Console
21
+ from rich.table import Table
22
+
23
+ from ...network.trust.tools import (
24
+ CO_DIR,
25
+ get_level,
26
+ promote_to_contact,
27
+ promote_to_whitelist,
28
+ demote_to_stranger,
29
+ block,
30
+ unblock,
31
+ add_admin,
32
+ remove_admin,
33
+ load_admins,
34
+ get_self_address,
35
+ )
36
+
37
+ console = Console()
38
+
39
+
40
+ def _read_list(list_name: str) -> list[str]:
41
+ """Read entries from a list file."""
42
+ list_path = CO_DIR / f"{list_name}.txt"
43
+ if not list_path.exists():
44
+ return []
45
+ content = list_path.read_text(encoding='utf-8')
46
+ return [line.strip() for line in content.splitlines()
47
+ if line.strip() and not line.startswith('#')]
48
+
49
+
50
+ def handle_trust_list():
51
+ """List all trust lists."""
52
+ contacts = _read_list("contacts")
53
+ whitelist = _read_list("whitelist")
54
+ blocklist = _read_list("blocklist")
55
+ admins = load_admins()
56
+ self_addr = get_self_address()
57
+
58
+ console.print()
59
+
60
+ # Admins
61
+ console.print("[bold]Admins[/bold]")
62
+ if admins:
63
+ for addr in admins:
64
+ label = " [dim](self)[/dim]" if addr == self_addr else ""
65
+ console.print(f" {addr}{label}")
66
+ else:
67
+ console.print(" [dim]No admins configured[/dim]")
68
+ console.print()
69
+
70
+ # Whitelist
71
+ console.print("[bold]Whitelist[/bold] [dim](full trust)[/dim]")
72
+ if whitelist:
73
+ for addr in whitelist:
74
+ console.print(f" {addr}")
75
+ else:
76
+ console.print(" [dim]Empty[/dim]")
77
+ console.print()
78
+
79
+ # Contacts
80
+ console.print("[bold]Contacts[/bold] [dim](verified via invite/payment)[/dim]")
81
+ if contacts:
82
+ for addr in contacts:
83
+ console.print(f" {addr}")
84
+ else:
85
+ console.print(" [dim]Empty[/dim]")
86
+ console.print()
87
+
88
+ # Blocklist
89
+ console.print("[bold]Blocklist[/bold] [dim](denied access)[/dim]")
90
+ if blocklist:
91
+ for addr in blocklist:
92
+ console.print(f" [red]{addr}[/red]")
93
+ else:
94
+ console.print(" [dim]Empty[/dim]")
95
+ console.print()
96
+
97
+ console.print(f"[dim]Lists stored in: {CO_DIR}[/dim]")
98
+
99
+
100
+ def handle_trust_level(address: str):
101
+ """Check trust level of an address."""
102
+ level = get_level(address)
103
+
104
+ level_colors = {
105
+ "stranger": "dim",
106
+ "contact": "cyan",
107
+ "whitelist": "green",
108
+ "blocked": "red",
109
+ }
110
+ color = level_colors.get(level, "white")
111
+
112
+ console.print(f"\n{address}: [{color}]{level}[/{color}]\n")
113
+
114
+
115
+ def handle_trust_add(address: str, whitelist: bool = False):
116
+ """Add address to contacts or whitelist."""
117
+ if whitelist:
118
+ result = promote_to_whitelist(address)
119
+ console.print(f"\n[green]✓[/green] {result}\n")
120
+ else:
121
+ result = promote_to_contact(address)
122
+ console.print(f"\n[green]✓[/green] {result}\n")
123
+
124
+
125
+ def handle_trust_remove(address: str):
126
+ """Remove address from all lists (demote to stranger)."""
127
+ result = demote_to_stranger(address)
128
+ console.print(f"\n[yellow]✓[/yellow] {result}\n")
129
+
130
+
131
+ def handle_trust_block(address: str, reason: str = ""):
132
+ """Block an address."""
133
+ result = block(address, reason)
134
+ console.print(f"\n[red]✓[/red] {result}\n")
135
+
136
+
137
+ def handle_trust_unblock(address: str):
138
+ """Unblock an address."""
139
+ result = unblock(address)
140
+ console.print(f"\n[green]✓[/green] {result}\n")
141
+
142
+
143
+ def handle_admin_add(address: str):
144
+ """Add an admin."""
145
+ result = add_admin(address)
146
+ console.print(f"\n[green]✓[/green] {result}\n")
147
+
148
+
149
+ def handle_admin_remove(address: str):
150
+ """Remove an admin."""
151
+ result = remove_admin(address)
152
+ console.print(f"\n[yellow]✓[/yellow] {result}\n")
connectonion/cli/main.py CHANGED
@@ -56,6 +56,7 @@ def _show_help():
56
56
  console.print(" [green]init[/green] Initialize in current directory")
57
57
  console.print(" [green]copy[/green] <name> Copy tool/plugin source to project")
58
58
  console.print(" [green]eval[/green] Run evals and show status")
59
+ console.print(" [green]trust[/green] Manage trust lists")
59
60
  console.print(" [green]deploy[/green] Deploy to ConnectOnion Cloud")
60
61
  console.print(" [green]auth[/green] Authenticate for managed keys")
61
62
  console.print(" [green]status[/green] Check account balance")
@@ -174,6 +175,87 @@ def eval(
174
175
  handle_eval(name=name, agent_file=agent)
175
176
 
176
177
 
178
+ # Trust command group
179
+ trust_app = typer.Typer(help="Manage trust lists (contacts, whitelist, blocklist, admins)")
180
+ app.add_typer(trust_app, name="trust")
181
+
182
+
183
+ @trust_app.callback(invoke_without_command=True)
184
+ def trust_callback(ctx: typer.Context):
185
+ """Trust list management."""
186
+ if ctx.invoked_subcommand is None:
187
+ # Default to list
188
+ from .commands.trust_commands import handle_trust_list
189
+ handle_trust_list()
190
+
191
+
192
+ @trust_app.command("list")
193
+ def trust_list():
194
+ """List all trust lists."""
195
+ from .commands.trust_commands import handle_trust_list
196
+ handle_trust_list()
197
+
198
+
199
+ @trust_app.command("level")
200
+ def trust_level(address: str = typer.Argument(..., help="Address to check")):
201
+ """Check trust level of an address."""
202
+ from .commands.trust_commands import handle_trust_level
203
+ handle_trust_level(address)
204
+
205
+
206
+ @trust_app.command("add")
207
+ def trust_add(
208
+ address: str = typer.Argument(..., help="Address to add"),
209
+ whitelist: bool = typer.Option(False, "-w", "--whitelist", help="Add to whitelist instead of contacts"),
210
+ ):
211
+ """Add address to contacts (default) or whitelist."""
212
+ from .commands.trust_commands import handle_trust_add
213
+ handle_trust_add(address, whitelist)
214
+
215
+
216
+ @trust_app.command("remove")
217
+ def trust_remove(address: str = typer.Argument(..., help="Address to remove")):
218
+ """Remove address from all lists (demote to stranger)."""
219
+ from .commands.trust_commands import handle_trust_remove
220
+ handle_trust_remove(address)
221
+
222
+
223
+ @trust_app.command("block")
224
+ def trust_block(
225
+ address: str = typer.Argument(..., help="Address to block"),
226
+ reason: str = typer.Option("", "-r", "--reason", help="Reason for blocking"),
227
+ ):
228
+ """Block an address."""
229
+ from .commands.trust_commands import handle_trust_block
230
+ handle_trust_block(address, reason)
231
+
232
+
233
+ @trust_app.command("unblock")
234
+ def trust_unblock(address: str = typer.Argument(..., help="Address to unblock")):
235
+ """Unblock an address."""
236
+ from .commands.trust_commands import handle_trust_unblock
237
+ handle_trust_unblock(address)
238
+
239
+
240
+ # Admin subcommand group
241
+ admin_app = typer.Typer(help="Manage admins (super admin only)")
242
+ trust_app.add_typer(admin_app, name="admin")
243
+
244
+
245
+ @admin_app.command("add")
246
+ def admin_add(address: str = typer.Argument(..., help="Address to add as admin")):
247
+ """Add an admin."""
248
+ from .commands.trust_commands import handle_admin_add
249
+ handle_admin_add(address)
250
+
251
+
252
+ @admin_app.command("remove")
253
+ def admin_remove(address: str = typer.Argument(..., help="Address to remove from admins")):
254
+ """Remove an admin."""
255
+ from .commands.trust_commands import handle_admin_remove
256
+ handle_admin_remove(address)
257
+
258
+
177
259
  def cli():
178
260
  """Entry point."""
179
261
  app()
connectonion/core/llm.py CHANGED
@@ -3,7 +3,7 @@ Purpose: Unified LLM provider abstraction with factory pattern for OpenAI, Anthr
3
3
  LLM-Note:
4
4
  Dependencies: imports from [abc, typing, dataclasses, json, os, base64, openai, anthropic, requests, pathlib, toml, pydantic, .usage, .exceptions] | imported by [agent.py, llm_do.py, conftest.py] | tested by [tests/test_llm.py, tests/test_llm_do.py, tests/test_real_*.py, tests/test_billing_error_agent.py]
5
5
  Data flow: Agent/llm_do calls create_llm(model, api_key) → factory routes to provider class → Provider.__init__() validates API key → Agent calls complete(messages, tools) OR structured_complete(messages, output_schema) → provider converts to native format → calls API → parses response → returns LLMResponse(content, tool_calls, raw_response) OR Pydantic model instance
6
- State/Effects: reads environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENONION_API_KEY) | reads ~/.connectonion/.co/config.toml for OpenOnion auth | makes HTTP requests to LLM APIs | no caching or persistence
6
+ State/Effects: reads environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENONION_API_KEY) | reads ~/.co/config.toml for OpenOnion auth | makes HTTP requests to LLM APIs | no caching or persistence
7
7
  Integration: exposes create_llm(model, api_key), LLM abstract base class, OpenAILLM, AnthropicLLM, GeminiLLM, OpenOnionLLM, LLMResponse, ToolCall dataclasses | providers implement complete() and structured_complete() | OpenAI message format is lingua franca | tool calling uses OpenAI schema converted per-provider
8
8
  Performance: stateless (no caching) | synchronous (no streaming) | default max_tokens=8192 for Anthropic (required) | each call hits API
9
9
  Errors: raises ValueError for missing API keys, unknown models, invalid parameters | provider-specific errors bubble up (openai.APIError, anthropic.APIError, etc.) | OpenOnionLLM transforms 402 errors to InsufficientCreditsError with formatted message and typed attributes | Pydantic ValidationError for invalid structured output
@@ -97,7 +97,7 @@ Required (pick one):
97
97
  - OPENAI_API_KEY: For OpenAI models
98
98
  - ANTHROPIC_API_KEY: For Claude models
99
99
  - GEMINI_API_KEY or GOOGLE_API_KEY: For Gemini models
100
- - OPENONION_API_KEY: For co/ managed keys (or from ~/.connectonion/.co/config.toml)
100
+ - OPENONION_API_KEY: For co/ managed keys (or from ~/.co/config.toml)
101
101
 
102
102
  Optional:
103
103
  - OPENONION_DEV: Use localhost:8000 for OpenOnion (development)
@@ -0,0 +1,237 @@
1
+ # Fast Rules
2
+
3
+ Fast rules are code-executed trust checks that run **before** the LLM. They burn zero tokens and are instant.
4
+
5
+ ## Why Fast Rules?
6
+
7
+ Trust verification that uses an LLM costs tokens. Every verification burns money.
8
+
9
+ - 1000 requests/day × $0.001/verification = $365/year just for "should I trust this?"
10
+ - 90% of trust decisions are mechanical (whitelist check, blocklist check)
11
+ - Only 10% need LLM judgment
12
+
13
+ Fast rules handle the 90%. LLM handles the 10%.
14
+
15
+ ## How It Works
16
+
17
+ ```
18
+ Request comes in
19
+
20
+
21
+ ┌─────────────────┐
22
+ │ Fast Rules │ ← No tokens, instant
23
+ │ │
24
+ │ 1. blocked? │──deny──►
25
+ │ 2. whitelisted? │──allow──►
26
+ │ 3. contact? │──allow──►
27
+ │ 4. onboard? │──promote + allow──►
28
+ └─────────────────┘
29
+
30
+ │ No rule matched
31
+
32
+ ┌─────────────────┐
33
+ │ Trust Agent │ ← LLM, burns tokens
34
+ │ (if enabled) │
35
+ └─────────────────┘
36
+ ```
37
+
38
+ ## Policy File Format
39
+
40
+ Trust policies are markdown files with YAML frontmatter:
41
+
42
+ ```yaml
43
+ ---
44
+ # YAML config (fast rules)
45
+ ---
46
+
47
+ # Markdown body (LLM system prompt)
48
+ ```
49
+
50
+ ### Full Example
51
+
52
+ ```yaml
53
+ ---
54
+ # Who has access
55
+ allow:
56
+ - whitelisted
57
+ - contact
58
+
59
+ # Who is blocked
60
+ deny:
61
+ - blocked
62
+
63
+ # How strangers become contacts (onboarding)
64
+ onboard:
65
+ invite_code: [BETA2024, FRIEND123]
66
+ payment: 10
67
+
68
+ # Strangers without credentials
69
+ default: ask # allow | deny | ask
70
+ ---
71
+
72
+ # Careful Trust
73
+
74
+ You evaluate unknown agents...
75
+ ```
76
+
77
+ ## Config Options
78
+
79
+ ### `allow:`
80
+
81
+ Who has access. List of conditions:
82
+
83
+ ```yaml
84
+ ---
85
+ allow:
86
+ - whitelisted # Users in ~/.co/whitelist.txt
87
+ - contact # Users in ~/.co/contacts.txt
88
+ ---
89
+ ```
90
+
91
+ ### `deny:`
92
+
93
+ Who is blocked:
94
+
95
+ ```yaml
96
+ ---
97
+ deny:
98
+ - blocked # Users in ~/.co/blocklist.txt
99
+ ---
100
+ ```
101
+
102
+ ### `onboard:`
103
+
104
+ How strangers become contacts (OR logic):
105
+
106
+ ```yaml
107
+ ---
108
+ onboard:
109
+ invite_code: [CODE1, CODE2] # Valid invite codes
110
+ payment: 10 # Minimum payment amount
111
+ ---
112
+ ```
113
+
114
+ - `invite_code`: Client sends `invite_code` in request → becomes contact
115
+ - `payment`: Client sends `payment` >= amount → becomes contact
116
+
117
+ **OR logic**: Either invite_code OR payment passes → promoted to contact.
118
+
119
+ ### `default:`
120
+
121
+ What to do with strangers who don't onboard:
122
+
123
+ ```yaml
124
+ ---
125
+ default: ask # Use LLM to decide (careful mode)
126
+ # OR
127
+ default: deny # Reject (strict mode)
128
+ # OR
129
+ default: allow # Accept (open mode)
130
+ ---
131
+ ```
132
+
133
+ ## The Three Presets
134
+
135
+ ### open (Development)
136
+
137
+ ```yaml
138
+ ---
139
+ default: allow
140
+ ---
141
+ ```
142
+
143
+ Everyone allowed. No verification.
144
+
145
+ ### careful (Staging)
146
+
147
+ ```yaml
148
+ ---
149
+ allow:
150
+ - whitelisted
151
+ - contact
152
+
153
+ deny:
154
+ - blocked
155
+
156
+ onboard:
157
+ invite_code: [BETA2024]
158
+ payment: 10
159
+
160
+ default: ask
161
+ ---
162
+ ```
163
+
164
+ Whitelisted and contacts allowed. Strangers can onboard or be evaluated by LLM.
165
+
166
+ ### strict (Production)
167
+
168
+ ```yaml
169
+ ---
170
+ allow:
171
+ - whitelisted
172
+
173
+ deny:
174
+ - blocked
175
+
176
+ default: deny
177
+ ---
178
+ ```
179
+
180
+ Whitelist only. Everyone else denied.
181
+
182
+ ## Execution Order
183
+
184
+ Fast rules execute in this order:
185
+
186
+ 1. Check `deny` list → deny if blocked
187
+ 2. Check `allow` list → allow if whitelisted/contact
188
+ 3. Try `onboard` → promote + allow if valid credentials
189
+ 4. `default` → allow / deny / ask
190
+
191
+ ## List Files
192
+
193
+ Fast rules read from `~/.co/`:
194
+
195
+ ```
196
+ ~/.co/
197
+ ├── whitelist.txt # Trusted clients
198
+ ├── contacts.txt # Verified clients
199
+ └── blocklist.txt # Blocked clients
200
+ ```
201
+
202
+ ### Format
203
+
204
+ ```
205
+ # Comments start with #
206
+ client-id-123
207
+ another-client
208
+
209
+ # Wildcards supported
210
+ payment-*
211
+ *.trusted.com
212
+ ```
213
+
214
+ ## Implementation
215
+
216
+ See `connectonion/network/trust/fast_rules.py`:
217
+
218
+ ```python
219
+ from connectonion.network.trust import parse_policy, evaluate_request
220
+
221
+ # Parse policy file
222
+ config, markdown_body = parse_policy(policy_text)
223
+
224
+ # Evaluate request
225
+ result = evaluate_request(config, client_id, request)
226
+ # Returns: 'allow', 'deny', or None (needs LLM)
227
+ ```
228
+
229
+ ## Summary
230
+
231
+ | Feature | Fast Rules | LLM Trust Agent |
232
+ |---------|------------|-----------------|
233
+ | Tokens | Zero | Burns tokens |
234
+ | Speed | Instant | Slow (API call) |
235
+ | Logic | Simple checks | Complex reasoning |
236
+ | Use | 90% of requests | 10% of requests |
237
+ | Config | YAML frontmatter | Markdown body |