Learnings: Paperclip Railway deployment (March 2026)

Context: First deployment of Paperclip (agent orchestration app) from the brainforge-platform monorepo to Railway, with custom domain paperclip.brainforge.ai and adapter API keys (e.g. Gemini).

Use these learnings to update skills, playbook, and platform when deploying new services or similar apps.


1. Monorepo deploy: use --path-as-root

  • Problem: Pushing the full repo triggered 413 Payload Too Large (huge upload).
  • Fix: Deploy from repo root with path-as-root so only the app directory is the build context:
    railway up apps/paperclip --path-as-root
  • For skills/playbook: Any “deploy app from monorepo to Railway” flow should document railway up <app-path> --path-as-root. Add to standards/03-knowledge/engineering/setup/railway-*.md or a generic “Railway monorepo deploy” note.

2. Railway Dockerfile constraints

  • VOLUME: Railway disallows the VOLUME instruction. Remove it or comment it out; use Railway volumes in the dashboard if persistent storage is needed.
  • For skills: A “Railway Dockerfile” checklist or skill could include: no VOLUME, no other banned instructions per current Railway docs.

3. TypeScript in Docker: global augmentation

  • Problem: Express.Request augmentation in a separate express.d.ts was not applied during the Docker build, so req.actor failed in routes even though it worked locally.
  • Fix: Inline the declare global { namespace Express { interface Request { actor: ... } } } block into the main entry file (server/src/index.ts) so it’s part of the compiled unit.
  • Learning: In strict Docker/CI builds, keep critical global type augmentations in a file that is definitely compiled (e.g. entry), or ensure tsconfig include/exclude clearly picks up the .d.ts and the same options as local.

4. Adapter API keys via service env (no UI)

  • Observation: Users prefer setting adapter API keys (e.g. Gemini) as service environment variables (e.g. Railway vars) rather than adding more UI.
  • Already supported: Paperclip adapters (e.g. Gemini) read GEMINI_API_KEY / GOOGLE_API_KEY from server process.env for both environment checks and runs.
  • Action taken: Documented in apps/paperclip/docs/deploy/environment-variables.md. For other providers (e.g. GOOGLE_AI_STUDIO_GEMINI_API_KEY), document “rename when setting on host” (e.g. set as GEMINI_API_KEY on Railway).
  • For skills/playbook: When adding a new adapter or provider, document the server env var name(s) in the app’s deploy docs and, if useful, in playbook setup so operators know what to set on Railway/Heroku/etc.

5. Domain strategy: subdomain vs subpath

  • Tried first: platform.brainforge.ai/paperclip (subpath proxy from Platform app).
  • Switched to: paperclip.brainforge.ai (dedicated subdomain).
  • Learning: Subpath requires base-path handling in the app, proxy + auth bypass in the parent app, and more moving parts. For a net-new service, a dedicated subdomain is simpler: no proxy, no base path, healthcheck at /api/health, and auth (e.g. Better Auth) configured for one origin.
  • For skills: A “deploy new service with custom domain” skill could default to subdomain and only use subpath when there’s a strong reason (e.g. single-domain requirement).

6. Better Auth: trusted origins

  • Problem: Account creation returned 403 when using the Railway URL or custom domain; Better Auth was rejecting the request origin.
  • Fix: Set BETTER_AUTH_TRUSTED_ORIGINS (comma-separated) to include both the Railway default URL and the custom domain, e.g. https://paperclip-production-xxx.up.railway.app,https://paperclip.brainforge.ai.
  • For skills/playbook: Any app using Better Auth behind a proxy or with multiple domains should document “set BETTER_AUTH_TRUSTED_ORIGINS (and optionally PAPERCLIP_ALLOWED_HOSTNAMES / PAPERCLIP_AUTH_PUBLIC_BASE_URL) for all public URLs.”

7. First-time instance setup in production

  • Need: Run paperclip onboard and optionally paperclip auth bootstrap-ceo inside the live Railway container.
  • Gotcha: Interactive prompts (e.g. Corepack “Do you want to continue?”) block non-interactive use.
  • Fix: Use non-interactive invocations, e.g. npx --yes pnpm@9.15.4 to avoid Corepack prompt when running commands via railway ssh.
  • For skills/playbook: Document “first-time instance setup in production” (onboard, bootstrap) and non-interactive CLI patterns so scripts and agents don’t hang.

8. Healthcheck path

  • With a root-mounted app (no base path), healthcheck is /api/health. With a subpath (e.g. /paperclip), it would be /paperclip/api/health. Set healthcheckPath in railway.toml (or Railway service config) to match.

Summary: what to change

AreaChange
PlaybookAdd “Monorepo deploy: railway up <path> --path-as-root” to Railway setup docs; add “Better Auth trusted origins” and “first-time instance setup (non-interactive)” for Paperclip or similar apps.
SkillsConsider a “deploy net-new service to Railway” skill: path-as-root, env checklist, trusted origins, healthcheck, optional subdomain vs subpath.
Platform / Cursor rulesIf we add more in-repo services: reference playbook for Railway monorepo deploy and Dockerfile constraints.
Paperclip docsAlready updated: env vars (GEMINI_API_KEY, GOOGLE_API_KEY), deploy environment-variables.md.

Captured 2026-03-17 from Paperclip Railway deployment and post-deploy configuration (custom domain, Gemini key, Better Auth).