{"repo": "alphabeath/chilerisk", "files": {"docs/ARCHITECTURE.md": {"f935e34a521d0e8231a27c574fa0bd127f3eb6a1": {"t": 1785747217.8357275, "n": 5689, "h": "d010ebe7188effc3", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML) SERNAGEOMIN (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4) alertas-volcanicas\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC, SERNAGEOMIN |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; SERNAGEOMIN scrape → `sernageomin_volcanic_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile, SERNAGEOMIN) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port | Notas |\n|---------|------|-------|\n| frontend | 3000 | Healthcheck `127.0.0.1` (Alpine/IPv4) |\n| backend | 8000 (host `127.0.0.1`) | Healthcheck `127.0.0.1`; no público |\n| db | 5434 (host `127.0.0.1`) | `POSTGRES_*` vía env; no Internet |\n| adminer | 8080 (host `127.0.0.1`) | Solo profile `tools`; **no** en Dokploy |\n\n`make up` = `docker compose --profile tools up --build` (incluye Adminer). Frontend imagen: Next **standalone** + **bun**. Dockerfiles incluyen `HEALTHCHECK`. Plantilla env: [`.env.example`](../.env.example).\n\n### Dokploy (VPS) — `chilerisk.cl`\n\nApp tipo **Docker Compose** → este repo / `docker-compose.yml`:\n\n1. **DNS:** `A`/`AAAA` de `chilerisk.cl` (+ `www` si aplica) → IP del VPS.\n2. **Domains (Dokploy):** servicio **`frontend`**, puerto **3000**, HTTPS/Let’s Encrypt. El FE proxya la API (`/api/backend` → `backend:8000` en red Docker); no hace falta dominio público al backend.\n3. **No** actives profile `tools` (sin Adminer).\n4. **Environment** (pegar desde `.env.example` sección prod; mismo `AUTH_SECRET` FE+BE):\n - `AUTH_SECRET` ≥32 bytes (`openssl rand -base64 48`)\n - `AUTH_URL=https://chilerisk.cl`\n - `BACKEND_CORS_ORIGINS=[\"https://chilerisk.cl\",\"https://www.chilerisk.cl\"]`\n - `POSTGRES_PASSWORD` fuerte (y `POSTGRES_USER` / `POSTGRES_DB` si los cambias)\n - Opcional: `GOOGLE_*`, `RESEND_API_KEY`, `AUTH_EMAIL_FROM=noreply@chilerisk.cl`, `DEEPSEEK_API_KEY`\n5. **OAuth Google:** redirect `https://chilerisk.cl/api/auth/callback/google`.\n6. **Health:** IPv4 `127.0.0.1`; backend `start_period: 180s` (cold start con syncs; flood Open-Meteo va en background para no marcar unhealthy por 429).\n7. **Volumen** `pgdata` persistente; no publiques Postgres fuera del host.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-07-25*"}, "a3a61583a016977fbcdebd210bf08e0b9abfd73f": {"t": 1785771444.966782, "n": 5689, "h": "d010ebe7188effc3", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML) SERNAGEOMIN (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4) alertas-volcanicas\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC, SERNAGEOMIN |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; SERNAGEOMIN scrape → `sernageomin_volcanic_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile, SERNAGEOMIN) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port | Notas |\n|---------|------|-------|\n| frontend | 3000 | Healthcheck `127.0.0.1` (Alpine/IPv4) |\n| backend | 8000 (host `127.0.0.1`) | Healthcheck `127.0.0.1`; no público |\n| db | 5434 (host `127.0.0.1`) | `POSTGRES_*` vía env; no Internet |\n| adminer | 8080 (host `127.0.0.1`) | Solo profile `tools`; **no** en Dokploy |\n\n`make up` = `docker compose --profile tools up --build` (incluye Adminer). Frontend imagen: Next **standalone** + **bun**. Dockerfiles incluyen `HEALTHCHECK`. Plantilla env: [`.env.example`](../.env.example).\n\n### Dokploy (VPS) — `chilerisk.cl`\n\nApp tipo **Docker Compose** → este repo / `docker-compose.yml`:\n\n1. **DNS:** `A`/`AAAA` de `chilerisk.cl` (+ `www` si aplica) → IP del VPS.\n2. **Domains (Dokploy):** servicio **`frontend`**, puerto **3000**, HTTPS/Let’s Encrypt. El FE proxya la API (`/api/backend` → `backend:8000` en red Docker); no hace falta dominio público al backend.\n3. **No** actives profile `tools` (sin Adminer).\n4. **Environment** (pegar desde `.env.example` sección prod; mismo `AUTH_SECRET` FE+BE):\n - `AUTH_SECRET` ≥32 bytes (`openssl rand -base64 48`)\n - `AUTH_URL=https://chilerisk.cl`\n - `BACKEND_CORS_ORIGINS=[\"https://chilerisk.cl\",\"https://www.chilerisk.cl\"]`\n - `POSTGRES_PASSWORD` fuerte (y `POSTGRES_USER` / `POSTGRES_DB` si los cambias)\n - Opcional: `GOOGLE_*`, `RESEND_API_KEY`, `AUTH_EMAIL_FROM=noreply@chilerisk.cl`, `DEEPSEEK_API_KEY`\n5. **OAuth Google:** redirect `https://chilerisk.cl/api/auth/callback/google`.\n6. **Health:** IPv4 `127.0.0.1`; backend `start_period: 180s` (cold start con syncs; flood Open-Meteo va en background para no marcar unhealthy por 429).\n7. **Volumen** `pgdata` persistente; no publiques Postgres fuera del host.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-07-25*"}, "6a8393eec72c4e8165a5785d5562bc60413d3b72": {"t": 1785771446.457405, "n": 5621, "h": "89dff8e9f9a4c481", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML) SERNAGEOMIN (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4) alertas-volcanicas\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC, SERNAGEOMIN |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; SERNAGEOMIN scrape → `sernageomin_volcanic_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile, SERNAGEOMIN) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port | Notas |\n|---------|------|-------|\n| frontend | 3000 | Healthcheck `127.0.0.1` (Alpine/IPv4) |\n| backend | 8000 (host `127.0.0.1`) | Healthcheck `127.0.0.1`; no público |\n| db | 5434 (host `127.0.0.1`) | `POSTGRES_*` vía env; no Internet |\n| adminer | 8080 (host `127.0.0.1`) | Solo profile `tools`; **no** en Dokploy |\n\n`make up` = `docker compose --profile tools up --build` (incluye Adminer). Frontend imagen: Next **standalone** + **bun**. Dockerfiles incluyen `HEALTHCHECK`. Plantilla env: [`.env.example`](../.env.example).\n\n### Dokploy (VPS) — `chilerisk.cl`\n\nApp tipo **Docker Compose** → este repo / `docker-compose.yml`:\n\n1. **DNS:** `A`/`AAAA` de `chilerisk.cl` (+ `www` si aplica) → IP del VPS.\n2. **Domains (Dokploy):** servicio **`frontend`**, puerto **3000**, HTTPS/Let’s Encrypt. El FE proxya la API (`/api/backend` → `backend:8000` en red Docker); no hace falta dominio público al backend.\n3. **No** actives profile `tools` (sin Adminer).\n4. **Environment** (pegar desde `.env.example` sección prod; mismo `AUTH_SECRET` FE+BE):\n - `AUTH_SECRET` ≥32 bytes (`openssl rand -base64 48`)\n - `AUTH_URL=https://chilerisk.cl`\n - `BACKEND_CORS_ORIGINS=[\"https://chilerisk.cl\",\"https://www.chilerisk.cl\"]`\n - `POSTGRES_PASSWORD` fuerte (y `POSTGRES_USER` / `POSTGRES_DB` si los cambias)\n - Opcional: `GOOGLE_*`, `RESEND_API_KEY`, `AUTH_EMAIL_FROM=noreply@chilerisk.cl`, `DEEPSEEK_API_KEY`\n5. **OAuth Google:** redirect `https://chilerisk.cl/api/auth/callback/google`.\n6. **Health:** IPv4 `127.0.0.1`; backend `start_period: 180s` (cold start con syncs).\n7. **Volumen** `pgdata` persistente; no publiques Postgres fuera del host.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-07-25*"}, "7fc01550d038015258f24a870c3a32b1a24e343a": {"t": 1785771447.7186701, "n": 5414, "h": "77c533d8702eb524", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML) SERNAGEOMIN (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4) alertas-volcanicas\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC, SERNAGEOMIN |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; SERNAGEOMIN scrape → `sernageomin_volcanic_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile, SERNAGEOMIN) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port | Notas |\n|---------|------|-------|\n| frontend | 3000 | Healthcheck `127.0.0.1` (Alpine/IPv4) |\n| backend | 8000 | Healthcheck `127.0.0.1` |\n| db | 5434 (host `127.0.0.1` only) | No exponer a Internet |\n| adminer | 8080 | Solo profile `tools` (local); **no** en Dokploy/prod |\n\n`make up` = `docker compose --profile tools up --build` (incluye Adminer). Frontend imagen: Next **standalone** + **bun**. Dockerfiles incluyen `HEALTHCHECK` (deploy Dockerfile-only en Dokploy).\n\n### Dokploy (VPS)\n\nApp tipo **Docker Compose** apuntando a este repo/`docker-compose.yml`:\n\n1. **No** actives profile `tools` → Adminer no arranca ni abre el puerto 8080.\n2. Env obligatorias (mismo valor FE+BE donde aplique):\n - `AUTH_SECRET` ≥32 bytes (`openssl rand -base64 48`)\n - `AUTH_URL` = URL pública del frontend (https://…)\n - `BACKEND_CORS_ORIGINS` = JSON con el origen del frontend (ej. `[\"https://tudominio.cl\"]`)\n - OAuth/email si aplica: `GOOGLE_*`, `RESEND_API_KEY`, `AUTH_EMAIL_FROM`\n3. Dominios Traefik/Dokploy: frontend → servicio `frontend:3000`; API pública solo si la necesitas en `backend:8000` (el FE ya habla con `backend` por red interna via `BACKEND_INTERNAL_URL`).\n4. Health: probes usan IPv4 (`127.0.0.1`). Backend `start_period: 180s` — el cold start sincroniza fuentes antes de escuchar. Si el FE queda `unhealthy` con app OK, revisa `localhost`→`::1`.\n5. Postgres: volumen `pgdata`; no publiques `5432`/`5434` fuera del host.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-07-25*"}, "ad46309135dcbd4336290967d5a158a1c5192dbe": {"t": 1785771449.9359922, "n": 4147, "h": "98fc54ac3129d748", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML) SERNAGEOMIN (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4) alertas-volcanicas\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC, SERNAGEOMIN |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; SERNAGEOMIN scrape → `sernageomin_volcanic_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile, SERNAGEOMIN) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port |\n|---------|------|\n| frontend | 3000 |\n| backend | 8000 |\n| db | 5434 (host) |\n| adminer | 8080 |\n\n`make up` = build + run. Frontend imagen: Next **standalone** + **bun**.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-07-24*"}, "ea1956053955475e825f192071ccdef1f7785757": {"t": 1785771451.1637223, "n": 4015, "h": "963288b19724437c", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync Aire Chile (HTML)\n (CSN scrape) (batch REST) (Cognito Identity + SigV4)\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED, Aire Chile GEC |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Aire Chile GEC** — scrape HTML → `airechile_daily`; API `/air-quality` (cobertura parcial PPDA).\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED, Aire Chile) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port |\n|---------|------|\n| frontend | 3000 |\n| backend | 8000 |\n| db | 5434 (host) |\n| adminer | 8080 |\n\n`make up` = build + run. Frontend imagen: Next **standalone** + **bun**.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-06-17*"}, "f08477139fa6885641eb7c4c7af6c02d3cc8181b": {"t": 1785771452.4943414, "n": 3865, "h": "49354b36991363e8", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync\n (CSN scrape) (batch REST) (Cognito Identity + SigV4)\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — CSN (sismologia.cl) → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Data sources\n\nAll data comes from real providers (CSN, Open-Meteo, SERNAPRED) when the corresponding `USE_REAL_*` flags are enabled. When disabled, the source contributes no data (no synthetic/mock fallbacks).\n\nVariables documented in [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port |\n|---------|------|\n| frontend | 3000 |\n| backend | 8000 |\n| db | 5434 (host) |\n| adminer | 8080 |\n\n`make up` = build + run. Frontend imagen: Next **standalone** + **bun**.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-06-17*"}, "a898379e7a15f203dac1a5f0abea31432d92f1ff": {"t": 1785771454.0225472, "n": 4016, "h": "cc87b86d9cce4157", "c": "# ChileRisk — Architecture\n\nVista de sistema. Detalle: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md), [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md), [QUERY-DATE.md](./QUERY-DATE.md). Agentes: [../AGENTS.md](../AGENTS.md).\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ Next.js 16 │─────────▶│ + APScheduler │─────────▶│ 16 │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n sismologia.cl Open-Meteo SERNAPRED AppSync\n (CSN scrape) (batch REST) (Cognito Identity + SigV4)\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre, @dnd-kit | Mapa, overlays, consulta por día |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API, riesgo, integraciones, alertas unificadas |\n| Database | PostgreSQL 16 | Geo, scores live, snapshots diarios, eventos, SERNAPRED |\n| Scheduler | APScheduler | Risk refresh, CSN, meteo, SERNAPRED |\n\n---\n\n## Data flow\n\n1. **Geografía** — seed idempotente: 16 regiones + 346 comunas (`regional.geojson`, `comunas.geojson`).\n2. **Sismos** — mock o CSN → `seismic_events`; impacto → `seismic_impacts` al insertar.\n3. **Clima** — mock o Open-Meteo (lotes) → `climate_readings` + actualización `risk_scores`.\n4. **Riesgo live** — `risk_service.recompute_all_scores` cada N min (scheduler).\n5. **Riesgo histórico** — `daily_risk_service` materializa `daily_risk_scores` por `score_date` bajo demanda.\n6. **Alertas** — SERNAPRED sync → `senapred_alerts`; evaluador → alertas ChileRisk; API unifica `/alerts/active`.\n7. **Frontend** — `lib/api.ts` + React Query; fecha global en `ui-store` para mapa y listados.\n\nConsulta por día: [QUERY-DATE.md](./QUERY-DATE.md).\n\n---\n\n## Hybrid mode\n\n| `USE_REAL_CSN` | `USE_REAL_METEO` | `USE_REAL_SENAPRED` | Sismos | Clima | Alertas |\n|----------------|------------------|---------------------|--------|-------|---------|\n| false | false | false | Mock | Mock | Vacío / mock |\n| true | false | true | CSN | Mock | SERNAPRED |\n| true | true | true | CSN | Open-Meteo | SERNAPRED |\n\nVariables en root `.env`. Matriz startup: [backend/docs/BACKEND.md](../backend/docs/BACKEND.md).\n\n---\n\n## Deployment\n\n`docker-compose.yml` en raíz:\n\n| Service | Port |\n|---------|------|\n| frontend | 3000 |\n| backend | 8000 |\n| db | 5434 (host) |\n| adminer | 8080 |\n\n`make up` = build + run. Frontend imagen: Next **standalone** + **bun**.\n\n---\n\n## Monorepo structure\n\nPolyglot monorepo sin Turborepo:\n\n- **Ownership:** `AGENTS.md` por área; docs de stack en `backend/docs/` y `frontend/docs/`; cross-cutting en `docs/`.\n- **Docker:** contextos `./frontend` y `./backend` separados.\n- **Ignores:** `.gitignore` / `.dockerignore` por área; bun lock en frontend.\n- **Contrato:** Pydantic ↔ TypeScript manual ([DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md)).\n- **Makefile:** atajos (`up`, `clean`, `dev-frontend`, `check-ignores`).\n\nNueva área (worker, etc.): mismo patrón + entrada en [README.md](./README.md) y ARCHITECTURE.\n\n---\n\n## Documentation map\n\n| Ubicación | Role |\n|-----------|------|\n| [docs/README.md](./README.md) | Índice monorepo (cross-cutting) |\n| [backend/docs/README.md](../backend/docs/README.md) | Índice backend |\n| [frontend/docs/README.md](../frontend/docs/README.md) | Índice frontend |\n| [HARNESS.md](./HARNESS.md) | Playbooks + `make verify` |\n| [DOC-MAINTENANCE.md](./DOC-MAINTENANCE.md) | Política de actualización |\n| [backend/docs/BACKEND.md](../backend/docs/BACKEND.md) | API, modelos, servicios |\n| [frontend/docs/FRONTEND.md](../frontend/docs/FRONTEND.md) | Componentes y hooks |\n| [backend/docs/ML-INTEGRATION.md](../backend/docs/ML-INTEGRATION.md) | ML futuro |\n\n---\n\n*Last updated: 2026-06-05*"}, "b989ae0d8685535286d8ce322dc95b7d10b3fa49": {"t": 1785771455.5860834, "n": 5331, "h": "8531c009f51d6c36", "c": "# ChileRisk — Architecture\n\nHigh-level view of the system. For implementation details see `backend/AGENTS.md` and `frontend/AGENTS.md`.\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ (Next.js) │─────────▶│ + Scheduler │─────────▶│ (risk data) │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n │ External APIs (hybrid mode)\n ▼\n ┌─────────────────────┐\n │ CSN / sismologia.cl │\n │ Open-Meteo │\n └─────────────────────┘\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre GL | Interactive map + risk visualization |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API + risk computation + data integration |\n| Database | PostgreSQL 16 | Regions, comunas, risk scores, seismic events |\n| Scheduler | APScheduler | Background jobs (risk refresh, real data sync) |\n| External | CSN, Open-Meteo | Real seismic + climate data (when enabled) |\n\n---\n\n## Data Flow\n\n1. **Geography** (seeded once): 16 regions + 346 comunas with centroids\n2. **Seismic events**: mock generators OR real CSN scraper → `seismic_events` table\n3. **Climate data**: mock generators OR Open-Meteo API → `risk_scores` table\n4. **Risk engine**: recomputes all comuna scores every 15 min using Haversine + attenuation model\n5. **API**: serves aggregated risk data to frontend\n\n---\n\n## Hybrid Mode\n\n| `USE_REAL_CSN` | `USE_REAL_METEO` | Seismic source | Climate source |\n|----------------|------------------|----------------|----------------|\n| false | false | Mock | Mock |\n| true | false | CSN/sismologia.cl | Mock |\n| false | true | Mock | Open-Meteo |\n| true | true | CSN/sismologia.cl | Open-Meteo |\n\nConfigure via root `.env` file. Fallback to mocks if external APIs fail.\n\n---\n\n## Deployment\n\nSingle `docker-compose.yml` at project root:\n- `frontend` (Next.js standalone) → port 3000\n- `backend` (FastAPI + uvicorn) → port 8000\n- `db` (PostgreSQL 16) → port 5434 (host)\n- `adminer` (DB visual tool) → port 8080\n\n---\n\n## Monorepo Structure & Boundaries\n\nChileRisk is a **polyglot monorepo** (`frontend/` Next.js + `backend/` FastAPI + shared orchestration) without heavy monorepo tooling (no pnpm workspaces or Turborepo yet). Structure is enforced by a combination of:\n\n- **Directory ownership + AGENTS.md routing** (root + `frontend/AGENTS.md` + `backend/AGENTS.md`): \"all work happens exclusively inside your area\". Cross-area or root-file changes require explicit approval.\n- **Isolated Docker build contexts** (`build: ./frontend` and `build: ./backend` in compose). The root tree is *not* sent to the images.\n- **Ignore hygiene** (as of 2026-06):\n - `backend/.gitignore` — complete Python ignores (`__pycache__/`, `*.py[cod]`, envs, build artifacts, local sqlite, caches...).\n - `backend/.dockerignore` — prevents the above (and .git, .env*, logs, tests, etc.) from entering the image build context.\n - `frontend/.gitignore` + `frontend/.dockerignore` — enforce **bun** as the single package manager (package-lock.json / pnpm / yarn locks are ignored).\n - Root `.gitignore` — cross-cutting (OS, .env, TrueRisk, Docker override, top-level logs, defense-in-depth Python caches + alt package manager locks).\n- **Root `Makefile`** — convenient monorepo entrypoint (`make up`, `make clean`, `make dev-frontend`, `make check-ignores`, `make help`, etc.). Delegates to docker compose or `cd && ...`. Does not replace the per-area commands.\n- **Explicit contract sync**: Backend Pydantic schemas (`backend/app/schemas/`) and frontend TypeScript types (`frontend/lib/types.ts`) must be kept in lockstep manually (documented in root AGENTS.md). No automatic codegen today.\n\n### Why this shape?\n\n- Docker isolation + per-area manifests already gave good boundaries.\n- The original pain point (persistent `__pycache__` / `.pyc` appearing in `git status` and Docker contexts) was caused by non-recursive patterns in the old root-only Python ignore section + missing `backend/.dockerignore`.\n- Adding the per-area ignore files + cleaning the root `.gitignore` + adding `Makefile` + `ENV PYTHONDONTWRITEBYTECODE=1` (and later standardizing the frontend on bun) makes the structure self-documenting and resilient.\n\n### Adding something new\n\n- A new HTTP resource or Python service → `backend/` only (see `backend/AGENTS.md`).\n- A new UI component/hook/page → `frontend/` only (see `frontend/AGENTS.md`).\n- A cross-cutting change (new env var, API shape, root tooling, docs) → plan it and obtain approval.\n- Future extra area (e.g. a worker) → follow the same pattern (own Dockerfile, own .gitignore/.dockerignore, entry in compose, update ARCHITECTURE + AGENTS).\n\nSee also:\n- `Makefile` (root targets)\n- `backend/.gitignore`, `backend/.dockerignore`\n- `frontend/.gitignore`, `frontend/.dockerignore`\n- `root AGENTS.md` (prohibitions and routing table)\n- `backend/AGENTS.md` / `frontend/AGENTS.md` (detailed decision rules)\n\n---\n\n*Last updated: 2026-06 (monorepo hygiene + structure section added)*\n"}, "b7386989558c1dc6a8dff153cae1ea0ba5aa3e52": {"t": 1785771457.2889721, "n": 2377, "h": "b783ee8b6842427f", "c": "# ChileRisk — Architecture\n\nHigh-level view of the system. For implementation details see `backend/AGENTS.md` and `frontend/AGENTS.md`.\n\n---\n\n## System\n\n```\n┌─────────────┐ ┌──────────────────────┐ ┌─────────────────┐\n│ Frontend │ HTTP │ Backend (FastAPI) │ Async │ PostgreSQL │\n│ (Next.js) │─────────▶│ + Scheduler │─────────▶│ (risk data) │\n└─────────────┘ └──────────┬───────────┘ └─────────────────┘\n │\n │ External APIs (hybrid mode)\n ▼\n ┌─────────────────────┐\n │ CSN / sismologia.cl │\n │ Open-Meteo │\n └─────────────────────┘\n```\n\n---\n\n## Components\n\n| Layer | Tech | Purpose |\n|-------|------|---------|\n| Frontend | Next.js 16, React 19, MapLibre GL | Interactive map + risk visualization |\n| Backend | FastAPI, SQLAlchemy 2.0 async | API + risk computation + data integration |\n| Database | PostgreSQL 16 | Regions, comunas, risk scores, seismic events |\n| Scheduler | APScheduler | Background jobs (risk refresh, real data sync) |\n| External | CSN, Open-Meteo | Real seismic + climate data (when enabled) |\n\n---\n\n## Data Flow\n\n1. **Geography** (seeded once): 16 regions + 346 comunas with centroids\n2. **Seismic events**: mock generators OR real CSN scraper → `seismic_events` table\n3. **Climate data**: mock generators OR Open-Meteo API → `risk_scores` table\n4. **Risk engine**: recomputes all comuna scores every 15 min using Haversine + attenuation model\n5. **API**: serves aggregated risk data to frontend\n\n---\n\n## Hybrid Mode\n\n| `USE_REAL_CSN` | `USE_REAL_METEO` | Seismic source | Climate source |\n|----------------|------------------|----------------|----------------|\n| false | false | Mock | Mock |\n| true | false | CSN/sismologia.cl | Mock |\n| false | true | Mock | Open-Meteo |\n| true | true | CSN/sismologia.cl | Open-Meteo |\n\nConfigure via root `.env` file. Fallback to mocks if external APIs fail.\n\n---\n\n## Deployment\n\nSingle `docker-compose.yml` at project root:\n- `frontend` (Next.js standalone) → port 3000\n- `backend` (FastAPI + uvicorn) → port 8000\n- `db` (PostgreSQL 16) → port 5434 (host)\n- `adminer` (DB visual tool) → port 8080\n\n---\n\n*Last updated: 2026-05-29*\n"}}}}