PortfolioCheck · Architecture-fit sheet For a bank / Bank of Ghana IT & infrastructure review Security briefing Knowledge Centre Technical spec /health Home

Architecture-fit sheet

Deployed version 2.12.0. What the tool is built from, where its data lives, how it lands on your servers, and how it relates to ORASS. Every statement maps to code or a configuration flag your team can verify.
Footprint (current runtime): on-prem; a single process on one host, outbound calls all opt-in and OFF by default. One Python process, one SQLite file, CPU-only — no cloud, no external database server, no borrower PII required.

Quick reference — the questions IT/QA open with

What language is it written in?Python 3 (runs on 3.11 or newer).
What framework / web server?FastAPI, served by Uvicorn behind the institution's own Nginx/TLS. Pages are server-rendered HTML — no heavyweight JavaScript framework to vet.
What third-party dependencies does it pull in?Three: fastapi, uvicorn[standard], python-multipart. Everything else is the Python standard library (sqlite3, hashlib, secrets, urllib). No numpy/pandas, no GIS runtime, no ML framework.
Where is the data stored?A single SQLite file on the host's local disk — default data/portfoliocheck.db beside the app, overridable with the PORTFOLIOCHECK_DB environment variable. No separate database server to provision, licence or patch.
Does any data leave the machine?No, by default. The built-in analyser (the offline default) makes zero outbound calls; every external caller is opt-in and OFF; OFFLINE=1 hard-disables all of them. It can run fully air-gapped inside your perimeter.
Is there any cloud dependency?None. No SaaS back-end, no multi-tenant cloud store, no external licence server.
What compute does it need?One modest CPU-only Linux VM. No GPU. The 200-asset demo screens in well under a second on a single core.
How are users authenticated?Passwords hashed with PBKDF2-HMAC-SHA256, server-side sessions, Secure/HttpOnly/SameSite cookies, login throttling. No public sign-up.
How is it monitored / is it alive?GET /health returns status, version and the offline flag; the service is memory-capped and self-restarts under systemd.
How is it backed up?Copy the one SQLite file. The deploy archive excludes the data directory, so a redeploy can never overwrite a saved book.

System architecture — schematic

How it runs on your estate: one request path, one process, one file — inside your perimeter — and the screening pipeline that turns a book into a supervisory return.

BANK / BoG PERIMETER — nothing leaves by default Bank staff browser · any device Nginx + TLS reverse proxy · your cert G-CRFR Platform 1 FastAPI · uvicorn process systemd · unprivileged SQLite file local disk · you own it SafeGround / FloodGuard 127.0.0.1 · opt-in · lat/lon only HTTPS :8012 read / write loopback External callers (address geocoder, GhanaPostGPS, rainfall) are opt-in and OFF by default; OFFLINE=1 disables every outbound call. No cloud, no external database server.

Processing pipeline — the same steps every screen follows, deterministic and stdlib-only:

1 · Ingestbook via CSV or /api/screen — no borrower PII
→
2 · Geolocatecoordinates · GhanaPostGPS · geocode
→
3 · Screen 6 hazardsflood · coastal · heat · riparian · land · drought
→
4 · Compositevulnerability band + confidence (PCAF)
→
5 · Hazard → moneyAAL → repricing → climate ECL
→
6 · Project & adaptIPCC/NGFS scenarios · evidence-gated adaptation
→
Outputsdashboard · board report · BoG Para-48 return

Deployment topologies — you choose, we don't prescribe

A

BoG-hosted supervisor instance Recommended

One VM inside BoG, behind your reverse proxy and SSO. Banks submit their climate return; BoG aggregates, peer-benchmarks and runs the system-wide view. This is the SARB-style «constrained bottom-up» home for the tool.

B

Bank-side instances, indicators to BoG Max data-minimisation

Each bank runs its own copy; only screened / aggregated indicators flow to BoG. Raw borrower and loan-level data never leaves the bank — strongest fit for Act 843 and inter-institution confidentiality.

C

Container image Optional

