- Replace all STEAM branding with AEGIS (Advanced Engineering Group Intelligence System) across login, header, nav drawer, manifest, and browser title - Add shield logo to login page, main header, and nav drawer - Fix BU drift checker recording incorrect previous_bu values by building a previousBuMap snapshot BEFORE the upsert/delete cycle instead of querying the DB after rows are already gone - Clean 526 bogus BU history entries generated by the broken logic - Add docs and scripts from prior session
7.5 KiB
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:
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
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
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)
./start-servers.sh # Start backend + frontend in background
./stop-servers.sh # Stop all servers
Database Migrations (run from backend/)
node migrations/run-all.js # Runs all migrations in order (idempotent)
Python Scripts (from backend/scripts/)
# 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 credentialsfrontend/.env— REACT_APP_API_BASE, REACT_APP_API_HOST- Both
.envfiles are gitignored; see.env.examplefiles 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— fromhostAdditionalDetails[].["IPv6 Address"](resolves in CARD)primary_ipv6— fromassetCustomAttributes['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. ThevarsIgnorePattern: "^_"andargsIgnorePattern: "^_"rules are configured infrontend/package.json. - Common patterns:
const [_unused, setFoo] = useState(...)— destructured value you don't needconst _legacyRef = useRef(...)— kept for future usefunction 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 are not available in production or staging:
navigator.clipboard(Clipboard API) — usedocument.execCommand('copy')with a hidden textarea insteadnavigator.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:18image - Deploy stages:
alpine:latestimage (installsopenssh-clientandrsyncat 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.