# Changelog — Pedrito Hechakuaa Todos los cambios notables se documentan acá. Formato: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Versiones: [Semantic Versioning](https://semver.org/spec/v2.0.0.html) --- ## [Unreleased] --- ## [0.3.1] — 2026-05-26 ### Security - **`/health/history` requiere auth** — `Authorization: Bearer `; sin token devuelve 401 con header `WWW-Authenticate`. Sin auth configurada, retorna 401 igualmente. - **chat_ids enmascarados en historial** — reemplazados por hash SHA256[:8] opaco (`usr_a3b4c5d6`); preserva correlación de eventos sin exponer Telegram IDs (PII). - **`/health` público mínimo** — sin auth devuelve solo `{status, timestamp}`. Con auth, respuesta completa con métricas internas. Elimina exposición de conteo de tenants y timing del poller. - **Env vars borradas del proceso** — después de cargar `Settings()`, se eliminan de `os.environ`: `FERNET_KEY`, `TELEGRAM_BOT_TOKEN`, `PJ_USUARIO_ENC`, `PJ_PASSWORD_ENC`, `WEBHOOK_SECRET`, `HEALTH_AUTH_TOKEN`, `INVITE_CODE`. Protege contra `/proc//environ`. - **Race condition TOCTOU en invite codes eliminada** — `SELECT + UPDATE` reemplazado por `UPDATE WHERE codigo=? AND usado=0`; `rowcount=0` indica código no válido o ya usado. Imposible que dos requests simultáneos consuman el mismo código. - **Timing oracle en validación de código mitigado** — delay mínimo garantizado de 800ms en `_paso_codigo()` independientemente del resultado; `secrets.compare_digest()` para comparación contra código del `.env`. - **Respuestas a usuarios no registrados reducidas al mínimo** — `_rechazar_no_registrado` ya no revela la arquitectura de comandos; texto libre de usuarios sin registro recibe silencio total. - **Webhook inválido devuelve 404** — en vez de 403, no confirma la existencia del endpoint. - **Nginx rate limiting** documentado en `DEPLOY.md`: `limit_req_zone` 30r/s para webhook, 10r/min para health; headers `X-Content-Type-Options`, `X-Frame-Options`. - **`HEALTH_AUTH_TOKEN`** agregado a `config.py` y a la guía de `.env` en `DEPLOY.md`. --- ## [0.3.0] — 2026-05-25 ### Added - **Docker** — `Dockerfile` (imagen `ghcr.io/astral-sh/uv:python3.12-bookworm-slim`), `docker-compose.yml` con `restart: unless-stopped` y healthcheck, `.dockerignore`. - **Health check HTTP** (`health.py`) — servidor aiohttp en puerto 8080: - `GET /health` — estado del bot (ok/degraded) - `GET /health/history` — últimos N eventos del audit log - Watcher loop que alerta al admin via Telegram si el poller no actualiza en N horas - **Schema migrations** — directorio `migrations/001_initial_schema.sql`, función `run_migrations()` en `database.py`; idempotente con `CREATE TABLE IF NOT EXISTS`. Tabla `schema_migrations` lleva registro de versiones aplicadas. - **Webhook opt-in** — `POST /webhook` en aiohttp valida `X-Telegram-Bot-Api-Secret-Token` con `compare_digest` y pone el Update en `application.update_queue` de PTB. Se activa con `WEBHOOK_URL` en `.env`; vacío = long-polling. - `aiohttp>=3.9` como nueva dependencia. - `WEBHOOK_URL`, `WEBHOOK_SECRET`, `HEALTH_PORT`, `HEALTH_ALERT_HOURS` en `config.py`. - Poller notifica a health vía callback `on_poll_done(tenant_count)`. - `revisar_ahora()` retorna `int` (cantidad de tenants revisados). ### Changed - `init_db()` reemplazado por `run_migrations()` en `main.py`; `init_db()` queda como alias para compatibilidad con scripts. - `main.py` arranca `HealthServer` antes del poller; elige polling vs webhook según config; registra webhook con Telegram en modo webhook; elimina webhook al detener. - `DEPLOY.md` actualizado con guía completa de 20 secciones: DNS Bluehost/cPanel (con mensaje exacto en inglés para delegar), Nginx, Certbot/SSL, Docker, webhook, health check, rate limiting. --- ## [0.2.1] — 2026-05-25 ### Fixed - **Campos del API del PJ corregidos** — la API real usa `fechaNotificacion` (no `fecha`), `descripcionDespacho` (no `juzgado`), `descripcionCircunscripcion` (no `circunscripcion`). `NotificacionPendiente.__init__` ahora usa fallback `or` para ambos nombres. - **PDF callback** — usaba `cod_notificacion_origen` como ID de actuación; corregido a `cod_actuacion_caso` (campo `codActuacionCaso` de la API). - **403 en `listar_notificaciones`** — manejado explícitamente como `CSJAuthError` en vez de caer en `raise_for_status()` genérico. --- ## [0.2.0] — 2026-05-25 ### Added - **Multi-tenant** — `database.py` con SQLite vía aiosqlite; tabla `tenants` con credenciales cifradas por chat_id; `list_active_tenants()`, `save_tenant()`, `delete_tenant()`. - **Onboarding flow** (`onboarding.py`) — máquina de estados de 8 pasos: código de invitación → consentimiento → nombre → tono → timezone → cédula → password → guardado. - **Códigos de invitación** — tabla `invite_codes`, validación de un solo uso atómica, fallback al código maestro del `.env`; `scripts/generar_codigo.py`. - **Audit log** — tabla `audit_log`, función `audit.log()` fire-and-forget; cobertura de todos los eventos de onboarding, polling, comandos y errores. - **Rate limiting** (`utils/rate_limiter.py`) — ventana deslizante por chat_id, 20 req/60s; reset en `/resetear`. - **FERNET_KEY rotation** (`scripts/rotar_fernet_key.py`) — re-cifra todos los tenants de forma atómica; hace backup de `.env` antes de modificar; aborta si algún tenant falla. - Comando `/exp` — búsqueda de expedientes por número o carátula. - Comando `/pdf` — descarga PDF de la actuación más reciente via `permisoFirmado`. - Tono personalizable por abogado: formal / cotidiano / informal. - Zona horaria configurable por abogado. - `TELEGRAM_EXTRA_IDS` para autorizar IDs adicionales sin onboarding. - `DEPLOY.md` — primera versión de la guía de despliegue en VPS Ubuntu con systemd. ### Changed - `poller.py` itera sobre todos los tenants activos (era single-tenant). - Snapshots por tenant: `data/snapshot_{chat_id}.json` (era `data/snapshot.json` global). - `vault.py` usa derivación de clave Fernet por chat_id: `base64(SHA256(FERNET_KEY + str(chat_id)))`. ### Security - Contraseña del PJ borrada de memoria inmediatamente tras cifrar (`del password` en onboarding). - Mensaje de Telegram con la contraseña eliminado del chat vía `delete_message` antes de procesar. --- ## [0.1.0] — 2026-05-25 ### Added - Login al sistema judicial `apps.csj.gov.py`: `GET /login` → cookie `cookiesession1` → `POST /autenticador/login` → bearer token (TTL 1h). - Polling de `GET /Notificaciones/PorRecibir` cada N minutos en horario hábil (lun-vie, 7-18hs). - Diff vs snapshot JSON local — solo notifica notificaciones con `codNotificacionOrigen` nuevo. - Relogin automático transparente al expirar el token. - Bot de Telegram con comandos: `/start`, `/notif`, `/estado`, `/ayuda`, `/salir`. - Credenciales del PJ cifradas con Fernet en `.env` (`scripts/cifrar_credenciales.py`). - `scrub()` en `utils/sanitize.py` — sanitiza bearer tokens, JWTs, cookies y passwords antes de pasar cualquier dato externo a los logs. - Logging estructurado con `structlog` (JSON en prod, ConsoleRenderer en dev vía `isatty`). - Configuración vía `pydantic-settings` — único punto de config de la app (`config.py`). - Stack base: `httpx`, `python-telegram-bot[ext] v21`, `aiosqlite`, `cryptography`, `structlog`, `pydantic-settings`, `apscheduler`. - Manejo de señales `SIGINT`/`SIGTERM` para shutdown limpio.