Environment Variables
This page lists the environment variables Paperclip reads for server configuration and the variables it injects into agent processes at runtime.
Use it when you are wiring a deployment, debugging a startup issue, or checking what an adapter can see inside its process environment.
Server Configuration
| Variable | Default | Meaning |
|---|---|---|
PORT |
3100 |
Server port |
HOST |
127.0.0.1 |
Server host binding |
DATABASE_URL |
embedded PostgreSQL | PostgreSQL connection string |
DATABASE_MIGRATION_URL |
falls back to DATABASE_URL |
Optional PostgreSQL URL used only when running migrations — useful when your runtime user lacks DDL rights and a separate role applies schema changes. |
PAPERCLIP_HOME |
~/.paperclip |
Base directory for all Paperclip data |
PAPERCLIP_INSTANCE_ID |
default |
Instance identifier for multiple local instances |
PAPERCLIP_DEPLOYMENT_MODE |
local_trusted |
Runtime mode override |
SERVE_UI |
true (from server.serveUi in config.json) |
When set, overrides the file-config flag that controls whether the server serves the bundled UI. SERVE_UI=true enables it; SERVE_UI=false disables it. |
PAPERCLIP_BIND |
inferred from HOST |
Bind mode for the server socket. One of the values in BIND_MODES (see packages/shared); overrides server.bind in config.json. |
PAPERCLIP_BIND_HOST |
inferred | Custom host when PAPERCLIP_BIND is set to a custom mode; overrides server.customBindHost. |
PAPERCLIP_TAILNET_BIND_HOST |
auto-detected via tailscale ip -4 |
Tailnet IPv4 address the server binds to when bind mode is tailnet. Set explicitly to skip the tailscale CLI probe. |
Note:
DATABASE_URLis the main switch between the embedded database and external PostgreSQL.
Deployment And Auth
These variables matter most once you move beyond a default local install.
| Variable | Meaning |
|---|---|
PAPERCLIP_PUBLIC_URL |
Canonical public URL for invites, redirects, and auth origin wiring. |
PAPERCLIP_AUTH_PUBLIC_BASE_URL |
Explicit auth base URL when you want Better Auth to use a fixed public origin. |
BETTER_AUTH_URL |
Alternate Better Auth base URL input. |
BETTER_AUTH_SECRET |
Signing secret for Better Auth sessions and tokens. Falls back to PAPERCLIP_AGENT_JWT_SECRET when unset; the server refuses to start if neither is configured. For local development the .env.example ships paperclip-dev-secret. |
BETTER_AUTH_BASE_URL |
Alternate Better Auth base URL input used by some deployments. |
BETTER_AUTH_TRUSTED_ORIGINS |
Comma-separated allowlist of trusted auth origins. |
PAPERCLIP_AGENT_JWT_SECRET |
Secret used to mint agent API JWTs. Required for local adapter auth. |
PAPERCLIP_AGENT_JWT_TTL_SECONDS |
Agent JWT lifetime in seconds. |
PAPERCLIP_AGENT_JWT_ISSUER |
Agent JWT issuer. |
PAPERCLIP_AGENT_JWT_AUDIENCE |
Agent JWT audience. |
Related deployment variables:
| Variable | Meaning |
|---|---|
PAPERCLIP_DEPLOYMENT_EXPOSURE |
Exposure policy override, typically private or public in authenticated mode. |
PAPERCLIP_AUTH_BASE_URL_MODE |
Base URL handling mode, such as auto or explicit. |
PAPERCLIP_ALLOWED_HOSTNAMES |
Comma-separated allowlist for authenticated/private host validation. |
TRUST_PROXY |
How much to trust the X-Forwarded-For header when the server sits behind a reverse proxy or load balancer. Defaults to unset (trust nothing). See below. |
Tip: If
paperclipai doctoris failing on hostnames, redirects, or auth origins, inspect this group first.
Trusting a reverse proxy (TRUST_PROXY)
When you run Paperclip behind a load balancer or reverse proxy (nginx, Caddy, a cloud LB), the real client IP arrives in the X-Forwarded-For header rather than on the socket. TRUST_PROXY tells the server how far to trust that header so req.ip and rate-limiting see the actual client instead of your proxy.
The default is unset, which trusts nothing — the safe choice, because an untrusted client can otherwise spoof its address by sending its own X-Forwarded-For. Only opt in when there really is a proxy in front of the server.
Accepted values:
| Value | Meaning |
|---|---|
unset, "", false, 0 |
Trust nothing. The default. |
true |
Trust the header unconditionally. Unsafe unless the server is unreachable except through your proxy. |
a positive integer (e.g. 1) |
Trust that many proxy hops. Use 1 for a single LB in front of the server. |
| a comma-separated list | Trust specific sources by named subnet (loopback, linklocal, uniquelocal) or CIDR (e.g. 10.0.0.0/8, fd00::/8). |
A malformed value (a stray sign, leading zeros, or an unrecognised token) makes the server refuse to start with an explanatory error, so a typo fails loudly rather than silently disabling proxy trust. After setting it, confirm req.ip in the request log matches the real client IP through your proxy.
Secrets
| Variable | Meaning |
|---|---|
PAPERCLIP_SECRETS_MASTER_KEY |
32-byte encryption key as base64, hex, or raw |
PAPERCLIP_SECRETS_MASTER_KEY_FILE |
Path to the local key file |
PAPERCLIP_SECRETS_STRICT_MODE |
Require secret refs for server-side env bindings. Does not apply to paperclipai configure --section llm or config.llm.apiKey. |
These values are covered in more detail in Secrets.
Storage
| Variable | Meaning |
|---|---|
PAPERCLIP_STORAGE_PROVIDER |
Storage backend, usually local_disk or s3. |
PAPERCLIP_STORAGE_LOCAL_DIR |
Base directory for local-disk storage. |
PAPERCLIP_STORAGE_S3_BUCKET |
S3 bucket name. |
PAPERCLIP_STORAGE_S3_REGION |
S3 region. |
PAPERCLIP_STORAGE_S3_ENDPOINT |
Custom S3-compatible endpoint for MinIO, R2, and similar providers. |
PAPERCLIP_STORAGE_S3_PREFIX |
Optional object key prefix. |
PAPERCLIP_STORAGE_S3_FORCE_PATH_STYLE |
Enable path-style S3 requests when the provider needs them. |
Scheduler
| Variable | Default | Meaning |
|---|---|---|
HEARTBEAT_SCHEDULER_ENABLED |
true |
Enables or disables timer-based scheduling. |
HEARTBEAT_SCHEDULER_INTERVAL_MS |
30000 |
Scheduler poll interval in milliseconds. |
Telemetry & Feedback Export
These variables control where the server forwards operator-submitted feedback (and the deprecated telemetry channel that backs the same export pipeline). They are read by server/src/config.ts and are only consulted when you want to ship feedback events off your instance to a separate collector.
| Variable | Default | Meaning |
|---|---|---|
PAPERCLIP_FEEDBACK_EXPORT_BACKEND_URL |
unset | URL of the external feedback collector. When set, the server forwards paperclipai feedback submissions to this endpoint. |
PAPERCLIP_FEEDBACK_EXPORT_BACKEND_TOKEN |
unset | Bearer token used to authenticate the forwarding request. |
PAPERCLIP_TELEMETRY_BACKEND_URL |
unset | Legacy alias for PAPERCLIP_FEEDBACK_EXPORT_BACKEND_URL. Honoured for backwards compatibility — set the feedback variant in new deployments. |
PAPERCLIP_TELEMETRY_BACKEND_TOKEN |
unset | Legacy alias for PAPERCLIP_FEEDBACK_EXPORT_BACKEND_TOKEN. |
If neither variable is set, feedback submissions are stored locally and never leave the instance.
Observability (OpenTelemetry)
Paperclip can emit distributed traces over OpenTelemetry (OTLP) so you can watch requests flow through the server in a tracing backend like Jaeger, Tempo, or Honeycomb. It is opt-in and off by default — nothing is loaded until you point it at a collector.
To turn it on, set OTEL_EXPORTER_OTLP_ENDPOINT and install the OpenTelemetry packages the server needs (@opentelemetry/sdk-node, @opentelemetry/auto-instrumentations-node, the exporter for your protocol, @opentelemetry/resources, and @opentelemetry/semantic-conventions). If the endpoint is set but the packages are missing, the server logs a one-line hint and keeps running without tracing.
| Variable | Default | Meaning |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
unset | OTLP collector endpoint. Setting it is the master switch that enables tracing. |
OTEL_EXPORTER_OTLP_PROTOCOL |
grpc |
Exporter protocol: grpc, http/protobuf, or http/json. An unknown value logs a warning and falls back to grpc. |
OTEL_SERVICE_NAME |
paperclip |
Service name reported on spans. |
OTEL_SERVICE_VERSION |
unknown |
Service version reported on spans. |
A bad endpoint or an unreachable collector never takes the server down — the SDK logs the failure and tracing simply stays off. On shutdown the server flushes buffered spans (with a short timeout) before exiting.
Agent Runtime
The server injects these variables into agent processes when it starts a run:
| Variable | Meaning |
|---|---|
| Variable | Always set? |
| --- | --- |
PAPERCLIP_AGENT_ID |
yes |
PAPERCLIP_COMPANY_ID |
yes |
PAPERCLIP_API_URL |
yes |
PAPERCLIP_API_KEY |
local adapters |
PAPERCLIP_RUN_ID |
yes |
PAPERCLIP_TASK_ID |
wake-driven |
PAPERCLIP_WAKE_REASON |
wake-driven |
PAPERCLIP_WAKE_COMMENT_ID |
comment wakes |
PAPERCLIP_WAKE_PAYLOAD_JSON |
some adapters |
PAPERCLIP_APPROVAL_ID |
approval wakes |
PAPERCLIP_APPROVAL_STATUS |
approval wakes |
PAPERCLIP_LINKED_ISSUE_IDS |
optional |
Use these values when your agent runtime needs to authenticate back to Paperclip or understand what context triggered the run.
PAPERCLIP_WAKE_REASON values
| Value | When it fires |
|---|---|
issue_assigned |
A task was newly assigned to this agent. |
issue_commented |
A new comment was posted on an issue this agent owns. The triggering comment id is in PAPERCLIP_WAKE_COMMENT_ID. |
issue_comment_mentioned |
The agent was @-mentioned in a comment on an issue it does not own. |
issue_blockers_resolved |
Every issue listed in this issue's blockedBy reached done. |
issue_children_completed |
All direct children of this issue reached a terminal state (done or cancelled). |
approval_resolved |
An approval the agent requested was approved or rejected. PAPERCLIP_APPROVAL_ID and PAPERCLIP_APPROVAL_STATUS are populated. |
scheduled |
A scheduled run from the heartbeat scheduler or a routine cron. |
assignment |
Generic assignment-triggered run with no more specific reason. |
When Paperclip realizes an execution workspace, it can also inject workspace-specific variables such as:
PAPERCLIP_WORKSPACE_CWDPAPERCLIP_WORKSPACE_PATHPAPERCLIP_WORKSPACE_REPO_ROOTPAPERCLIP_WORKSPACE_BRANCHPAPERCLIP_PROJECT_IDPAPERCLIP_ISSUE_ID
Those are mainly useful for adapter authors and agent-side tooling that need direct access to the resolved execution workspace.
Audit trail: Every mutating API request from an agent run should include the
X-Paperclip-Run-Id: $PAPERCLIP_RUN_IDheader. The server uses it to attribute issue updates, comments, checkouts, and subtasks to the heartbeat run that produced them. Read-only requests do not require it.
LLM Provider Keys
| Variable | Meaning |
|---|---|
ANTHROPIC_API_KEY |
Anthropic API key for claude_local |
OPENAI_API_KEY |
OpenAI API key for codex_local |
GEMINI_API_KEY |
Gemini API key for gemini_local |
GOOGLE_API_KEY |
Alternate Google API key path for gemini_local |
Tip: If an adapter test is failing, start by checking whether the expected provider key is present in the process environment.
Adapter Provider Overrides
The local CLI adapters can be pointed at a custom or remote OpenAI-compatible gateway through these server-read variables. Each takes a JSON value that Paperclip writes into the adapter's own runtime config before a run, so you can route an adapter at your own provider without editing the agent's machine by hand. The full JSON shape and behaviour for each live on the adapter's reference page.
| Variable | Adapter | Meaning |
|---|---|---|
PAPERCLIP_CODEX_PROVIDERS |
codex_local |
JSON of custom providers (and an optional model_provider) written into Codex's managed config.toml. See Codex Local. |
PAPERCLIP_PI_PROVIDERS |
pi_local |
JSON of custom providers written into Pi's managed models.json. See Pi Local. |
PAPERCLIP_OPENCODE_PROVIDERS |
opencode_local |
JSON merged into OpenCode's provider config. See OpenCode Local. |
PAPERCLIP_OPENCODE_SMALL_MODEL |
opencode_local |
Sets OpenCode's small_model (the auxiliary/helper model). See OpenCode Local. |
Values support {env:VAR} placeholders, which are expanded server-side so secrets stay out of the stored JSON.