# How Paperclip Handles Authentication for Deployments: A Complete Technical Guide

> Discover how Paperclip handles authentication for deployments. Explore its deployment-mode driven system, supporting local trusted and authenticated production modes with robust access controls.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: how-to-guide
- Published: 2026-08-14

---

**Paperclip uses a deployment-mode-driven authentication system that supports two mutually exclusive runtime configurations—`local_trusted` for development and `authenticated` for production—with distinct paths for board operators (Better Auth sessions) and agents (JWTs or API keys), all enforcing strict company-scoped access controls.**

Paperclip's deployment authentication architecture is designed to protect multi-tenant production environments while remaining frictionless for local development. In [`server/src/middleware/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/middleware/auth.ts), the core `actorMiddleware` determines which authentication path to execute based on the runtime `DeploymentMode`. This article examines the complete authentication flow, from deployment configuration through company-scoped access enforcement.

## Understanding Deployment Modes

Paperclip operates in exactly one of two modes at runtime, defined by the `DeploymentMode` type in [`server/src/middleware/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/middleware/auth.ts) (lines 88-89):

- **`local_trusted`** — Development or single-tenant instances where the local board process is implicitly trusted
- **`authenticated`** — Production-grade, multi-tenant instances requiring verified credentials

The `actorMiddleware` (lines 92-100) inspects this mode to branch authentication logic. This dual-mode design eliminates credential complexity during development without compromising production security.

## Board Operator Authentication

Board operators—human users interacting with Paperclip's web interface—authenticate through different mechanisms depending on deployment mode.

### Authenticated Mode: Better Auth Sessions

In production deployments, board operators use **Better Auth** cookie-based sessions. The middleware attempts to resolve a session via `opts.resolveSession`. When successful, the actor object is populated with the user's ID, email, and affiliated companies (lines 31-52). This session persists across requests through standard HTTP cookie semantics.

### Local Trusted Mode: Automatic Board Actor

In `local_trusted` mode, no credentials are required. The middleware automatically injects a board actor with `type: "board"` and `userId: "local-board"`, granting full administrative rights to the local board process. This bypass enables rapid local development without authentication infrastructure.

## Agent Authentication Methods

Agents—automated execution contexts—must authenticate even in `local_trusted` mode. Paperclip supports two authentication methods for agents, as implemented in [`server/src/middleware/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/middleware/auth.ts) (lines 90-100).

### Run-Scoped JWT (Recommended)

During each heartbeat cycle, the server issues a short-lived JWT and injects it into the `PAPERCLIP_API_KEY` environment variable. Agents send this token in the `Authorization: Bearer <jwt>` header on every request. The middleware validates this JWT through `verifyLocalAgentJwt` (lines 98-100) before loading the associated agent record.

This approach provides automatic key rotation and tight coupling to the agent's execution lifecycle, minimizing exposure window if a token is compromised.

### Long-Lived Agent API Key

For scenarios requiring persistent credentials, operators can create long-lived API keys via `POST /api/agents/{agentId}/keys`. The implementation follows security best practices:

- Keys are stored as SHA-256 hashes (`keyHash`) in the database
- Incoming bearer tokens are hashed via `hashToken` (lines 53-55) before comparison
- Successful matches trigger an update to `lastUsedAt` for audit purposes
- Revoked keys (where `revokedAt` is non-null) are rejected by the lookup logic

```typescript
// Agent authentication with short-lived JWT (automatically cycled)
fetch('https://api.paperclip.ai/api/agents/me', {
  headers: { Authorization: `Bearer ${process.env.PAPERCLIP_API_KEY}` },
});

// Creating a long-lived API key (board operator action)
await fetch('https://api.paperclip.ai/api/agents/123/keys', {
  method: 'POST',
  headers: { Authorization: `Bearer <board-session-or-key>` },
});

```

## Company Scoping and Access Enforcement

Every entity in Paperclip belongs to a **company**. The authentication middleware enforces strict isolation through the actor object structure defined in [`server/src/middleware/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/middleware/auth.ts) (lines 10-12) and validated by [`server/src/services/authorization.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/authorization.ts).

