# Forge Server Deployment Guide

Deploy Forge as a web application on a Linux server. Users access it through a browser — no Electron desktop app needed.

## Quick Start

```bash
# 1. Build (on your dev machine or CI)
pnpm build:server

# 2. Upload to server
scp -r dist-server/ deploy@your-server:/opt/forge/

# 3. Run on server
ssh deploy@your-server
cd /opt/forge
PORT=3000 HOSTNAME=0.0.0.0 node server.js
```

Open `http://your-server:3000` in a browser.

---

## Architecture

```
Browser ──→ Nginx (optional) ──→ Node.js (Next.js standalone)
                                        │
                                        ├── /api/*          (REST API routes)
                                        ├── /api/fs/browse   (server directory browser)
                                        ├── /api/fs/defaults (default paths info)
                                        └── SQLite (better-sqlite3)
                                              │
                                        ~/.forge/forge.db
```

The `ServerFolderPicker` component lets users browse server-side directories through the browser — no Electron `dialog.showOpenDialog` needed.

---

## Build Output

`pnpm build:server` produces `dist-server/`:

```
dist-server/
├── server.js           ← Next.js standalone entry point
├── .next/              ← Compiled routes + static assets
│   └── static/         ← JS/CSS chunks
├── public/             ← Static files (mascot.png, etc.)
├── templates/          ← Workspace initialization templates
├── node_modules/       ← Production dependencies (incl. better-sqlite3 .node)
├── package.json        ← Minimal metadata
├── .env.example        ← Environment variable reference
└── deploy/
    ├── forge.service      ← systemd unit file
    ├── Dockerfile         ← Docker image definition
    ├── docker-compose.yml ← Docker Compose config
    └── nginx.conf         ← Nginx reverse proxy config
```

---

## Environment Variables

| Variable | Default | Description |
|---|---|---|
| `PORT` | `3000` | HTTP listen port |
| `HOSTNAME` | `localhost` | Bind address. Use `0.0.0.0` for remote access |
| `NODE_ENV` | — | Set to `production` |
| `FORGE_DATA_DIR` | `~/.forge` | Database + uploads directory |
| `FORGE_WORKSPACES_ROOT` | `~/workspaces` | Default workspace root (folder picker opens here) |

Create `.env.local` in the deploy directory (or set in systemd/Docker):

```bash
PORT=3000
HOSTNAME=0.0.0.0
NODE_ENV=production
FORGE_DATA_DIR=/opt/forge-data
FORGE_WORKSPACES_ROOT=/opt/forge/workspaces
```

---

## Method 1: Direct Node.js

### Prerequisites

- Node.js 22+ installed on the server
- Linux (Ubuntu 22.04+ / Debian 12+ recommended)

### Steps

```bash
# Upload build
scp -r dist-server/ deploy@server:/opt/forge/

# Create data directories
ssh deploy@server
sudo mkdir -p /opt/forge-data /opt/forge/workspaces
sudo chown -R deploy:deploy /opt/forge-data /opt/forge/workspaces

# Test run
cd /opt/forge
PORT=3000 HOSTNAME=0.0.0.0 \
  FORGE_DATA_DIR=/opt/forge-data \
  FORGE_WORKSPACES_ROOT=/opt/forge/workspaces \
  node server.js
```

### systemd Service (recommended)

```bash
# Copy the provided unit file
sudo cp /opt/forge/deploy/forge.service /etc/systemd/system/

# Edit user/path if needed
sudo vim /etc/systemd/system/forge.service

# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now forge

# Check status
systemctl status forge

# View logs
journalctl -u forge -f
```

---

## Method 2: Docker

### Quick Run

```bash
cd /opt/forge
docker compose -f deploy/docker-compose.yml up -d

# View logs
docker compose -f deploy/docker-compose.yml logs -f
```

### Custom Build on Server

If you prefer building the Docker image on the server (avoids uploading the full `dist-server/`):

```bash
# Clone repo on server
git clone https://github.com/feicaiclub/forge.git /opt/forge-src
cd /opt/forge-src

# Build server bundle + Docker image
pnpm install
pnpm build:server
docker compose -f dist-server/deploy/docker-compose.yml up -d --build
```

### Volume Mapping

The Docker Compose config uses named volumes. To map host directories instead:

```yaml
volumes:
  - /data/forge-db:/opt/forge-data       # Database persistence
  - /data/forge-workspaces:/opt/forge/workspaces  # Project files
```

---

## Method 3: Nginx Reverse Proxy

For production with HTTPS and domain names:

```bash
# Copy nginx config
sudo cp /opt/forge/deploy/nginx.conf /etc/nginx/sites-available/forge
sudo ln -s /etc/nginx/sites-available/forge /etc/nginx/sites-enabled/

# Edit server_name
sudo vim /etc/nginx/sites-available/forge

# Test and reload
sudo nginx -t
sudo systemctl reload nginx

# HTTPS with Let's Encrypt
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d forge.example.com
```

Key Nginx settings for Forge:
- `proxy_buffering off` — required for SSE streaming
- `proxy_read_timeout 86400s` — SSE connections are long-lived
- `proxy_http_version 1.1` — required for SSE

---

## Directory Structure on Server

```
/opt/forge/                    ← Application (dist-server/ contents)
├── server.js
├── .next/
├── public/
├── templates/
├── node_modules/
└── deploy/

/opt/forge-data/               ← FORGE_DATA_DIR
├── forge.db                   ← SQLite database
├── forge.db-shm
├── forge.db-wal
└── uploads/                   ← User uploaded files

/opt/forge/workspaces/         ← FORGE_WORKSPACES_ROOT
├── README.md                  ← Auto-generated on first browse
├── backend-api/               ← User project 1
│   └── .claude/
├── frontend-web/              ← User project 2
│   └── .claude/
└── scripts/                   ← User project 3
    └── .claude/
```

---

## Security Considerations

1. **Directory browsing scope** — The `/api/fs/browse` API only allows paths under:
    - `FORGE_WORKSPACES_ROOT` (always allowed)
    - User home directory
    - `/opt`, `/srv`, `/data`, `/home`, `/var/www`, `/tmp`
    - Edit `ALLOWED_PREFIXES` in `src/app/api/fs/browse/route.ts` to customize

2. **Authentication** — Currently no auth. For production:
    - Put behind a VPN or private network
    - Add HTTP Basic Auth via Nginx: `auth_basic` directive
    - Or add application-level auth (future enhancement)

3. **File system permissions** — Run the process as a dedicated user (`forge`), not root

4. **SQLite** — Single-writer; only one Forge instance should write to `forge.db`

5. **Firewall** — Restrict port 3000 to Nginx/local only if using reverse proxy

---

## Updating

```bash
# Build new version
pnpm build:server

# Upload (overwrite existing)
scp -r dist-server/* deploy@server:/opt/forge/

# Restart
ssh deploy@server sudo systemctl restart forge
```

---

## Troubleshooting

| Problem | Solution |
|---|---|
| `better-sqlite3` native module error | Ensure Node.js version matches build machine, or rebuild: `npm rebuild better-sqlite3` |
| SSE connections drop | Nginx: increase `proxy_read_timeout`; check if Cloudflare has timeout limits |
| Cannot browse directories | Check `FORGE_WORKSPACES_ROOT` exists and process user has read permission |
| Database locked | Only one process should write to `forge.db`; stop duplicate instances |
| `EACCES` permission denied | Check directory ownership: `chown -R forge:forge /opt/forge-data /opt/forge/workspaces` |
