Files
resolutionflow/CLAUDE.md
Michael Chihlas 83df48291a feat: Complete backend and docs rebrand from Patherly to ResolutionFlow
Update APP_NAME, OpenAPI metadata, log messages, root endpoint response,
model docstrings, seed script comments, README heading, and CLAUDE.md
branding references. Frontend rebrand was completed in PR #26; this
covers everything else.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 00:41:22 -05:00

28 KiB

CLAUDE.md - Patherly / ResolutionFlow Project Context

Purpose: This file provides Claude Code with essential context for working on the Patherly project. Last Updated: February 4, 2026


Project Overview

Patherly (user-facing brand: ResolutionFlow) is a troubleshooting decision tree application designed for MSP engineers. It guides engineers through proven troubleshooting paths, captures decisions and notes automatically, and generates professional ticket documentation.

Tagline: "Take the path MOST traveled."

Primary User: Michael Chihlas - Senior Systems Engineer at an MSP

Goal: Michael uses this tool for 50% of his tickets within 3 months.

Branding

The project was rebranded from "Patherly" to "ResolutionFlow" in the frontend (PR #26, commit cfbd815). The naming is split:

Context Name Used
Repository / directory name patherly
Backend (FastAPI, env vars, APP_NAME) ResolutionFlow
Database / Docker container patherly / patherly_postgres
Production URLs app.patherly.com / api.patherly.com
Frontend UI (header, login, register) ResolutionFlow
Browser tab title ResolutionFlow - Decision Tree Platform

Brand details:

  • Colors: Purple gradient (#818cf8#a78bfa)
  • Fonts: Plus Jakarta Sans (headings), Inter (body), Outfit (labels) - loaded via Google Fonts
  • Logo: Inline SVG in AppLayout.tsx and LoginPage.tsx (decision-tree icon with gradient)
  • Brand assets: brand-assets/ (source SVGs), frontend/src/assets/brand/ (app assets), frontend/public/icons/ (favicon)
  • CSS utilities: text-gradient-brand, bg-gradient-brand, bg-gradient-brand-hover (defined in tailwind.config.js and index.css)
  • Rebrand guide: REBRAND-IMPLEMENTATION-GUIDE.md

When adding new frontend pages or components, use "ResolutionFlow" for any user-visible branding. Use font-heading for heading elements and the text-gradient-brand utility for brand-colored text.


Current State

  • Phase: Phase 2.5 - Step Library Foundation (In Progress)
  • Backend: Complete (20+ API endpoints, 40+ integration tests, all passing)
  • Frontend: Core features complete, Tree Editor functional, Settings page added
  • Database: PostgreSQL with Docker (container name: patherly_postgres)

What's Complete

  • User authentication (JWT, register, login, refresh, invite codes)
  • Trees CRUD with full-text search
  • Sessions tracking with decisions
  • Export API (Markdown, Text, HTML)
  • Tree Editor with form-based editing and visual preview
  • Dark/Light theme toggle
  • Markdown rendering in session player and node editor
  • 7 comprehensive seed decision trees
  • Tree Organization System:
    • Categories (global + team-specific, admin-managed)
    • Tags (author + admin managed, autocomplete)
    • User folders (personal tree collections)
      • Subfolder hierarchy (max 3 levels deep)
      • Right-click context menu for edit/delete/add subfolder
      • Cascade delete for subfolders
    • Team admin role with scoped permissions
    • Filter trees by category, tags, and folders
  • User Preferences (Issue #3):
    • Settings page at /settings
    • Default export format preference (persisted in localStorage)
    • Theme toggle integrated in Settings
  • Step Categories (Issue #5):
    • Database table with 10 seeded global categories
    • Full CRUD API at /api/v1/step-categories
    • Team scoping support (global + team-specific)
  • Step Library Schema (Issue #6):
    • step_library table for reusable troubleshooting steps
    • step_ratings table for user ratings/reviews
    • step_usage_log table for tracking verified use
    • Support for decision/action/solution step types
    • Visibility levels: private, team, public
  • Step Library API (Issue #7):
    • Full CRUD at /api/v1/steps
    • Full-text search endpoint
    • Popular tags endpoint
    • Rating/review system with verified use tracking
  • Frontend Rebrand (PR #26):
    • Renamed from "Patherly" to "ResolutionFlow" in all user-facing UI
    • Purple gradient theme, custom fonts (Plus Jakarta Sans, Inter, Outfit)
    • Custom SVG logo in header and auth pages
    • Updated favicon and browser tab title

What's In Progress

  • Custom step continuation flow refinements (Phase 2.5)
  • Tree forking from sessions with custom steps

Deployment

  • Production: Railway (app.patherly.com / api.patherly.com)
  • PR Environments: Enabled - auto-created for each pull request

Tech Stack

Backend

  • Framework: Python FastAPI
  • Database: PostgreSQL 16 (async via SQLAlchemy 2.0 + asyncpg)
  • Migrations: Alembic
  • Auth: JWT tokens (python-jose) + bcrypt passwords
  • Validation: Pydantic v2

Frontend

  • Framework: React 19 + Vite + TypeScript
  • Styling: Tailwind CSS v3 with ResolutionFlow brand theme (purple gradient)
  • Fonts: Plus Jakarta Sans (headings), Inter (body), Outfit (labels) via Google Fonts
  • State: Zustand (with immer + zundo for undo/redo)
  • Routing: React Router v7
  • API Client: Axios with token interceptors
  • Icons: Lucide React

Project Structure

patherly/
├── backend/
│   ├── app/
│   │   ├── main.py                 # FastAPI entry point
│   │   ├── api/
│   │   │   ├── endpoints/
│   │   │   │   ├── auth.py         # Auth: register, login, refresh, logout
│   │   │   │   ├── trees.py        # Trees CRUD + search
│   │   │   │   ├── sessions.py     # Sessions + export
│   │   │   │   └── invite.py       # Invite code management
│   │   │   ├── deps.py             # Auth dependencies
│   │   │   └── router.py
│   │   ├── core/
│   │   │   ├── config.py           # Settings (pydantic-settings)
│   │   │   ├── database.py         # Async SQLAlchemy
│   │   │   ├── security.py         # JWT + password hashing
│   │   │   ├── logging_config.py   # Structured logging
│   │   │   └── middleware.py       # Request logging
│   │   ├── models/                 # SQLAlchemy models
│   │   │   ├── user.py             # is_team_admin field added
│   │   │   ├── team.py
│   │   │   ├── tree.py             # JSONB tree_structure + category_id, tags
│   │   │   ├── session.py          # JSONB path_taken, decisions
│   │   │   ├── attachment.py
│   │   │   ├── invite_code.py
│   │   │   ├── category.py         # TreeCategory model
│   │   │   ├── tag.py              # TreeTag model
│   │   │   ├── folder.py           # UserFolder model
│   │   │   ├── step_category.py    # StepCategory model (NEW)
│   │   │   └── step_library.py     # StepLibrary, StepRating, StepUsageLog (NEW)
│   │   └── schemas/                # Pydantic schemas
│   ├── alembic/                    # Database migrations
│   ├── scripts/
│   │   ├── seed_data.py
│   │   └── seed_trees.py           # 7 comprehensive trees
│   ├── tests/                      # pytest integration tests
│   ├── docker-compose.yml          # PostgreSQL container
│   ├── requirements.txt
│   └── .env                        # Environment variables
│
├── brand-assets/                   # Source brand SVGs and guides
│
├── frontend/
│   ├── public/
│   │   └── icons/                  # Favicon and PWA icons
│   ├── src/
│   │   ├── main.tsx
│   │   ├── App.tsx
│   │   ├── router.tsx
│   │   ├── assets/brand/           # Brand logos (SVG)
│   │   ├── api/                    # Axios API client
│   │   │   ├── client.ts           # Axios instance with interceptors
│   │   │   ├── auth.ts
│   │   │   ├── trees.ts
│   │   │   └── sessions.ts
│   │   ├── store/
│   │   │   ├── authStore.ts        # Zustand auth state
│   │   │   ├── themeStore.ts       # Dark/light theme
│   │   │   ├── treeEditorStore.ts  # Tree editor state (immer + zundo)
│   │   │   └── userPreferencesStore.ts  # User preferences (NEW)
│   │   ├── components/
│   │   │   ├── common/             # Modal, ErrorBoundary, ThemeToggle
│   │   │   ├── layout/             # AppLayout, ProtectedRoute
│   │   │   ├── tree-editor/        # Tree editor components
│   │   │   ├── tree-preview/       # Visual tree preview
│   │   │   ├── step-library/       # Step library browser, forms, modals
│   │   │   └── ui/                 # MarkdownContent
│   │   ├── pages/
│   │   │   ├── LoginPage.tsx
│   │   │   ├── RegisterPage.tsx
│   │   │   ├── TreeLibraryPage.tsx
│   │   │   ├── TreeNavigationPage.tsx  # Core feature
│   │   │   ├── TreeEditorPage.tsx
│   │   │   ├── SessionHistoryPage.tsx
│   │   │   ├── SessionDetailPage.tsx
│   │   │   └── SettingsPage.tsx    # User preferences (NEW)
│   │   ├── types/                  # TypeScript interfaces
│   │   └── lib/utils.ts            # cn() utility for Tailwind
│   ├── package.json
│   ├── tailwind.config.js
│   └── vite.config.ts
│
├── CLAUDE.md                       # This file
├── CURRENT-STATE.md                # Quick status reference
├── LESSONS-LEARNED.md              # Bugs and fixes (READ THIS!)
├── PROGRESS.md                     # Detailed progress log
├── REBRAND-IMPLEMENTATION-GUIDE.md # Patherly → ResolutionFlow rebrand guide
├── 01-PROJECT-OVERVIEW.md          # Vision and goals
├── 02-TECHNICAL-ARCHITECTURE.md    # System design, API specs
├── 03-DEVELOPMENT-ROADMAP.md       # Phases and timeline
├── 04-FEATURE-SPECIFICATIONS.md    # Feature details
├── 05-QUESTIONS-AND-ACTION-ITEMS.md
└── PHASE-2.5-PERSONAL-BRANCHING.md # Future feature spec

Environment Variables

Backend (.env)

Required in backend/.env:

# Application
APP_NAME=ResolutionFlow
DEBUG=true  # Set false in production

# Database (matches docker-compose.yml)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/patherly
DATABASE_URL_SYNC=postgresql://postgres:postgres@localhost:5432/patherly

# JWT Settings - CHANGE THESE IN PRODUCTION
SECRET_KEY=<generate with: openssl rand -hex 32>
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_MINUTES=10080

# Auth
REQUIRE_INVITE_CODE=true  # Set false to allow open registration

Railway-specific (production):

ALLOW_RAILWAY_ORIGINS=true  # Enables CORS for PR environments

Frontend (.env.local - optional)

VITE_API_URL=http://localhost:8000  # Override API URL

Development Commands

Start Development Environment

# Terminal 1: Start PostgreSQL
docker start patherly_postgres

# Terminal 2: Backend (from project root)
cd backend
# Windows:
.\venv\Scripts\Activate
# Linux/Mac:
source venv/bin/activate
uvicorn app.main:app --reload

# Terminal 3: Frontend (from project root)
cd frontend
npm run dev

URLs

Run Tests

# From project root
cd backend

# First time only: create test database
docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"

# Install test dependencies (if not already installed)
pip install -r requirements-dev.txt

# Run tests
pytest

Frontend Operations

# From project root
cd frontend

# Build for production
npm run build

# Preview production build
npm run preview

# Lint code
npm run lint

Run Seed Scripts

# From project root
cd backend
pip install httpx  # Required for seed scripts
python -m scripts.seed_trees

Database Operations

# From project root
cd backend

# Run migrations
alembic upgrade head

# Create new migration
alembic revision --autogenerate -m "Description"

# Create migration without DB running (manual mode)
alembic revision -m "Description"  # Edit migration file manually
# Migration runs when DB is available - safe to commit without testing locally

# Access PostgreSQL (no local psql needed)
docker exec -it patherly_postgres psql -U postgres -d patherly

Critical Lessons Learned

ALWAYS read LESSONS-LEARNED.md before making changes!

DateTime Handling (Critical)

# CORRECT - Always use timezone-aware datetimes
from datetime import datetime, timezone
from sqlalchemy import DateTime

created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))

# WRONG - Never use this
datetime.utcnow()  # Deprecated, returns naive datetime

React State: Don't Store Object Snapshots

// WRONG - Snapshot won't update when store changes
const [editingNode, setEditingNode] = useState<TreeStructure | null>(null)

// CORRECT - Store ID only, fetch current object each render
const [editingNodeId, setEditingNodeId] = useState<string | null>(null)
const editingNode = editingNodeId ? findNode(editingNodeId) : null

Modal Draft State: Don't Overwrite Store-Managed Fields

// WRONG - Overwrites children with stale snapshot
const handleSave = () => {
  updateNode(node.id, draft)  // draft.children is stale!
}

// CORRECT - Exclude store-managed fields
const handleSave = () => {
  const { children, ...draftWithoutChildren } = draft
  updateNode(node.id, draftWithoutChildren)
}

Database Name

  • Database name is patherly (not decision_tree)
  • Update .env if you see the old name

Virtual Environment

  • Always check for (venv) prefix before running pip
  • Don't use --break-system-packages when venv is active

PostgreSQL NULL Casting for UUID Columns

-- WRONG - PostgreSQL infers NULL as text type
INSERT INTO tree_tags (name, slug, team_id)
SELECT 'tag', 'slug', NULL as team_id  -- Error: column is uuid but expression is text

-- CORRECT - Explicitly cast NULL to uuid
INSERT INTO tree_tags (name, slug, team_id)
SELECT 'tag', 'slug', NULL::uuid as team_id  -- Works!

Always use NULL::uuid when inserting NULL values into UUID columns in raw SQL.

SQLAlchemy Async: Avoid Lazy Loading on New Objects

# WRONG - Triggers lazy load which fails in async context
new_tree = Tree(...)
db.add(new_tree)
await db.flush()
new_tree.tags.append(tag)  # MissingGreenlet error!

# CORRECT - Use direct SQL for junction tables
from app.models.tag import tree_tag_assignments
await db.execute(
    tree_tag_assignments.insert().values(
        tree_id=new_tree.id,
        tag_id=tag.id
    )
)

Accessing relationships on newly created objects triggers lazy loading, which fails in async SQLAlchemy. Use direct SQL inserts for junction tables instead.

React Router: Clear Dirty State Before Navigation

// WRONG - Navigation triggers before dirty flag is cleared
const newTree = await treesApi.create(data)
navigate(`/trees/${newTree.id}/edit`)  // Blocker fires here!
markSaved()  // Too late

// CORRECT - Clear dirty state first
const newTree = await treesApi.create(data)
markSaved()  // Clear isDirty first
navigate(`/trees/${newTree.id}/edit`)  // Blocker won't fire

When using useBlocker for unsaved changes, always clear the dirty flag before programmatic navigation.

CORS: Include Both allow_origins AND allow_origin_regex

# WRONG - Custom domains ignored when using regex
if settings.ALLOW_RAILWAY_ORIGINS:
    app.add_middleware(
        CORSMiddleware,
        allow_origin_regex=r"https://.*\.up\.railway\.app",  # Only matches Railway domains!
        # ...
    )

# CORRECT - Include both for custom domains + Railway PR environments
if settings.ALLOW_RAILWAY_ORIGINS:
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.allowed_origins,  # Custom domains like app.patherly.com
        allow_origin_regex=r"https://.*\.up\.railway\.app",  # Railway PR domains
        # ...
    )

When using allow_origin_regex for wildcard patterns, also include allow_origins for explicit custom domains. The regex alone won't match custom domains like app.patherly.com.

findNode Requires Tree Structure Parameter

// WRONG - Always returns null (structure defaults to undefined)
const node = findNode(nodeId)

// CORRECT - Pass tree structure explicitly
const node = findNode(nodeId, tree?.tree_structure)
// For custom steps, also check:
const customStep = findCustomStep(nodeId)

Frontend/Backend API Path Alignment

Always verify frontend API paths match backend route definitions. Example: backend serves /steps/tags/popular but frontend called /steps/popular-tags. Check backend/app/api/router.py and endpoint files for actual paths.

Custom Step Flow in Tree Navigation

The custom step insertion flow in TreeNavigationPage.tsx chains through multiple modals:

  1. CustomStepModal (create/browse) → 2. PostStepActionModal (save/use/both) → 3. ContinuationModal (pick descendant or build branch) → 4. Custom step view with "Continue to" button

Key state: pendingStep, pendingContinuationNodeId, customBranchMode, branchOriginNodeId Custom steps are stored in session JSONB (custom_steps field) and referenced by UUID in pathTaken. findNode() only searches tree structure -- use findCustomStep() for custom step UUIDs.


API Endpoints Reference

Full API documentation: http://localhost:8000/api/docs (interactive OpenAPI/Swagger UI)

Quick reference:

  • Auth: /api/v1/auth/* - register, login, refresh, logout, me
  • Trees: /api/v1/trees/* - CRUD, search, categories (supports filters: category_id, tags, folder_id)
  • Sessions: /api/v1/sessions/* - start, track, complete, export (markdown/text/html)
  • Tags: /api/v1/tags/* - manage tree tags, autocomplete search
  • Folders: /api/v1/folders/* - user folders with subfolder hierarchy (max 3 levels deep)
  • Categories: /api/v1/categories/* - tree categories (global + team-specific)
  • Step Categories: /api/v1/step-categories/* - step categories for library
  • Step Library: /api/v1/steps/* - reusable steps, ratings, reviews, full-text search
  • Invite Codes: /api/v1/invite-codes/* - admin management

Key constraints:

  • Folder hierarchy: max 3 levels deep (root → child → grandchild)
  • Step ratings: 1-5 stars with optional review text
  • Categories and tags: support both global and team-specific scoping

For detailed parameters, request/response schemas, and examples, visit the API docs.


Data Models

Tree Structure (JSONB)

interface TreeStructure {
  id: string
  type: 'decision' | 'action' | 'solution'

  // Decision nodes
  question?: string
  help_text?: string
  options?: Array<{
    id: string
    label: string
    next_node_id?: string
  }>

  // Action nodes
  title?: string
  description?: string
  commands?: string[]
  next_node_id?: string

  // Solution nodes
  title?: string
  description?: string
  steps?: string[]

  // All nodes can have children
  children?: TreeStructure[]
}

Session Decisions (JSONB)

interface Decision {
  node_id: string
  question?: string
  answer: string
  notes?: string
  timestamp: string  // ISO string, not datetime object
}

Frontend Patterns

State Management

  • Auth: useAuthStore - Zustand with localStorage persistence
  • Theme: useThemeStore - Dark/light/system preference
  • Tree Editor: useTreeEditorStore - Zustand + immer + zundo (undo/redo)
  • User Preferences: useUserPreferencesStore - Zustand with localStorage persistence (export format default)

Component Guidelines

  • Use cn() from @/lib/utils for Tailwind class merging
  • Use Lucide icons (no title prop - wrap in <span> instead)
  • Modals: Use fixed header/footer with scrollable body
  • Modals: Import and render at end of parent component JSX
  • Forms: Show field-level validation errors
  • Conditional rendering: Add null checks when node might not exist (currentNode && currentNode.type)

TypeScript Type Organization

  • New type modules: Create in types/ directory (e.g., types/step.ts)
  • Export from types/index.ts with export * from './modulename'
  • Import types using import type { Type } from '@/types' (type-only import)

API Client Pattern

import api from '@/api/client'

// Token refresh handled automatically by interceptor
const response = await api.get('/api/v1/trees')

Common Tasks

Adding a New API Endpoint

  1. Create route in backend/app/api/endpoints/
  2. Add to router in backend/app/api/router.py
  3. Create/update Pydantic schemas in backend/app/schemas/
  4. Add tests in backend/tests/
  5. Update API client in frontend/src/api/

Adding a New API Client Module (Frontend)

  1. Create types in frontend/src/types/modulename.ts
  2. Export types from frontend/src/types/index.ts
  3. Create API client in frontend/src/api/modulename.ts following existing pattern
  4. Export from frontend/src/api/index.ts
  5. Import in components: import { moduleApi } from '@/api'

Adding a New Page

  1. Create page component in frontend/src/pages/
  2. Add route in frontend/src/router.tsx
  3. Add navigation link in AppLayout.tsx if needed

Modifying Database Schema

  1. Update model in backend/app/models/
  2. Create migration: alembic revision --autogenerate -m "description"
  3. Review generated migration file
  4. Apply: alembic upgrade head

Coding Standards

Python (Backend)

  • Use type hints everywhere
  • Use async/await for database operations
  • Use Pydantic for validation
  • Log with correlation IDs for tracing
  • Always use DateTime(timezone=True) for timestamps

TypeScript (Frontend)

  • Enable strict mode (when ready)
  • Use TypeScript interfaces for all data structures
  • Prefer const over let
  • Use functional components with hooks
  • Extract reusable logic into custom hooks

Git

  • Commit message format: type: description
  • Types: feat, fix, refactor, docs, test, chore
  • Always include Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Commit Strategy for Large Features

  • Break implementation into logical phases (foundation → components → integration)
  • Commit after each phase with npm run build validation
  • Phase commits enable easier debugging and rollback
  • Example: API clients (Phase 1) → UI components (Phase 2) → Page integration (Phase 3)

Future Roadmap

Phase 2.5 (In Progress)

  • Step Categories database and API
  • Step Library database schema (step_library, step_ratings, step_usage_log)
  • Step Library CRUD API with search and ratings
  • User Preferences (Settings page, export format default)
  • 🔲 Step Library browser component (frontend)
  • 🔲 Add Custom Step button in tree navigation
  • 🔲 Custom step creation modal
  • 🔲 Personal tree branching (add custom steps during sessions)
  • 🔲 Tree forking and sharing

Phase 3 (Planned)

  • File attachments (screenshots, logs)
  • Offline mode (Service Workers + IndexedDB)
  • Client context system
  • Analytics dashboard

Phase 4 (Planned)

  • API & integrations (ConnectWise, Kaseya)
  • PowerShell automation execution
  • Enterprise features (SSO, white-labeling)

Troubleshooting

Backend won't start

  1. Check Docker: docker ps - is patherly_postgres running?
  2. Check .env - is DATABASE_URL correct (patherly not decision_tree)?
  3. Check venv: is (venv) prefix showing?

Frontend compile errors

  1. Check frontend/src/lib/utils.ts exists (provides cn() function)
  2. Run npm install to ensure dependencies are installed

Tests failing

  1. Create test database: docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"
  2. Install dev deps: pip install -r requirements-dev.txt
  3. Ensure pytest-asyncio version: pip install pytest-asyncio==0.24.0

API 500 errors

  1. Check server logs for datetime errors (timezone issue)
  2. Ensure all datetimes use datetime.now(timezone.utc)

Git Patterns

Always gitignore:

# Secrets and local config
backend/.env
.claude.local.md

# Dependencies
backend/venv/
frontend/node_modules/

# Build artifacts
frontend/dist/
backend/**/__pycache__/
*.pyc

# Railway CLI (local tooling only)
/node_modules/
/package.json
/package-lock.json

# IDE
.vscode/
.idea/
*.swp

Quick Reference

What Where
API Docs http://localhost:8000/api/docs
Current Status CURRENT-STATE.md
Bug Fixes LESSONS-LEARNED.md
Feature Specs 04-FEATURE-SPECIFICATIONS.md
Phase 2.5 Spec PHASE-2.5-PERSONAL-BRANCHING.md
Rebrand Guide REBRAND-IMPLEMENTATION-GUIDE.md

Railway Deployment

Production

PR Environments

Railway creates isolated preview environments for each pull request.

Workflow:

  1. Create feature branch: git checkout -b feature/my-feature
  2. Make changes, commit, push to origin
  3. Open Pull Request on GitHub
  4. Railway auto-creates preview environment (backend + frontend + DB)
  5. Generate domains manually in Railway dashboard:
    • Switch to the PR environment
    • Click on each service → Settings → Networking → Generate Domain
  6. Set VITE_API_URL on frontend service to point to the PR backend URL
    • IMPORTANT: Must include https:// prefix (e.g., https://patherly-patherly-pr-24.up.railway.app)
  7. Redeploy frontend if needed
  8. Test at preview URLs
  9. Merge PR → auto-deploys to production
  10. Railway cleans up PR environment after merge

Environment Variables:

  • PR environments inherit from production base environment
  • REQUIRE_INVITE_CODE=true is inherited (create invite codes in PR DB if needed)
  • DATABASE_URL is auto-provided for isolated PR database
  • ALLOW_RAILWAY_ORIGINS=true (shared variable) - enables CORS for all *.up.railway.app origins

Notes:

  • Each PR gets a fresh database - no existing users/trees
  • Migrations run automatically via releaseCommand
  • Domains must be generated manually for each PR service

Debug Endpoints (available in PR environments):

  • /debug/cors - Check CORS configuration (allow_railway_origins, cors_mode)

Railway CLI (Local)

The Railway CLI can be installed locally for managing deployments, viewing logs, and checking service status.

Setup on a new machine:

# 1. Install Railway CLI via npm (in project root)
cd C:\Dev\Projects\patherly
npm init -y
npm install @railway/cli

# 2. Login to Railway
./node_modules/.bin/railway login

# 3. Link to the project
./node_modules/.bin/railway link
# Select: selfless-grace → production

Common Commands:

# Check service status
./node_modules/.bin/railway service status --service patherly
./node_modules/.bin/railway service status --all

# View logs
./node_modules/.bin/railway service logs --service patherly

# Check environment variables
./node_modules/.bin/railway variables --service patherly

# Deploy new code (from backend directory)
cd backend && ../node_modules/.bin/railway up --service patherly

# Redeploy existing build
./node_modules/.bin/railway service redeploy --service patherly --yes

Services:

  • patherly - Backend API
  • hopeful-liberation - Frontend
  • Postgres - Database

Note: The node_modules/, package.json, and package-lock.json files for the Railway CLI are gitignored (local tooling only).


Contact

Primary User: Michael Chihlas Communication: GitHub Issues / Direct chat