# AEGIS CYBER — API Integration Guide (Bank / Customer Environment)

**Audience:** a software engineer joining the project who needs to understand how AEGIS CYBER talks to a bank's / customer's environment over its API — what is genuinely wired, what is a simulation harness, and what would have to be built to connect to a live core banking system.

**Date:** 2026-06-02
**Status of this document:** engineering onboarding reference. Every maturity classification below was verified against the codebase on the date above (file and symbol cited inline). This is a *realistic-not-aspirational* document: it states what the code does today, not what the architecture is designed to eventually do. Where the gap between the two matters, it is named explicitly.

---

## How to read this document — calibration key

This mirrors the key used in `docs/BANK_PRESENTATION_AND_ENGINEER_BRIEF_2026-06-01.md`:

- 🟢 **Live today** — implemented and exercised in the running platform on real or genuinely-computed data.
- 🟡 **Simulation / pilot / integration-ready** — implemented and demonstrable, but running on simulated/seeded data, or the integration *interface* is defined but not wired to a live external system.
- 🔵 **In-flight / not built** — active engineering, or a path that does not exist yet and would need to be built.

> **The one thing to internalise first.** AEGIS's *inbound control surface* toward a core banking system (account freeze, transaction hold/reverse, kill-switches) is a **simulation harness** today — it does not call any real bank core. AEGIS's *own* APIs (authentication, KYT scoring, outbound webhooks, the regulator/trust surfaces) are genuinely running. Do not tell a bank "we are integrated with your Finacle/Flexcube/T24 core" — that wiring is designed but not built. See §2.1 and §6.

---

## Part 0 — The integration map at a glance

```
        BANK / CUSTOMER ENV                       AEGIS CYBER                         REGULATOR / PUBLIC
        ───────────────────                       ───────────                         ──────────────────
  core banking system (CBS) ──X──▶ /api/cbs/*  (🟡 SIMULATED gateway)
  transaction feed          ─────▶ /api/kyt/analyze (🟢)  ─────▶ KYT rules engine (🟢)
  transaction feed          ─────▶ /api/ai-scoring/transaction (🟡 Anthropic, unbenchmarked)
  USSD telco callback       ─────▶ /api/ussd/callback (🟡 demo, public)
  open-banking consent      ─────▶ /api/open-banking/consent/* (🟢 records)
                                                  │
                                   webhook-delivery (🟢 real fetch) ─────▶ customer/bank webhook endpoint
                                   notification-gateway (🟡/🟢) ─────────▶ email/SMS/webhook
                                   siem-forwarder (🟡) ───────────────────▶ bank SIEM
                                   regulator-gateway SAR/CTR (🟡) ────────▶ (persists; does NOT transmit to FIA)
                                   regulator-push/dispatch ──────────────▶ regulator endpoint
                                                  │
                                   /api/public/openapi.json + /swagger (🟢, curated 13-path subset)
                                   /api/public/status, /metrics, /trust-portal (🟢)
                                   /api/regulator/pilot-pack.zip (🟢 signed bundle)
                                   WebSocket broadcast (🟢 push-only, auth-at-upgrade)
```

The platform registers **~1,306 HTTP routes** (`server/routes.ts`). This guide covers the subset that constitutes the external integration boundary; it is not an exhaustive route catalogue. For a machine-readable contract see §4.1.

---

## Part 1 — Authentication & the request pipeline (how an external caller is authenticated)

### 1.1 The live middleware chain — 🟢

Every `/api` request passes through this chain, in this order (`server/routes.ts`):

1. `app.use(sessionMiddleware)` — cookie-based session (express-session + `connect-pg-simple`, Postgres-backed). *(line ~525)*
2. `app.use("/api", tenantMiddleware)` — resolves the request's tenant context. *(line ~528, `server/lib/tenant-middleware.ts`)*
3. `app.use(csrfProtection)` — token-based CSRF protection for state-changing requests. *(line ~632)*
4. Per-route guards: `requireAuth` (must be logged in) or `requireRole("admin", "risk_manager", "auditor")` (RBAC). *(defined `server/routes.ts` ~823–832, built via `server/lib/rbac-middleware.ts`)*

