# Production Deployment Guide — HenKaiPan ASPM

This guide covers deploying HenKaiPan ASPM in production environments with Docker Compose. For Kubernetes deployments, see [Kubernetes Deployment Guide](kubernetes.md).

## Architecture Overview

```
                         ┌──────────────┐
                         │   Reverse     │
                         │   Proxy       │
                         │  (nginx/      │
                         │   caddy)      │
                         └──┬─────────┬──┘
                            │         │
                      :443  │   :9090 │
                      HTTPS │   metrics
                     ┌──────▼──┐ ┌────▼─────┐
                     │  API    │ │ Prometheus│
                     │ :8080   │ │ (optional)│
                     └──┬───┬──┘ └──────────┘
                        │   │
                  ┌─────▼───▼──────┐
                  │  PostgreSQL    │
                  │  + Redis       │
                  └────────────────┘

                    ┌──────────────┐
                    │   Worker     │
                    │  (embedded   │
                    │   scanners)  │
                    └──────────────┘
```

- **API** (`ghcr.io/dyallab/henkaipan-api`): Serves the frontend and REST API on port 8080. Exposes Prometheus metrics on port 9090.
- **Worker** (`ghcr.io/dyallab/henkaipan-worker`): Executes background jobs (scans, webhooks, emails). Scanner binaries are bundled in the image.
- **PostgreSQL** (port 5432): Primary data store.
- **Redis** (port 6379): Job queue, rate-limiting, and SSE event relay between worker and API processes.

## Prerequisites

- Docker & Docker Compose v2.24+
- Linux host (recommended) or Windows with WSL2
- 8 GB RAM minimum (16 GB recommended for concurrent scans)
- 30 GB free disk (scanner images ~6 GB, plus data)
- Domain name with DNS pointing to your server
- Ports 80 and 443 open (for reverse proxy)

## 1. Initial Setup

```bash
# Clone the repository
git clone https://github.com/Dyallab/HenKaiPan-self-hosted.git
cd HenKaiPan-self-hosted

# Run the installer (checks prerequisites, generates secrets)
./install.sh

# To skip Ollama installation (e.g. if you use another AI provider):
# ./install.sh --skip-ollama

# Start the stack
docker compose up -d

# Verify
curl http://localhost:8080/api/health
```

The API serves both the backend API and the embedded frontend on port 8080. Open `http://your-server:8080` and log in with the admin credentials generated by `install.sh`.

> **Ollama**: By default, the installer sets up Ollama (free, self-hosted AI) for summary generation. Use `./install.sh --skip-ollama` if you don't need it or plan to use a different AI provider.

## 2. Reverse Proxy with TLS

### Option A: Nginx + Let's Encrypt (Recommended)

Create `/etc/nginx/sites-available/henkaipan`:

```nginx
server {
    listen 80;
    server_name aspm.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name aspm.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/aspm.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/aspm.yourdomain.com/privkey.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    # API and frontend
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        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;
    }
}
```

Enable and obtain TLS certificate:

```bash
sudo ln -s /etc/nginx/sites-available/henkaipan /etc/nginx/sites-enabled/
sudo certbot --nginx -d aspm.yourdomain.com
sudo systemctl reload nginx
```

### Option B: Caddy (Simpler, Auto-TLS)

Create `/etc/caddy/Caddyfile`:

```
aspm.yourdomain.com {
    reverse_proxy 127.0.0.1:8080

    headers {
        Strict-Transport-Security "max-age=63072000"
    }
}
```

Caddy automatically obtains and renews TLS certificates.

## 3. Environment Configuration

After running `install.sh`, review and adjust your `.env` file:

### Required Security Settings

```env
# Must be set behind HTTPS
COOKIE_SECURE=true

# Generate strong random values
JWT_SECRET=<openssl rand -base64 32>
SECRET_ENCRYPTION_KEY=<openssl rand -hex 32>

# Database (defaults are fine for single-host)
DATABASE_URL=postgres://aspm:aspm@postgres:5432/aspm?sslmode=disable
```

### Performance Tuning (optional)

```env
# Increase API server timeouts for large scan payloads
# (configurable via Go server, defaults are reasonable)

# Redis for rate limiting / queue
REDIS_ADDR=redis:6379
```

## 4. Security Hardening

### Scanner Execution

Scanner binaries run directly within the worker process. All scanners are bundled in the worker Docker image, eliminating the need for Docker socket access.

### Network Security

```bash
# Block external access to PostgreSQL and Redis
# These should only be accessible from the Docker network
docker compose port postgres 5432  # Should NOT be exposed publicly
```

If you do not need external DB access, remove `ports` from `postgres` and `redis` in `docker-compose.yml`. They will remain accessible within the Docker network.

### Secrets Management

- The `.env` file contains sensitive credentials. Set `COOKIE_SECURE=true` so auth cookies are only sent over HTTPS.
- Restrict `.env` file permissions: `chmod 600 .env`
- Consider using Docker Secrets or a secret manager (Hashicorp Vault, AWS Secrets Manager) for production.

### Firewall Rules

```bash
# Allow only SSH, HTTP, and HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8080  # API should only be accessed via reverse proxy
sudo ufw enable
```

## 5. Database Backups

### Automated Backup Script

The landing page provides a [Backup & Restore guide](https://henkaipan.dyallab.com.ar/docs/backup/) with a backup script. Set up automated daily backups:

```bash
# Install backup script
mkdir -p /opt/henkaipan/backups
cp scripts/backup.sh /opt/henkaipan/backups/

# Add cron job (runs daily at 2 AM)
echo "0 2 * * * /opt/henkaipan/backups/backup.sh >> /var/log/henkaipan-backup.log 2>&1" | crontab -
```

### Restore Procedure

```bash
# Stop the API to prevent writes
docker compose stop api worker

# Restore from backup
docker compose exec -T postgres psql -U aspm -d aspm < /path/to/backup.sql

# Restart
docker compose up -d
```

## 6. Monitoring

### Prometheus Metrics

The API exposes metrics on port 9090 (configurable via `PROMETHEUS_PORT`):

```bash
# Quick check
curl http://localhost:9090/metrics
```

A sample Prometheus configuration is available at `monitoring/prometheus.yml`. Key metrics include:

- Queue depth (Asynq)
- Request latency and rate
- Database connection pool stats
- Go runtime metrics

### Health Checks

```bash
# Full health check (DB, Redis, Worker status)
curl http://localhost:8080/api/health

# Version info
curl http://localhost:8080/api/version
```

### Logging

```bash
# View API logs
docker compose logs -f --tail=100 api

# View Worker logs
docker compose logs -f --tail=100 worker

# Limit log size in docker-compose.yml
x-logging: &logging
  logging:
    options:
      max-size: "10m"
      max-file: "3"
```

## 7. Upgrading

### Standard Upgrade

```bash
# Pull new images
docker compose pull

# Recreate containers with new images
docker compose up -d

# Verify health
curl http://localhost:8080/api/health
```

Migrations run automatically on API startup. Check API logs for migration output:

```bash
docker compose logs api | grep -i migration
```

### Pinning to a Specific Version

To control exactly which version runs, override the image tag in `docker-compose.yml`:

```yaml
services:
  api:
    image: ghcr.io/dyallab/henkaipan-api:v1.0.0
  worker:
    image: ghcr.io/dyallab/henkaipan-worker:v1.0.0
```

### Rollback

If an upgrade causes issues:

1. **Revert image tags** to the previous version in `docker-compose.yml`
2. **Recreate containers:**

```bash
docker compose up -d
```

3. **Verify rollback:**

```bash
curl http://localhost:8080/api/version
curl http://localhost:8080/api/health
```

**Database note:** Migrations are additive (new columns, new tables). Downgrading the application code does not automatically revert schema changes. This is safe — the application remains compatible with the newer schema. If you need a full rollback including database schema, restore from a backup taken before the upgrade.

### Pre-Upgrade Checklist

- [ ] Review the [CHANGELOG.md](../CHANGELOG.md) for breaking changes
- [ ] Take a database backup
- [ ] Note current version: `curl http://localhost:8080/api/version`
- [ ] Test in a staging environment if possible

## 8. Production Checklist

Before going live:

### Security

- [ ] `COOKIE_SECURE=true` set in `.env`
- [ ] `COOKIE_DOMAIN=.example.com` set for your domain
- [ ] `COOKIE_SAMESITE=lax` (default) or `strict` for stricter CSRF protection
- [ ] `CORS_ALLOWED_ORIGINS=https://aspm.example.com` set (comma-separated for multiple origins)
- [ ] `TRUSTED_PROXIES` set if behind a reverse proxy (e.g. `10.0.0.0/8`)
- [ ] `PUBLIC_API_BASE=` (empty) so API and frontend are served from same origin
- [ ] Reverse proxy (nginx/caddy) with valid TLS certificate
- [ ] Firewall restricts direct access to API port 8080
- [ ] PostgreSQL and Redis ports not exposed to the internet
- [ ] Strong random `JWT_SECRET` and `SECRET_ENCRYPTION_KEY` generated
- [ ] `.env` file permissions: `chmod 600 .env`
- [ ] Default credentials rotated (`ADMIN_USER`, `ADMIN_PASS`)
- [ ] SSO configured with TLS-terminated `SSO_REDIRECT_URI` and matching IdP redirect URI (see [SSO with Authelia](sso-authelia.md))

### Reliability

- [ ] Database backups configured (daily + offsite)
- [ ] Backup restore procedure tested
- [ ] Monitoring set up (health endpoint, Prometheus metrics)
- [ ] Log aggregation configured (log rotation, external sink if needed)
- [ ] Resource limits set in `docker-compose.yml` (optional but recommended)

### Maintenance

- [ ] Upgrade procedure documented and tested
- [ ] Rollback plan documented
- [ ] DNS records configured (A/AAAA record for your domain)
- [ ] Email notifications configured (SMTP) for alerts

## 9. Resource Sizing

| Deployment Size | Users | RAM | Disk | Workers |
|----------------|-------|-----|------|---------|
| Small team | 1-10 | 8 GB | 30 GB | 1 |
| Medium team | 10-50 | 16 GB | 100 GB | 2 |
| Large team | 50-200 | 32 GB | 250 GB | 3-4 |

Disk usage is dominated by Docker images (scanners). Actual database storage is minimal for most deployments (< 1 GB for thousands of findings).

## 10. Related Documentation

- [Operations Guide](operations.md) — Worker scaling, scanner requirements, troubleshooting
- [Kubernetes Deployment Guide](kubernetes.md) — K8s production deployment
- [Quickstart](../README.md) — Getting started with Docker Compose
- [Backup & Restore](https://henkaipan.dyallab.com.ar/docs/backup/) — Detailed backup procedures

