# Tech Stack & Build System

## Stack

| Layer | Technology |
|-------|-----------|
| Backend | Node.js 18+, Express 5 |
| Database | PostgreSQL (via `pg` pool in `backend/db.js`) |
| Auth | bcryptjs, cookie-based sessions (httpOnly, 24h expiry) |
| File uploads | Multer 2 (10MB limit) |
| Frontend | React 19 (Create React App / react-scripts 5) |
| Frontend serving | Express serves `frontend/build/` as static files on port 3001 |
| UI Icons | lucide-react |
| Charts | recharts |
| Spreadsheet parsing | xlsx (frontend), pandas + openpyxl (backend Python scripts) |
| Markdown rendering | react-markdown |
| Diagrams | mermaid |

## Architecture: Single-Port Serving

Express on port 3001 serves **both** the API and the production frontend build:
- API routes: `/api/*` — handled by Express route handlers
- Frontend: everything else — served as static files from `frontend/build/`

There is no separate frontend server in production. The React dev server (`npm start` on port 3000) is only for local development with hot-reload. In production and on the dev server, you must run `npm run build` in `frontend/` after any frontend code change, then restart the backend.

**After editing frontend source files:**
```bash
cd frontend && npm run build    # Compile new bundle into frontend/build/
# Then restart backend (or it will serve the new static files on next request)
```

The CI/CD pipeline handles this automatically — `build-frontend` stage runs before deploy.

## Common Commands

### Backend
```bash
cd backend
node setup.js          # Initialize DB, tables, indexes, default admin user
node server.js         # Start backend on port 3001 (serves API + frontend build)
```

### Frontend
```bash
cd frontend
npm install            # Install dependencies
npm run build          # Production build → frontend/build/ (REQUIRED after code changes)
npm start              # Dev server on port 3000 (local dev only, NOT used in production)
npm test               # Run tests (react-scripts test)
```

### Both servers (from project root)
```bash
./start-servers.sh     # Start backend + frontend in background
./stop-servers.sh      # Stop all servers
```

### Database Migrations (run from `backend/`)
```bash
node migrations/run-all.js    # Runs all migrations in order (idempotent)
```

### Python Scripts (from `backend/scripts/`)
```bash
# Compliance xlsx parsing (called automatically by upload flow)
python3 parse_compliance_xlsx.py <file>

# Bulk notes import
python3 import_notes_from_csv.py input.csv --dry-run
python3 import_notes_from_csv.py input.csv
```

Python dependencies: `pandas>=2.0.0`, `openpyxl>=3.0.0` (install via apt or venv).

## Environment Configuration

- `backend/.env` — PORT, CORS_ORIGINS, SESSION_SECRET, NVD_API_KEY, Ivanti API credentials, CARD API credentials
- `frontend/.env` — REACT_APP_API_BASE, REACT_APP_API_HOST
- Both `.env` files are gitignored; see `.env.example` files for templates.
- React env vars are baked in at **build time** — you must rebuild (`npm run build`) after changing them.

### Key Backend Env Vars

| Variable | Purpose |
|---|---|
| `IVANTI_API_KEY` | RiskSense platform API key |
| `IVANTI_CLIENT_ID` | RiskSense client ID (default: 1550) |
| `IVANTI_BU_FILTER` | Comma-separated BU teams to sync findings for (default: `NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM`) |
| `IVANTI_FIRST_NAME` / `IVANTI_LAST_NAME` | Fallback Ivanti identity for workflow sync (used only if no per-user identities configured) |
| `CARD_API_URL` | CARD API base URL (e.g., `https://card.charter.com`) |
| `CARD_API_USER` / `CARD_API_PASS` | CARD OAuth credentials for Bearer token acquisition |
| `CARD_SKIP_TLS` | Set to `true` to skip TLS verification (for SSL inspection proxies) |
| `DATABASE_URL` | PostgreSQL connection string |

### CARD API and Ivanti Integration Details

