flowforge-io 1.3.0__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. {flowforge_io-1.3.0/flowforge_io.egg-info → flowforge_io-2.0.0}/PKG-INFO +68 -30
  2. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/README.md +62 -27
  3. flowforge_io-2.0.0/flowforge/__init__.py +1 -0
  4. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/app.py +25 -14
  5. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/auth.py +44 -5
  6. flowforge_io-2.0.0/flowforge/api/email_utils.py +22 -0
  7. flowforge_io-2.0.0/flowforge/api/routes/ai.py +45 -0
  8. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/auth.py +34 -1
  9. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/password_reset.py +3 -18
  10. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/pipelines.py +20 -0
  11. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/setup.py +3 -15
  12. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/users.py +74 -3
  13. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/validators.py +20 -0
  14. flowforge_io-2.0.0/flowforge/brain.py +21 -0
  15. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/celery_app.py +7 -1
  16. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/base.py +20 -1
  17. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/bigquery.py +2 -1
  18. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/factory.py +47 -15
  19. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/mssql.py +2 -1
  20. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/mysql.py +2 -1
  21. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/odbc.py +2 -1
  22. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/oracle.py +2 -1
  23. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/postgres.py +2 -1
  24. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/snowflake.py +2 -1
  25. flowforge_io-2.0.0/flowforge/db/migrations/versions/0033_must_change_password.py +26 -0
  26. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/auth.py +4 -0
  27. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/context.py +52 -7
  28. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/notifications.py +15 -4
  29. flowforge_io-2.0.0/flowforge/net_guard.py +175 -0
  30. flowforge_io-2.0.0/flowforge/steps/ai_analyze.py +20 -0
  31. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/base.py +1 -1
  32. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/bulk_load.py +55 -2
  33. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/notification.py +7 -5
  34. {flowforge_io-1.3.0 → flowforge_io-2.0.0/flowforge_io.egg-info}/PKG-INFO +68 -30
  35. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/SOURCES.txt +7 -2
  36. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/requires.txt +6 -2
  37. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/pyproject.toml +13 -4
  38. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_app_coverage.py +45 -0
  39. flowforge_io-2.0.0/tests/test_brain_gate.py +120 -0
  40. flowforge_io-2.0.0/tests/test_bulk_load_maint01.py +184 -0
  41. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_step.py +8 -2
  42. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connection_base.py +21 -0
  43. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_leader_election.py +0 -1
  44. flowforge_io-2.0.0/tests/test_no_llm_in_core.py +53 -0
  45. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_notification_step.py +16 -16
  46. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_oracle_bulk_load.py +2 -2
  47. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipelines.py +64 -0
  48. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_plugin_loader.py +12 -7
  49. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner.py +5 -5
  50. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner_extended.py +4 -4
  51. flowforge_io-2.0.0/tests/test_safe_urlopen.py +236 -0
  52. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_setup_extended.py +0 -50
  53. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_users.py +127 -0
  54. flowforge_io-1.3.0/flowforge/__init__.py +0 -1
  55. flowforge_io-1.3.0/flowforge/api/routes/ai.py +0 -344
  56. flowforge_io-1.3.0/flowforge/net_guard.py +0 -63
  57. flowforge_io-1.3.0/flowforge/steps/ai_analyze.py +0 -222
  58. flowforge_io-1.3.0/tests/test_ai.py +0 -488
  59. flowforge_io-1.3.0/tests/test_ai_analyze.py +0 -438
  60. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/LICENSE +0 -0
  61. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/__init__.py +0 -0
  62. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/pipeline_service.py +0 -0
  63. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/project_access.py +0 -0
  64. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/__init__.py +0 -0
  65. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/audit.py +0 -0
  66. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/bulk_loads.py +0 -0
  67. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/connections.py +0 -0
  68. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/emails.py +0 -0
  69. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/metrics.py +0 -0
  70. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/mfa.py +0 -0
  71. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/projects.py +0 -0
  72. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/providers.py +0 -0
  73. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/recipients.py +0 -0
  74. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/registry.py +0 -0
  75. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/reports.py +0 -0
  76. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/runs.py +0 -0
  77. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/settings.py +0 -0
  78. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/sso.py +0 -0
  79. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/steps.py +0 -0
  80. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/serializers.py +0 -0
  81. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/audit.py +0 -0
  82. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/cli.py +0 -0
  83. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/__init__.py +0 -0
  84. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/redshift.py +0 -0
  85. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/ssh.py +0 -0
  86. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/crypto.py +0 -0
  87. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/__init__.py +0 -0
  88. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/__init__.py +0 -0
  89. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/env.py +0 -0
  90. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0001_baseline.py +0 -0
  91. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0002_timezone_timestamps.py +0 -0
  92. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0003_indexes_and_constraints.py +0 -0
  93. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0004_json_report_format.py +0 -0
  94. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0005_bulk_load_configs.py +0 -0
  95. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0006_projects.py +0 -0
  96. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0007_pipeline_variables_index.py +0 -0
  97. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0008_token_blocklist.py +0 -0
  98. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0009_narrow_db_type_constraint.py +0 -0
  99. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0010_webhook_tokens.py +0 -0
  100. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0011_fix_step_type_constraint.py +0 -0
  101. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0012_onedrive_support.py +0 -0
  102. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0013_ai_analyze_step.py +0 -0
  103. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0014_sftp_transfer_step.py +0 -0
  104. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0015_datetime_timezone.py +0 -0
  105. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0016_pipeline_failure_webhook.py +0 -0
  106. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0017_mysql_connection_type.py +0 -0
  107. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0018_user_role.py +0 -0
  108. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019_backfill_admin_role.py +0 -0
  109. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019a_add_auditlog_model.py +0 -0
  110. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019b_add_sshconnection_and_update_step_type.py +0 -0
  111. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019c_add_send_only_on_failure_to_ff_pipelines.py +0 -0
  112. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019d_add_ssh_health_check_step_type.py +0 -0
  113. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0020_mfa_sso.py +0 -0
  114. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0021_mssql_odbc_pwreset.py +0 -0
  115. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0023_pipeline_deps_parallel.py +0 -0
  116. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0024_report_column_formatting.py +0 -0
  117. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0025_new_providers_notification.py +0 -0
  118. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0026_relax_step_type_constraint.py +0 -0
  119. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0027_snowflake_bigquery_redshift.py +0 -0
  120. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0028_project_members.py +0 -0
  121. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0029_system_settings.py +0 -0
  122. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0030_recipient_group_cc_bcc.py +0 -0
  123. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0031_email_body_format.py +0 -0
  124. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0032_step_dependencies.py +0 -0
  125. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/__init__.py +0 -0
  126. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/__init__.py +0 -0
  127. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/_shared.py +0 -0
  128. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/audit.py +0 -0
  129. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/bulk_load.py +0 -0
  130. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/connections.py +0 -0
  131. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/email.py +0 -0
  132. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/pipelines.py +0 -0
  133. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/projects.py +0 -0
  134. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/reports.py +0 -0
  135. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/settings.py +0 -0
  136. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/__init__.py +0 -0
  137. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/base.py +0 -0
  138. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/factory.py +0 -0
  139. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/gmail.py +0 -0
  140. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/mailgun.py +0 -0
  141. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/microsoft365.py +0 -0
  142. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/sendgrid.py +0 -0
  143. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/ses.py +0 -0
  144. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/smtp.py +0 -0
  145. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/__init__.py +0 -0
  146. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/cleanup.py +0 -0
  147. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/concurrency.py +0 -0
  148. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/dag.py +0 -0
  149. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/dependency_trigger.py +0 -0
  150. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/launcher.py +0 -0
  151. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/leader.py +0 -0
  152. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/loader.py +0 -0
  153. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/run_records.py +0 -0
  154. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/runner.py +0 -0
  155. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/scheduler.py +0 -0
  156. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/settings.py +0 -0
  157. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/shutdown.py +0 -0
  158. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/step_exec.py +0 -0
  159. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/waves.py +0 -0
  160. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/registry.py +0 -0
  161. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/__init__.py +0 -0
  162. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/csv_report.py +0 -0
  163. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/excel_report.py +0 -0
  164. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/health_report.py +0 -0
  165. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/json_report.py +0 -0
  166. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/pdf_report.py +0 -0
  167. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/__init__.py +0 -0
  168. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/azure_blob_upload.py +0 -0
  169. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/data_load.py +0 -0
  170. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_health_check.py +0 -0
  171. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_procedure.py +0 -0
  172. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_query.py +0 -0
  173. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/drive_upload.py +0 -0
  174. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/email_step.py +0 -0
  175. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/onedrive_upload.py +0 -0
  176. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/report.py +0 -0
  177. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/s3_upload.py +0 -0
  178. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/script_report.py +0 -0
  179. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/sftp_transfer.py +0 -0
  180. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/ssh_command.py +0 -0
  181. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/ssh_health_check.py +0 -0
  182. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/__init__.py +0 -0
  183. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/azure_blob.py +0 -0
  184. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/google_drive.py +0 -0
  185. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/onedrive.py +0 -0
  186. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/s3.py +0 -0
  187. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/tasks.py +0 -0
  188. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/dependency_links.txt +0 -0
  189. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/entry_points.txt +0 -0
  190. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/top_level.txt +0 -0
  191. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/setup.cfg +0 -0
  192. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_audit.py +0 -0
  193. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_audit_routes.py +0 -0
  194. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_auth.py +0 -0
  195. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_auth_coverage.py +0 -0
  196. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_azure_blob_upload.py +0 -0
  197. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_configs.py +0 -0
  198. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_dry_run.py +0 -0
  199. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_preview.py +0 -0
  200. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_validate_route.py +0 -0
  201. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_loads_coverage.py +0 -0
  202. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cleanup.py +0 -0
  203. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cli_cleanup.py +0 -0
  204. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_concurrency.py +0 -0
  205. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_concurrency_failure_injection.py +0 -0
  206. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections.py +0 -0
  207. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_bigquery.py +0 -0
  208. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_coverage.py +0 -0
  209. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_factory.py +0 -0
  210. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_factory_coverage.py +0 -0
  211. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_mssql_odbc.py +0 -0
  212. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_redshift.py +0 -0
  213. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_snowflake.py +0 -0
  214. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_context.py +0 -0
  215. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cron_endpoint.py +0 -0
  216. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_crypto.py +0 -0
  217. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_crypto_file_encryption.py +0 -0
  218. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_dag_engine.py +0 -0
  219. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_dag_integration.py +0 -0
  220. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_data_load_create.py +0 -0
  221. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_data_load_step.py +0 -0
  222. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_health_check.py +0 -0
  223. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_health_check_coverage.py +0 -0
  224. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_procedure_unit.py +0 -0
  225. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_query_capture.py +0 -0
  226. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_drive_upload_step.py +0 -0
  227. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_configs.py +0 -0
  228. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers.py +0 -0
  229. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers_ses_sg_mg.py +0 -0
  230. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers_unit.py +0 -0
  231. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_step_coverage.py +0 -0
  232. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_step_unit.py +0 -0
  233. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_emails_coverage.py +0 -0
  234. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_engine_settings.py +0 -0
  235. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_excel_report_coverage.py +0 -0
  236. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_jinja_errors.py +0 -0
  237. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_jwt_expiry.py +0 -0
  238. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_launcher_extended.py +0 -0
  239. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_launcher_unit.py +0 -0
  240. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_loader_unit.py +0 -0
  241. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_metrics_coverage.py +0 -0
  242. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mfa.py +0 -0
  243. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mysql.py +0 -0
  244. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mysql_extended.py +0 -0
  245. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_net_guard.py +0 -0
  246. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_onedrive.py +0 -0
  247. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_oracle.py +0 -0
  248. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_password_reset.py +0 -0
  249. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipeline_variables.py +0 -0
  250. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipelines_extended_coverage.py +0 -0
  251. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_plugin_loader_multi_category.py +0 -0
  252. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_postgres_unit.py +0 -0
  253. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_project_members.py +0 -0
  254. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_projects.py +0 -0
  255. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_providers_extended.py +0 -0
  256. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_rbac.py +0 -0
  257. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_recipients.py +0 -0
  258. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry.py +0 -0
  259. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry_api.py +0 -0
  260. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry_contract.py +0 -0
  261. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_e2e.py +0 -0
  262. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_generators.py +0 -0
  263. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_step_coverage.py +0 -0
  264. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_reports.py +0 -0
  265. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner_dag_gate.py +0 -0
  266. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs.py +0 -0
  267. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs_api.py +0 -0
  268. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs_extended.py +0 -0
  269. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_s3_upload.py +0 -0
  270. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler.py +0 -0
  271. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler_jobstore_persistence.py +0 -0
  272. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler_prune.py +0 -0
  273. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_script_report.py +0 -0
  274. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_serializers.py +0 -0
  275. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_settings_routes.py +0 -0
  276. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_sftp.py +0 -0
  277. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_shutdown.py +0 -0
  278. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_shutdown_cancel_runs.py +0 -0
  279. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_smart_attachments.py +0 -0
  280. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_smtp_test_method.py +0 -0
  281. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_command.py +0 -0
  282. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_connection_unit.py +0 -0
  283. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_health_check.py +0 -0
  284. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_sso.py +0 -0
  285. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_step_dependencies_model.py +0 -0
  286. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_steps_db.py +0 -0
  287. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_steps_extended.py +0 -0
  288. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_validators.py +0 -0
  289. {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowforge-io
3
- Version: 1.3.0
3
+ Version: 2.0.0
4
4
  Summary: Database-driven pipeline orchestrator. Configure everything from the UI — no YAML required.
5
5
  License: MIT License
6
6
 
@@ -79,10 +79,12 @@ Provides-Extra: sftp
79
79
  Requires-Dist: paramiko>=3.0; extra == "sftp"
80
80
  Provides-Extra: mssql
81
81
  Requires-Dist: pyodbc>=5.0; extra == "mssql"
82
+ Provides-Extra: redis
83
+ Requires-Dist: redis>=5.0; extra == "redis"
84
+ Provides-Extra: celery
85
+ Requires-Dist: celery[redis]>=5.6; extra == "celery"
82
86
  Provides-Extra: ses
83
87
  Requires-Dist: boto3>=1.34; extra == "ses"
84
- Provides-Extra: claude
85
- Requires-Dist: anthropic>=0.25; extra == "claude"
86
88
  Provides-Extra: sso
87
89
  Requires-Dist: google-auth-oauthlib>=1.2; extra == "sso"
88
90
  Requires-Dist: msal>=1.28; extra == "sso"
@@ -109,6 +111,7 @@ Requires-Dist: flowforge-io[oracle]; extra == "all"
109
111
  Requires-Dist: flowforge-io[mysql]; extra == "all"
110
112
  Requires-Dist: flowforge-io[sftp]; extra == "all"
111
113
  Requires-Dist: flowforge-io[mssql]; extra == "all"
114
+ Requires-Dist: flowforge-io[celery]; extra == "all"
112
115
  Requires-Dist: flowforge-io[ses]; extra == "all"
113
116
  Requires-Dist: flowforge-io[sso]; extra == "all"
114
117
  Requires-Dist: flowforge-io[snowflake]; extra == "all"
@@ -129,7 +132,7 @@ Dynamic: license-file
129
132
 
130
133
  # FlowForge
131
134
 
132
- **Database-driven pipeline orchestrator. Configure everything from the UI — DB procedures, reports, email (Gmail/M365/SMTP), Google Drive, OneDrive, SFTP, smart attachments, scheduling. No YAML. No Airflow complexity.**
135
+ **Enterprise Telecom Revenue Assurance: a deterministic data-orchestration core (the "Muscle") paired with a separate AI root-cause engine (the "Brain"). Configure everything from the UI — no YAML, no Airflow complexity.**
133
136
 
134
137
  [![Tests](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml/badge.svg)](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml)
135
138
  [![codecov](https://codecov.io/gh/jagdeepvirdi/flowforge/graph/badge.svg)](https://codecov.io/gh/jagdeepvirdi/flowforge)
@@ -140,11 +143,45 @@ Dynamic: license-file
140
143
 
141
144
  ---
142
145
 
143
- ## What is FlowForge?
146
+ ## Product Vision
144
147
 
145
- FlowForge runs ordered data pipelines: call database procedures, run queries, generate reports (Excel/PDF/CSV/JSON), send emails with smart attachment handling, upload files to Google Drive or OneDrive, and transfer files over SFTP. Everything is configured through a web frontend — no config files to edit, no Python DAGs to write.
148
+ Telecom operators lose millions to **revenue leakage**: CDRs dropped between the switch and billing, tariffs that
149
+ rate usage wrongly, and subscriptions that are active in the network but not billed (or the reverse). FlowForge is
150
+ being built to find that leakage on a schedule — by combining **heavy-duty data orchestration** with **AI-assisted
151
+ root-cause analysis** — and to put the findings, charts and diagnoses in one place for the enterprise user.
146
152
 
147
- Designed for solo developers, data analysts, and small teams who need lightweight automation without the overhead of enterprise orchestration tools.
153
+ > **Status, honestly:** the orchestration engine below is real, tested and shipping today. The telecom-specific
154
+ > layer (CDR/mediation tooling, reconciliation steps, leakage KPIs, case management) and the Brain are the
155
+ > **direction**, not yet shipped features. [`ARCHITECTURE.md` §7](ARCHITECTURE.md#7-telecom-revenue-assurance-mapping)
156
+ > maps each use case to what exists and what is missing.
157
+
158
+ ## Architecture: "Muscle" and "Brain"
159
+
160
+ We strictly separate **deterministic data pipelines** from **non-deterministic LLM operations**:
161
+
162
+ | | **Muscle — FlowForge Core** *(this repository)* | **Brain — FlowForge Intelligence** *(separate service)* |
163
+ |---|---|---|
164
+ | Role | Deterministic data orchestrator and ETL pipeline | Non-deterministic AI diagnostic engine |
165
+ | Stack | Python 3.11+ / Flask / PostgreSQL (+ Oracle and others for customer data), APScheduler, optional Celery + Redis, React frontend | FastAPI (Python), Google Gemini via Vertex AI / AI Studio |
166
+ | Does | Ingests CDRs / mediation logs / CRM data at volume; runs complex SQL audits (e.g. Network HLR vs. Billing status); schedules, retries, alerts | Performs Root Cause Analysis: given anonymized error evidence (e.g. 5,000 dropped sessions) it returns a structured, plain-English diagnosis and remediation step |
167
+ | AI/LLM code | **None. Ever.** | All of it |
168
+ | Must be | Fast, predictable, highly available | Optional — everything works without it |
169
+
170
+ The **React frontend** is the single pane of glass: it pulls operational data and charts from Core and fetches AI
171
+ diagnostics from the Brain over HTTP. If no Brain is attached, no AI UI is shown.
172
+
173
+ **Rules the core must keep** (full detail in [`ARCHITECTURE.md`](ARCHITECTURE.md#8-hard-rules)):
174
+ - The core backend **never imports LLM SDKs** (no `anthropic`, `openai`, `google-generativeai`, `vertexai`,
175
+ `langchain`, …) and contains no prompts, model names or AI provider keys.
176
+ - The core **never makes long-running or long-polling AI calls** and never waits on the Brain. Hand-offs to the
177
+ Brain are asynchronous and off the pipeline's critical path, so a slow or unavailable model can never block an
178
+ API worker, the scheduler, or a data pipeline.
179
+ - Evidence is **anonymized and minimized in the core** before it leaves; Brain output is advisory and never
180
+ triggers changes automatically.
181
+
182
+ ## What is FlowForge Core?
183
+
184
+ FlowForge Core runs ordered data pipelines: call database procedures, run queries, generate reports (Excel/PDF/CSV/JSON), send emails with smart attachment handling, upload files to Google Drive, OneDrive, S3 or Azure Blob, and transfer files over SFTP. Everything is configured through a web frontend — no config files to edit, no Python DAGs to write. These are the building blocks for the revenue-assurance audits above.
148
185
 
149
186
  ---
150
187
 
@@ -153,7 +190,7 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
153
190
  ### Pipeline Engine
154
191
  - **No-YAML config** — pipelines, reports, email templates, and schedules are all managed in the UI
155
192
  - **Multi-project workspace** — organize resources by team or department in one instance, with team-scoped project membership
156
- - **17 step types** — db_procedure, db_query, report, email, drive_upload, onedrive_upload, s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer, ssh_command, ssh_health_check, db_health_check, data_report, notification (Slack/Teams/Telegram), ai_analyze
193
+ - **16 step types** — db_procedure, db_query, report, email, drive_upload, onedrive_upload, s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer, ssh_command, ssh_health_check, db_health_check, data_report, notification (Slack/Teams/Telegram)
157
194
  - **Plugin system** — drop a custom `BaseStep` subclass into `FLOWFORGE_PLUGIN_DIR` to add community/internal step types without forking
158
195
  - **Pipeline dependencies** — chain pipelines so a downstream one launches automatically after all its upstreams succeed; cycle detection included
159
196
  - **Parallel step execution** — group steps with `parallel_group` to run them concurrently in a pipeline
@@ -195,7 +232,7 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
195
232
  - **Visual cron builder**: frequency presets, live expression preview, next-5-runs display
196
233
  - **Hot reload**: schedule changes sync to the scheduler automatically every 60 seconds
197
234
  - **Celery / Redis task queue** (optional): when `FLOWFORGE_REDIS_URL` is set, pipeline runs are dispatched as Celery tasks; falls back to background threads without Redis
198
- - **Distributed concurrency lock**: Redis-backed run-limit lock so multiple API/worker instances can be scaled horizontally without over-running a pipeline
235
+ - **Distributed concurrency lock & rate limiting**: Redis-backed run-limit lock and API rate limits so multiple API/worker instances can be scaled horizontally without over-running a pipeline or under-enforcing a rate limit (each otherwise falls back to a per-process limit — correct for a single worker only)
199
236
  - **Graceful shutdown**: SIGTERM handler drains in-flight runs before exit
200
237
 
201
238
  ### Observability & Production Ops
@@ -204,14 +241,13 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
204
241
  - **Gunicorn + Nginx deployment guide**: gevent workers, systemd units, TLS termination — see `docs/deployment.md` / `RUNBOOK.md`
205
242
  - **SQLAlchemy pool tuning**: pool size/overflow/timeout/recycle configurable via env vars, with PgBouncer guidance for 100+ concurrent pipelines
206
243
 
207
- ### AI Features (Ollama, Claude API, or Gemini API)
208
- All AI-powered UI features (below) run through Ollama by default (local, zero cost) and fall back to Claude or Gemini — in that order — if Ollama is unreachable and one of `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` is set. Configure in Settings → AI.
209
- - **AI chart generator**: "Visualize" button on Report Preview — sends column names + sample rows to the AI provider; renders result with Recharts (bar, line, area, pie, scatter)
210
- - **SQL Explainer**: "Explain" button in the SQL editor — plain-English summary of what a query does
211
- - **SQL Optimizer**: "Optimize" button — side-by-side diff of original vs AI-suggested rewrite; accept with one click
212
- - **Pipeline failure diagnosis**: "Explain this error" on failed step logs — 2–4 sentence cause and fix
213
- - **Data Profiler**: one-click narrative summary of query result (structure, ranges, nulls, outliers)
214
- - **Run history anomaly alerts**: statistical outlier detection (>2σ) on row counts and durations; AI narrative when anomaly is detected
244
+ ### Run-Health Anomaly Detection (deterministic)
245
+ - **Statistical outlier detection** (>2σ) on per-step row counts and durations across run history — flagged in the Run Detail view. No AI involved.
246
+
247
+ ### AI Diagnostics — external (not part of this repository's runtime)
248
+ Root-cause analysis lives in the separate **Brain** service ([`ARCHITECTURE.md`](ARCHITECTURE.md)). When a Brain is attached
249
+ (`FLOWFORGE_BRAIN_URL`), the UI can show its diagnoses next to the run or finding they explain; when it is not,
250
+ FlowForge behaves exactly the same minus those panels.
215
251
 
216
252
  ### Security
217
253
  - **AES-256-GCM** credential encryption for all DB and email provider configs stored in the database
@@ -223,7 +259,7 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
223
259
  - **Env var allowlist** (`FLOWFORGE_TEMPLATE_ENV_VARS`) — restrict which env vars are accessible in templates
224
260
  - **Path containment** check on report file download — prevents directory traversal
225
261
  - **Procedure name validation** — blocks SQL injection via stored procedure names
226
- - **SSRF guard** on outbound webhook/notification URLs — rejects internal/private-network targets
262
+ - **SSRF guard** on every editor-configurable outbound URL (notification steps and the pipeline failure webhook) — rejects internal/private-network targets, never follows redirects, and connects to the exact IP it validated (no DNS-rebinding window); unsafe webhook URLs are also rejected at save time
227
263
  - **SFTP strict host key checking** (`FLOWFORGE_SFTP_STRICT_HOSTKEYS=true`)
228
264
  - **Audit log** — every login, pipeline run, config change, and webhook trigger logged with username attribution; file (rotating 10 MB × 5) + structured JSON stdout
229
265
  - **SAST in CI** — `bandit` (Python) + `npm audit` on every push; TruffleHog secrets scan; property-based fuzz tests (Hypothesis)
@@ -249,7 +285,7 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
249
285
  | Google Drive / OneDrive | Built-in | DIY | DIY | DIY |
250
286
  | Smart attachments | Built-in | DIY | DIY | DIY |
251
287
  | Multi-user roles | Built-in | Built-in | Built-in | DIY |
252
- | AI analysis | Built-in (Ollama/Claude/Gemini) | DIY | DIY | DIY |
288
+ | AI root-cause analysis | Separate optional Brain service | DIY | DIY | DIY |
253
289
  | Scheduling | Built-in UI | Built-in | Built-in | OS cron |
254
290
  | Run history / logs | Built-in UI | Built-in | Built-in | DIY |
255
291
  | SFTP transfer | Built-in | Plugin | Plugin | DIY |
@@ -277,7 +313,6 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
277
313
  | `db_health_check` | Collect DB health metrics (sessions, cache hit ratio, replication lag, etc.); outputs a report file |
278
314
  | `data_report` | Run an arbitrary script and turn its output into a report |
279
315
  | `notification` | Send a message to Slack, Microsoft Teams, or Telegram |
280
- | `ai_analyze` | Run a SQL query, summarize results with Ollama, Claude, or Gemini; result available as `{{ ai_summary }}` |
281
316
 
282
317
  Custom step types, database connections, and email providers can also be added via the [plugin system](docs/plugins.md) without forking FlowForge.
283
318
 
@@ -297,7 +332,7 @@ Custom step types, database connections, and email providers can also be added v
297
332
  | Task queue | Celery + Redis (optional — threads used when Redis is not configured) |
298
333
  | Auth | Username/password + JWT, MFA (TOTP), SSO (Google, Microsoft, SAML 2.0) |
299
334
  | Monitoring | Prometheus metrics endpoint, Celery Flower dashboard |
300
- | AI (optional) | Ollama (local, zero cost), Claude API (Anthropic), Gemini API (Google, free tier) |
335
+ | AI (optional) | External Brain microservice — FastAPI + Gemini (Vertex AI / AI Studio); not part of this repo's runtime |
301
336
 
302
337
  ---
303
338
 
@@ -309,7 +344,7 @@ Custom step types, database connections, and email providers can also be added v
309
344
  git clone https://github.com/jagdeepvirdi/flowforge.git
310
345
  cd flowforge
311
346
  cp .env.example .env
312
- # Edit .env — set FLOWFORGE_SECRET_KEY and FLOWFORGE_PASSWORD at minimum
347
+ # Edit .env — set FLOWFORGE_SECRET_KEY, FLOWFORGE_JWT_SECRET, and FLOWFORGE_PASSWORD at minimum
313
348
  docker compose up
314
349
  ```
315
350
 
@@ -346,17 +381,16 @@ pip install -e ".[ses]" # AWS SES email provider + S3 upload
346
381
  pip install -e ".[azure_blob]" # Azure Blob Storage upload
347
382
  pip install -e ".[sftp]" # SFTP transfer step (paramiko)
348
383
  pip install -e ".[sso]" # SSO: Google, Microsoft, SAML 2.0
349
- pip install -e ".[claude]" # ai_analyze step via Claude API
384
+ pip install -e ".[celery]" # Celery task queue (FLOWFORGE_REDIS_URL) — pulls in redis too
385
+ pip install -e ".[redis]" # Redis-backed concurrency limit + rate limiting, no Celery task queue
350
386
  pip install -e ".[all]" # Everything above
351
387
  ```
352
388
 
353
- `ai_analyze` step via Gemini API needs no extra install — it's a plain REST call, just set `GEMINI_API_KEY`.
354
-
355
389
  #### 3. Configure
356
390
 
357
391
  ```bash
358
392
  cp .env.example .env
359
- # Set FLOWFORGE_DB_URL, FLOWFORGE_SECRET_KEY, and FLOWFORGE_PASSWORD at minimum
393
+ # Set FLOWFORGE_DB_URL, FLOWFORGE_SECRET_KEY, FLOWFORGE_JWT_SECRET, and FLOWFORGE_PASSWORD at minimum
360
394
  ```
361
395
 
362
396
  #### 4. Initialize the database and create the admin user
@@ -457,7 +491,6 @@ Available in every config field rendered via Jinja2:
457
491
  | `{{ steps.name.drive_url }}` | Drive / OneDrive URL from a previous step |
458
492
  | `{{ steps.name.rows_affected }}` | Row count from a previous step |
459
493
  | `{{ steps.name.table_html }}` | HTML table of captured query rows |
460
- | `{{ steps.name.ai_summary }}` | AI narrative from an `ai_analyze` step |
461
494
  | `{{ my_var }}` / `{{ vars.my_var }}` | Pipeline-level variable (secrets encrypted at rest) |
462
495
 
463
496
  ---
@@ -501,7 +534,7 @@ flowforge/
501
534
  │ ├── steps/ # db_procedure, db_query, report, email, drive_upload, onedrive_upload,
502
535
  │ │ # s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer,
503
536
  │ │ # ssh_command, ssh_health_check, db_health_check, script_report,
504
- │ │ # notification, ai_analyze
537
+ │ │ # notification
505
538
  │ ├── connections/ # PostgreSQL, Oracle, MySQL, MSSQL, ODBC, Snowflake, BigQuery, Redshift + factory
506
539
  │ ├── email_providers/ # Gmail, M365, SMTP, SendGrid, SES, Mailgun + factory
507
540
  │ ├── reports/ # Excel, CSV, PDF, JSON generators
@@ -516,7 +549,9 @@ flowforge/
516
549
  │ │ # RunHistory, Settings, Users, BulkLoads, Projects, Login, AuditLog
517
550
  │ ├── components/ # pipeline/, email/, report/, runs/, shared/
518
551
  │ └── lib/ # api.ts, auth.ts, types.ts, store.ts
519
- ├── docs/ # Guides and runbook
552
+ │ # lib/generated/ — OpenAPI-generated types (see docs/openapi/)
553
+ ├── docs/
554
+ │ └── openapi/ # Hand-authored OpenAPI specs (source of truth for lib/generated/)
520
555
  ├── tests/ # pytest suite (1,800+ tests)
521
556
  ├── .github/workflows/ # GitHub Actions CI
522
557
  ├── docker-compose.yml # API + frontend + PostgreSQL + scheduler + Redis + worker
@@ -532,6 +567,8 @@ flowforge/
532
567
 
533
568
  ## Documentation
534
569
 
570
+ - [`ARCHITECTURE.md`](ARCHITECTURE.md) — Muscle/Brain architecture, integration flows, hard rules, telecom RA mapping
571
+ - [`docs/BRAIN_DECOUPLING_PLAN.md`](docs/BRAIN_DECOUPLING_PLAN.md) — plan for extracting the legacy in-repo AI code
535
572
  - [`docs/getting-started.md`](docs/getting-started.md) — end-to-end setup walkthrough
536
573
  - [`docs/step-types.md`](docs/step-types.md) — full config reference for all step types
537
574
  - [`docs/email-providers.md`](docs/email-providers.md) — SMTP, M365, Gmail, SendGrid, SES, and Mailgun setup guides
@@ -560,6 +597,7 @@ flowforge/
560
597
  | v1.1.0 | ✅ | MySQL / MariaDB, OneDrive / SharePoint upload, SFTP transfer step, AI features (chart generator, SQL explainer/optimizer, failure diagnosis, data profiler, anomaly alerts), pipeline clone, YAML import/export, step retry + backoff, failure webhook, webhook/API trigger, JWT revocation, RBAC guards, SAST in CI |
561
598
  | v1.2.0 | ✅ | Multi-user roles (admin / editor / viewer), user management UI, role-based frontend visibility, Celery / Redis task queue, `flowforge worker` CLI, responsive mobile layout, audit log username attribution |
562
599
  | Since v1.2.0 | ✅ | SSH steps (command, health check) + DB health check + script reports; Gunicorn/Nginx production hardening, connection-pool tuning, Prometheus metrics, Celery Flower; MFA (TOTP), SSO (Google/Microsoft/SAML 2.0), GDPR export/purge, IP allowlisting, report encryption at rest, secrets scanning, password reset; MSSQL/ODBC/Snowflake/BigQuery/Redshift connectors; SendGrid/SES/Mailgun providers + Slack/Teams/Telegram notification step; S3/Azure Blob upload; plugin step-type system; pipeline dependencies + parallel step execution; run diff view; report column formatting; environment promotion; team-scoped project membership; distributed Redis concurrency lock; fuzzing (Hypothesis), signed releases (SLSA), PyPI publish; full Tailwind CSS frontend migration; visual pipeline canvas (list/graph toggle in the pipeline editor); step-level DAG dependencies (real edge-drawing on the canvas, branch-scoped `on_error: stop`, ancestors-only step context) |
600
+ | **Direction (2026-10-27)** | 🧭 | Telecom Revenue Assurance: extract AI into the external Brain service; CDR/mediation tooling, reconciliation steps, conditional alerting, leakage KPIs and case management — see [`ARCHITECTURE.md`](ARCHITECTURE.md) |
563
601
  | **Not yet built** | — | OSS-Fuzz registration; cosign-signed release artifacts |
564
602
 
565
603
  ---
@@ -570,7 +608,7 @@ flowforge/
570
608
 
571
609
  **Feedback:** Use [GitHub Issues](https://github.com/jagdeepvirdi/flowforge/issues) to report bugs or request features. Please include your FlowForge version, Python version, OS, steps to reproduce, and any relevant log output from `ff_step_runs.logs`.
572
610
 
573
- **Contribute:** See [`CONTRIBUTING.md`](CONTRIBUTING.md) for dev setup, running tests, adding step types, and the PR process. All contributions welcome — bug fixes, new step types, documentation improvements, and test coverage.
611
+ **Contribute:** See [`CONTRIBUTING.md`](CONTRIBUTING.md) for dev setup, running tests, adding step types, and the PR process. Contributions must respect the core's architectural rule: **no LLM SDKs, prompts or blocking AI calls in this repository** ([`ARCHITECTURE.md` §8](ARCHITECTURE.md#8-hard-rules)). All contributions welcome — bug fixes, new step types, documentation improvements, and test coverage.
574
612
 
575
613
  ---
576
614
 
@@ -1,6 +1,6 @@
1
1
  # FlowForge
2
2
 
3
- **Database-driven pipeline orchestrator. Configure everything from the UI — DB procedures, reports, email (Gmail/M365/SMTP), Google Drive, OneDrive, SFTP, smart attachments, scheduling. No YAML. No Airflow complexity.**
3
+ **Enterprise Telecom Revenue Assurance: a deterministic data-orchestration core (the "Muscle") paired with a separate AI root-cause engine (the "Brain"). Configure everything from the UI — no YAML, no Airflow complexity.**
4
4
 
5
5
  [![Tests](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml/badge.svg)](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml)
6
6
  [![codecov](https://codecov.io/gh/jagdeepvirdi/flowforge/graph/badge.svg)](https://codecov.io/gh/jagdeepvirdi/flowforge)
@@ -11,11 +11,45 @@
11
11
 
12
12
  ---
13
13
 
14
- ## What is FlowForge?
14
+ ## Product Vision
15
15
 
16
- FlowForge runs ordered data pipelines: call database procedures, run queries, generate reports (Excel/PDF/CSV/JSON), send emails with smart attachment handling, upload files to Google Drive or OneDrive, and transfer files over SFTP. Everything is configured through a web frontend — no config files to edit, no Python DAGs to write.
16
+ Telecom operators lose millions to **revenue leakage**: CDRs dropped between the switch and billing, tariffs that
17
+ rate usage wrongly, and subscriptions that are active in the network but not billed (or the reverse). FlowForge is
18
+ being built to find that leakage on a schedule — by combining **heavy-duty data orchestration** with **AI-assisted
19
+ root-cause analysis** — and to put the findings, charts and diagnoses in one place for the enterprise user.
17
20
 
18
- Designed for solo developers, data analysts, and small teams who need lightweight automation without the overhead of enterprise orchestration tools.
21
+ > **Status, honestly:** the orchestration engine below is real, tested and shipping today. The telecom-specific
22
+ > layer (CDR/mediation tooling, reconciliation steps, leakage KPIs, case management) and the Brain are the
23
+ > **direction**, not yet shipped features. [`ARCHITECTURE.md` §7](ARCHITECTURE.md#7-telecom-revenue-assurance-mapping)
24
+ > maps each use case to what exists and what is missing.
25
+
26
+ ## Architecture: "Muscle" and "Brain"
27
+
28
+ We strictly separate **deterministic data pipelines** from **non-deterministic LLM operations**:
29
+
30
+ | | **Muscle — FlowForge Core** *(this repository)* | **Brain — FlowForge Intelligence** *(separate service)* |
31
+ |---|---|---|
32
+ | Role | Deterministic data orchestrator and ETL pipeline | Non-deterministic AI diagnostic engine |
33
+ | Stack | Python 3.11+ / Flask / PostgreSQL (+ Oracle and others for customer data), APScheduler, optional Celery + Redis, React frontend | FastAPI (Python), Google Gemini via Vertex AI / AI Studio |
34
+ | Does | Ingests CDRs / mediation logs / CRM data at volume; runs complex SQL audits (e.g. Network HLR vs. Billing status); schedules, retries, alerts | Performs Root Cause Analysis: given anonymized error evidence (e.g. 5,000 dropped sessions) it returns a structured, plain-English diagnosis and remediation step |
35
+ | AI/LLM code | **None. Ever.** | All of it |
36
+ | Must be | Fast, predictable, highly available | Optional — everything works without it |
37
+
38
+ The **React frontend** is the single pane of glass: it pulls operational data and charts from Core and fetches AI
39
+ diagnostics from the Brain over HTTP. If no Brain is attached, no AI UI is shown.
40
+
41
+ **Rules the core must keep** (full detail in [`ARCHITECTURE.md`](ARCHITECTURE.md#8-hard-rules)):
42
+ - The core backend **never imports LLM SDKs** (no `anthropic`, `openai`, `google-generativeai`, `vertexai`,
43
+ `langchain`, …) and contains no prompts, model names or AI provider keys.
44
+ - The core **never makes long-running or long-polling AI calls** and never waits on the Brain. Hand-offs to the
45
+ Brain are asynchronous and off the pipeline's critical path, so a slow or unavailable model can never block an
46
+ API worker, the scheduler, or a data pipeline.
47
+ - Evidence is **anonymized and minimized in the core** before it leaves; Brain output is advisory and never
48
+ triggers changes automatically.
49
+
50
+ ## What is FlowForge Core?
51
+
52
+ FlowForge Core runs ordered data pipelines: call database procedures, run queries, generate reports (Excel/PDF/CSV/JSON), send emails with smart attachment handling, upload files to Google Drive, OneDrive, S3 or Azure Blob, and transfer files over SFTP. Everything is configured through a web frontend — no config files to edit, no Python DAGs to write. These are the building blocks for the revenue-assurance audits above.
19
53
 
20
54
  ---
21
55
 
@@ -24,7 +58,7 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
24
58
  ### Pipeline Engine
25
59
  - **No-YAML config** — pipelines, reports, email templates, and schedules are all managed in the UI
26
60
  - **Multi-project workspace** — organize resources by team or department in one instance, with team-scoped project membership
27
- - **17 step types** — db_procedure, db_query, report, email, drive_upload, onedrive_upload, s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer, ssh_command, ssh_health_check, db_health_check, data_report, notification (Slack/Teams/Telegram), ai_analyze
61
+ - **16 step types** — db_procedure, db_query, report, email, drive_upload, onedrive_upload, s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer, ssh_command, ssh_health_check, db_health_check, data_report, notification (Slack/Teams/Telegram)
28
62
  - **Plugin system** — drop a custom `BaseStep` subclass into `FLOWFORGE_PLUGIN_DIR` to add community/internal step types without forking
29
63
  - **Pipeline dependencies** — chain pipelines so a downstream one launches automatically after all its upstreams succeed; cycle detection included
30
64
  - **Parallel step execution** — group steps with `parallel_group` to run them concurrently in a pipeline
@@ -66,7 +100,7 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
66
100
  - **Visual cron builder**: frequency presets, live expression preview, next-5-runs display
67
101
  - **Hot reload**: schedule changes sync to the scheduler automatically every 60 seconds
68
102
  - **Celery / Redis task queue** (optional): when `FLOWFORGE_REDIS_URL` is set, pipeline runs are dispatched as Celery tasks; falls back to background threads without Redis
69
- - **Distributed concurrency lock**: Redis-backed run-limit lock so multiple API/worker instances can be scaled horizontally without over-running a pipeline
103
+ - **Distributed concurrency lock & rate limiting**: Redis-backed run-limit lock and API rate limits so multiple API/worker instances can be scaled horizontally without over-running a pipeline or under-enforcing a rate limit (each otherwise falls back to a per-process limit — correct for a single worker only)
70
104
  - **Graceful shutdown**: SIGTERM handler drains in-flight runs before exit
71
105
 
72
106
  ### Observability & Production Ops
@@ -75,14 +109,13 @@ Designed for solo developers, data analysts, and small teams who need lightweigh
75
109
  - **Gunicorn + Nginx deployment guide**: gevent workers, systemd units, TLS termination — see `docs/deployment.md` / `RUNBOOK.md`
76
110
  - **SQLAlchemy pool tuning**: pool size/overflow/timeout/recycle configurable via env vars, with PgBouncer guidance for 100+ concurrent pipelines
77
111
 
78
- ### AI Features (Ollama, Claude API, or Gemini API)
79
- All AI-powered UI features (below) run through Ollama by default (local, zero cost) and fall back to Claude or Gemini — in that order — if Ollama is unreachable and one of `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` is set. Configure in Settings → AI.
80
- - **AI chart generator**: "Visualize" button on Report Preview — sends column names + sample rows to the AI provider; renders result with Recharts (bar, line, area, pie, scatter)
81
- - **SQL Explainer**: "Explain" button in the SQL editor — plain-English summary of what a query does
82
- - **SQL Optimizer**: "Optimize" button — side-by-side diff of original vs AI-suggested rewrite; accept with one click
83
- - **Pipeline failure diagnosis**: "Explain this error" on failed step logs — 2–4 sentence cause and fix
84
- - **Data Profiler**: one-click narrative summary of query result (structure, ranges, nulls, outliers)
85
- - **Run history anomaly alerts**: statistical outlier detection (>2σ) on row counts and durations; AI narrative when anomaly is detected
112
+ ### Run-Health Anomaly Detection (deterministic)
113
+ - **Statistical outlier detection** (>2σ) on per-step row counts and durations across run history — flagged in the Run Detail view. No AI involved.
114
+
115
+ ### AI Diagnostics — external (not part of this repository's runtime)
116
+ Root-cause analysis lives in the separate **Brain** service ([`ARCHITECTURE.md`](ARCHITECTURE.md)). When a Brain is attached
117
+ (`FLOWFORGE_BRAIN_URL`), the UI can show its diagnoses next to the run or finding they explain; when it is not,
118
+ FlowForge behaves exactly the same minus those panels.
86
119
 
87
120
  ### Security
88
121
  - **AES-256-GCM** credential encryption for all DB and email provider configs stored in the database
@@ -94,7 +127,7 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
94
127
  - **Env var allowlist** (`FLOWFORGE_TEMPLATE_ENV_VARS`) — restrict which env vars are accessible in templates
95
128
  - **Path containment** check on report file download — prevents directory traversal
96
129
  - **Procedure name validation** — blocks SQL injection via stored procedure names
97
- - **SSRF guard** on outbound webhook/notification URLs — rejects internal/private-network targets
130
+ - **SSRF guard** on every editor-configurable outbound URL (notification steps and the pipeline failure webhook) — rejects internal/private-network targets, never follows redirects, and connects to the exact IP it validated (no DNS-rebinding window); unsafe webhook URLs are also rejected at save time
98
131
  - **SFTP strict host key checking** (`FLOWFORGE_SFTP_STRICT_HOSTKEYS=true`)
99
132
  - **Audit log** — every login, pipeline run, config change, and webhook trigger logged with username attribution; file (rotating 10 MB × 5) + structured JSON stdout
100
133
  - **SAST in CI** — `bandit` (Python) + `npm audit` on every push; TruffleHog secrets scan; property-based fuzz tests (Hypothesis)
@@ -120,7 +153,7 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
120
153
  | Google Drive / OneDrive | Built-in | DIY | DIY | DIY |
121
154
  | Smart attachments | Built-in | DIY | DIY | DIY |
122
155
  | Multi-user roles | Built-in | Built-in | Built-in | DIY |
123
- | AI analysis | Built-in (Ollama/Claude/Gemini) | DIY | DIY | DIY |
156
+ | AI root-cause analysis | Separate optional Brain service | DIY | DIY | DIY |
124
157
  | Scheduling | Built-in UI | Built-in | Built-in | OS cron |
125
158
  | Run history / logs | Built-in UI | Built-in | Built-in | DIY |
126
159
  | SFTP transfer | Built-in | Plugin | Plugin | DIY |
@@ -148,7 +181,6 @@ All AI-powered UI features (below) run through Ollama by default (local, zero co
148
181
  | `db_health_check` | Collect DB health metrics (sessions, cache hit ratio, replication lag, etc.); outputs a report file |
149
182
  | `data_report` | Run an arbitrary script and turn its output into a report |
150
183
  | `notification` | Send a message to Slack, Microsoft Teams, or Telegram |
151
- | `ai_analyze` | Run a SQL query, summarize results with Ollama, Claude, or Gemini; result available as `{{ ai_summary }}` |
152
184
 
153
185
  Custom step types, database connections, and email providers can also be added via the [plugin system](docs/plugins.md) without forking FlowForge.
154
186
 
@@ -168,7 +200,7 @@ Custom step types, database connections, and email providers can also be added v
168
200
  | Task queue | Celery + Redis (optional — threads used when Redis is not configured) |
169
201
  | Auth | Username/password + JWT, MFA (TOTP), SSO (Google, Microsoft, SAML 2.0) |
170
202
  | Monitoring | Prometheus metrics endpoint, Celery Flower dashboard |
171
- | AI (optional) | Ollama (local, zero cost), Claude API (Anthropic), Gemini API (Google, free tier) |
203
+ | AI (optional) | External Brain microservice — FastAPI + Gemini (Vertex AI / AI Studio); not part of this repo's runtime |
172
204
 
173
205
  ---
174
206
 
@@ -180,7 +212,7 @@ Custom step types, database connections, and email providers can also be added v
180
212
  git clone https://github.com/jagdeepvirdi/flowforge.git
181
213
  cd flowforge
182
214
  cp .env.example .env
183
- # Edit .env — set FLOWFORGE_SECRET_KEY and FLOWFORGE_PASSWORD at minimum
215
+ # Edit .env — set FLOWFORGE_SECRET_KEY, FLOWFORGE_JWT_SECRET, and FLOWFORGE_PASSWORD at minimum
184
216
  docker compose up
185
217
  ```
186
218
 
@@ -217,17 +249,16 @@ pip install -e ".[ses]" # AWS SES email provider + S3 upload
217
249
  pip install -e ".[azure_blob]" # Azure Blob Storage upload
218
250
  pip install -e ".[sftp]" # SFTP transfer step (paramiko)
219
251
  pip install -e ".[sso]" # SSO: Google, Microsoft, SAML 2.0
220
- pip install -e ".[claude]" # ai_analyze step via Claude API
252
+ pip install -e ".[celery]" # Celery task queue (FLOWFORGE_REDIS_URL) — pulls in redis too
253
+ pip install -e ".[redis]" # Redis-backed concurrency limit + rate limiting, no Celery task queue
221
254
  pip install -e ".[all]" # Everything above
222
255
  ```
223
256
 
224
- `ai_analyze` step via Gemini API needs no extra install — it's a plain REST call, just set `GEMINI_API_KEY`.
225
-
226
257
  #### 3. Configure
227
258
 
228
259
  ```bash
229
260
  cp .env.example .env
230
- # Set FLOWFORGE_DB_URL, FLOWFORGE_SECRET_KEY, and FLOWFORGE_PASSWORD at minimum
261
+ # Set FLOWFORGE_DB_URL, FLOWFORGE_SECRET_KEY, FLOWFORGE_JWT_SECRET, and FLOWFORGE_PASSWORD at minimum
231
262
  ```
232
263
 
233
264
  #### 4. Initialize the database and create the admin user
@@ -328,7 +359,6 @@ Available in every config field rendered via Jinja2:
328
359
  | `{{ steps.name.drive_url }}` | Drive / OneDrive URL from a previous step |
329
360
  | `{{ steps.name.rows_affected }}` | Row count from a previous step |
330
361
  | `{{ steps.name.table_html }}` | HTML table of captured query rows |
331
- | `{{ steps.name.ai_summary }}` | AI narrative from an `ai_analyze` step |
332
362
  | `{{ my_var }}` / `{{ vars.my_var }}` | Pipeline-level variable (secrets encrypted at rest) |
333
363
 
334
364
  ---
@@ -372,7 +402,7 @@ flowforge/
372
402
  │ ├── steps/ # db_procedure, db_query, report, email, drive_upload, onedrive_upload,
373
403
  │ │ # s3_upload, azure_blob_upload, data_load, bulk_load, sftp_transfer,
374
404
  │ │ # ssh_command, ssh_health_check, db_health_check, script_report,
375
- │ │ # notification, ai_analyze
405
+ │ │ # notification
376
406
  │ ├── connections/ # PostgreSQL, Oracle, MySQL, MSSQL, ODBC, Snowflake, BigQuery, Redshift + factory
377
407
  │ ├── email_providers/ # Gmail, M365, SMTP, SendGrid, SES, Mailgun + factory
378
408
  │ ├── reports/ # Excel, CSV, PDF, JSON generators
@@ -387,7 +417,9 @@ flowforge/
387
417
  │ │ # RunHistory, Settings, Users, BulkLoads, Projects, Login, AuditLog
388
418
  │ ├── components/ # pipeline/, email/, report/, runs/, shared/
389
419
  │ └── lib/ # api.ts, auth.ts, types.ts, store.ts
390
- ├── docs/ # Guides and runbook
420
+ │ # lib/generated/ — OpenAPI-generated types (see docs/openapi/)
421
+ ├── docs/
422
+ │ └── openapi/ # Hand-authored OpenAPI specs (source of truth for lib/generated/)
391
423
  ├── tests/ # pytest suite (1,800+ tests)
392
424
  ├── .github/workflows/ # GitHub Actions CI
393
425
  ├── docker-compose.yml # API + frontend + PostgreSQL + scheduler + Redis + worker
@@ -403,6 +435,8 @@ flowforge/
403
435
 
404
436
  ## Documentation
405
437
 
438
+ - [`ARCHITECTURE.md`](ARCHITECTURE.md) — Muscle/Brain architecture, integration flows, hard rules, telecom RA mapping
439
+ - [`docs/BRAIN_DECOUPLING_PLAN.md`](docs/BRAIN_DECOUPLING_PLAN.md) — plan for extracting the legacy in-repo AI code
406
440
  - [`docs/getting-started.md`](docs/getting-started.md) — end-to-end setup walkthrough
407
441
  - [`docs/step-types.md`](docs/step-types.md) — full config reference for all step types
408
442
  - [`docs/email-providers.md`](docs/email-providers.md) — SMTP, M365, Gmail, SendGrid, SES, and Mailgun setup guides
@@ -431,6 +465,7 @@ flowforge/
431
465
  | v1.1.0 | ✅ | MySQL / MariaDB, OneDrive / SharePoint upload, SFTP transfer step, AI features (chart generator, SQL explainer/optimizer, failure diagnosis, data profiler, anomaly alerts), pipeline clone, YAML import/export, step retry + backoff, failure webhook, webhook/API trigger, JWT revocation, RBAC guards, SAST in CI |
432
466
  | v1.2.0 | ✅ | Multi-user roles (admin / editor / viewer), user management UI, role-based frontend visibility, Celery / Redis task queue, `flowforge worker` CLI, responsive mobile layout, audit log username attribution |
433
467
  | Since v1.2.0 | ✅ | SSH steps (command, health check) + DB health check + script reports; Gunicorn/Nginx production hardening, connection-pool tuning, Prometheus metrics, Celery Flower; MFA (TOTP), SSO (Google/Microsoft/SAML 2.0), GDPR export/purge, IP allowlisting, report encryption at rest, secrets scanning, password reset; MSSQL/ODBC/Snowflake/BigQuery/Redshift connectors; SendGrid/SES/Mailgun providers + Slack/Teams/Telegram notification step; S3/Azure Blob upload; plugin step-type system; pipeline dependencies + parallel step execution; run diff view; report column formatting; environment promotion; team-scoped project membership; distributed Redis concurrency lock; fuzzing (Hypothesis), signed releases (SLSA), PyPI publish; full Tailwind CSS frontend migration; visual pipeline canvas (list/graph toggle in the pipeline editor); step-level DAG dependencies (real edge-drawing on the canvas, branch-scoped `on_error: stop`, ancestors-only step context) |
468
+ | **Direction (2026-10-27)** | 🧭 | Telecom Revenue Assurance: extract AI into the external Brain service; CDR/mediation tooling, reconciliation steps, conditional alerting, leakage KPIs and case management — see [`ARCHITECTURE.md`](ARCHITECTURE.md) |
434
469
  | **Not yet built** | — | OSS-Fuzz registration; cosign-signed release artifacts |
435
470
 
436
471
  ---
@@ -441,7 +476,7 @@ flowforge/
441
476
 
442
477
  **Feedback:** Use [GitHub Issues](https://github.com/jagdeepvirdi/flowforge/issues) to report bugs or request features. Please include your FlowForge version, Python version, OS, steps to reproduce, and any relevant log output from `ff_step_runs.logs`.
443
478
 
444
- **Contribute:** See [`CONTRIBUTING.md`](CONTRIBUTING.md) for dev setup, running tests, adding step types, and the PR process. All contributions welcome — bug fixes, new step types, documentation improvements, and test coverage.
479
+ **Contribute:** See [`CONTRIBUTING.md`](CONTRIBUTING.md) for dev setup, running tests, adding step types, and the PR process. Contributions must respect the core's architectural rule: **no LLM SDKs, prompts or blocking AI calls in this repository** ([`ARCHITECTURE.md` §8](ARCHITECTURE.md#8-hard-rules)). All contributions welcome — bug fixes, new step types, documentation improvements, and test coverage.
445
480
 
446
481
  ---
447
482
 
@@ -0,0 +1 @@
1
+ __version__ = '2.0.0'
@@ -67,18 +67,15 @@ def create_app(config: dict | None = None) -> Flask:
67
67
  # AES-256 encryption key — used exclusively by flowforge/crypto.py
68
68
  app.config['SECRET_KEY'] = os.environ.get('FLOWFORGE_SECRET_KEY', '') # NOSONAR
69
69
 
70
- # JWT signing secret — separate from the encryption key (SEC-2)
71
- # Falls back to SECRET_KEY for backward compatibility; set FLOWFORGE_JWT_SECRET in production.
70
+ # JWT signing secret — separate from the encryption key (SEC-2).
71
+ # SEC-01: no fallback to SECRET_KEY outside TESTING — reusing the AES encryption
72
+ # key to also sign JWTs is its own secret-hygiene issue, so FLOWFORGE_JWT_SECRET
73
+ # must be set explicitly in every real deployment; _validate_secret() below turns
74
+ # an unset value into a hard boot failure instead of a silently-reused key. TESTING
75
+ # keeps the fallback so tests that only configure SECRET_KEY keep working.
72
76
  jwt_secret = os.environ.get('FLOWFORGE_JWT_SECRET', '')
73
- if not jwt_secret:
77
+ if not jwt_secret and (config or {}).get('TESTING'):
74
78
  jwt_secret = app.config['SECRET_KEY']
75
- if jwt_secret and not (config or {}).get('TESTING'):
76
- import warnings
77
- warnings.warn(
78
- 'FLOWFORGE_JWT_SECRET is not set — falling back to FLOWFORGE_SECRET_KEY for JWT '
79
- 'signing. Set a separate FLOWFORGE_JWT_SECRET in production.',
80
- stacklevel=2,
81
- )
82
79
  app.config['JWT_SECRET'] = jwt_secret
83
80
  app.config['JWT_ALGORITHM'] = 'HS256'
84
81
  app.config['JWT_EXPIRY_HOURS'] = 24
@@ -101,14 +98,17 @@ def create_app(config: dict | None = None) -> Flask:
101
98
  # N x the configured limit, not the limit. GUNICORN_WORKERS is set as a real
102
99
  # env var (not just a shell CLI default) whenever .env.example's documented
103
100
  # value flows through — see Dockerfile / docs/RUNBOOK.md's systemd units.
101
+ # PERF-02: the API rate limiter has the identical failure mode — flask-limiter
102
+ # defaults to in-memory counters, which reset independently per worker — so it
103
+ # shares this warning and the same FLOWFORGE_REDIS_URL fix below.
104
104
  gunicorn_workers = int(os.environ.get('GUNICORN_WORKERS', '1') or '1')
105
105
  if gunicorn_workers > 1 and not os.environ.get('FLOWFORGE_REDIS_URL'):
106
106
  app.logger.warning(
107
107
  'CAPACITY: GUNICORN_WORKERS=%d with no FLOWFORGE_REDIS_URL configured — '
108
- 'FLOWFORGE_MAX_CONCURRENT_RUNS is enforced per-process, so the real '
109
- 'deployment-wide concurrency ceiling is up to %d x the configured limit, '
110
- 'not the limit itself. Set FLOWFORGE_REDIS_URL for a distributed counter '
111
- 'that holds the limit across all workers.',
108
+ 'FLOWFORGE_MAX_CONCURRENT_RUNS and API rate limits are both enforced '
109
+ 'per-process, so the real deployment-wide ceiling for each is up to %d x the '
110
+ 'configured limit, not the limit itself. Set FLOWFORGE_REDIS_URL for '
111
+ 'Redis-backed counters that hold both limits across all workers.',
112
112
  gunicorn_workers, gunicorn_workers,
113
113
  )
114
114
 
@@ -118,6 +118,17 @@ def create_app(config: dict | None = None) -> Flask:
118
118
  if num_proxies > 0:
119
119
  app.wsgi_app = ProxyFix(app.wsgi_app, x_for=num_proxies, x_proto=num_proxies, x_host=num_proxies)
120
120
 
121
+ # PERF-02: route rate-limit counters through the same Redis instance used for the
122
+ # distributed concurrency counter (engine/concurrency.py) and leader election
123
+ # (engine/leader.py), instead of flask-limiter's per-process in-memory default.
124
+ # Config, not the Limiter() constructor, because FLOWFORGE_REDIS_URL is only known
125
+ # at create_app() time — the module-level `limiter` above is constructed at import
126
+ # time, before any env var has been read. A test-provided RATELIMIT_STORAGE_URI in
127
+ # `config` (applied above) wins via setdefault.
128
+ redis_url = os.environ.get('FLOWFORGE_REDIS_URL', '')
129
+ if redis_url:
130
+ app.config.setdefault('RATELIMIT_STORAGE_URI', redis_url)
131
+
121
132
  db.init_app(app)
122
133
  limiter.init_app(app)
123
134
 
@@ -15,8 +15,12 @@ def _algorithm() -> str:
15
15
 
16
16
 
17
17
  def _secret() -> str:
18
- # SEC-2: use JWT_SECRET (separate from the AES encryption key)
19
- return current_app.config.get('JWT_SECRET') or current_app.config['SECRET_KEY']
18
+ # SEC-2: use JWT_SECRET (separate from the AES encryption key).
19
+ # SEC-01: no fallback to SECRET_KEY here — create_app() requires
20
+ # FLOWFORGE_JWT_SECRET to be set (outside TESTING) and validates its length
21
+ # at boot, so JWT_SECRET is always a valid, distinct secret by the time a
22
+ # request reaches this function.
23
+ return current_app.config['JWT_SECRET']
20
24
 
21
25
 
22
26
  def _expiry_hours() -> int:
@@ -63,12 +67,42 @@ def verify_mfa_token(token: str) -> dict | None:
63
67
  return None
64
68
 
65
69
 
70
+ def generate_pwchange_token(user: User) -> str:
71
+ """Short-lived (15 min) challenge token issued when credentials are correct
72
+ but the account is flagged (must_change_password) to set a new password
73
+ before a full session is issued."""
74
+ payload = {
75
+ 'sub': user.username,
76
+ 'uid': user.id,
77
+ 'role': user.role,
78
+ 'jti': str(_uuid_mod.uuid4()),
79
+ 'pwd_step': True,
80
+ 'iat': datetime.now(UTC),
81
+ 'exp': datetime.now(UTC) + timedelta(minutes=15),
82
+ }
83
+ return jwt.encode(payload, _secret(), algorithm=_algorithm())
84
+
85
+
86
+ def verify_pwchange_token(token: str) -> dict | None:
87
+ """Verify a forced-password-change challenge token. Returns payload if valid and un-revoked, else None."""
88
+ try:
89
+ payload = jwt.decode(token, _secret(), algorithms=[_algorithm()])
90
+ if not payload.get('pwd_step'):
91
+ return None
92
+ jti = payload.get('jti')
93
+ if jti and db.session.get(TokenBlocklist, jti) is not None:
94
+ return None
95
+ return payload
96
+ except jwt.PyJWTError:
97
+ return None
98
+
99
+
66
100
  def verify_token(token: str) -> dict | None:
67
101
  """Return the payload if the token is valid and not revoked, else None."""
68
102
  try:
69
103
  payload = jwt.decode(token, _secret(), algorithms=[_algorithm()])
70
- # Reject MFA challenge tokens from being used as full session tokens.
71
- if payload.get('mfa_step'):
104
+ # Reject MFA/password-change challenge tokens from being used as full session tokens.
105
+ if payload.get('mfa_step') or payload.get('pwd_step'):
72
106
  return None
73
107
  jti = payload.get('jti')
74
108
  if jti and db.session.get(TokenBlocklist, jti) is not None:
@@ -101,7 +135,10 @@ def login(username: str, password: str) -> 'str | dict | None':
101
135
  """Verify credentials.
102
136
 
103
137
  Returns:
104
- str — full JWT (MFA not enabled or not configured)
138
+ str — full JWT (no pending challenge)
139
+ dict — {'password_change_required': True, 'change_token': '...'} when the
140
+ account must set a new password before a session is issued (checked
141
+ first — a freshly admin-invited account won't have MFA set up yet)
105
142
  dict — {'mfa_required': True, 'mfa_token': '...'} when MFA is enabled
106
143
  None — invalid credentials
107
144
  """
@@ -110,6 +147,8 @@ def login(username: str, password: str) -> 'str | dict | None':
110
147
  return None
111
148
  if not bcrypt.checkpw(password.encode(), user.password_hash.encode()):
112
149
  return None
150
+ if user.must_change_password:
151
+ return {'password_change_required': True, 'change_token': generate_pwchange_token(user)}
113
152
  if user.mfa_enabled:
114
153
  return {'mfa_required': True, 'mfa_token': generate_mfa_token(user)}
115
154
  return generate_token(user)