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>
73 lines
2.7 KiB
Markdown
73 lines
2.7 KiB
Markdown
# Agent Authentication — P0 Local Adapter JWT Implementation
|
|
|
|
## Scope
|
|
|
|
- In-scope adapters: `claude_local`, `codex_local`.
|
|
- Goal: zero-configuration auth for local adapters while preserving static keys for all other call paths.
|
|
- Out-of-scope for P0: rotation UX, per-device revocation list, and CLI onboarding.
|
|
|
|
## 1) Token format and config
|
|
|
|
- Use HS256 JWTs with claims:
|
|
- `sub` (agent id)
|
|
- `company_id`
|
|
- `adapter_type`
|
|
- `run_id`
|
|
- `iat`
|
|
- `exp`
|
|
- optional `jti` (run token id)
|
|
- New config/env settings:
|
|
- `PAPERCLIP_AGENT_JWT_SECRET`
|
|
- `PAPERCLIP_AGENT_JWT_TTL_SECONDS` (default: `172800`)
|
|
- `PAPERCLIP_AGENT_JWT_ISSUER` (default: `paperclip`)
|
|
- `PAPERCLIP_AGENT_JWT_AUDIENCE` (default: `paperclip-api`)
|
|
|
|
## 2) Dual authentication path in `actorMiddleware`
|
|
|
|
1. Keep the existing DB key lookup path unchanged (`agent_api_keys` hash lookup).
|
|
2. If no DB key matches, add JWT verification in `server/src/middleware/auth.ts`.
|
|
3. On JWT success:
|
|
- set `req.actor = { type: "agent", agentId, companyId }`.
|
|
- optionally guard against terminated agents.
|
|
4. Continue board fallback for requests without valid authentication.
|
|
|
|
## 3) Opt-in adapter capability
|
|
|
|
1. Extend `ServerAdapterModule` (likely `packages/adapter-utils/src/types.ts`) with a capability flag:
|
|
- `supportsLocalAgentJwt?: true`.
|
|
2. Enable it on:
|
|
- `server/src/adapters/registry.ts` for `claude_local` and `codex_local`.
|
|
3. Keep `process`/`http` adapters unset for P0.
|
|
4. In `server/src/services/heartbeat.ts`, when adapter supports JWT:
|
|
- mint JWT per heartbeat run before execute.
|
|
- include token in adapter execution context.
|
|
|
|
## 4) Local env injection behavior
|
|
|
|
1. In:
|
|
- `packages/adapters/claude-local/src/server/execute.ts`
|
|
- `packages/adapters/codex-local/src/server/execute.ts`
|
|
|
|
inject `PAPERCLIP_API_KEY` from context token.
|
|
|
|
- Preserve existing behavior for explicit user-defined env vars in `adapterConfig.env`:
|
|
- if user already sets `PAPERCLIP_API_KEY`, do not overwrite it.
|
|
- Continue injecting:
|
|
- `PAPERCLIP_AGENT_ID`
|
|
- `PAPERCLIP_COMPANY_ID`
|
|
- `PAPERCLIP_API_URL`
|
|
|
|
## 5) Documentation updates
|
|
|
|
- Update operator-facing docs to remove manual key setup expectation for local adapters:
|
|
- `skills/paperclip/SKILL.md`
|
|
- `cli/src/commands/heartbeat-run.ts` output/help examples if they mention manual API key setup.
|
|
|
|
## 6) P0 acceptance criteria
|
|
|
|
- Local adapters authenticate without manual `PAPERCLIP_API_KEY` config.
|
|
- Existing static keys (`agent_api_keys`) still work unchanged.
|
|
- Auth remains company-scoped (`req.actor.companyId` used by existing checks).
|
|
- JWT generation and verification errors are logged as non-leaking structured events.
|
|
- Scope remains local-only (`claude_local`, `codex_local`) while adapter capability model is generic.
|