How Paperclip Handles Authentication for Deployments: A Complete Technical Guide

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, 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 (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 (lines 90-100).

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
// 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 (lines 10-12) and validated by 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 Core middleware: token parsing, JWT validation, actor construction
server/src/services/board-auth.ts Board API key lookup and session management
server/src/agent-auth-jwt.ts JWT verification for run-scoped agent tokens
packages/shared/src/adapter-auth-session.ts TypeScript definitions for session and actor types
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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →