# Deployment Guide — nexus-vector

**Project**: CA Grow Ops Manager  
**Target**: nexus-vector (100.95.3.92)  
**Created**: 2025-12-08  
**Job ID**: CAGROW-XXXXXX-GM (to be assigned)

---

## Overview

This guide explains how to deploy CA Grow Ops Manager to **nexus-vector** following the established infrastructure patterns.

---

## Prerequisites

### On Local Machine

- Git with SSH keys configured
- Access to nexus-vector via Tailscale or SSH
- Forgejo account with SSH key added

### On nexus-vector

- Docker and Docker Compose installed ✅
- PostgreSQL 15 available (via Docker)
- Redis available (via Docker)
- Port 8XXX available (to be assigned)

---

## Deployment Architecture

```
ca-grow-ops-manager/
├── backend/              # Fastify + Prisma API
├── frontend/             # Vite + React UI
└── docker-compose.yml    # Service orchestration

Deployed to:
/srv/containers/ca-grow-ops-manager/
├── docker-compose.yml
├── docker-compose.env
├── backend/
│   ├── Dockerfile
│   ├── src/
│   └── prisma/
├── frontend/
│   ├── Dockerfile
│   └── src/
└── README.md
```

---

## Step 1: Create Forgejo Repository

### 1.1 Create Repository via API

```bash
# From nexus-vector or local machine with Tailscale
curl -X POST "https://git.runfoo.run/api/v1/org/runfoo/repos" \
  -H "Authorization: token ffd8c9837f5cac1751ab6b402fea70e392548069" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ca-grow-ops-manager",
    "description": "Production-grade web + mobile app for managing licensed California cannabis cultivation facilities",
    "private": true,
    "auto_init": false
  }'
```

### 1.2 Push Local Code to Forgejo

```bash
# From local machine (current directory: ca-grow-ops-manager)
cd /Users/ten/ANTIGRAVITY/777wolfpack/ca-grow-ops-manager

# Initialize Git if not already done
git init
git add .
git commit -m "Initial commit: Spec Kit foundation complete

- Constitution and project spec
- 7 comprehensive feature specs
- Phase 1 implementation plan
- Week 1 task breakdown
- Architecture and compliance docs
- Backend and frontend READMEs"

# Add Forgejo remote (use Tailscale IP or localhost if on nexus-vector)
git remote add origin ssh://git@git.runfoo.run:2222/runfoo/ca-grow-ops-manager.git

# Push to Forgejo
git push -u origin main
```

**Note**: If SSH to git.runfoo.run:2222 is blocked, use HTTPS:

```bash
git remote add origin https://git.runfoo.run/runfoo/ca-grow-ops-manager.git
git push -u origin main
```

---

## Step 2: Assign Port Number

### 2.1 Check Available Ports

```bash
# SSH to nexus-vector
ssh admin@nexus-vector

# Check currently used ports
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep "8[0-9][0-9][0-9]"
```

**Current Port Assignments**:

- 8000: Syncthing Exporter & Ingestion API
- 8001: Location Pipeline API
- 8003: Paperless-NGX
- 8005: Pinwheel
- 8020: Awesome MCP Servers

**Assign**: **8010** for CA Grow Ops Manager

---

## Step 3: Create Service Directory

### 3.1 Create Directory Structure

```bash
# On nexus-vector
ssh admin@nexus-vector

# Create service directory
sudo mkdir -p /srv/containers/ca-grow-ops-manager
sudo chown admin:admin /srv/containers/ca-grow-ops-manager
cd /srv/containers/ca-grow-ops-manager
```

### 3.2 Clone Repository

```bash
# Clone from Forgejo (use localhost on nexus-vector)
git clone ssh://git@localhost:2222/runfoo/ca-grow-ops-manager.git .

# Or via HTTPS
git clone https://git.runfoo.run/runfoo/ca-grow-ops-manager.git .
```

---

## Step 4: Create Docker Compose Configuration

### 4.1 Create docker-compose.yml

```bash
cd /srv/containers/ca-grow-ops-manager
```

Create `docker-compose.yml`:

```yaml
version: '3.8'

services:
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ca_grow_ops
      POSTGRES_USER: ca_grow_ops
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ca_grow_ops"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://ca_grow_ops:${DB_PASSWORD}@db:5432/ca_grow_ops
      REDIS_URL: redis://redis:6379
      JWT_SECRET: ${JWT_SECRET}
      JWT_ACCESS_EXPIRY: 15m
      JWT_REFRESH_EXPIRY: 7d
      NODE_ENV: production
      PORT: 3000
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    environment:
      VITE_API_URL: /api
    ports:
      - "8010:80"
    depends_on:
      - backend
    restart: unless-stopped

volumes:
  db_data:
```

### 4.2 Create Environment File

Create `docker-compose.env`:

```bash
# Database
DB_PASSWORD=CHANGE_THIS_SECURE_PASSWORD

# JWT
JWT_SECRET=CHANGE_THIS_SECURE_SECRET_KEY

# Email (optional for v1)
EMAIL_SERVICE=sendgrid
EMAIL_API_KEY=your_api_key_here
EMAIL_FROM=noreply@example.com
```

**Security**: Generate secure passwords:

```bash
# Generate DB password
openssl rand -base64 32

# Generate JWT secret
openssl rand -base64 64
```

---

## Step 5: Create Dockerfiles

### 5.1 Backend Dockerfile

Create `backend/Dockerfile`:

```dockerfile
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY prisma ./prisma/

# Install dependencies
RUN npm ci --only=production

# Copy source
COPY . .

# Generate Prisma Client
RUN npx prisma generate

# Build TypeScript
RUN npm run build

# Production image
FROM node:20-alpine

WORKDIR /app

# Copy built files
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/package*.json ./

# Run as non-root
USER node

EXPOSE 3000

CMD ["node", "dist/server.js"]
```

### 5.2 Frontend Dockerfile

Create `frontend/Dockerfile`:

```dockerfile
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source
COPY . .

# Build for production
RUN npm run build

# Production image with Nginx
FROM nginx:alpine

# Copy built files
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy Nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
```

### 5.3 Frontend Nginx Config

Create `frontend/nginx.conf`:

```nginx
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # API proxy
    location /api {
        proxy_pass http://backend:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Frontend SPA routing
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
}
```

---

## Step 6: Deploy Service

### 6.1 Build and Start

```bash
cd /srv/containers/ca-grow-ops-manager

# Load environment variables
set -a
source docker-compose.env
set +a

# Build containers
docker-compose build

# Start services
docker-compose up -d

# Check status
docker-compose ps
```

### 6.2 Run Database Migrations

```bash
# Run Prisma migrations
docker-compose exec backend npx prisma migrate deploy

# Seed initial data (if seed script exists)
docker-compose exec backend npx prisma db seed
```

### 6.3 Verify Deployment

```bash
# Check all containers are running
docker-compose ps

# Check backend health
curl http://localhost:8010/api/healthz

# Check frontend
curl -I http://localhost:8010/

# View logs
docker-compose logs -f --tail=100
```

---

## Step 7: Configure Domain (Optional)

### 7.1 DNS Configuration

Point domain to nexus-vector:

```
ca-grow-ops.runfoo.run → 216.158.230.94:8010
```

### 7.2 Reverse Proxy (Traefik/Nginx)

If using Traefik or Nginx reverse proxy, configure routing to port 8010.

---

## Step 8: Create Service Documentation

### 8.1 Create README

Create `/srv/containers/ca-grow-ops-manager/DEPLOYMENT.md`:

```markdown
# CA Grow Ops Manager — Deployment

**Job ID**: CAGROW-XXXXXX-GM
**Port**: 8010
**Purpose**: Cannabis cultivation facility management platform

## Quick Start

\`\`\`bash
cd /srv/containers/ca-grow-ops-manager
docker-compose up -d
curl http://localhost:8010/api/healthz
\`\`\`

## Configuration

Environment variables in `docker-compose.env`:
- `DB_PASSWORD`: PostgreSQL password
- `JWT_SECRET`: JWT signing secret
- `EMAIL_API_KEY`: SendGrid API key (optional)

## API Endpoints

- `GET /api/healthz` - Health check
- `POST /api/auth/login` - User login
- `GET /api/batches` - List batches
- `GET /api/tasks` - List tasks

## Health Monitoring

\`\`\`bash
# Check service health
curl http://localhost:8010/api/healthz

# Check container status
docker-compose ps

# View logs
docker-compose logs -f
\`\`\`

## Database Migrations

\`\`\`bash
# Apply migrations
docker-compose exec backend npx prisma migrate deploy

# View migration status
docker-compose exec backend npx prisma migrate status
\`\`\`

## Troubleshooting

### Backend won't start
- Check database connection: `docker-compose logs db`
- Verify environment variables: `docker-compose config`

### Frontend 404 errors
- Check Nginx config: `docker-compose exec frontend cat /etc/nginx/conf.d/default.conf`
- Verify build: `docker-compose exec frontend ls /usr/share/nginx/html`
\`\`\`

---

## Monitoring and Maintenance

### Daily Checks

```bash
# Check service health
curl http://localhost:8010/api/healthz

# Check container status
docker-compose ps

# Check disk usage
docker system df
```

### Log Monitoring

```bash
# View all logs
docker-compose logs -f

# View specific service
docker-compose logs -f backend
docker-compose logs -f frontend
docker-compose logs -f db
```

### Backup Database

```bash
# Backup PostgreSQL
docker-compose exec db pg_dump -U ca_grow_ops ca_grow_ops > backup_$(date +%Y%m%d).sql

# Restore
docker-compose exec -T db psql -U ca_grow_ops ca_grow_ops < backup_20251208.sql
```

---

## Updating the Service

### Pull Latest Code

```bash
cd /srv/containers/ca-grow-ops-manager

# Pull from Forgejo
git pull origin main

# Rebuild and restart
docker-compose down
docker-compose build
docker-compose up -d

# Run migrations
docker-compose exec backend npx prisma migrate deploy
```

---

## Troubleshooting

### Port Already in Use

```bash
# Check what's using port 8010
sudo netstat -tlnp | grep 8010

# Or use lsof
sudo lsof -i :8010
```

### Database Connection Issues

```bash
# Check database is running
docker-compose ps db

# Check database logs
docker-compose logs db

# Test connection
docker-compose exec backend npx prisma db pull
```

### Container Won't Start

```bash
# Check logs
docker-compose logs backend

# Check resource usage
docker stats

# Rebuild from scratch
docker-compose down -v
docker-compose build --no-cache
docker-compose up -d
```

---

## Security Checklist

- [ ] Changed default DB_PASSWORD
- [ ] Changed default JWT_SECRET
- [ ] Configured firewall rules (UFW)
- [ ] Set up HTTPS/SSL (if public-facing)
- [ ] Configured backup schedule
- [ ] Set up monitoring alerts
- [ ] Reviewed container security (non-root user)

---

## Next Steps

1. **Complete Week 1 tasks** (infrastructure setup)
2. **Implement authentication** (Week 2)
3. **Build core features** (Week 3-5)
4. **Test and polish** (Week 6)
5. **Deploy to production**

---

**Created**: 2025-12-08  
**Last Updated**: 2025-12-08  
**Maintained By**: Development team