See `.kiro/steering/integrations.md` for full API contracts, response shapes, and quirks for CARD, Ivanti, Atlas, and Jira.

### Ivanti Findings IPv6 Handling

Some Ivanti findings have no IPv4 address. The sync captures fallback addresses:
- `qualys_ipv6` — from `hostAdditionalDetails[].["IPv6 Address"]` (resolves in CARD)
- `primary_ipv6` — from `assetCustomAttributes['1550_host_6'][0]` (may not resolve in CARD)

Display priority in the UI: IPv4 > Qualys IPv6 (amber "Q" badge) > Primary IPv6 (indigo "v6" badge)

## Code Style & Lint Rules

### Unused Variables

The frontend ESLint config enforces `no-unused-vars` as a warning. The CI pipeline fails if warnings exceed 25. To avoid lint failures:

- **Prefix intentionally-unused variables with `_`** — this suppresses the warning. The `varsIgnorePattern: "^_"` and `argsIgnorePattern: "^_"` rules are configured in `frontend/package.json`.
- Common patterns:
  - `const [_unused, setFoo] = useState(...)` — destructured value you don't need
  - `const _legacyRef = useRef(...)` — kept for future use
  - `function handler(_event) { ... }` — required parameter signature but unused
- **Do not leave variables unprefixed if unused.** Either use them, remove them, or prefix with `_`.
- This applies to all frontend code written by the agent.

### Backend

No ESLint is configured for backend — the pipeline uses `node -c` syntax checking only. Keep code clean but there is no automated unused-var enforcement on the backend side.

## Ports

| Environment | URL | Notes |
|---|---|---|
| Production | http://71.85.90.6:3001 | Express serves API + static frontend build |
| Staging | http://71.85.90.9:3100 | Auto-deploy on master push |
| Local dev (frontend only) | http://localhost:3000 | React dev server with hot-reload, proxies API to :3001 |

## Secure Context Constraints

All environments serve over **plain HTTP** (not HTTPS). This means browser APIs that require a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) are **not available** in production or staging:

- `navigator.clipboard` (Clipboard API) — use `document.execCommand('copy')` with a hidden textarea instead
- `navigator.share` (Web Share API)
- `crypto.subtle` (Web Crypto API)
- `navigator.credentials` (Credential Management API)
- Service Workers and Push Notifications

When writing frontend code that needs clipboard, sharing, or crypto functionality, always use the non-secure fallback pattern. Do not use `navigator.clipboard.writeText()` or similar secure-context APIs.

## CI/CD Pipeline

### Infrastructure

| Role | Host | Notes |
|---|---|---|
| GitLab instance | steam-gitlab.charterlab.com | Self-hosted GitLab |
| CI Runner (LXC 108) | 71.85.90.8 | Docker executor, Runner #6, project-locked |
| Staging target | 71.85.90.9 | Auto-deploy on master, port 3100 |
| Production target | 71.85.90.6 | Manual deploy trigger, port 3001 |

### Executor: Docker

The pipeline uses **Docker executor** on Runner #6. Jobs run in isolated containers:

- **Install / Lint / Test / Build stages**: `node:18` image
- **Deploy stages**: `alpine:latest` image (installs `openssh-client` and `rsync` at runtime)

Deploy jobs SSH from inside the Alpine container to the target hosts using a base64-encoded `$SSH_PRIVATE_KEY` stored as a GitLab CI/CD variable.

### CI/CD Variables (project-level)

These are set in GitLab → Settings → CI/CD → Variables:

| Variable | Purpose |
|---|---|
| `DATABASE_URL` | PostgreSQL connection string for backend integration tests |
| `SSH_PRIVATE_KEY` | Base64-encoded private key for deploy SSH access |
| `GITLAB_PAT` | Project access token for issue comments and release creation |

### Pipeline file

The pipeline is defined in `.gitlab-ci.yml` at the project root. Stages: install → lint → test → build → deploy → verify.
