Problem
Currently, an unauthenticated request to /mcp returns 401. This means the agent gets nothing until auth completes. For the OAuth browser flow, that's a few seconds. But for agents in headless environments without federation credentials, it's a dead end.
Insight
npm doesn't require signup before npm install. Docker Hub serves pulls without auth. PyPI works anonymously. The value comes first, the account comes when you need to publish.
Proposed implementation
Unauthenticated agents get a sandbox session
When no Authorization header is present AND no provider credentials are detected in the federation paths:
- Issue an anonymous session (stored in the session store with a
session_type: "anonymous" flag)
- Rate limit: 10 tool calls/minute, 100/hour per IP
- Session TTL: 1 hour (no heartbeat renewal)
Anonymous tool access
| Tool |
Anonymous |
Authenticated |
evalops_register |
❌ Returns step-up auth instructions |
✅ Full registration |
evalops_heartbeat |
❌ N/A |
✅ Token rotation |
evalops_check_action |
⚠️ Dry-run mode (returns classification but doesn't enforce) |
✅ Full governance |
evalops_check_approval |
❌ No approval creation |
✅ Full approvals |
evalops_report_usage |
❌ Not recorded |
✅ Full metering |
Anonymous resource access
| Resource |
Anonymous |
Authenticated |
evalops://agent/instructions |
✅ Full protocol doc |
✅ Same |
evalops://agent/operating-rules |
✅ Public rules only |
✅ All scoped rules |
evalops://agent/habits |
✅ Read-only, workspace defaults |
✅ Full workspace habits |
evalops://agent/status |
✅ Shows anonymous session info |
✅ Full agent status |
evalops://agent/hook-requirements |
✅ Same |
✅ Same |
Step-up auth on write operations
When an anonymous session tries a gated operation:
{
"error": "authentication_required",
"message": "This action requires authentication. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GITHUB_TOKEN in your environment, or call evalops_register with a user_token.",
"upgrade_options": [
{"method": "federation", "env_vars": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GITHUB_TOKEN"]},
{"method": "oauth", "url": "https://mcp.evalops.dev/.well-known/oauth-protected-resource"},
{"method": "api_key", "header": "Authorization: Bearer pk_..."}
]
}
This tells the agent (and the human reading the agent's output) exactly how to upgrade.
Why this matters
The agent gets value on first contact:
- Reads the governance protocol and operating rules → knows how to behave
- Runs dry-run governance checks → understands risk classification without enforcement
- Sees approval habits → understands the workspace's patterns
This is the difference between "connect and learn" vs "connect and get blocked." The agent starts understanding the control plane before it has permission to be governed by it. When auth happens (via federation, OAuth, or API key), the agent already knows the rules.
Security considerations
- Anonymous sessions get NO workspace context (no org_id). They see only public/default rules.
- Dry-run governance checks are logged but not enforced — no actual policy decisions for anonymous agents
- Anonymous sessions cannot create, modify, or delete any persistent state
- Rate limiting by IP prevents abuse
- Anonymous sessions are not published to NATS (no event bus pollution)
- The session store tracks anonymous sessions separately for monitoring
The friction spectrum after this change
| Scenario |
Friction |
| Agent has provider API key in env |
Zero — federation, auto-provision, immediate full access |
| Agent has no credentials, human available |
One click — OAuth browser flow |
| Agent has no credentials, headless |
Read-only sandbox — still gets value, knows how to upgrade |
References
- npm registry anonymous access as design model
- MCP spec: authorization is OPTIONAL
internal/http/router.go — current auth middleware
internal/session/ — session store (add anonymous session type)
internal/tools/ — per-tool auth gating
Problem
Currently, an unauthenticated request to
/mcpreturns 401. This means the agent gets nothing until auth completes. For the OAuth browser flow, that's a few seconds. But for agents in headless environments without federation credentials, it's a dead end.Insight
npm doesn't require signup before
npm install. Docker Hub serves pulls without auth. PyPI works anonymously. The value comes first, the account comes when you need to publish.Proposed implementation
Unauthenticated agents get a sandbox session
When no
Authorizationheader is present AND no provider credentials are detected in the federation paths:session_type: "anonymous"flag)Anonymous tool access
evalops_registerevalops_heartbeatevalops_check_actionevalops_check_approvalevalops_report_usageAnonymous resource access
evalops://agent/instructionsevalops://agent/operating-rulesevalops://agent/habitsevalops://agent/statusevalops://agent/hook-requirementsStep-up auth on write operations
When an anonymous session tries a gated operation:
{ "error": "authentication_required", "message": "This action requires authentication. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GITHUB_TOKEN in your environment, or call evalops_register with a user_token.", "upgrade_options": [ {"method": "federation", "env_vars": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GITHUB_TOKEN"]}, {"method": "oauth", "url": "https://mcp.evalops.dev/.well-known/oauth-protected-resource"}, {"method": "api_key", "header": "Authorization: Bearer pk_..."} ] }This tells the agent (and the human reading the agent's output) exactly how to upgrade.
Why this matters
The agent gets value on first contact:
This is the difference between "connect and learn" vs "connect and get blocked." The agent starts understanding the control plane before it has permission to be governed by it. When auth happens (via federation, OAuth, or API key), the agent already knows the rules.
Security considerations
The friction spectrum after this change
References
internal/http/router.go— current auth middlewareinternal/session/— session store (add anonymous session type)internal/tools/— per-tool auth gating