**So today, an external system authenticates to AEGIS as a logged-in session principal with an RBAC role, over a CSRF-protected channel.** There is no separate "machine client" credential enforced on the route surface yet — see §1.3.

### 1.2 Tenant resolution — 🟢 RESOLVED (T002.5 closed in prod 2026-06-03)

`tenantMiddleware` resolves the active tenant from the authenticated session. The prior `x-tenant-id` header path — which allowed a caller to assert a tenant id (a known IDOR-shaped surface, T002.5) — has been **removed from prod** (2026-06-03T08:35 UTC; topology smoke confirmed `source:"header"` absent post-deploy). Tenant context is now **session-derived and authorization-bound**: on login, `session.tenantId` is set from the `user_tenant_roles` table via the `withBypassRls` LOGIN-UTR pattern; the DB-backed `utr_primary_per_user` partial unique index enforces one active primary tenant per user. There is no caller-assertable tenant path in the current codebase.

### 1.3 DB-backed API-key auth — present in code, **not mounted** — 🔵

`server/lib/api-key-auth.ts` implements a genuine API-key middleware:
- Keys are minted as `aegis_<64 hex>`, stored **only as a SHA-256 hash** (`hashApiKey`), looked up via `storage.getApiKeyByHash`.
- Accepts `X-API-Key:` or `Authorization: Bearer <key>`; checks `isActive`, `expiresAt`; attaches `req.apiKeyAuth = { tenantId, permissions, keyId }`; updates `lastUsedAt`.
- The management endpoints exist (`POST/GET/DELETE /api/api-keys`).

**However, `apiKeyAuth` is not added to the Express pipeline anywhere** — there is no `app.use(apiKeyAuth)` and no per-route usage (verified: zero call sites in `server/routes.ts`). `tenant-middleware.ts` will *consume* `req.apiKeyAuth.tenantId` if something set it, but nothing currently does. **Treat API-key authentication as scaffolded, not enforced.** Wiring it in is the natural path to a real machine-to-machine bank credential (see §6).

### 1.4 The `api-gateway.ts` "demo clients" — do not mistake this for the auth path — 🟡

`server/lib/api-gateway.ts` is a **separate, in-memory demonstration** of a commercial API gateway. It seeds hardcoded clients (Stanbic, DFCU, Centenary, MTN MoMo, Airtel Money) with **placeholder keys** of the form `sk_live_<org>_xxxxxxxx`, and computes their hash with a **non-cryptographic** routine (a `charCodeAt` accumulation loop, not SHA-256). It also holds in-memory rate-limit tiers (free/basic/professional/enterprise).

This module is **mounted and actively exposed** via admin-only routes (`GET/POST/PATCH /api/gateway/clients[...]`, `/rotate-key`, `/revoke`, `/usage`, `/analytics`, `/stats`, `/blocked-ips`, all `requireRole("admin")`) — it is not dormant code. But it demonstrates tiering and rate-limit behaviour only; **it is not the production authentication path and its hash must never be used for real credentials.** When you read code that references `sk_live_*` keys or `TIER_LIMITS`, you are in the demo gateway, not the real auth layer (§1.3).

---

## Part 2 — Inbound APIs (bank / customer environment → AEGIS)

### 2.1 Core Banking System (CBS) control surface — 🟡 SIMULATED

Endpoints (`server/routes.ts`, all under `requireRole`, admin/risk_manager):
- `POST /api/cbs/accounts/freeze` · `/unfreeze` · `/pnd-hold`
- `POST /api/cbs/transactions/flag` · `/reverse`
- `POST /api/cbs/emergency/freeze`
- `POST /api/cbs/kill-switches/:id/release` · `GET /api/cbs/kill-switches[/active]`
- `GET /api/cbs/health[/:provider]` · `/api/cbs/transaction-logs[/:id]` · `/api/cbs/stats`

These are served by `coreBankingGateway` (`server/lib/core-banking-gateway.ts`). What is real vs simulated:

- **Real:** the *interface and orchestration* — provider abstraction (`finacle | flexcube | t24 | generic`), a two-phase-commit log with prepare/commit/rollback semantics, kill-switch lifecycle, an account-status cache, and structured logging.
- **Simulated:** the actual CBS call. `simulateCBSCall()` does `setTimeout` for a randomised delay and throws a `CBS_TIMEOUT` error ~2% of the time. There is **no real outbound HTTP in this module** (verified — no `fetch`/`axios`/`http.request`). The provider configs point at placeholder hosts (`https://cbs.bank.local/finacle/api/v2`, mTLS cert paths under `/etc/aegis/certs/...`, OAuth2 `clientSecret: "***REDACTED***"`). The kill-switch list is seeded with demo rows (`KS-001..003`), and all state lives in in-memory `Map`s (lost on restart).

**Implication for a bank conversation:** AEGIS can *demonstrate* the freeze/hold/reverse workflow end-to-end, but connecting it to a live core requires building the actual provider clients (§6). Until then it is a harness.

### 2.2 KYT (Know-Your-Transaction) rules engine — 🟢

`POST /api/kyt/analyze` (`requireAuth`) → `analyzeTransaction()` in `server/lib/kyt-engine.ts`.
- **Genuinely computed:** `calculateRiskScore()` sums configured rule weights against the transaction context you submit, derives a risk level from thresholds, and returns score + contributing factors. It runs on whatever transaction payload you POST — it is real computation, not random.
- Rules are externalised to a validated JSON config with a hot-reload poller (`kyt-config-loader.ts` / `kyt-config-apply.ts`); config endpoints: `GET /api/kyt/current-config`, `/config-status`, `POST /api/kyt/validate-config`, `/apply-config`, `/rollback-config`.

This is the cleanest "feed us your transaction stream, get a risk decision" integration point that is live today.

### 2.3 AI threat / transaction scoring — 🟡

`POST /api/ai-scoring/transaction` and the commercial scoring routes (`/api/commercial/transaction-scoring/score` · `/simulate`) are wired to Anthropic Claude (`server/lib/ai-threat-scoring.ts`, `transaction-scoring.ts`, `ai-provider`). The model output is **real** when the integration is active, but **scoring quality is not benchmarked against labelled bank data**. Treat as pilot-grade signal, not a validated decision engine.

### 2.4 Open-banking consent — 🟢 (records)

`POST /api/open-banking/consent/grant`, `POST /api/open-banking/consent/:id/revoke`, `GET /api/open-banking/consents`, `GET /api/open-banking/catalog` (`requireRole`). These create and manage real consent records. There is no live account-data fetch from a third-party bank behind them — they model the consent ledger, not an AISP/PISP data pull.

### 2.5 USSD / mobile channel — 🟡 demo

- `POST /api/ussd/callback` — **public, no auth** — this is the telco-facing callback shape (the gateway POSTs session/text here).
- `POST /api/uganda/ussd/session`, `/api/uganda/ussd/validate-pin` (`requireRole`).

Backed by `server/lib/ussd-api.ts`: an in-memory session state machine (`Map<sessionId, …>`) rendering menu trees. Transaction-status replies are fabricated with `Math.random()` (e.g. a random UGX amount). Useful to demo the channel UX; not connected to a real mobile-money/CBS balance source.

### 2.6 Other inbound transaction entry points

`POST /api/edge/transaction` (`requireAuth`, edge/offline processing), `POST /api/uganda/a2a/transaction`, `POST /api/uganda/edge/process-transaction`, and the agentic-commerce set (`/api/industry/commerce/verify-transaction`, `/assign-bot`, `/confirm-delivery`, `/detect-mitm`). These accept transaction-shaped payloads and exercise the relevant engines; classify each as 🟡 unless you have confirmed a live data source behind it.

---

## Part 3 — Outbound integrations (AEGIS → bank / regulator / customer)