If your platform team standardises on OCI/Docker it packages cleanly — but the default «one Python process + one file» is less than a container and usually the lower-risk option for a first deployment.

First install — from a bare VM to running

On-premise (tiers A/B). For managed hosting (tier C) there is nothing to install — we host it and provision your team a login. There is no public download or self-sign-up: installs are provisioned. Full commands are in web/DEPLOY.md.

  1. Provision one Linux VM you control — CPU-only (~1 core / 1 GB is ample), with Python 3.11+ and Nginx. No database server, no GPU, no cloud account.
  2. Place the release and build the runtime (three dependencies, nothing else):
    tar xzf portfoliocheck-<ver>.tgz -C /opt/portfoliocheck
    python3 -m venv /opt/portfoliocheck/.venv
    /opt/portfoliocheck/.venv/bin/pip install -r /opt/portfoliocheck/web/requirements.txt  # fastapi, uvicorn, python-multipart
    chown -R www-data:www-data /opt/portfoliocheck
  3. Install as a service behind TLS — the systemd unit (User=www-data, memory-capped, port 8012) and an Nginx vhost proxying 127.0.0.1:8012 with a certificate (templates ship in infra/).
  4. Choose the data posture on the unit's environment: default is the built-in analyser (pure local compute, offline); PORTFOLIOCHECK_OFFLINE=1 for a hard air-gap; or point PORTFOLIOCHECK_SAFEGROUND_URL / _FLOODGUARD_URL at the on-box engines to add the live engine overlay. Every external caller stays OFF unless explicitly enabled.
  5. Provision accounts (no public sign-up):
    cd /opt/portfoliocheck && .venv/bin/python manage.py adduser \
        risk@yourbank.com 'strong-passphrase' --bank "Your Bank" --role maker
  6. Verify — the install is not done until these pass:
    curl -s http://127.0.0.1:8012/health  # {"status":"ok", ... }
    curl -s http://127.0.0.1:8012/diag    # migrations_ok:true, schema stamped
  7. Optional — calibrate once you have loss data: point PORTFOLIOCHECK_PARAMS at a JSON parameter set; its name then appears on every report.

Thereafter every update is applied with infra/update.sh (checksum → back up code + DB → apply → health-gate → auto-rollback) — see Fleet operations below.

Relationship to ORASS — we consume the pipe, we don't replace it

Bank of Ghana already runs ORASS (Vizor/Regnology) as its returns-submission channel. ORASS has no climate module — that is the gap this tool fills. PortfolioCheck is the analytics / computation layer, never a rival submission portal. A dedicated adapter (connectors/orass.py) is the seam, switched by one environment variable PORTFOLIOCHECK_ORASS_URL:

ModeBehaviour
Console-native
flag unset — pilot default
Banks file their climate return directly through the console upload path. No ORASS work needed to run a pilot today.
ORASS-routed
flag set
The identical return is pushed to / pulled from the ORASS API, so it flows through BoG's existing pipe. The adapter ships the interface today; the ORASS HTTP calls are wired at integration time against BoG's API contract, and the transport is fail-soft (a submission never breaks if ORASS is unreachable).
Bank loan bookproperty location + loan attributes (no borrower PII)
→
SubmissionORASS pipe — or console upload before integration
→
PortfolioCheckscreen + compute (AAL → ECL, IFRS 9 signal, PML)
→
BoG return + supervisor viewPara 48 return (CSV/Word) + system aggregation

Integration surface (fits your existing stack)

Fleet operations — one codebase, many banks, no vendor access

Enterprise IT & data — scale, identity, retention, logging, DR

Sizing, backups & operations

Verify any claim against the source: language & deps in the project manifest, the SQLite store and PORTFOLIOCHECK_DB in core/store.py, the ORASS seam in connectors/orass.py, connector gating (*_ONLINE, OFFLINE) in connectors/*.py, routes/headers in web/app.py, and fleet operations in docs/FLEET.md + infra/update.sh + core/diagnostics.py. See the Security & Data-Protection briefing for the full data-flow and controls. Screening-grade and illustrative until calibrated on the institution's own loss data.