# AEGIS CYBER — On-Prem Docker Deployment

Phase 1 packaging: laptop demonstration + bank infrastructure-engineer hand-off.

This package brings up the full AEGIS CYBER platform on a single host using Docker Compose. The bundled PostgreSQL 16 container is the default data store; an external PostgreSQL can be substituted via `DATABASE_URL`.

---

## Prerequisites

- **Docker Desktop** (Mac / Windows) or **Docker Engine** (Linux) — version 24+
- **Docker Compose v2** (bundled with modern Docker Desktop; `docker compose` not `docker-compose`)
- 4 GB RAM available to Docker, 2 GB free disk
- A terminal with `openssl` available (for generating secrets) — pre-installed on macOS/Linux; on Windows, use Git Bash or WSL

Verify:

```sh
docker --version          # Docker version 24.x or newer
docker compose version    # Docker Compose version v2.x
```

---

## First-time setup

### 1. Copy the env template and fill in values

```sh
cp .env.template .env
```

Open `.env` in a text editor and populate every line marked `REQUIRED`. Quick way to generate the 10 boot-guard secrets all at once:

```sh
for v in SESSION_SECRET WAVE10_SIGNING_SECRET WAVE10_PDF_SECRET \
         DEMO_PACK_SIGNING_SECRET SLSA_SIGNING_SECRET ONBOARD_SIGNING_SECRET \
         CROSS_PILLAR_SIGNING_SECRET TENANT_EXPORT_SIGNING_SECRET \
         TENANT_EXPORT_KEY EXAMINER_TOKEN_SALT; do
  printf '%s=%s\n' "$v" "$(openssl rand -hex 32)"
done
```

Pipe the output into `.env`, then set `POSTGRES_PASSWORD` to your chosen DB password.

**On Windows (PowerShell — no openssl required):** run the bundled helper script from the repository root. It generates all 10 secrets using Windows' native cryptographically-secure RNG, writes them straight into `.env`, and never echoes a secret to the terminal:

```powershell
.\docker\generate-env.ps1
```

After it finishes, open `.env` in your editor (`notepad .env`) and set `POSTGRES_PASSWORD` to a strong value you choose.

### 2. (Optional) pin initial admin passwords

If you leave `AEGIS_ADMIN_PASSWORD`, `AEGIS_CISO_PASSWORD`, `AEGIS_AUDITOR_PASSWORD` blank, the platform will generate a random password for each at first boot, print it once to stdout, and never log it again. Capture them from the boot logs (see step 4). To set your own instead, fill them in **before** first boot — once a user exists, this template is ignored for that user.

### 3. Build and start

```sh
docker compose up -d --build
```

First build takes 2–5 minutes (downloads `node:20-alpine` + `postgres:16-alpine`, runs `npm ci`, runs `npm run build`).

### 4. Watch first-boot logs and capture generated passwords

```sh
docker compose logs -f app
```

You should see, in order:

- `[entrypoint] probing database...` → `database is reachable`
- `[entrypoint] applying schema via drizzle-kit push...` → `schema is in sync`
- Server startup logs, then `serving on port 5000`
- If you did not set admin passwords, a banner like:

  ```
  ========================================================================
  [AEGIS BOOT] Generated initial password for user "admin"
  [AEGIS BOOT] Password: r4nd0m_b4se64_value_here
  [AEGIS BOOT] CAPTURE NOW — this value will NOT be logged again.
  [AEGIS BOOT] To pin this password across restarts, set AEGIS_ADMIN_PASSWORD in .env BEFORE first boot.
  ========================================================================
  ```

  **Capture these immediately** into a password manager. They are printed once and not stored anywhere recoverable.

Ctrl-C exits the log tail; the containers keep running in the background.

### 5. Open the platform