| Channel | Endpoint(s) / module | Maturity | What it actually does |
|---|---|---|---|
| **Outbound webhooks** | `POST /api/webhooks`, `/api/webhooks/deliver`; `webhook-delivery.ts` | 🟢 | Makes a **real `fetch()` POST** to the configured `endpointUrl` with auth headers and a timeout/abort controller; supports event fan-out. This is genuine outbound delivery to a customer/bank endpoint. |
| **Notification gateway** | `notification-gateway.ts` | 🟡/🟢 | Has real outbound delivery calls (email/SMS/webhook fan-out). Caveat: some channels can mark a notification `"sent"` via a fallback path **without confirmed provider acceptance** — a `"sent"` status is not proof the provider delivered it. Verify the concrete provider config before claiming a specific channel is live. |
| **SIEM forwarder** | `POST /api/siem/endpoints`, `/api/siem/endpoints/:id/test`; `siem-forwarder.ts` | 🟡 | Performs a **real `fetch()` POST to live SIEM endpoints**; endpoints tagged `demoEndpoint: true` (or matching placeholder host/token patterns) are deliberately skipped and recorded as `"demo-skipped"`. So a correctly-configured endpoint receives real traffic; the seeded demo set does not. |
| **Regulator Gateway (SAR/CTR)** | `regulator-gateway.ts`; `POST .../sar/create`, `.../ctr/create` | 🟡 | `createSAR()` / `submitSARToFIA()` build, validate, and **persist** SAR/CTR records and transition their status (DRAFT→SUBMITTED→ACKNOWLEDGED). The "submit to FIA" step is an **internal state transition**, not a live API call to the Financial Intelligence Authority — no external transmission is wired. |
| **SupTech gateway** | `suptech-gateway.ts` | 🟡 | In-app/DB report generation; no live external regulator push. |
| **Regulator push** | `POST /api/regulator-push/dispatch`, `/api/regulator-push/endpoints`, `GET /api/regulator-push/deliveries` | 🟡 | Dispatch/delivery records for regulator-facing push; confirm endpoint wiring before claiming live transmission. |

**Honest summary:** the outbound paths that genuinely call an external system over HTTP are the **webhook delivery** layer, the **notification fan-out**, and the **SIEM forwarder** (for live, non-demo endpoints). Regulator/SupTech "filing" is real *record generation and lifecycle*, but does **not** transmit to a live regulator endpoint.

---

## Part 4 — Trust / verification surfaces (consumable without logging in)

### 4.1 OpenAPI spec + Swagger UI — 🟢 (but a curated subset)

- `GET /api/public/openapi.json` → served by `generateOpenApiSpec()` (`server/lib/pilot-readiness-extras-8.ts`).
- `GET /api/public/swagger` → Swagger UI (loads the spec above).

**Important calibration:** the published spec is **OpenAPI 3.1.0**, titled *"AEGIS CYBER Regulator API"* v8.0.0, and declares **13 paths** — a deliberately **regulator/auditor-facing subset** (e.g. `/sar/create`, `/ctr/create`, `/auditor-portal/*`, `/public/trust-dashboard/latest`, `/public/threat-intel/stix`, `/regulator-push/dispatch`, `/security-scan/run`, `/demo-pack/generate`), with `securitySchemes: sessionAuth + auditorToken`. It is **not** a full catalogue of the ~1,306 routes. When you hand a partner the Swagger link, set expectations accordingly: it documents the regulator contract, not the entire API.

### 4.2 Public status / metrics / trust portal — 🟢

`GET /api/public/status`, `/api/public/metrics`, `/api/public/trust-portal[/wave5|/wave6]`, `/api/public/trust-dashboard/latest`, `/api/public/threat-intel/stix`. Independent verification surfaces a regulator or bank can read anonymously.

### 4.3 Signed pilot-pack + examiner access — 🟢

- `GET /api/regulator/pilot-pack.zip` (`requireRole` admin/risk_manager/auditor) — a cryptographically-signed, immutable evidence bundle.
- Examiner tokens: `POST /api/wave9/examiner-tokens/issue` (admin), `GET /list`, `DELETE /:id` — read-only, time-boxed regulator access (the `auditorToken` scheme in the OpenAPI spec).
- `GET /api/examiner/*` (status, whoami, readiness-score, sbom/latest, regulator-day).

---

## Part 5 — WebSocket real-time channel — 🟢 (push-only)

