Files
paperclip/doc/plans/agent-authentication.md
Forgotten fe6a8687c1 Implement local agent JWT authentication for adapters
Add HS256 JWT-based authentication for local adapters (claude_local, codex_local)
so agents authenticate automatically without manual API key configuration. The
server mints short-lived JWTs per heartbeat run and injects them as PAPERCLIP_API_KEY.
The auth middleware verifies JWTs alongside existing static API keys.

Includes: CLI onboard/doctor JWT secret management, env command for deployment,
config path resolution from ancestor directories, dotenv loading on server startup,
event payload secret redaction, multi-status issue filtering, and adapter transcript
parsing for thinking/user message kinds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:46:45 -06:00

223 lines
8.7 KiB
Markdown

# Agent Authentication & Onboarding
## Problem
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."
## Design Principles
1. **Match auth complexity to the trust boundary.** A local CLI adapter
shouldn't require the same ceremony as a remote webhook-based agent.
2. **Agents should be able to onboard themselves.** Humans shouldn't have to
copy-paste credentials into agent environments when the agent is capable of
doing it.
3. **Approval gates by default.** Self-registration must require explicit
approval (by a user or authorized agent) before the new agent can act within
a company.
---
## Authentication Tiers
### Tier 1: Local Adapter (claude-local, codex-local)
**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:**
- Coding agents can run for hours, so tokens can't expire too quickly.
- Infinite-lived tokens are undesirable even in local contexts.
- Use JWTs with a generous expiry (e.g. 48h) and overlap windows so a
heartbeat that starts near expiry still completes.
- The server doesn't need to store these tokens -- it just validates the JWT
signature.
**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.
### Tier 2: CLI-Driven Key Exchange
**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.
### Tier 3: Agent Self-Registration (Invite Link)
**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:**
1. A company admin (user or agent) generates an **invite URL** from Paperclip.
2. The invite URL is delivered to the target agent (via a message, a task
description, a webhook payload, etc.).
3. The agent fetches the URL, which returns an **onboarding document**
containing:
- Company identity and context
- The Paperclip SKILL.md (or a link to it)
- What information Paperclip needs from the agent (e.g. webhook URL, adapter
type, capabilities, preferred name/role)
- A registration endpoint to POST the response to
4. The agent responds with its configuration (e.g. "here's my webhook URL,
here's my name, here are my capabilities").
5. Paperclip stores the pending registration.
6. An approver (user or authorized agent) reviews and approves the new
employee. Approval includes assigning the agent's manager (chain of command)
and any initial role/permissions.
7. On approval, Paperclip provisions the agent's credentials and sends the
first heartbeat.
**Token format:** Paperclip issues an API key (or JWT) upon approval, delivered
to the agent via its declared communication channel.
**Inspiration:**
- [Allium self-registration](https://agents.allium.so/skills/skill.md) --
agent collects credentials, polls for confirmation, stores key automatically.
- [Allium x402](https://agents.allium.so/skills/x402-skill.md) -- multi-step
credential setup driven entirely by the agent.
- [OpenClaw webhooks](https://docs.openclaw.ai/automation/webhook) -- external
systems trigger agent actions via authenticated webhook endpoints.
---
## Self-Registration: Onboarding Negotiation Protocol
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:
```json
{
"company": {
"id": "...",
"name": "Acme Corp"
},
"onboarding": {
"instructions": "You are being invited to join Acme Corp as an employee agent...",
"skillUrl": "https://app.paperclip.dev/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:
```json
{
"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 as First External Integration
OpenClaw is the ideal first target for Tier 3 because:
- It already has webhook support (`POST /hooks/agent`) for receiving tasks.
- The webhook config (URL, auth token, session key) is exactly what we need the
agent to tell us during onboarding.
- OpenClaw agents can read a URL, parse instructions, and make HTTP calls.
**Workflow:**
1. Generate a Paperclip invite link for the company.
2. Send the invite link to an OpenClaw agent (via their existing messaging
channel).
3. The OpenClaw agent fetches the invite, reads the onboarding doc, and
responds with its webhook configuration.
4. A Paperclip company member approves the new agent.
5. Paperclip begins sending heartbeats to the OpenClaw webhook endpoint.
---
## Approval Model
All self-registration requires approval. This is non-negotiable for security.
- **Default:** A human user in the company must approve.
- **Delegated:** A manager-level agent with `approve_agents` permission can
approve (useful for scaling).
- **Auto-approve (opt-in):** Companies can configure auto-approval for invite
links that were generated with a specific trust level (e.g. "I trust anyone
with this link"). Even then, the invite link itself is a secret.
On approval, the approver sets:
- `reportsTo` -- who the new agent reports to in the chain of command
- `role` -- the agent's role within the company
- `budget` -- initial budget allocation
---
## Implementation Priorities
| 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 | `paperclip auth login` for developer-managed remote agents. |
## P0 Implementation Plan
See [`doc/plans/agent-authentication-implementation.md`](./agent-authentication-implementation.md) for the P0 local JWT execution plan.
---
## Open Questions
- **JWT signing key rotation:** How do we rotate the signing key without
invalidating in-flight heartbeats?
- **Invite link expiry:** Should invite links be single-use or multi-use? Time-limited?
- **Adapter negotiation:** Should the onboarding doc support arbitrary adapter
types, or should we enumerate supported adapters and have the agent pick one?
- **Credential renewal:** For long-lived external agents, how do we handle API
key rotation without downtime?