Motor de workflows event-driven basado en grafos acíclicos dirigidos (DAG), con ejecución paralela asíncrona, expresiones JSONPath para payloads dinámicos y Time-Travel Debugging: recorré, auditá y reproducí el estado histórico de cualquier ejecución, nodo por nodo.
Stack: React Flow · FastAPI · PostgreSQL · asyncio
chronoflow.mateopavoni.com.ar — registrate con cualquier
email (no hay verificación, es una demo) y tu cuenta arranca con 3 workflows de ejemplo ya
cargados para explorar las features sin armar nada desde cero. Deployado en un VPS propio vía
Dokku (apps/api y apps/web como apps separadas), con deploy automático en cada push a
main (.github/workflows/deploy.yml).
| Time-Travel Debugger | Editor de DAG |
|---|---|
![]() |
![]() |
| Workflows Hub (oscuro) | Workflows Hub (claro) |
|---|---|
![]() |
![]() |
Las herramientas de automatización (Zapier, n8n, Airflow) ejecutan grafos de tareas, pero depurar por qué una corrida produjo cierto resultado suele ser opaco. ChronoFlow trata cada ejecución como una secuencia de snapshots inmutables: podés rebobinar la corrida y ver, en cada instante, qué nodos corrieron, en qué orden (incluido el paralelismo) y con qué payload de entrada/salida. Es un "debugger con viaje en el tiempo" para workflows.
No es un CRUD con formularios. El valor está en tres piezas de ingeniería real:
- Paralelismo real, no simulado — el scheduler corre por ready-set (no por niveles): dos
ramas independientes con
delay(3s)ydelay(1s)terminan en ~3s, no 4s. Comprobable en la demo con un cronómetro. - Time-Travel Debugging sobre un log
ExecutionEventappend-only — el scrubber no re-ejecuta nada, reconstruye el estado del DAG en cualquier instante a partir de snapshots inmutables. La función que lo hace es pura y está testeada. - Seguridad tratada como feature, no como afterthought: evaluador de condiciones propio
(nunca
eval), autorización por recurso conowner_id(un IDOR real se encontró y se cerró, documentado sin vueltas), guard anti-SSRF en el nodohttp, rate limiting. VerARCHITECTURE.md§7.
- Editor visual de DAG (React Flow): arrastrá nodos, conectá edges, configurá cada paso. Undo/redo, copy/paste, import/export JSON, auto-arrange.
- Ejecución paralela asíncrona: scheduler por ready-set — ramas independientes corren a la vez (dos
delay(3s)ydelay(1s)en paralelo ⇒ ~3s, no 4s). - Time-Travel Debugging: timeline scrubber paso a paso sobre
ExecutionEventappend-only. - Replay: reproducí una corrida idéntica desde su payload de disparo.
- Live: seguimiento en vivo de la corrida por WebSocket.
- Nodos:
start · transform · http · delay · branch · end. - Expresiones JSONPath para mapear payloads entre nodos + plantillas en URLs/bodies.
- Branches condicionales con evaluador propio y seguro (sin
eval). - Auth + multi-tenant: registro/login con JWT en cookie httpOnly; cada cuenta ve solo sus propios workflows/runs (autorización por recurso, no solo gate de UI).
- SSRF guard + rate limiting: el nodo
httpbloquea IPs privadas/loopback/metadata de cloud; login/registro/runs están rate-limiteados. - UI "Conductor OS": estética Swiss Minimalist / consola industrial, con tema claro/oscuro (toggle persistido, respeta
prefers-color-scheme) e íconos SVG (lucide-react).
154 tests (108 backend + 46 frontend) · 11 pull requests mergeadas con revisión propia (historial real, no un solo commit gigante) · deploy en producción con CI/CD propio · 0 desalineaciones en la auditoría de contrato front↔back.
Monorepo de 3 componentes. El detalle —modelo de dominio, contrato de API, algoritmo del
scheduler y decisiones técnicas— está en ARCHITECTURE.md.
flowchart LR
subgraph Client["apps/web — React + Vite"]
Editor["Editor (React Flow)"]
Debugger["Time-Travel Debugger"]
end
subgraph Server["apps/api — FastAPI"]
REST["REST routes\n/workflows /runs /auth"]
WS["WebSocket\n/ws/runs/:id"]
Engine["Execution Engine\nasyncio ready-set scheduler"]
end
DB[("PostgreSQL\nworkflows · runs · execution_events")]
HTTP[["Servicio externo\n(nodo http, SSRF-guarded)"]]
Editor -- "POST /workflows, /run" --> REST
Debugger -- "eventos en vivo" --> WS
REST -- "asyncio.Task" --> Engine
WS -- "push por ExecutionEvent" --> Engine
Engine -- "SQLAlchemy async" --> DB
Engine -- "httpx (nodo http)" --> HTTP
REST -- "SQLAlchemy async" --> DB
chronoflow/
├── apps/
│ ├── web/ # React + Vite + TS + React Flow (UI: editor + debugger)
│ └── api/ # FastAPI + SQLAlchemy 2.x async (engine + REST + WS)
├── docker-compose.yml # db + api + web
├── ARCHITECTURE.md # contrato central
└── docs/ # capturas, diagramas
cp .env.example .env
docker compose up --build
# web → http://localhost:8080
# api → http://localhost:8000/docs (Swagger)
# db → localhost:5432# Backend
cd apps/api
python -m venv .venv && .venv\Scripts\activate # Unix: source .venv/bin/activate
pip install -r requirements.txt
alembic upgrade head
uvicorn app.main:app --reload # http://localhost:8000/docs
# Frontend (otra terminal)
cd apps/web
npm install
npm run dev # http://localhost:5173Andá a chronoflow.mateopavoni.com.ar (o :8080/:5173
en local) y registrate con cualquier email — no hay verificación, es una demo. Al crear la
cuenta el backend siembra 3 workflows de ejemplo automáticamente. No hace falta armar nada
para ver las features clave:
1. Paralelismo real — abrí "Parallel Delays Demo" → Run con payload {}.
Dos delay (3s y 1s) corren a la vez: la corrida termina en ~3s, no 4s. En /runs/:id movés
el scrubber y ves ambos nodos arrancar en el mismo instante.
2. Branch + JSONPath + HTTP — abrí "Branch + Transform + HTTP" (condición $.trigger.amount > 100):
| Payload de disparo | Qué pasa |
|---|---|
{"amount": 150} |
rama true → fetch HTTP real + normaliza con JSONPath |
{"amount": 50, "note": "low"} |
rama false → pasa derecho (ves la poda de la rama no tomada) |
3. Time-Travel — abrí "Simple Pipeline" → Run con {"user_id": 1, "action": "login"}.
En /runs/:id recorré los snapshots inmutables por nodo (input/output en cada paso) y probá Replay.
4. Editor desde cero — desde el hub creás un workflow y armás el grafo con la paleta de la izquierda:
start → … → end. Recordá la regla del validador: exactamente un start y al menos un end.
Guía de pruebas exhaustiva (rutas de smoke-test, errores esperados, WS en vivo):
docs/QA-CHECKLIST.md.
Lighthouse (npx lighthouse, Chrome for Testing headless) contra la demo en vivo, 2026-07-20:
| Performance | Accessibility | Best Practices | SEO | |
|---|---|---|---|---|
| Mobile (throttled, preset default) | 90 | 100 | 100 | 100 |
Desktop (--preset=desktop) |
100 | 100 | 100 | 100 |
Mobile — FCP 2.6s, LCP 3.0s, TBT 0ms, CLS 0.008, TTI 3.0s. Reproducible con:
npx lighthouse https://chronoflow.mateopavoni.com.ar/ --preset=desktop # o sin --preset para mobilecd apps/api && pytest # 108 tests: engine (paralelismo, ciclos, JSONPath, time-travel),
# auth, autorización (IDOR), SSRF guard, endpoints
cd apps/web && npm run test # 46 tests: Vitest (lib puras, cliente API, componentes)
npx playwright test --config e2e/playwright.config.ts # crear → run → time-travel, 3 viewports (requiere stack levantado)- El task manager es in-process (
asyncio.create_task): ideal para la demo, no sobrevive reinicios ni escala multi-worker. En producción se reemplaza por una cola durable (Arq/Celery + Redis). - El nodo
httpes no-determinista en replay (depende de un servicio externo).
© 2026 Mateo Pavoni. Todos los derechos reservados. Software propietario, publicado solo con fines de evaluación/portfolio. Prohibida su copia, redistribución o reuso sin autorización escrita. Ver LICENSE.



