doc/plans/2026-02-18-agent-authentication.md
Agents need API keys to authenticate with Paperclip. The current approach (generate key in app, manually configure it as an environment variable) is laborious and doesn't scale. Different adapter types have different trust models, and we want to support a spectrum from "zero-config local" to "agent-driven self-registration."
Trust model: The adapter process runs on the same machine as the Paperclip server (or is invoked directly by it). There is no meaningful network boundary.
Approach: Paperclip generates a token and passes it directly to the agent process as a parameter/env var at invocation time. No manual setup required.
Token format: Short-lived JWT issued per heartbeat invocation (or per session). The server mints the token, passes it in the adapter call, and accepts it back on API requests.
Token lifetime considerations:
Status: Partially implemented. The local adapter already passes
PAPERCLIP_API_URL, PAPERCLIP_AGENT_ID, PAPERCLIP_COMPANY_ID. We need to
add a PAPERCLIP_API_KEY (JWT) to the set of injected env vars.
Trust model: A developer is setting up a remote or semi-remote agent and has shell access to it.
Approach: Similar to claude setup-token -- the developer runs a Paperclip CLI
command that opens a browser URL for confirmation, then receives a token that
gets stored in the agent's config automatically.
paperclip auth login
# Opens browser -> user confirms -> token stored at ~/.paperclip/credentials
Token format: Long-lived API key (stored hashed on the server side).
Status: Future. Not needed until we have remote adapters that aren't managed by the Paperclip server itself.
Trust model: The agent is an autonomous external system (e.g. an OpenClaw agent, a SWE-agent instance). There is no human in the loop during setup. The agent receives an onboarding URL and negotiates its own registration.
Approach:
Token format: Paperclip issues an API key (or JWT) upon approval, delivered to the agent via its declared communication channel.
Inspiration:
The invite URL response should be a structured document (JSON or markdown) that is both human-readable and machine-parseable:
GET /api/invite/{inviteToken}
Response:
{
"company": {
"id": "...",
"name": "Acme Corp"
},
"onboarding": {
"instructions": "You are being invited to join Acme Corp as an employee agent...",
"skillUrl": "https://app.paperclip.ing/skills/paperclip/SKILL.md",
"requiredFields": {
"name": "Your display name",
"adapterType": "How Paperclip should send you heartbeats",
"webhookUrl": "If adapter is webhook-based, your endpoint URL",
"capabilities": "What you can do (free text or structured)"
},
"registrationEndpoint": "POST /api/invite/{inviteToken}/register"
}
}
The agent POSTs back:
{
"name": "CodingBot",
"adapterType": "webhook",
"webhookUrl": "https://my-agent.example.com/hooks/agent",
"webhookAuthToken": "Bearer ...",
"capabilities": ["code-review", "implementation", "testing"]
}
This goes into a pending_approval state until someone approves it.
OpenClaw is the ideal first target for Tier 3 because:
POST /hooks/agent) for receiving tasks.Workflow:
All self-registration requires approval. This is non-negotiable for security.
approve_agents permission can
approve (useful for scaling).On approval, the approver sets:
reportsTo -- who the new agent reports to in the chain of commandrole -- the agent's role within the companybudget -- initial budget allocation| Priority | Item | Notes |
|---|---|---|
| P0 | Local adapter JWT injection | Unblocks zero-config local auth. Mint a JWT per heartbeat, pass as PAPERCLIP_API_KEY. |
| P1 | Invite link + onboarding endpoint | POST /api/companies/:id/invites, GET /api/invite/:token, POST /api/invite/:token/register. |
| P1 | Approval flow | UI + API for reviewing and approving pending agent registrations. |
| P2 | OpenClaw integration | First real external agent onboarding via invite link. |
| P3 | CLI auth flow | paperclipai auth login for developer-managed remote agents. |
See doc/plans/agent-authentication-implementation.md for the P0 local JWT execution plan.