| Actor Type | Company Access | Validation |
|------------|---------------|------------|
| **Agent** | Single company only | `companyId` embedded in JWT or linked to API key |
| **Board operator** | Multiple companies (membership-based) | `companyIds` array from Better Auth session |

Cross-company access attempts are rejected with `403 Forbidden`. Downstream services use the actor's company identifiers to scope all database queries, ensuring tenants remain isolated.

## Security Mechanisms and Audit Logging

Paperclip implements multiple safeguards beyond basic token validation.

### Token Hashing

All long-lived API keys are stored as SHA-256 hashes. The `hashToken` utility (lines 53-55) ensures that raw tokens never persist in the database, limiting blast radius if database access is compromised.

### Request Integrity Auditing

The middleware validates consistency between JWT-embedded claims and request headers. Specifically, mismatches between the `run_id` claim and `X-Paperclip-Run-Id` header trigger:

- An `activityLog` audit record documenting the anomaly
- A `422 Unprocessable Entity` response rejecting the request

This detects token replay attacks where a JWT is extracted and used outside its intended execution context.

### Key Revocation

API keys include a `revokedAt` timestamp column. The authentication lookup excludes revoked keys, enabling immediate credential invalidation without database deletion.

## Complete Authentication Flow

The following sequence illustrates how `actorMiddleware` processes each request:

1. **Request arrives** → `actorMiddleware` executes
2. **Authorization header present?**
   - **Yes** → Token extracted and stripped
     - Matches **board API key** → Build board session actor
     - Matches **hashed agent key** → Load associated agent
     - No match → Treat as **JWT** and verify signature/claims
   - **No** → Branch by deployment mode
     - `authenticated` mode → Attempt Better Auth session resolution
     - `local_trusted` mode → Inject local board actor
3. **Actor object** attached to `req.actor` with `type`, `userId`/`agentId`, and company scope
4. **Downstream services** enforce company-scoped access based on `req.actor`

## Key Implementation Files

| File | Responsibility |
|------|---------------|
| [`server/src/middleware/auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/middleware/auth.ts) | Core middleware: token parsing, JWT validation, actor construction |
| [`server/src/services/board-auth.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/board-auth.ts) | Board API key lookup and session management |
| [`server/src/agent-auth-jwt.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/agent-auth-jwt.ts) | JWT verification for run-scoped agent tokens |
| [`packages/shared/src/adapter-auth-session.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/shared/src/adapter-auth-session.ts) | TypeScript definitions for session and actor types |
| [`docs/api/authentication.md`](https://github.com/paperclipai/paperclip/blob/main/docs/api/authentication.md) | External documentation for API consumers |

## Summary

- **Deployment modes** (`local_trusted` vs. `authenticated`) determine whether credentials are required
- **Board operators** authenticate via Better Auth sessions in production, or automatically in local development
- **Agents** use either short-lived JWTs (preferred) or long-lived hashed API keys
- **Company scoping** enforces tenant isolation through the actor object on every request
- **Security features** include SHA-256 token hashing, request integrity auditing, and key revocation capabilities

## Frequently Asked Questions

### What happens if I send requests to a production Paperclip instance without authentication?

In `authenticated` deployment mode, requests without valid credentials receive `401 Unauthorized`. The middleware only proceeds without credentials in `local_trusted` mode, which should never be exposed to untrusted networks.

### How do I migrate from local development to production authentication?

Change the `DeploymentMode` configuration from `local_trusted` to `authenticated` and ensure Better Auth session infrastructure is operational. No application code changes are required—the same middleware handles both modes transparently.

### Why does Paperclip hash API keys instead of encrypting them?

SHA-256 hashing provides **verification without recovery**, meaning even with database access, an attacker cannot extract usable credentials. Encryption would allow key recovery, increasing breach impact. Hashing aligns with industry standards for API key storage.

### Can an agent access resources across multiple companies?

No. Agents are strictly single-tenant. Each agent record links to exactly one `companyId`, and the authentication middleware enforces this scope on every request. Cross-company operations require board operator authentication with explicit membership in both companies.