Open Climate Due-Diligence for Real Estate · Public Hazard Data · Explainable 0-100 Risk Scores · Self-Hosted API
Turn any street address into a readable climate-risk screening report with live public data, deterministic scoring rules, buyer checklists, insurance questions, and full source-level auditability.
ClimateRiskIQ is an open-source climate due-diligence screener for real estate. It geocodes a property address, pulls free public climate and hazard signals, maps those signals into transparent 0-100 risk bands, and returns a plain-English report that explains what drove the score, what a buyer should inspect, and what insurance questions to ask before moving forward.
Built for: real-estate buyers doing early property screening, agents and brokers who want a transparent pre-diligence aid, civic-tech builders exploring public climate datasets, climate-risk researchers who prefer inspectable rules over opaque scores, and developers who want a self-hosted .NET reference implementation for address-to-risk report generation.
Key capabilities: address-to-report API · live OSM Nominatim geocoding · live NASA POWER climate signals · key-gated NASA FIRMS wildfire checks · FEMA flood proxy clearly labeled as phase-1 · deterministic weighted composite scoring · per-signal explanations · buyer checklist, insurance questions, and maintenance notes · PostgreSQL persistence · OpenAPI/Swagger surface · Docker Compose self-hosting · OpenTelemetry-ready instrumentation.
ClimateRiskIQ is designed to run locally or on your own infrastructure with one Docker Compose command.
git clone https://github.com/xops-labs/ClimateRiskIQ.git
cd ClimateRiskIQ
cp .env.example .env
docker compose up --build -dOnce the stack is up:
- API / Swagger — http://localhost:8080/swagger
- Health — http://localhost:8080/health
- Provider status — http://localhost:8080/api/data-sources/status
No paid API keys are required for the default local demo. Add a free NASA_FIRMS_MAP_KEY when you want the wildfire signal included in reports.
Climate exposure now affects real-estate diligence, insurance conversations, maintenance planning, and buyer confidence. But the tools around it tend to split into two extremes: polished commercial scores that hide their model internals, or raw public datasets that require GIS fluency before they become useful.
ClimateRiskIQ sits deliberately between those poles. It is not trying to be a catastrophe model. It is a transparent screening layer: take public data, show the rules, surface the limits, and produce a report a human can challenge. Every score should be traceable back to source code and provider status, not hidden behind a private model.
| Existing path | What it gives you | What it misses |
|---|---|---|
| Commercial climate scores | Polished property-risk products | Model opacity, paid access, limited local auditability |
| Raw public climate data | Source-level public information | Hard to turn into a buyer-readable property report |
| Static hazard maps | Useful regional context | No unified address-to-report API or checklist |
| LLM-generated diligence notes | Fast narrative summaries | No deterministic scoring spine or reproducible calculations |
ClimateRiskIQ is the auditable middle layer. One address in. Public signals out. Rules in the open. Caveats shown in the report.
ClimateRiskIQ composes five data and decision surfaces into one report flow. Each surface is intentionally separable so the system can grow without hiding what changed.
| # | Surface | What the user gets |
|---|---|---|
| 1 | Address resolution | A street address converted to latitude/longitude through OSM Nominatim |
| 2 | Public data adapters | NASA POWER climate data, optional NASA FIRMS wildfire detections, and the current FEMA flood proxy |
| 3 | Signal normalization | Hazard-specific scores for flood, wildfire, heat, rainfall, drought, solar exposure, and long-term trend |
| 4 | Composite scoring | A weighted 0-100 property score over only the signals available for that report |
| 5 | Decision support | Plain-English explanation, buyer checklist, insurance questions, maintenance notes, confidence, and warnings |
| # | Capability | Where it lives | Status |
|---|---|---|---|
| 01 | Address-to-report API — submit a property address and receive a persisted report | Program.cs, ClimateRiskIQReportService.cs |
shipped |
| 02 | OSM Nominatim geocoding — live address lookup | NominatimGeocodingProvider.cs |
shipped |
| 03 | NASA POWER climate ingestion — heat, rainfall, drought, and solar inputs | NasaPowerClient.cs, NasaPowerDataParser.cs |
shipped |
| 04 | NASA FIRMS wildfire signal — recent active-fire proximity when keyed | NasaFirmsWildfireRiskProvider.cs |
shipped, key-gated |
| 05 | FEMA flood branch — clearly labeled phase-1 coastal-box proxy | FemaFloodRiskProvider.cs |
proxy |
| 06 | Deterministic scoring engine — fixed bands, weighted composite, confidence rules | RiskScoringEngine.cs, RiskLevelClassifier.cs |
shipped |
| 07 | Report recommendations — checklist, insurance questions, maintenance notes | ReportRecommendationEngine.cs, ReportContentFactory.cs |
shipped |
| 08 | Persistence — PostgreSQL-backed reports and signals | EfClimateRiskIQDataStore.cs |
shipped |
| 09 | Provider status endpoint — expose configured providers and readiness | DataSourceStatusProvider.cs |
shipped |
| 10 | Auth scaffold — demo JWT login exists but is not enforced on report routes | Program.cs |
scaffolded |
| 11 | Redis caching — Compose service exists, no code path uses it yet | docker-compose.yml |
planned |
| 12 | PDF export — report content helpers exist, no renderer yet | ClimateRiskIQ.Reports |
planned |
| Role | What ClimateRiskIQ does for you |
|---|---|
| Homebuyer / property researcher | Run a first-pass climate diligence check before paying for deeper professional review. See what hazards deserve follow-up and which questions to ask an insurer or inspector. |
| Real-estate agent or broker | Produce a transparent screening artifact for early conversations without pretending it is underwriting-grade or model-validated. |
| Civic-tech / open-data builder | Study a concrete implementation that turns public climate and hazard feeds into an address-level API, provider-status endpoint, and persisted report format. |
| Climate-risk researcher | Inspect the exact scoring bands and composite weights instead of reverse-engineering an opaque property score. |
| Software developer | Use a clean .NET 10 modular monolith with provider abstractions, EF Core persistence, OpenAPI, Docker Compose, and OpenTelemetry-ready hosting. |
ClimateRiskIQ is useful because it is explicit about what it is and what it is not.
It is:
- A rules-based screening tool that converts free public climate data into a readable, auditable risk summary for a property.
- Self-hosted and transparent — every score comes from source code you can read (see
RiskScoringEngine.csandClimateRiskIQSignalFactory.cs). No hidden model, no external scoring service. - Deterministic by design — the same inputs always produce the same output, and every number is explainable.
It is NOT:
- Not a validated catastrophe model. It performs no accuracy validation and makes no accuracy claims.
- Not a competitor to First Street, Jupiter Intelligence, ClimateCheck, Climate X, Moody's/RMS, CoreLogic, or insurer catastrophe models. Those are validated, calibrated products; this is a screening aid on open data.
- Not for underwriting, lending, insurance pricing, or regulatory/financial disclosure. Do not make money or coverage decisions from its output.
- Not AI. There is no ML, no LLM, and no statistical model anywhere in the scoring path — it is a deterministic rules engine on purpose. Transparency is the entire point.
An honest snapshot of what the current code actually does. "Live" means it makes a real network call that changes a real report.
| Source | Status | Key | Notes |
|---|---|---|---|
| NASA POWER | Live | Keyless | Daily climate at power.larc.nasa.gov. Seven parameters are fetched; 3 currently drive scoring (T2M_MAX → heat, PRECTOTCORR → rainfall + drought, ALLSKY_SFC_SW_DWN → solar). Humidity (RH2M) and wind (WS2M) are fetched but not yet used. |
| OSM Nominatim | Live | Keyless | Address → lat/lon geocoding (User-Agent only). Hard dependency today — if it fails the request fails; a fallback provider is planned. |
| NASA FIRMS | Live, key-gated | Free NASA_FIRMS_MAP_KEY |
Recent active-fire detections drive the wildfire signal. Without the key, wildfire is silently omitted from the composite and confidence drops. |
| FEMA flood | Phase-1 proxy (stub) | None | No network call. A hardcoded coastal-box heuristic emits a flood score of 58 inside roughly lat 24–32°N / lon −98…−70°W, else 35. Flood zone is always "Unknown" and confidence is always low. Live NFHL lookup is not yet implemented. |
| NOAA NCEI | Planned | NOAA_API_TOKEN (no effect today) |
Placeholder only. Makes no request and contributes nothing to any report. |
| Long-term trend | Placeholder | — | Emitted as a constant score of 35, not yet data-driven. |
Each report runs a sequential pipeline — every stage awaits the one before it; there is no concurrent fan-out.
- Geocode the address via Nominatim (hard dependency today).
- Fetch NASA POWER daily climate for the coordinates.
- Evaluate the FEMA flood proxy (in-process coastal-box heuristic).
- Fetch NASA FIRMS wildfire detections — only if a MAP key is set.
- Score & compose — build per-signal
RiskSignalrecords, compute the weighted composite, write recommendation / checklist / questions. - Persist & serve — store in PostgreSQL and return JSON.
The composite is a weighted mean, renormalized over only the signals that are available for this property.
| Signal | Weight |
|---|---|
| Flood | 0.25 |
| Wildfire | 0.20 |
| Heat | 0.15 |
| Rainfall | 0.15 |
| Drought | 0.10 |
| Long-term trend | 0.10 |
| Solar | 0.05 |
composite = round( Σ (signalScore × weight) / Σ (weight) )
The denominator (Σ weight, called the effective weight) sums only the weights of signals that were produced. If FIRMS is not keyed, wildfire's 0.20 is excluded from both sums, so the remaining weights are 0.80 and the mean is taken over that.
Every signal maps a measured value to one of a fixed set of scores. These ladders are the whole model — there is nothing else.
Heat (from average daily max temp °C, or count of high-heat days):
| Score | Condition |
|---|---|
| 90 | ≥ 40 °C or ≥ 120 high-heat days |
| 78 | ≥ 35 °C or ≥ 60 days |
| 65 | ≥ 32 °C or ≥ 30 days |
| 50 | ≥ 28 °C or ≥ 10 days |
| 35 | ≥ 24 °C |
| 15 | otherwise |
Rainfall (annual precipitation, mm): ≥2500→90 · ≥1800→75 · ≥1200→58 · ≥700→38 · else→20
Drought (annual precipitation, mm): <350→82 · <600→66 · <900→45 · else→20
Solar (kWh/m²/day): ≥6.5→85 · ≥5.5→70 · ≥4.5→50 · ≥3.5→30 · else→15
Wildfire (nearest FIRMS detection): <10 mi→92 · <25 mi→74 · <50 mi→52 · none→20
Flood (coastal-box proxy): 58 inside the coastal box, else 35
Long-term trend: constant 35
| Score | Level |
|---|---|
| 0–20 | Very Low |
| 21–40 | Low |
| 41–60 | Moderate |
| 61–80 | High |
| 81–100 | Severe |
Confidence is derived from the effective weight and the number of warnings:
- Low — effective weight < 0.5, or ≥ 3 warnings.
- Medium — any warnings present, or effective weight < 0.85.
- High — otherwise.
Because the FEMA proxy always adds a warning, report confidence can never exceed "Medium" today. That is a deliberate, honest ceiling — it will lift once a real NFHL lookup replaces the stub.
There is no black box to explain away. Explainability here means three concrete things: (1) every rule is readable source code, (2) each signal carries a plain-language explanation string, and (3) each signal names its source as a coarse label — "NASA_POWER", "FEMA_NFHL", "NASA_FIRMS". Those are labels, not URLs, DOIs, or per-record timestamps; richer provenance is on the roadmap.
The block below shows the report shape and values the current code produces for a Miami Beach address without a FIRMS key (so no wildfire signal). It is arithmetically consistent with the engine:
effective weight = .25 + .15 + .15 + .10 + .05 + .10 = 0.80
composite = round( (58×.25 + 65×.15 + 58×.15 + 20×.10 + 70×.05 + 35×.10) / 0.80 )
= round( 41.95 / 0.80 ) = round(52.44) = 52 → Moderate
Prerequisites: Docker (with Compose) and outbound internet access to NASA POWER and OSM Nominatim.
# 1. Configuration — every key is optional for the default local demo
cp .env.example .env
# 2. Build and start the stack (API + Worker + PostgreSQL + Redis)
docker compose up --build -d
# 3. Health check
curl http://localhost:8080/healthThen run the three seed addresses. Space report POSTs at least 2 seconds apart — Nominatim's usage policy is roughly one request per second.
# Miami Beach — FEMA coastal-box branch (flood 58)
curl -s -X POST http://localhost:8080/api/reports \
-H "Content-Type: application/json" \
-d '{"address":"1601 Collins Ave, Miami Beach, FL 33139"}'
sleep 2
# Houston — coastal branch (flood 58)
curl -s -X POST http://localhost:8080/api/reports \
-H "Content-Type: application/json" \
-d '{"address":"1500 McKinney St, Houston, TX 77010"}'
sleep 2
# Paradise, CA — inland (flood 35); strongest FIRMS wildfire signal when a MAP key is set
curl -s -X POST http://localhost:8080/api/reports \
-H "Content-Type: application/json" \
-d '{"address":"5637 Skyway, Paradise, CA 95969"}'Each POST returns a reportId. Fetch the full report and check provider status:
curl http://localhost:8080/api/reports/<reportId>
curl http://localhost:8080/api/data-sources/statusA demo login exists (POST /api/auth/login with demo / demo in Development), but no endpoint currently requires authorization — the API is effectively open today. Enforcing authorization is on the roadmap. In non-Development environments, JWT login requires JWT_SIGNING_KEY and an explicit LOCAL_AUTH_PASSWORD.
Endpoints are defined as ASP.NET Core minimal APIs in Program.cs.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/auth/login |
Issue a JWT (demo/demo in Development). Login works but is not enforced. |
POST |
/api/reports |
Generate a report from { "address": "..." }. Rate-limited to 30 requests/min/IP (in-memory). |
GET |
/api/reports/{id} |
Retrieve a report by GUID. |
GET |
/api/reports/{id}/risk-signals |
Return just the per-signal records for a report. |
GET |
/api/data-sources/status |
Report provider availability and key configuration. |
GET |
/health |
Liveness / readiness probe. |
Swagger UI is served at /swagger in Development and Testing.
flowchart TB
subgraph Api["ClimateRiskIQ.Api — minimal API endpoints (.NET 10)"]
E1["POST /api/auth/login"]
E2["POST /api/reports"]
E3["GET /api/reports/{id}"]
E4["GET /api/reports/{id}/risk-signals"]
E5["GET /api/data-sources/status"]
E6["GET /health"]
end
subgraph App["ClimateRiskIQ.Application — services"]
SVC["ClimateRiskIQReportService"]
ENG["RiskScoringEngine (rules)"]
FAC["ClimateRiskIQSignalFactory"]
end
subgraph Domain["ClimateRiskIQ.Domain — entities"]
ENT["Property · ClimateRiskIQReport · RiskSignal · RawClimateData"]
end
subgraph Infra["ClimateRiskIQ.Infrastructure — providers + EF Core"]
GEO["Geocoding — Nominatim (live)"]
POWER["NASA POWER (live)"]
FIRMS["NASA FIRMS (live, key-gated)"]
FEMA["FEMA flood — proxy stub"]
NOAA["NOAA NCEI — planned"]
end
subgraph Worker["ClimateRiskIQ.Worker"]
HB["no-op heartbeat (scheduled jobs planned)"]
end
subgraph Reports["ClimateRiskIQ.Reports"]
RC["report content factories (PDF export planned)"]
end
subgraph Data["Data plane"]
PG[("PostgreSQL 16")]
REDIS[("Redis — provisioned, not yet used")]
end
subgraph Ext["External"]
X1["OSM Nominatim"]
X2["NASA POWER"]
X3["NASA FIRMS"]
end
E2 --> SVC
E3 --> SVC
E4 --> SVC
E5 --> SVC
SVC --> ENG --> FAC
SVC --> GEO & POWER & FIRMS & FEMA
SVC --> ENT
SVC --> RC
SVC --> PG
GEO --> X1
POWER --> X2
FIRMS --> X3
The Worker is a heartbeat that logs one line every five minutes and touches nothing else. ClimateRiskIQ.Reports holds string-list content factories (checklist / insurance / maintenance) — there is no PDF renderer yet. Redis is wired into the compose stack but no code reads or writes it today.
| Layer | Technology | Why |
|---|---|---|
| API | ASP.NET Core minimal APIs on .NET 10 | Compact OpenAPI-first service surface for report creation, lookup, provider status, health, and local login |
| Application | C# services and provider abstractions | Keeps orchestration, scoring, recommendations, and DTO shaping separate from infrastructure adapters |
| Domain | Pure C# entities | Property, report, risk signal, and raw climate data types with no I/O dependencies |
| Infrastructure | EF Core, PostgreSQL, OSM Nominatim, NASA POWER, NASA FIRMS | Swappable data adapters and durable report storage |
| Reports | Business-readable content factories | Buyer checklist, insurance questions, and maintenance-risk text without a PDF dependency yet |
| Worker | .NET hosted worker | Scheduled portfolio/rescoring work is planned; current worker is a no-op heartbeat |
| Container | Docker Compose | API, worker, PostgreSQL, and Redis in one reproducible local stack |
| Observability | OpenTelemetry | Optional OTLP traces/metrics when OTEL_EXPORTER_OTLP_ENDPOINT is configured |
| Tests | xUnit, EF Core InMemory, hand-written fakes | Offline tests for scoring math, report assembly, and provider parsing behavior |
| Document | Purpose |
|---|---|
| Architecture overview | Modular-monolith layout, project responsibilities, and report-generation flow |
| API endpoints | Minimal API route list, request/response shapes, auth caveat, and provider status endpoint |
| Provider status | Current truth table for live providers, stubs, placeholders, and keyed integrations |
| Roadmap | Planned work, explicit non-goals, and the rule for when a capability may be called shipped |
| Changelog | Released changes once planned work becomes real implementation |
All variables are environment-based; the Docker Compose stack sets sensible local defaults.
| Variable | Default | Required? |
|---|---|---|
ConnectionStrings__Postgres |
local Compose Postgres | Required (defaulted in Compose) |
ConnectionStrings__Redis |
empty (redis:6379 in Compose; unused today) |
Optional |
Database__ApplyMigrationsOnStartup |
false (true in Compose) |
Optional |
JWT_SIGNING_KEY |
random in Development/Testing | Required outside Development/Testing |
LOCAL_AUTH_USERNAME |
demo |
Optional |
LOCAL_AUTH_PASSWORD |
demo (Development/Testing only) |
Optional; required for login outside Development |
NASA_FIRMS_MAP_KEY |
empty | Optional — enables the wildfire signal |
NOAA_API_TOKEN |
empty | Optional — no effect today (NOAA is planned) |
GEOCODING_API_KEY |
empty | Optional — bound but not read by the current Nominatim provider (reserved) |
OTEL_EXPORTER_OTLP_ENDPOINT |
unset | Optional — export traces/metrics to your collector |
dotnet test ClimateRiskIQ.slnTests use xUnit only, run fully offline (EF Core InMemory plus hand-written fakes — no live HTTP), and are green today. Coverage is strongest on the aggregation math (band thresholds, weighted composite, renormalization, confidence rules); provider HTTP paths are exercised through fakes rather than against live services.
Toolchain: .NET 10 SDK (10.0.200), solution file ClimateRiskIQ.sln.
See ROADMAP.md for detail. In priority order:
- Live FEMA NFHL flood lookup to replace the coastal-box proxy.
- Real NOAA NCEI integration.
- A data-driven long-term trend signal to replace the constant.
- Enforced endpoint authorization (the API is open today).
- Put fetched humidity and wind to use in scoring.
- A geocoding fallback so Nominatim is no longer a hard dependency.
- Redis caching for geocoding and hot reports.
- PDF export of reports.
- A validation study before any accuracy claim is ever made.
Standing rule: nothing is called "shipped" until it makes a real call and changes a real report.
- NASA POWER and NASA FIRMS data are courtesy of NASA. This project is not endorsed by or affiliated with NASA.
- Geocoding © OpenStreetMap contributors, available under the Open Database License (ODbL). Use of the public Nominatim service is subject to its usage policy (roughly one request per second, descriptive User-Agent required).
Disclaimer: ClimateRiskIQ is a screening aid, not professional advice. It does not validate its outputs and must not be used for underwriting, lending, insurance pricing, or regulatory disclosure. Always confirm hazard status with authoritative sources and qualified professionals before acting.
- CONTRIBUTING.md
- SECURITY.md — report vulnerabilities via GitHub security advisories
- CODE_OF_CONDUCT.md
- CHANGELOG.md
Questions and bug reports: please open a GitHub issue.
climate-risk · climate-data · real-estate · due-diligence · property-risk · hazard-risk · flood-risk · wildfire-risk · heat-risk · drought · nasa-power · nasa-firms · open-data · rules-engine · self-hosted · dotnet · aspnet-core · postgresql · docker · opentelemetry
Licensed under the Apache License 2.0 — see LICENSE.
Copyright 2026 Yasvanth Udayakumar.
ClimateRiskIQ · Transparent climate-risk screening you can run, read, and challenge.