Files
PaperClipAI/doc/AGENT-IDENTITY.md
DottaandPaperclip a9a20fb5c6 feat(security): add read-only customer-success inspection APIs (#15405)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need a persistent identity and a verified active run for
governed access.
> - Customer-success inspection needs broad reads without tenant writes
or secret access.
> - Ordinary board login and database credentials give more authority
than this task needs.
> - This pull request adds a dedicated inspection API and strict
managed-run authority.
> - Cloud owns short grants, human approval, replay protection, and
audit records.
> - The benefit is inspectable access that an operator can disable
immediately.

## Linked Issues or Issue Description

Refs: #15352. This change reuses the persistent Ed25519 identity from
that PR.

**Subsystem affected**

Server authentication, pure resource readers, and the shared wire
contract.

**Problem or motivation**

One internal Paperclip agent must inspect customer onboarding work. It
must not receive owner login or database credentials. Reads must not
create customer sessions, memberships, activity, or read receipts.

**Proposed solution**

Add disabled-by-default run authority and versioned tenant inspection
endpoints. Require strict instance-bound managed-run JWTs on the home
instance. Require exact-operation, single-use Cloud permits on tenants.
Execute a reviewed company-scoped catalog in read-only transactions.
Cloud applies seven-day stack-age eligibility and human exceptions.

**Roadmap alignment**

This is access support for Cloud deployments and governed agent
identities. Bot creation, scheduling, scoring, and reports are separate
work. The maintainer requested this implementation.

## What Changed

- Reuse existing public identity reads and managed private-key
injection. Reject unprovisioned keys, paused agents, ended runs, legacy
signatures, and wrong instances.
- Mount `/api/customer-success/v1` before actor/session synchronization.
Verify Cloud permits and consume them centrally before reading.
- Add explicit company-scoped database readers and bounded instruction,
skill snapshot, run log, workspace, and asset reads. Preserve existing
redactions and file protections.
- Add protocol, security, database immutability, and managed-agent
qualification tests. Add deployment and rollback documentation.

## Verification

- Full `pnpm -r typecheck` and `pnpm build` passed. Server typecheck
passed after review fixes.
- The broad local `pnpm test:run` recorded 14,277 passes and four
failures in unchanged suites: two timeouts and two PR-metadata mock
assertions. All three affected suites passed on isolated reruns (36
tests). The complete CI matrix passes at the final head, including every
test lane, typecheck, build, runner checks, canary dry run, and the
security scan.
- Focused inspection, JWT, and existing identity tests pass. The catalog
test compares every public database table before and after reads.
- Inspection and route-contract tests: 22 passed. The coordinated test
runs a real managed process agent against separate home/customer
PostgreSQL databases and a PostgreSQL broker over HTTP. It proves wake
through the existing controller, bounded binary file reads, single
challenge consumption across replicas, concurrent grants with a
two-connection pool, scoped SQL audits, append-only runtime auditing,
one-year retention, and unchanged tenant data/files.
- Run the coordinated test with `PAPERCLIP_INSPECTION_CLOUD_DIST`
pointing at the sibling Cloud build. Normal unit runs skip that optional
private integration.
- Final-head Greptile is 5/5 with no unresolved findings.
- No production deployment or customer inspection occurred.

## Risks

- This adds an authentication boundary. Keep both feature flags disabled
until coordinated staging and canary qualification.
- Cloud support must deploy after this API. Unsupported tenants fail
closed. There is no owner-login or database fallback.
- Existing redactions remain the content boundary. Arbitrary pasted
secrets in readable prose or files may remain.
- Remote files and suppressed provider traces remain unavailable. Wake
can cause normal startup/background writes; test those separately.
- Disable Cloud policy first during rollback. Preserve existing identity
material and Cloud audit history.

## Model Used

OpenAI GPT-6 (Codex), with reasoning, code execution, and browser
testing. The session does not expose a more specific deployment ID or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (focused checks and
isolated reruns; broad-run flakes are documented above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-06 21:38:15 -05:00

5.8 KiB
Raw Permalink Blame History

Agent cryptographic identity

Each agent has one persistent Ed25519 keypair. New agents receive it in the agent-creation transaction. Existing agents receive it when Paperclip prepares their next managed run. Migration, startup, public API reads, and opening the agent page do not provision existing agents. There is no backfill command.

Storage

agent_identity_keys lives in the agent's home instance database, including a hosted stack's database. Its agent primary key and composite company/agent foreign key enforce one identity and company-consistent ownership. Deleting an agent cascades to its identity. Creation locks the agent row inside a transaction, so simultaneous first runs use the same stored pair.

Public keys use SPKI PEM and private keys use PKCS#8 PEM. keyId is sha256: followed by the base64url SHA-256 digest of the public key's SPKI DER bytes. Private material is encrypted using the existing local_encrypted provider and stored separately from company secrets. The wrapping key is the stack's PAPERCLIP_SECRETS_MASTER_KEY or the local instance's secrets master-key file. The file is published atomically with mode 0600 on first use. No per-agent Cloud secret or Cloud registry record is needed.

Decryption and public/private key validation happen before launch. A wrong master key, corrupt ciphertext, or mismatched public key fails the run; none causes automatic key replacement. Renames, model changes, pauses, resumes, and restarts preserve the keypair.

Managed runs

Paperclip supplies these server-owned environment variables:

PAPERCLIP_AGENT_KEY_ID
PAPERCLIP_AGENT_PUBLIC_KEY
PAPERCLIP_AGENT_PRIVATE_KEY

The two key values are multiline PEM strings. Runtime-only adapter context and native runner environment fields deliver them to managed processes. Configured or inherited environment values cannot replace the assigned identity. Session compatibility includes the public key ID, so an older warm process is replaced before its next turn. Private values are excluded from invocation metadata and execution configuration; output and failure diagnostics redact known private material. The run-log redactor handles split stdout/stderr chunks independently. Native output deltas also buffer potential key fragments before persistence. Raw provider trace contents are suppressed for identity-enabled runs; trace routing, timing, and interpretation metadata remain available.

Managed remote providers, including Cursor Cloud, receive the same private key as managed local processes. Selecting a runtime therefore trusts its host with the agent's persistent identity: the agent and its host can retain the key and sign outside Paperclip. Encryption at rest protects database storage, not an executing runtime. Move an agent only between runtime hosts trusted with that identity.

Independently hosted HTTP and gateway agents, including Runner's API-hosted Claude Managed and AWS AgentCore providers, do not receive private keys in v1. Their new agent records still get stored identities; their existing records stay unprovisioned until a supported managed run.

An agent can sign using an ordinary cryptographic library, for example Node.js:

import { sign } from "node:crypto";
const signature = sign(null, challengeBytes, process.env.PAPERCLIP_AGENT_PRIVATE_KEY);

This identity does not change Paperclip bearer-token authentication. There is no signing API, private-key download endpoint, Git integration, rotation UI, or external-key import. Agents possess their private keys. External consumers must define their own authorization, expiry, and revocation rules.

Public identity

GET /api/agents/:id/identity applies the existing agent-read access checks. It returns null before provisioning, otherwise:

{ algorithm: "Ed25519", keyId: string, publicKeyPem: string, createdAt: string }

The agent's Identity section shows a shortened fingerprint and copies the full public PEM. Before provisioning it shows “Not created yet” and explains that creation happens on the next managed run.

Copies and recovery

Company/template imports and duplication use normal agent creation and get new identities. Minimal and full development seeds omit identity rows, while retaining the schema, even with live-work preservation. Those copied agents get fresh identities on their next managed runs.

Ordinary disaster-recovery backups include encrypted identities. Recovery needs both the database and the matching wrapping key; back up the key separately. Do not use a disaster-recovery backup as a development clone when agents should have distinct identities.

Native failure records and reports redact the assigned key before truncating diagnostics. Streaming output buffers settle at item or turn completion: short structural prefixes (such as a trailing dash) are preserved, longer interrupted key fragments become redaction markers, and pending buffers are cleared. Terminal events carry these settled outputTails; transcript projection displays them as deltas without changing the source event receipt.

Codex shell delivery preserves its default KEY/SECRET/TOKEN name exclusions for other configured credentials. The exceptions are the three identity variables and PAPERCLIP_API_KEY: the latter is the short-lived, scoped run/bridge credential required by the Paperclip agent skill’s Bash/curl API calls. Disabling Codex’s automatic exclusions is paired with this explicit filtered allowlist; it does not admit arbitrary host environment variables. Provider authentication secrets can still reach the provider process without being newly exposed to shell commands by this feature.

Cloud customer-success inspection can consume this existing identity together with strict active-run authority. See inspection support. No additional agent keypair or private-key distribution is introduced.