From 303c00b61b529d7986f7313a3bc2204289693cf9 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:22:27 -0700 Subject: [PATCH 1/2] fix(server): load .env from cwd as fallback when .paperclip/.env is missing The server only loads environment variables from .paperclip/.env, which is not the standard location users expect. When DATABASE_URL is set in a .env file in the project root (cwd), it is silently ignored, requiring users to manually export the variable. Add a fallback that loads cwd/.env after .paperclip/.env with override:false, so the Paperclip-specific env file always takes precedence but standard .env files in the project root are also picked up. Co-Authored-By: Claude Opus 4.6 Co-Authored-By: Paperclip --- server/src/config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/config.ts b/server/src/config.ts index 983eba22..0d25172c 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,5 +1,6 @@ import { readConfigFile } from "./config-file.js"; import { existsSync } from "node:fs"; +import { resolve } from "node:path"; import { config as loadDotenv } from "dotenv"; import { resolvePaperclipEnvPath } from "./paths.js"; import { @@ -27,6 +28,11 @@ if (existsSync(PAPERCLIP_ENV_FILE_PATH)) { loadDotenv({ path: PAPERCLIP_ENV_FILE_PATH, override: false, quiet: true }); } +const CWD_ENV_PATH = resolve(process.cwd(), ".env"); +if (CWD_ENV_PATH !== PAPERCLIP_ENV_FILE_PATH && existsSync(CWD_ENV_PATH)) { + loadDotenv({ path: CWD_ENV_PATH, override: false, quiet: true }); +} + type DatabaseMode = "embedded-postgres" | "postgres"; export interface Config { From ff4f326341680c2dcf92610d7db4149eb13c781a Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sat, 14 Mar 2026 09:05:51 -0700 Subject: [PATCH 2/2] fix(server): use realpathSync for .env path dedup to handle symlinks realpathSync resolves symlinks and normalizes case, preventing double-loading the same .env file when paths differ only by symlink indirection or filesystem case. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/src/config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/config.ts b/server/src/config.ts index 0d25172c..6943af7a 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,5 +1,5 @@ import { readConfigFile } from "./config-file.js"; -import { existsSync } from "node:fs"; +import { existsSync, realpathSync } from "node:fs"; import { resolve } from "node:path"; import { config as loadDotenv } from "dotenv"; import { resolvePaperclipEnvPath } from "./paths.js"; @@ -29,7 +29,10 @@ if (existsSync(PAPERCLIP_ENV_FILE_PATH)) { } const CWD_ENV_PATH = resolve(process.cwd(), ".env"); -if (CWD_ENV_PATH !== PAPERCLIP_ENV_FILE_PATH && existsSync(CWD_ENV_PATH)) { +const isSameFile = existsSync(CWD_ENV_PATH) && existsSync(PAPERCLIP_ENV_FILE_PATH) + ? realpathSync(CWD_ENV_PATH) === realpathSync(PAPERCLIP_ENV_FILE_PATH) + : CWD_ENV_PATH === PAPERCLIP_ENV_FILE_PATH; +if (!isSameFile && existsSync(CWD_ENV_PATH)) { loadDotenv({ path: CWD_ENV_PATH, override: false, quiet: true }); }