You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

14 KiB

Ghostfolio AI Agent — Setup Guide

For partner setup. Copy this, follow steps, run locally + VPS.


Quick Decision Tree (READ THIS FIRST!)

Before starting, check what's running:

docker ps | grep postgres

If you see gf-postgres-dev:

  • You have existing containers with data
  • → Skip to "Option A: Use Existing Containers"
  • → No need for docker-compose
  • → Fast start, your data is already there

If you see nothing (or only ghostfolio-db):

  • You need fresh containers
  • → Follow "Option B: Fresh Setup" below
  • → One-time setup, then data persists

This prevents:

  • Long container spin-ups
  • Losing data by switching databases
  • Needing to sign up repeatedly

One-Shot Quick Start

After cloning and editing .env:

# 1. Install dependencies
pnpm install

# 2. Start services (PostgreSQL + Redis)
docker-compose up -d

# 3. Run database migrations
pnpm nx run api:prisma:migrate

# 4. Start server
pnpm start:server

# 5. In another terminal, create account and get token:
# Open http://localhost:4200, sign up, then:
export GHOSTFOLIO_TOKEN="paste-token-from-browser-devtools"

# 6. Test AI endpoint
curl -X POST http://localhost:3333/api/v1/ai/chat \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $GHOSTFOLIO_TOKEN" \
  -d '{"query": "Show my portfolio", "sessionId": "test"}'

Important: Two Container Options

READ THIS FIRST — You may have existing Ghostfolio containers running.

Check what's running:

docker ps | grep postgres

If you see gf-postgres-dev:

  • You have OLD containers with your data
  • Skip to "Option A: Use Existing Containers" below

If you see no postgres containers:

  • Use "Option B: Fresh Setup with docker-compose"

Option A: Use Existing Containers (If Already Running)

IF you already have gf-postgres-dev and gf-redis-dev running:

# Don't run docker-compose up -d
# Just start the app
pnpm start

# Your existing account and data should work

Why: Your old containers already have your user account and holdings.


Option B: Fresh Setup with docker-compose

IF you want a fresh start or don't have containers yet:

Follow the steps below.


Local Setup (5 min)

1. Clone & Install

# Clone repo
git clone https://github.com/ghostfolio/ghostfolio.git
cd ghostfolio

# Install dependencies
pnpm install

2. Environment Variables

Create .env file in root:

# Database
DATABASE_URL="postgresql://ghostfolio:password@localhost:5432/ghostfolio"

# Redis (for AI agent memory)
REDIS_HOST=localhost
REDIS_PORT=6379

# OpenRouter (AI LLM provider)
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODEL=anthropic/claude-3.5-sonnet

# JWT Secrets (generate random strings)
ACCESS_TOKEN_SALT=your-random-salt-string-here
JWT_SECRET_KEY=your-random-jwt-secret-here

# Optional: Supabase (if using)
SUPABASE_URL=your-supabase-url
SUPABASE_ANON_KEY=your-anon-key

Generate random secrets:

# Generate ACCESS_TOKEN_SALT
openssl rand -hex 32

# Generate JWT_SECRET_KEY
openssl rand -hex 32

3. Start Docker Services

# Start PostgreSQL + Redis
docker-compose up -d

# Or individual containers:
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=password -e POSTGRES_USER=ghostfolio -e POSTGRES_DB=ghostfolio postgres:16
docker run -d -p 6379:6379 redis:alpine

4. Get Authentication Token

The AI endpoint requires a JWT token. Get it by:

Option A: Web UI (Recommended)

  1. Open http://localhost:4200 in browser
  2. Sign up for a new account
  3. Open DevTools → Application → Local Storage
  4. Copy the accessToken value

Option B: API Call

# Sign up and get token
curl -X POST http://localhost:3333/api/v1/auth/anonymous \
  -H "Content-Type: application/json" \
  -d '{"accessToken": "any-string"}'

Save this token as GHOSTFOLIO_TOKEN in your shell:

export GHOSTFOLIO_TOKEN="your-jwt-token-here"

5. Run Project

# Start API server
pnpm start:server

# Or run all services
pnpm start

6. Test AI Agent

# Run AI tests
pnpm test:ai

# Run MVP evals
pnpm test:mvp-eval

VPS Setup (Hostinger) — External Services

What Goes on VPS

  • Redis — AI agent session memory
  • PostgreSQL — Optional (can use local)
  • LangSmith — Observability (optional, for tracing)

Hostinger VPS Steps

1. SSH into VPS

ssh root@your-vps-ip

2. Install Docker

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

3. Deploy Redis

docker run -d \
  --name ghostfolio-redis \
  -p 6379:6379 \
  redis:alpine

4. Deploy PostgreSQL (Optional)

docker run -d \
  --name ghostfolio-db \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=your-secure-password \
  -e POSTGRES_USER=ghostfolio \
  -e POSTGRES_DB=ghostfolio \
  postgres:16

5. Firewall Rules

# Allow Redis (restrict to your IP)
ufw allow from YOUR_IP_ADDRESS to any port 6379

# Allow PostgreSQL (restrict to your IP)
ufw allow from YOUR_IP_ADDRESS to any port 5432

Update Local .env for VPS

# Use VPS services
REDIS_HOST=your-vps-ip
REDIS_PORT=6379

DATABASE_URL="postgresql://ghostfolio:your-secure-password@your-vps-ip:5432/ghostfolio"

# Keep local
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODEL=anthropic/claude-3.5-sonnet

Run AI Agent Locally

Start Services

# Terminal 1: Docker services (if using local)
docker-compose up -d

# Terminal 2: API server
pnpm start:server

Test Chat Endpoint

# Using env variable (after export GHOSTFOLIO_TOKEN)
curl -X POST http://localhost:3333/api/v1/ai/chat \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $GHOSTFOLIO_TOKEN" \
  -d '{
    "query": "Analyze my portfolio risk",
    "sessionId": "test-session-1"
  }'

# Or paste token directly
curl -X POST http://localhost:3333/api/v1/ai/chat \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "query": "What is my portfolio allocation?",
    "sessionId": "test-session-2"
  }'

Docker Compose (All-in-One)

Save as docker-compose.yml:

version: '3.8'

services:
  postgres:
    image: postgres:16
    container_name: ghostfolio-db
    environment:
      POSTGRES_USER: ghostfolio
      POSTGRES_PASSWORD: password
      POSTGRES_DB: ghostfolio
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data

  redis:
    image: redis:alpine
    container_name: ghostfolio-redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  postgres-data:
  redis-data:

Run:

docker-compose up -d

Troubleshooting

Redis Connection Failed

# Check if Redis is running
docker ps | grep redis

# View logs
docker logs ghostfolio-redis

# Test connection
redis-cli -h localhost ping

Database Migration Failed

# Run migrations manually
pnpm nx run api:prisma:migrate

API Key Errors

# Verify OpenRouter key
curl https://openrouter.ai/api/v1/auth/key \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

Project Structure (AI Agent)

apps/api/src/app/endpoints/ai/
├── ai.controller.ts           # POST /chat endpoint
├── ai.service.ts              # Main orchestrator
├── ai-agent.chat.helpers.ts   # Tool runners
├── ai-agent.utils.ts          # Tool planning
├── ai-chat.dto.ts             # Request validation
├── evals/                     # Evaluation framework
└── *.spec.ts                  # Tests

Quick Commands Reference

# Install
pnpm install

# Start services
docker-compose up -d

# Run API
pnpm start:server

# Run tests
pnpm test:ai
pnpm test:mvp-eval

# Stop services
docker-compose down

Seed Money Runbook (Local / VPS / Railway)

Use this section to add portfolio activities quickly for demos and AI testing. If activities exist but cash shows 0.00, add account balance snapshots (Ghostfolio reads cash from AccountBalance).

Local

# 1) Seed baseline AI MVP dataset
npm run database:seed:ai-mvp

# 2) Add extra money/orders dataset (idempotent)
npx dotenv-cli -e .env -- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f tools/seed/seed-money.sql

VPS

# Run from project root on the VPS with env loaded
npm run database:migrate
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f tools/seed/seed-money.sql

Railway

# Link project/service once
railway link
railway service link ghostfolio-api

# Seed money dataset into Railway Postgres
tools/railway/seed-money.sh

# Optional health check after seeding
curl -sS https://ghostfolio-api-production.up.railway.app/api/v1/health

Notes:

  • tools/seed/seed-money.sql is idempotent and uses railway-seed:* markers.
  • tools/railway/seed-money.sh uploads SQL and executes it inside the Railway postgres service.
  • Railway Redis default often uses no password auth. Keep REDIS_PASSWORD empty on ghostfolio-api unless Redis auth is enabled.

No Repo Access: Copy/Paste Cash Top-Up SQL

Use this when only CLI/DB access is available.

WITH target_balances AS (
  SELECT
    a."id" AS account_id,
    a."userId" AS user_id,
    CASE
      WHEN a."name" = 'MVP Portfolio' THEN 10000::double precision
      WHEN a."name" = 'Income Portfolio' THEN 5000::double precision
      WHEN a."name" = 'My Account' THEN 2000::double precision
      ELSE NULL
    END AS value
  FROM "Account" a
  WHERE a."name" IN ('MVP Portfolio', 'Income Portfolio', 'My Account')
)
INSERT INTO "AccountBalance" ("id", "accountId", "userId", "date", "value", "createdAt", "updatedAt")
SELECT
  gen_random_uuid()::text,
  t.account_id,
  t.user_id,
  CURRENT_DATE,
  t.value,
  now(),
  now()
FROM target_balances t
WHERE t.value IS NOT NULL
ON CONFLICT ("accountId", "date")
DO UPDATE SET
  "value" = EXCLUDED."value",
  "updatedAt" = now();

Railway one-liner with inline SQL:

railway ssh -s postgres -- sh -lc 'cat >/tmp/topup.sql <<'"'"'"'"'"'"'"'"'SQL'"'"'"'"'"'"'"'"'
WITH target_balances AS (
  SELECT
    a."id" AS account_id,
    a."userId" AS user_id,
    CASE
      WHEN a."name" = $$MVP Portfolio$$ THEN 10000::double precision
      WHEN a."name" = $$Income Portfolio$$ THEN 5000::double precision
      WHEN a."name" = $$My Account$$ THEN 2000::double precision
      ELSE NULL
    END AS value
  FROM "Account" a
  WHERE a."name" IN ($$MVP Portfolio$$, $$Income Portfolio$$, $$My Account$$)
)
INSERT INTO "AccountBalance" ("id", "accountId", "userId", "date", "value", "createdAt", "updatedAt")
SELECT gen_random_uuid()::text, t.account_id, t.user_id, CURRENT_DATE, t.value, now(), now()
FROM target_balances t
WHERE t.value IS NOT NULL
ON CONFLICT ("accountId", "date")
DO UPDATE SET "value" = EXCLUDED."value", "updatedAt" = now();
SQL
psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f /tmp/topup.sql'

Next Steps

  1. Set up local environment
  2. Run pnpm test:ai to verify
  3. Deploy to Railway (5 min) or Hostinger VPS (1-2 hours)
  4. 🔄 See docs/DEPLOYMENT.md for full deployment guide
  5. 🔄 Update MVP-VERIFICATION.md with deployed URL

Why Do I Need To Sign Up Each Time?

Problem: If you keep needing to sign up, you're switching between databases.

Cause: You have TWO sets of possible containers:

Old Containers New Containers (docker-compose.yml)
gf-postgres-dev ghostfolio-db
gf-redis-dev ghostfolio-redis

Each has its own database. When you switch between them, you get a fresh database.

Solution: Pick ONE and use it consistently.

Option A: Keep using old containers

# Don't run docker-compose
# Just:
pnpm start

Option B: Switch to new containers

# Stop old ones
docker stop gf-postgres-dev gf-redis-dev

# Start new ones
docker-compose up -d

# Migrate
pnpm nx run api:prisma:migrate

# Create account ONCE
# Data persists from now on

Data Persistence:

  • User accounts persist in Docker volumes
  • Holdings persist
  • No need to re-sign up if using same containers

For full details: See docs/DATA-PERSISTENCE.md


Deployment

Quick options:

Platform Time Cost Guide
Railway 5 min Free tier railway.toml included
Hostinger VPS 1-2 hours Already paid See docs/DEPLOYMENT.md

Railway quick start:

# 1. Push to GitHub
git add . && git commit -m "Ready for Railway" && git push

# 2. Go to https://railway.app/new → Connect GitHub repo

# 3. Add env vars in Railway dashboard:
#    API_KEY_OPENROUTER=sk-or-v1-...
#    OPENROUTER_MODEL=anthropic/claude-3.5-sonnet
#    JWT_SECRET_KEY=(openssl rand -hex 32)
#    ACCESS_TOKEN_SALT=(openssl rand -hex 32)
#    REDIS_PASSWORD=(leave empty unless Redis auth is enabled)

# 4. Deploy → Get URL like:
#    https://your-app.up.railway.app

Full deployment guide: docs/DEPLOYMENT.md


Speed Up Docker Builds

Use these commands for faster iteration loops:

# 1) Build with BuildKit enabled
DOCKER_BUILDKIT=1 docker build -t ghostfolio:dev .

# 2) Warm dependency layer first (runs fast when package-lock.json is unchanged)
docker build --target builder -t ghostfolio:builder-cache .

# 3) Deploy in detached mode on Railway to keep terminal free
railway up --detach --service ghostfolio-api

# 4) Build with explicit local cache reuse
docker buildx build \
  --cache-from type=local,src=.buildx-cache \
  --cache-to type=local,dest=.buildx-cache-new,mode=max \
  -t ghostfolio:dev .
mv .buildx-cache-new .buildx-cache

High-impact optimization path:

  • Keep package-lock.json stable to maximize Docker cache hits.
  • Group dependency changes into fewer commits.
  • Use prebuilt image deployment for Railway when push frequency is high.

Questions?