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.
- {flowforge_io-1.3.0/flowforge_io.egg-info → flowforge_io-2.0.0}/PKG-INFO +68 -30
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/README.md +62 -27
- flowforge_io-2.0.0/flowforge/__init__.py +1 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/app.py +25 -14
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/auth.py +44 -5
- flowforge_io-2.0.0/flowforge/api/email_utils.py +22 -0
- flowforge_io-2.0.0/flowforge/api/routes/ai.py +45 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/auth.py +34 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/password_reset.py +3 -18
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/pipelines.py +20 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/setup.py +3 -15
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/users.py +74 -3
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/validators.py +20 -0
- flowforge_io-2.0.0/flowforge/brain.py +21 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/celery_app.py +7 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/base.py +20 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/bigquery.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/factory.py +47 -15
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/mssql.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/mysql.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/odbc.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/oracle.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/postgres.py +2 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/snowflake.py +2 -1
- flowforge_io-2.0.0/flowforge/db/migrations/versions/0033_must_change_password.py +26 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/auth.py +4 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/context.py +52 -7
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/notifications.py +15 -4
- flowforge_io-2.0.0/flowforge/net_guard.py +175 -0
- flowforge_io-2.0.0/flowforge/steps/ai_analyze.py +20 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/base.py +1 -1
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/bulk_load.py +55 -2
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/notification.py +7 -5
- {flowforge_io-1.3.0 → flowforge_io-2.0.0/flowforge_io.egg-info}/PKG-INFO +68 -30
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/SOURCES.txt +7 -2
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/requires.txt +6 -2
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/pyproject.toml +13 -4
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_app_coverage.py +45 -0
- flowforge_io-2.0.0/tests/test_brain_gate.py +120 -0
- flowforge_io-2.0.0/tests/test_bulk_load_maint01.py +184 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_step.py +8 -2
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connection_base.py +21 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_leader_election.py +0 -1
- flowforge_io-2.0.0/tests/test_no_llm_in_core.py +53 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_notification_step.py +16 -16
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_oracle_bulk_load.py +2 -2
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipelines.py +64 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_plugin_loader.py +12 -7
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner.py +5 -5
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner_extended.py +4 -4
- flowforge_io-2.0.0/tests/test_safe_urlopen.py +236 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_setup_extended.py +0 -50
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_users.py +127 -0
- flowforge_io-1.3.0/flowforge/__init__.py +0 -1
- flowforge_io-1.3.0/flowforge/api/routes/ai.py +0 -344
- flowforge_io-1.3.0/flowforge/net_guard.py +0 -63
- flowforge_io-1.3.0/flowforge/steps/ai_analyze.py +0 -222
- flowforge_io-1.3.0/tests/test_ai.py +0 -488
- flowforge_io-1.3.0/tests/test_ai_analyze.py +0 -438
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/LICENSE +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/pipeline_service.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/project_access.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/audit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/bulk_loads.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/connections.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/emails.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/metrics.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/mfa.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/projects.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/providers.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/recipients.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/registry.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/reports.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/runs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/settings.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/sso.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/routes/steps.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/api/serializers.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/audit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/cli.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/redshift.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/connections/ssh.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/crypto.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/env.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0001_baseline.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0002_timezone_timestamps.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0003_indexes_and_constraints.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0004_json_report_format.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0005_bulk_load_configs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0006_projects.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0007_pipeline_variables_index.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0008_token_blocklist.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0009_narrow_db_type_constraint.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0010_webhook_tokens.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0011_fix_step_type_constraint.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0012_onedrive_support.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0013_ai_analyze_step.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0014_sftp_transfer_step.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0015_datetime_timezone.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0016_pipeline_failure_webhook.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0017_mysql_connection_type.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0018_user_role.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019_backfill_admin_role.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019a_add_auditlog_model.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019b_add_sshconnection_and_update_step_type.py +0 -0
- {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
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0019d_add_ssh_health_check_step_type.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0020_mfa_sso.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0021_mssql_odbc_pwreset.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0023_pipeline_deps_parallel.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0024_report_column_formatting.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0025_new_providers_notification.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0026_relax_step_type_constraint.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0027_snowflake_bigquery_redshift.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0028_project_members.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0029_system_settings.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0030_recipient_group_cc_bcc.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0031_email_body_format.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/0032_step_dependencies.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/migrations/versions/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/_shared.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/audit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/bulk_load.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/connections.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/email.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/pipelines.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/projects.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/reports.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/db/models/settings.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/base.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/factory.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/gmail.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/mailgun.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/microsoft365.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/sendgrid.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/ses.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/email_providers/smtp.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/cleanup.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/concurrency.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/dag.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/dependency_trigger.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/launcher.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/leader.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/loader.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/run_records.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/runner.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/scheduler.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/settings.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/shutdown.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/step_exec.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/engine/waves.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/registry.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/csv_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/excel_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/health_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/json_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/reports/pdf_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/azure_blob_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/data_load.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_health_check.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_procedure.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/db_query.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/drive_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/email_step.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/onedrive_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/s3_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/script_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/sftp_transfer.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/ssh_command.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/steps/ssh_health_check.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/__init__.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/azure_blob.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/google_drive.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/onedrive.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/storage/s3.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge/tasks.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/dependency_links.txt +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/entry_points.txt +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/flowforge_io.egg-info/top_level.txt +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/setup.cfg +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_audit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_audit_routes.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_auth.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_auth_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_azure_blob_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_configs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_dry_run.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_preview.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_load_validate_route.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_bulk_loads_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cleanup.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cli_cleanup.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_concurrency.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_concurrency_failure_injection.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_bigquery.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_factory.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_factory_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_mssql_odbc.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_redshift.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_connections_snowflake.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_context.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_cron_endpoint.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_crypto.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_crypto_file_encryption.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_dag_engine.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_dag_integration.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_data_load_create.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_data_load_step.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_health_check.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_health_check_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_procedure_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_db_query_capture.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_drive_upload_step.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_configs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers_ses_sg_mg.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_providers_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_step_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_email_step_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_emails_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_engine_settings.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_excel_report_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_jinja_errors.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_jwt_expiry.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_launcher_extended.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_launcher_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_loader_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_metrics_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mfa.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mysql.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_mysql_extended.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_net_guard.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_onedrive.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_oracle.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_password_reset.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipeline_variables.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_pipelines_extended_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_plugin_loader_multi_category.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_postgres_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_project_members.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_projects.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_providers_extended.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_rbac.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_recipients.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry_api.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_registry_contract.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_e2e.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_generators.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_report_step_coverage.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_reports.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runner_dag_gate.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs_api.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_runs_extended.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_s3_upload.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler_jobstore_persistence.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_scheduler_prune.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_script_report.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_serializers.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_settings_routes.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_sftp.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_shutdown.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_shutdown_cancel_runs.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_smart_attachments.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_smtp_test_method.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_command.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_connection_unit.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_ssh_health_check.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_sso.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_step_dependencies_model.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_steps_db.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_steps_extended.py +0 -0
- {flowforge_io-1.3.0 → flowforge_io-2.0.0}/tests/test_validators.py +0 -0
- {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:
|
|
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
|
-
**
|
|
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
|
[](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml)
|
|
135
138
|
[](https://codecov.io/gh/jagdeepvirdi/flowforge)
|
|
@@ -140,11 +143,45 @@ Dynamic: license-file
|
|
|
140
143
|
|
|
141
144
|
---
|
|
142
145
|
|
|
143
|
-
##
|
|
146
|
+
## Product Vision
|
|
144
147
|
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
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
|
-
###
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
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 |
|
|
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) |
|
|
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 ".[
|
|
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
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
[](https://github.com/jagdeepvirdi/flowforge/actions/workflows/test.yml)
|
|
6
6
|
[](https://codecov.io/gh/jagdeepvirdi/flowforge)
|
|
@@ -11,11 +11,45 @@
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Product Vision
|
|
15
15
|
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
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
|
-
###
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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 |
|
|
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) |
|
|
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 ".[
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
109
|
-
'deployment-wide
|
|
110
|
-
'not the limit itself. Set FLOWFORGE_REDIS_URL for
|
|
111
|
-
'that
|
|
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
|
-
|
|
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 (
|
|
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)
|