`server/routes.ts` stands up a `WebSocketServer` (`ws`). Key facts you need:
- **Authenticated at the upgrade boundary** via `verifyClient` (reuses the session middleware) — auth happens *before* the connection is accepted, not after. *(see the FIND-F1 notes around lines ~1336–1368)*
- The `wss.on("connection")` handler registers **no `ws.on("message")` handler** — this is a **server→client broadcast channel** (e.g. `broadcastThreat()` pushes new threats), not a bidirectional command channel. Clients receive; they do not issue commands over WS.
- Mesh/threat broadcast HTTP triggers exist (`POST /api/mesh/broadcast-threat`, `/api/mesh/broadcast`).

If you need a client→server real-time command path, it does not exist yet and would have to be added (with its own auth/validation).

---

## Part 6 — What a real bank/CBS integration would require (🔵 not built)

To move the inbound CBS surface (§2.1) from simulation to live, the following is the honest work list — none of it exists today:

1. **Real CBS provider clients.** Replace `simulateCBSCall()` with actual HTTP clients per provider (Finacle/Flexcube/T24), including the request/response mapping for freeze/unfreeze/hold/flag/reverse.
2. **mTLS + OAuth2 wiring.** The config *shape* exists (`useMTLS`, `certPath`, `oauth2Config`) but nothing loads certs or fetches tokens. Real cert provisioning and a token-acquisition flow are required, with secrets handled per Standing Rule 1 (never logged/printed).
3. **Durable state.** The two-phase-commit logs, kill-switches, and account-status cache are in-memory `Map`s today — they must move to the database to survive restarts and be auditable.
4. **Enforced machine credential.** Mount `apiKeyAuth` (§1.3) on the bank-facing routes (or an equivalent mTLS client-cert check) so a bank system authenticates as a machine principal rather than a logged-in session.
5. **Trustworthy tenant binding — CLOSED.** T002.5 closed in prod 2026-06-03: `x-tenant-id` header removed, tenant context session-derived and authorization-bound via `user_tenant_roles` + `utr_primary_per_user` constraint. This prerequisite is satisfied; the remaining items above are the live integration work.
6. **Live regulator transmission** (if required): wire `submitSARToFIA` / SupTech push to the actual FIA/BoU endpoints instead of internal status transitions (§3).

This list is the difference between "we can demonstrate the workflow" and "we are integrated with your core."

---

## Part 7 — Where to look in the code

| Concern | File |
|---|---|
| All HTTP routes (~1,306) | `server/routes.ts` (large — grep, don't full-read) |
| Live auth chain (session/tenant/csrf) | `server/routes.ts` ~525–632; `server/lib/tenant-middleware.ts`; `server/lib/rbac-middleware.ts` |
| DB-backed API keys (scaffolded) | `server/lib/api-key-auth.ts` |
| Demo API gateway (not the auth path) | `server/lib/api-gateway.ts` |
| CBS control surface (simulated) | `server/lib/core-banking-gateway.ts` |
| KYT rules engine (live) | `server/lib/kyt-engine.ts`, `kyt-config-*.ts` |
| AI scoring | `server/lib/ai-threat-scoring.ts`, `transaction-scoring.ts`, `ai-provider/` |
| USSD channel (demo) | `server/lib/ussd-api.ts` |
| Outbound webhooks (live) | `server/lib/webhook-delivery.ts` |
| Notifications / SIEM | `server/lib/notification-gateway.ts`, `siem-forwarder.ts` |
| Regulator / SupTech (persist, not transmit) | `server/lib/regulator-gateway.ts`, `suptech-gateway.ts` |
| OpenAPI spec generator | `server/lib/pilot-readiness-extras-8.ts` (`generateOpenApiSpec`) |
| WebSocket server | `server/routes.ts` (`WebSocketServer`, `verifyClient`, `broadcastThreat`) |

Live machine-readable contract: **`/api/public/openapi.json`** · interactive: **`/api/public/swagger`**.

---

*Maintenance note: the maturity labels above were set 2026-06-02; the tenant-binding row (§1.2) was updated 2026-06-07 to reflect T002.5 CLOSED IN PROD. The FU-052/§8.1–§8.2 remediation arc (T001–T005) is COMPLETE IN PROD (2026-06-07): 69-table RLS 16/16 prod-verified, PII encryption across 17 tables, schema-conformance boot guard, DSAR worker fixed. The CBS gateway and API-key enforcement rows remain as documented — re-verify those sections against the code before quoting them to a partner.*
