Handoff: Backend-Vitto-API Railway Production Setup

Complete instructions for deploying the new standalone backend-vitto-api to Railway production

Handoff: Backend-Vitto-API Railway Production Setup (v2 — Updated)

From: Testing Director (Cowork, Session 99) To: Coding Agent (Cursor/Claude Code) Priority: HIGH — Railway project created but configured with WRONG backend-ai-api values Estimated Scope: ~8 files modified/created, 4 phases (Phase 2 already done by Paco)


Context

Session 98 rebuilt Vitto from a 2119-line god-class embedded in backend-ai-api into a standalone backend-vitto-api on port 3003. The app structure exists (~40 files, 7 modules, ~5,100 lines), with a Dockerfile and railway.json already created.

Paco has already completed Phase 2 (Railway project creation):

However, the railway.toml on the deploy/vitto-api branch was copied from backend-ai-api with ALL WRONG VALUES. The current deployment is stuck failing healthchecks because it's building the wrong app entirely.

Railway Project Details (Confirmed from Dashboard)

Field Value
Project ID 3990549d-4313-47e4-9682-d17b382bdba2
Environment ID 0d84c89d-6bc7-425c-8d17-14e0a5760041
Service ID 1acab221-cd3e-4c3f-9cbb-bcbc5feb7954
Service Name Vitto API
Domain vitto-api.gograb.space
Source Repo GoGrabSpace/editor
Source Branch deploy/vitto-api
Token Name vitto-api-token
Token Value be1eefc9-fae9-478e-830c-472ee9b99155
Dashboard https://railway.com/project/3990549d-4313-47e4-9682-d17b382bdba2

Current Production Railway Status (Tested 2026-02-07)

Service Domain Health Status
Frontend gograb.space ✅ Working Live
Backend AI API ai-api.gograb.space ✅ /ai-api/health returns 200 Live
Backend Docs API docs-api.gograb.space ✅ /docs-api/health returns 200 Live
Backend API api.gograb.space ⚠️ /api/health returns 404 Running but health endpoint missing
Backend Vitto API vitto-api.gograb.space ❌ Failing — wrong config This handoff

BLOCKER #1: railway.toml Has ALL Wrong Values (deploy/vitto-api branch)

The railway.toml at the repo root on the deploy/vitto-api branch was copied directly from backend-ai-api. Every service-specific value is wrong. This is why deployments are stuck on "Performing healthchecks..." indefinitely.

Current WRONG railway.toml (on deploy/vitto-api branch):

# ❌ THIS IS THE CURRENT BROKEN CONFIG — ALL VALUES FROM BACKEND-AI-API
[build]
builder = "DOCKERFILE"
dockerfilePath = "deployment/Dockerfile.railway.backend-ai"          # ❌ WRONG
dockerContext = "."
watchPatterns = [
  "apis/backend-ai-api/**",                                          # ❌ WRONG
  "apps/claude-agent-mcp/**",                                        # ❌ WRONG (vitto doesn't use this)
  "libs/libs-api/backend-core/**",                                   # ✅ OK (vitto imports this)
  "libs/libs-api/auth-core/**",                                      # ❌ WRONG (vitto doesn't import this)
  "libs/libs-api/llm-integration/**",                                # ❌ WRONG (vitto doesn't import this)
  "libs/libs-foundational/core-models/**",                           # ⚠️ Optional (keep for safety)
  "deployment/Dockerfile.railway.backend-ai",                        # ❌ WRONG
  "package.json",                                                     # ✅ OK
  "yarn.lock",                                                        # ✅ OK
  "tsconfig.base.json",                                              # ✅ OK
  "nx.json"                                                          # ✅ OK
]

[deploy]
startCommand = "node dist/apis/backend-ai-api/main.js"               # ❌ WRONG
healthcheckPath = "/ai-api/health"                                   # ❌ WRONG
healthcheckTimeout = 300                                              # ✅ OK
restartPolicyType = "ON_FAILURE"                                     # ✅ OK
restartPolicyMaxRetries = 10                                          # ✅ OK

Required CORRECT railway.toml (REPLACE on deploy/vitto-api branch):

# Railway Configuration for backend-vitto-api
# Branch: deploy/vitto-api
# Session 99: Standalone Vitto Super-Agent API

[build]
builder = "DOCKERFILE"
dockerfilePath = "deployment/Dockerfile.railway.backend-vitto"
dockerContext = "."