[http://localhost:5000](http://localhost:5000)

Log in as `admin` / `ciso` / `auditor` with the password you set or captured.

---

## Operations

| Action | Command |
|---|---|
| Tail app logs | `docker compose logs -f app` |
| Tail postgres logs | `docker compose logs -f postgres` |
| Restart app only | `docker compose restart app` |
| Stop everything (keep data) | `docker compose down` |
| Stop and **wipe all data** | `docker compose down -v` ⚠ destructive |
| Shell into app container | `docker compose exec app sh` |
| Run a SQL query | `docker compose exec postgres psql -U aegis -d aegis_cyber` |
| Rebuild after code change | `docker compose up -d --build app` |

---

## Persistent state

Three named Docker volumes hold all stateful data:

| Volume | Mounted at | Holds |
|---|---|---|
| `aegis-pgdata` | `/var/lib/postgresql/data` (postgres container) | Database |
| `aegis-exports` | `/var/aegis/exports` (app container) | Generated CSV/JSON/PDF reports |
| `aegis-backups` | `/var/aegis/backups` (app container) | pg_dump artefacts from the backup scheduler |

`docker compose down` preserves all three. Only `docker compose down -v` wipes them.

---

## Troubleshooting

**App container exits immediately with "must be set in .env"** — one of the REQUIRED secrets is blank. Check the message for the variable name, set it in `.env`, then `docker compose up -d`.

**App container loops restarting with `boot-guard FAILED`** — the boot-time secret guard rejected your secrets (likely too short or default values). Generate fresh ones with the openssl loop in step 1.

**Browser shows `ECONNREFUSED localhost:5000`** — the host port `${HOST_PORT}` is in use by something else. Either stop the other process or change `HOST_PORT` in `.env` to e.g. `8080` and `docker compose up -d`.

**Database push fails on first boot** — usually means an older volume exists with stale schema. Either run `docker compose down -v` (wipes data) or shell into postgres and inspect.

**Logs show "AEGIS BOOT Generated initial password" but you missed capturing it** — rotate by either: (a) shelling into postgres and updating the bcrypt hash directly, or (b) `docker compose down -v` to wipe and start fresh.

---

## Updating to a new build

After pulling new code:

```sh
docker compose down
docker compose up -d --build
```

`drizzle-kit push` runs at every boot and is idempotent — schema changes apply automatically. No data is lost as long as you do not use `-v`.

---

## Pointing at an external PostgreSQL

To skip the bundled postgres and use an existing PostgreSQL instance (e.g. the bank's managed database):

1. Set `DATABASE_URL=postgresql://user:pass@host:5432/dbname` in `.env`
2. Comment out the `postgres:` service block in `docker-compose.yml`
3. Remove `depends_on: postgres:` from the `app:` service
4. `docker compose up -d --build`

The schema push at first boot creates all required tables in the target database.

---

## Security notes

- The `.env` file contains every secret the platform needs. Treat it like a private key — store it in a password manager or secret manager, never commit it.
- Initial admin passwords are printed ONCE to stdout at first boot if not pre-set. They never appear in the audit log, the application log, or anywhere on disk.
- The boot-time secret guard refuses to start the platform if any of the 10 required signing secrets are missing or weak. There is no override flag for production.
- The regulator demo seed (`REGULATOR_SEED_PASSWORD`) does NOT auto-create a known credential in production. The historical `DEMO_MODE=true` shortcut was hardened on 2026-05-21 to require an explicit operator-set password in production.

---

## Known limitations of Phase 1

This package is a **single-host development / demonstration deployment**. It is suitable for:

- Laptop demos (BoU, HFB, internal review)
- Bank infrastructure engineer evaluation
- Air-gapped sovereign deployments where a single host is acceptable

It does NOT yet include:

- Multi-node clustering or HA
- TLS termination (use a reverse proxy in front: nginx, Caddy, Traefik)
- Backup retention / off-site replication automation
- Production-grade observability (Prometheus, log shipping)
- Kubernetes / OpenShift manifests

These are Phase 2 deliverables.
