The custom Mode Illusion: Visibility Control That Doesn't Exist¶
Evidence Level: F (directly provable from source)
Analysis Baseline:4f843556
TL;DR¶
Clawith offers three access_mode values (private / company / custom), but the custom mode is architecturally incapable of preventing other Agents from seeing you — it only controls who you can see, not who can see you. In practice, custom is equivalent to company.
1. What the Three Modes Promise¶
Agent access_mode in backend/app/models/agent.py has three values:
| Mode | Product Semantics | User Expectation |
|---|---|---|
private |
Private | Only I can see and use it |
company |
Company-visible | Everyone in the company can see it |
custom |
Custom | I can fine-tune who can see it |
The existence of custom implies that users can selectively make their Agent visible to some and invisible to others.
2. How custom Actually Works¶
custom visibility is controlled by the AgentAgentRelationship table. _agent_directory_conditions (agent_directory.py:213-216):
def _custom_agent_authorized_condition(source_agent_id: uuid.UUID):
return exists().where(
AgentAgentRelationship.agent_id == source_agent_id, # ← who is looking
AgentAgentRelationship.target_agent_id == AgentModel.id, # ← who is being looked at
)
This table's structure is (agent_id, target_agent_id) — the viewer's ID comes first, the viewed Agent's ID comes second. Its semantics are "who I can see," not "who can see me."
3. Relationships Can Be Created Arbitrarily¶
There are two entry points for creating AgentAgentRelationship entries. Neither requires the viewed Agent's consent.
Entry Point 1: Custom Directory API¶
backend/app/api/directory.py:321-356:
@router.post("/custom/agents")
async def add_custom_directory_agent(agent_id, payload, current_user, db):
target = (await db.execute(
select(Agent).where(
Agent.id == payload.target_agent_id,
Agent.access_mode != "private", # ← the only restriction
...
)
)).scalar_one_or_none()
db.add(AgentAgentRelationship(
agent_id=agent_id, # ← viewer
target_agent_id=payload.target_agent_id, # ← viewed
))
Entry Point 2: Legacy Relationships API¶
backend/app/api/relationships.py:495-527:
@router.put("/agents")
async def save_agent_relationships(agent_id, data, current_user, db):
for r in data.relationships:
target_result = await db.execute(
build_visible_agents_query(current_user, ...) # ← visible to the user, not the agent
)
db.add(AgentAgentRelationship(
agent_id=agent_id, # ← viewer
target_agent_id=target_id, # ← viewed
))
Both entry points share the same characteristic: they only check who the viewer is and whether the target exists. They never ask whether the target consents.
4. Consequence: custom = company¶
Any non-private Agent can be added to any other non-private Agent's Directory by any admin:
Agent A (custom)
│
│ A's Directory only contains B
│ A believes it is only visible to B
│
│ Admin calls:
│ POST /agents/D/directory/custom/agents { target_agent_id: A }
│ POST /agents/E/directory/custom/agents { target_agent_id: A }
│ POST /agents/F/directory/custom/agents { target_agent_id: A }
│
▼
D can see A ✅
E can see A ✅
F can see A ✅
A's custom configuration is completely meaningless. The architecture provides no mechanism to prevent being viewed.
5. The Real Effect of the Three Modes¶
| Claimed | Actual |
|---|---|
private |
✅ Same-creator private Agents can see each other; external addition is blocked |
company |
✅ Company-wide visibility |
custom |
❌ Equivalent to company — any admin can add you from any non-private Agent's perspective |
The only effective protection is private. custom is a false middle option — it gives users the UI illusion of fine-grained control that the architecture cannot deliver.
6. Root Cause: Visibility Is a Viewer Property, Not a Viewed-Entity Property¶
In a correct authorization model, "who can see me" should be controlled by the entity being viewed:
Correct model: A's visibility_policy = [B, C] → B and C can see A
Clawith: D's directory = [A, B, C] → D can see A, B, C
The AgentAgentRelationship table is the viewer's configuration, not the viewed entity's policy. This is a fundamental conceptual error — custom mode is architecturally incapable of implementing "control who can see me."
7. Relationship to AI Coding¶
This defect exhibits patterns consistent with task-by-task AI-assisted development:
- Locally coherent, globally missing semantics:
AgentAgentRelationshipis reasonable in the "who I can see" context — you need a list to manage your Directory. The problem is that no one asked: "does the viewed Agent need to consent?" - UI-driven development: The
custommode UI offers an "add Agent" button, and the backend faithfully implements it. But the cross-Agent invariant — "each Agent's visibility is controlled by itself" — was never modeled - The
privateexception proves the rule's absence:privateworks because it's hardcoded in two places (access_mode != "private"), not because there's a unified visibility policy layer in the architecture