# Watch patterns — only files that affect backend-vitto-api builds
watchPatterns = [
  # Main application
  "apis/backend-vitto-api/**",

  # Shared API libraries (actual imports from grep analysis)
  "libs/libs-api/backend-core/**",

  # Foundational libraries (transitive dependency)
  "libs/libs-foundational/core-models/**",

  # Build configuration
  "deployment/Dockerfile.railway.backend-vitto",
  "package.json",
  "yarn.lock",
  "tsconfig.base.json",
  "nx.json"
]

[deploy]
startCommand = "node dist/apis/backend-vitto-api/main.js"
healthcheckPath = "/vitto-api/health"
healthcheckTimeout = 300
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10

Exact Changes Summary (railway.toml):

Setting WRONG (current) CORRECT (required)
dockerfilePath deployment/Dockerfile.railway.backend-ai deployment/Dockerfile.railway.backend-vitto
startCommand node dist/apis/backend-ai-api/main.js node dist/apis/backend-vitto-api/main.js
healthcheckPath /ai-api/health /vitto-api/health
watchPatterns[0] apis/backend-ai-api/** apis/backend-vitto-api/**
watchPatterns[1] apps/claude-agent-mcp/** REMOVE
watchPatterns[3] libs/libs-api/auth-core/** REMOVE
watchPatterns[4] libs/libs-api/llm-integration/** REMOVE
watchPatterns[6] deployment/Dockerfile.railway.backend-ai deployment/Dockerfile.railway.backend-vitto

BLOCKER #2: main.ts Missing Railway PORT Detection

File: apis/backend-vitto-api/src/main.ts

Even after fixing the railway.toml, the app itself won't bind to the correct port.

Current (BROKEN for Railway):

// Line ~30
const isProduction = process.env.NODE_ENV === 'production';

// Line ~59
const port = process.env.VITTO_API_PORT || 3003;

Required (follow backend-ai-api pattern from lines 37-39, 74-76):

// Determine if Railway (check multiple indicators)
const isRailway = process.env.RAILWAY_ENVIRONMENT === 'true' ||
                  process.env.RAILWAY_STATIC_URL !== undefined ||
                  process.env.RAILWAY_SERVICE_NAME !== undefined;

// Reduce logging in production to avoid Railway rate limits
const isProduction = process.env.NODE_ENV === 'production' || isRailway;

// ... later in bootstrap() ...

// Railway sets PORT dynamically (usually 8080). Locally use VITTO_API_PORT.
const port = isRailway
  ? (process.env.PORT || 3003)
  : (process.env.VITTO_API_PORT || 3003);

Why this matters: Railway assigns a dynamic PORT (typically 8080). Without reading process.env.PORT, the app binds to 3003 while Railway health checks hit 8080. Deployment will fail every time.

Reference: Compare apis/backend-ai-api/src/main.ts lines 37-39, 74-76.


BLOCKER #3: Environment Variables — Wrong + Missing

Dashboard Audit (Variables Tab — 10 Service Variables Currently Set):

Variable Currently Set Status Action
MONGODB_CONNECTION_STRING ✅ Set ❌ WRONG NAME — Vitto reads VITTO_MONGODB_URI Rename or add VITTO_MONGODB_URI
MONGODB_DATABASE ✅ Set ❌ NOT USED — Vitto embeds DB name in URI Remove (optional)
OPENAI_API_KEY ✅ Set ⚠️ Optional Keep if multi-provider planned
ANTHROPIC_API_KEY ✅ Set ✅ CORRECT Keep — needed for Claude LLM
GOOGLE_API_KEY ✅ Set ❌ NOT USED by Vitto Remove (optional)
JWT_SECRET ✅ Set ✅ CORRECT — must match other services Keep — verify matches
JWT_EXPIRATION ✅ Set ⚠️ Optional Keep (harmless)
NODE_ENV ✅ Set ✅ CORRECT — should be "production" Keep
ALLOWED_ORIGINS ✅ Set ✅ CORRECT — verify includes vitto-api.gograb.space Verify value
PORT ✅ Set ⚠️ Railway auto-sets this Keep (no harm)

Missing Variables (MUST ADD):

Variable Value Why
RAILWAY_ENVIRONMENT true CRITICAL — Required for isRailway detection in main.ts
VITTO_MONGODB_URI mongodb+srv://<user>:<pass>@<cluster>/vitto-mind?retryWrites=true&w=majority CRITICAL — The env var Vitto's app.module.ts actually reads (VITTO_MONGODB_URI, NOT MONGODB_CONNECTION_STRING)
SYSTEM_TOKEN <MUST MATCH OTHER SERVICES> Inter-service authentication
VITTO_LLM_PROVIDER claude Default LLM provider
VITTO_LLM_MODEL claude-sonnet-4-20250514 Default model
VITTO_MAX_CONCURRENT 3 Task concurrency limit
VITTO_MAX_ITERATIONS 25 Agent loop limit
VITTO_MAX_TOKENS 100000 Token budget per task
VITTO_TASK_TIMEOUT_MS 300000 5min task timeout
BACKEND_API_URL https://api.gograb.space HTTP client for cross-service calls
BACKEND_AI_API_URL https://ai-api.gograb.space HTTP client for cross-service calls
BACKEND_DOCS_API_URL https://docs-api.gograb.space HTTP client for cross-service calls

ALLOWED_ORIGINS — Full Value (verify in dashboard):

https://gograb.space,https://vitto-api.gograb.space,https://api.gograb.space,https://ai-api.gograb.space,https://docs-api.gograb.space,http://localhost:5200,http://localhost:5100,http://localhost:5400,http://localhost:3000,http://localhost:3001,http://localhost:3002,http://localhost:3003

Additional Issues (Non-Blocking but Important)

Issue 4: CORS additionalOrigins in main.ts

File: apis/backend-vitto-api/src/main.ts (lines 46-53)

Currently only has localhost origins in additionalOrigins. The createCorsConfig utility reads ALLOWED_ORIGINS env var which handles production, but consider adding production URLs as fallback like backend-ai-api does.

Issue 5: No .railway/projects.json Entry

File: .railway/projects.json

Add this entry to the projects object:

"backend-vitto": {
  "id": "3990549d-4313-47e4-9682-d17b382bdba2",
  "environmentId": "0d84c89d-6bc7-425c-8d17-14e0a5760041",
  "name": "GoGrab Backend Vitto API",
  "description": "Vitto super-agent API (orchestration, mind, tools)",
  "type": "backend-vitto",
  "apps": ["backend-vitto-api"],
  "dockerfile": "deployment/Dockerfile.railway.backend-vitto",
  "startCommand": "node dist/apis/backend-vitto-api/main.js",
  "healthCheckPath": "/vitto-api/health",
  "environment": {
    "NODE_ENV": "production",
    "RAILWAY_ENVIRONMENT": "true",
    "PORT": "${PORT}",
    "VITTO_MONGODB_URI": "${VITTO_MONGODB_URI}",
    "JWT_SECRET": "${JWT_SECRET}",
    "ALLOWED_ORIGINS": "${ALLOWED_ORIGINS}",
    "ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
    "SYSTEM_TOKEN": "${SYSTEM_TOKEN}"
  },
  "dashboardUrl": "https://railway.com/project/3990549d-4313-47e4-9682-d17b382bdba2?environmentId=0d84c89d-6bc7-425c-8d17-14e0a5760041"
}

Issue 6: No Deploy Script

Create: deployment/deploy-vitto-api.sh

Copy from deployment/deploy-backend-ai.sh and change:

Issue 7: Missing Reference Config

Create: deployment/railway.vitto.toml

# Railway Backend Vitto API Project Configuration
# Project ID: 3990549d-4313-47e4-9682-d17b382bdba2
# Environment: 0d84c89d-6bc7-425c-8d17-14e0a5760041
# Service: backend-vitto-api (Vitto Super-Agent)

[build]
builder = "DOCKERFILE"
dockerfilePath = "deployment/Dockerfile.railway.backend-vitto"
dockerContext = "."

[deploy]
startCommand = "node dist/apis/backend-vitto-api/main.js"
healthcheckPath = "/vitto-api/health"
healthcheckTimeout = 300
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10

Issue 8: Missing Package.json Deploy Commands

File: package.json

Add these scripts:

"railway:deploy:vitto": "./deployment/deploy-vitto-api.sh",
"railway:build:vitto": "npx nx build backend-vitto-api --configuration=production",

Update railway:deploy:all to include Vitto if it exists.

Issue 9: .env.local — Add Vitto Token

Add to .env.local:

RAILWAY_VITTO_TOKEN=be1eefc9-fae9-478e-830c-472ee9b99155

Issue 10 (LOW): Dockerfile HEALTHCHECK Default Port

File: deployment/Dockerfile.railway.backend-vitto (line ~82)

Change ${PORT:-3003} to ${PORT:-8080} in HEALTHCHECK. Cosmetic since Railway always sets PORT, but aligns with expected behavior.


Implementation Plan (Updated — Phase 2 Already Done)

Phase 1: Fix railway.toml on deploy/vitto-api branch (CRITICAL — DO FIRST)

# 1. Switch to the deploy branch
git checkout deploy/vitto-api

# 2. Merge latest main
git merge main --no-edit

# 3. Replace railway.toml at repo root with the CORRECT config (see BLOCKER #1 above)
# The file must be at the repo root: ./railway.toml

# 4. Commit and push
git add railway.toml
git commit -m "fix(deploy): correct railway.toml for vitto-api (was using backend-ai-api values)"
git push

This push will automatically trigger a new Railway deployment with the correct config.

Phase 2: Fix main.ts (CRITICAL — Do Immediately After)

File: apis/backend-vitto-api/src/main.ts

  1. Add isRailway detection at the top of bootstrap()
  2. Update isProduction to include isRailway
  3. Change port binding to use process.env.PORT on Railway
  4. Commit to main, then merge into deploy/vitto-api branch

Verification: Build locally with npx nx build backend-vitto-api --configuration=production and test with:

RAILWAY_ENVIRONMENT=true PORT=8080 node dist/apis/backend-vitto-api/main.js

Should log: 🤖 VITTO Super-Agent API READY on port 8080

Phase 3: Create Deploy Infrastructure (on main branch)

  1. Add entry to .railway/projects.json (see Issue 5 above)
  2. Create deployment/deploy-vitto-api.sh (see Issue 6 above)
  3. Create deployment/railway.vitto.toml (see Issue 7 above)
  4. Update package.json with deploy/build commands (see Issue 8 above)
  5. Add token to .env.local (see Issue 9 above)
  6. Merge main into deploy/vitto-api after all changes

Phase 4: Configure Railway Environment Variables (Dashboard — Paco or Agent)

Add the missing variables listed in BLOCKER #3 above. The most critical ones are:


Files to Create/Modify Summary

# File Action Priority Branch
1 railway.toml (repo root) REPLACE — Fix all wrong backend-ai-api values CRITICAL deploy/vitto-api
2 apis/backend-vitto-api/src/main.ts Modify — Add isRailway + PORT detection CRITICAL main → merge to deploy/vitto-api
3 .railway/projects.json Modify — Add backend-vitto entry HIGH main
4 deployment/deploy-vitto-api.sh Create — Deploy script HIGH main
5 deployment/railway.vitto.toml Create — Reference config MEDIUM main
6 package.json Modify — Add deploy/build commands HIGH main
7 .env.local Modify — Add RAILWAY_VITTO_TOKEN HIGH local only
8 deployment/Dockerfile.railway.backend-vitto Modify — Fix HEALTHCHECK default port LOW main

Verification Steps (Testing Director Will Verify)

After deployment, I will:

  1. Health check: curl https://vitto-api.gograb.space/vitto-api/health — expect 200 OK
  2. Cross-service auth: Get a JWT from backend-api login, send to vitto-api — should authenticate
  3. CORS: Make fetch from gograb.space to vitto-api — should not be blocked
  4. Mind DB: Check MongoDB Atlas for vitto-mind database with collections
  5. Logs: Check Railway dashboard for clean startup (no rate limit warnings)
  6. Startup message: Logs should show 🤖 VITTO Super-Agent API READY on port <dynamic>

Side Finding: Backend API Health Endpoint Missing

Unrelated to Vitto but discovered during audit:

https://api.gograb.space/api/health returns 404. The NestJS app IS running (it returns {"message":"Cannot GET /api/health","error":"Not Found","statusCode":404}) but the health controller isn't registered or was removed.

Action: Check apis/backend-api/src/app/ for a health controller. Low priority — separate task.


Port Reference

Service Local Port Railway Port Health Endpoint Deploy Branch Domain
backend-api 3000 Dynamic /api/health deploy/api api.gograb.space
backend-ai-api 3001 Dynamic /ai-api/health deploy/ai-api ai-api.gograb.space
backend-docs-api 3002 Dynamic /docs-api/health deploy/docs-api docs-api.gograb.space
backend-vitto-api 3003 Dynamic /vitto-api/health deploy/vitto-api vitto-api.gograb.space