This document gives new developers a working mental model of OpenOrchestrator. For end-user docs see the docs site.
OpenOrchestrator is a single Python package that ships three deployable parts, all backed by one shared database.
| Component | Stack | Entry point | Purpose |
|---|---|---|---|
| Orchestrator | NiceGUI (web) | python -m OpenOrchestrator o |
Admin UI: triggers, queues, jobs, logs, constants, credentials |
| Scheduler | tkinter (desktop) | python -m OpenOrchestrator s |
Polls the DB for due triggers and launches the configured Python processes on a worker machine |
| OrchestratorConnection | library | imported by RPA scripts | SDK that the launched processes use to log, fetch credentials/constants, and read/write queue elements |
The Orchestrator and Scheduler communicate only through the database — there is no direct RPC. Many Schedulers can run against one Orchestrator/database; jobs are dispatched via the trigger priority field and an optional scheduler_whitelist.
The Scheduler is intentionally a local desktop process: it runs on the RPA worker machine where the bots execute, often unattended, and needs no remote UI. The Orchestrator is a web admin app and uses NiceGUI. The two settings tabs (scheduler/settings_tab.py vs orchestrator/tabs/settings_tab.py) deliberately use different UI stacks for this reason — do not "consolidate" them.
All ORM classes inherit from a single SQLAlchemy DeclarativeBase in database/base.py. Tables:
- Triggers — polymorphic on
type(database/triggers.py):SingleTrigger— runs once atnext_run.ScheduledTrigger— recurring, driven by acron_expr(cronsim).QueueTrigger— fires when at leastmin_batch_sizeelements with statusNEWexist forqueue_name.- Common fields:
process_path(file path or git URL),is_git_repo,git_branch,is_blocking,priority,scheduler_whitelist,process_status.
- Queues — database/queues.py. Work items consumed by
QueueTrigger-driven processes. Status:NEW → IN_PROGRESS → DONE | FAILED | ABANDONED. - Jobs — database/jobs.py. One row per launched process. Tracks
start_time,end_time,status(RUNNING/DONE/FAILED/KILLED) and thescheduler_namethat launched it. - Logs — database/logs.py. Trace/Info/Error entries written by processes via
OrchestratorConnection. Linked to aJobviajob_id. - Schedulers — database/schedulers.py. Heartbeat row per running Scheduler instance (
machine_nameis PK). - Constants / Credentials — database/constants.py. Shared config; credential passwords are AES-encrypted with a key managed by common/crypto_util.py.
Schema migrations live under alembic_migrations/ and are applied via python -m OpenOrchestrator upgrade <conn_string>.
Implemented in scheduler/runner.py.
┌─────────┐
│ IDLE │◄────────────────────┐
└────┬────┘ │
│ poll_triggers picks it │ end_job (recurring)
▼ │
┌─────────┐ process exits ok │
┌───────►│ RUNNING ├──────────────────►──┘
│ └────┬────┘
│ │ user disables in UI
│ ▼ process exits
│ ┌─────────┐ ┌─────────┐
│ │ PAUSING ├────────►──┤ PAUSED │
│ └────┬────┘ └────┬────┘
│ │ user re-enables │ user re-enables
│ └─────────────────────┘
│
│ uncaught exception user kills via UI
│ │ │
│ ▼ ▼
│ ┌────────┐ ┌─────────┐ taskkill ┌────────┐
│ │ FAILED │ │ KILLING ├────────────►│ KILLED │
│ └────────┘ └─────────┘ └────────┘
│
└── Single triggers go to DONE on success (terminal state)
poll_triggers sorts pending triggers by (-priority, type) where Single > Scheduled > Queue. is_blocking=True triggers wait for any other running job on the same Scheduler to finish.
When the Scheduler runs a trigger, it:
- Optionally clones a git repo (
is_git_repo=True) into~/Desktop/Scheduler_Repos/<uuid>/and findsmain.py. - Spawns a Python subprocess with
[python, path, process_name, conn_string, crypto_key, args, trigger_id, job_id]. - The launched process receives those args and instantiates
OrchestratorConnection.create_connection_from_args()to log, fetch credentials, etc. - On exit, the Scheduler updates trigger and job status, then deletes the cloned repo folder if any.
OpenOrchestrator/
├── __main__.py # CLI: o | s | upgrade
├── common/ # Shared utils (datetime, crypto, NiceGUI connection frame)
├── database/ # ORM models + query helpers (db_util et al.) + Alembic data types
├── orchestrator/ # NiceGUI admin app
│ ├── application.py # NiceGUI app entry
│ ├── tabs/ # Top-level tabs (triggers, queues, jobs, logs, constants, schedulers, settings)
│ ├── popups/ # Modal create/edit dialogs (BasePopup-based)
│ ├── ui_util.py # UI test instrumentation (auto-id props for Selenium)
│ ├── datetime_input.py # Custom date/time picker
│ └── input_chips_blur.py # Custom InputChips with blur-event commit
├── scheduler/ # tkinter runner
│ ├── application.py
│ ├── runner.py # Trigger polling + process lifecycle (see above)
│ ├── run_tab.py # Run tab UI
│ ├── settings_tab.py # Connection settings UI (tkinter)
│ └── util.py # Scheduler name resolution etc.
├── orchestrator_connection/ # Public SDK consumed by RPA scripts
│ └── connection.py
└── tests/
├── *.py # Unit + integration tests (unittest, against SQLite or MSSQL)
└── ui_tests/ # Selenium-based UI tests
- Diagram sources: .Illustrations/Architecture.drawio, .Illustrations/Scheduler.drawio
- Contributing guide: CONTRIBUTING.md
- Changelog: changelog.md