Skip to content

Multi-AutoML Interface v5.0.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 20:36
· 65 commits to main since this release

Multi-AutoML Interface 5.0.1

5.0.1 - 2026-09-28

Second release, published after an audit of the codebase and of the packaged app. It
fixes the local-first MLflow default, closes the multi-session exposures, repairs
several runtime defects and leaves the end-of-life Electron 28 shell.

Fixed

  • The app recorded no MLflow runs. safe_set_experiment failed on every startup
    because MLflow 3 refuses a file-based tracking store unless
    MLFLOW_ALLOW_FILE_STORE is set; the test suite set it, so the breakage was
    invisible in CI. The desktop app now logs the experiment setup successfully, and a
    configured MLFLOW_TRACKING_URI is honoured instead of being overwritten with the
    local path (a shared server or database store was silently ignored before).
  • python run.py listened on every interface. Streamlit binds 0.0.0.0 when no
    address is given, so the local launcher exposed the app, and the code execution of
    the Python behind it, to the whole network. It now binds 127.0.0.1 unless
    --server.address or STREAMLIT_SERVER_ADDRESS is supplied, and the DagsHub
    credential gate treats an unset address as shared.
  • Cross-session credential leak. The DagsHub panel wrote a visitor's username and
    token into process-global os.environ and never cleared them; in multi-session mode
    another user's run would authenticate with them. Per-user tokens are accepted only
    when the server is bound to loopback.
  • Untrusted model loading (CWE-502). All six MLflow flavors are restored with
    pickle/joblib, and the run id came from a free-text field against a tracking URI that
    the sidebar can repoint. Loading now requires an explicit confirmation in the UI and
    rejects run ids containing path characters.
  • CORS was disabled everywhere. Both containers and the Electron launcher passed
    --server.enableCORS=false; with the app reachable from other origins, any page that
    could reach the port could read and post to it. Streamlit's defaults now stand, and
    Compose publishes 8501/5000 on loopback only.
  • Leaked host repository into containers. Compose bind-mounted .:./app, which
    also exposed .git and let the container overwrite source; it now mounts data_lake/
    and mlruns/ only. Its MLflow server image (v2.11.1) was also two majors behind the
    pinned client and is now version-matched.
  • Threads that never stopped. The H2O cancellation watcher and telemetry loop only
    exited when training returned, so a failed run left them polling inside the shared
    process; they are released from a finally. Two concurrent FLAML runs wrote the same
    flaml.log, now named per run.
  • Requests that could hang forever. dvc init and dvc add ran without timeouts and
    so could block a session indefinitely; they now bound at 120 and 900 seconds, and the
    interpreter probe in run.py at 10.
  • Run history destroyed by the auto-healer. heal_mlruns deleted any numeric
    mlruns/ directory lacking meta.yaml, which under multi-session is an experiment
    being written right now. It quarantines to mlruns/.trash instead and skips anything
    touched within the last hour.
  • shutil.rmtree on a path built from user input, ZIP extraction without member
    checks, and 14 bare except: clauses
    that swallowed KeyboardInterrupt.
  • queue_experiment() crashed when called without a manager, because it fell back
    to get_or_create_manager() without the session state that function requires; the
    manager is now an explicit argument, so the orchestrator cannot silently share one
    across sessions.
  • run.py accepted any interpreter newer than 3.11 while the frameworks need 3.11.
    It now re-launches on 3.11 whenever available, warns and continues on a newer
    interpreter, and hard-fails only on older ones.
  • Generated notebooks could not be written in the installed app: the exporter wrote
    into the working directory, which is inside Program Files there. They now land under
    the system temp directory.
  • Progress bars disappeared in a terminal. The stdout/stderr router used to capture
    per-run logs inherited io.TextIOBase, whose isatty() always answers False and whose
    fileno() raises - so H2O, FLAML and tqdm disabled their bars even in a real terminal,
    and anything probing the descriptor failed. Both now delegate to the underlying stream
    and degrade cleanly when there is none.
  • A cancelled run dropped its result. refresh_all only polled entries that were
    running or queued, so the payload a cancelled worker still delivered was never read:
    entry.result stayed empty and the UI reported "Unknown" instead of the real outcome.
    Cancelled runs are polled too, and a late result no longer relabels the row as
    completed or failed.
  • Desktop shell: external links (file://, custom schemes) were passed straight to
    shell.openExternal with no navigation guard; Electron moves from the unsupported
    28.3.3 to 44.4.5 with electron-builder 26.15.3; the preload assigned
    window.electron in its own isolated world where no page could read it, now exposed
    through contextBridge; npm ci replaces npm install so the lockfile is respected.
  • Dependency advisories: mlflow and mlflow-tracing to 3.16.1 and cryptography to
    50.0.1, which closes the two advisories 5.0.0 had to leave open (CVE-2026-69247,
    CVE-2026-71211). OSV reports no applicable vulnerability for any pin in
    requirements.txt and npm audit reports none for the desktop toolchain.
    The unused skops pin was dropped.

Added

  • CI gates that mean something: the nightly full suite is now authoritative when the
    dependency stack installs (it was continue-on-error), pip-audit --strict runs over
    requirements.txt, npm audit --audit-level=high runs before packaging, and both
    Python and JS installers now build from lockfiles. pytest invocations pass
    -o addopts="" so the pass/skip summary is not swallowed by a double -q.
  • Multi-session deployment notes in docs/DOCUMENTATION.md, plus troubleshooting
    entries for the loopback default and the artifact-trust confirmation.
  • A missing DVC remote is now reported after an upload, because the .dvc pointer will
    not resolve on another machine.

Changed

  • build-electron.yml no longer runs the 3-OS matrix on every push to main; it builds
    on packaging changes in pull requests and on manual dispatch, since release.yml
    already builds and publishes on tags.

Known limitations

  • The app still has no authentication or per-user quota of its own: an internet-facing
    deployment must terminate TLS and authentication in a reverse proxy, and sessions
    continue to share mlruns/, models/ and the data lake in one working directory.
  • electron/renderer.js is still not wired into the window; enabling it would overlay a
    custom header on the Streamlit UI, which is a design decision rather than a bug fix.
  • 23 use_container_width calls in app.py emit Streamlit deprecation warnings past
    their announced removal date. They cannot be replaced mechanically: st.pyplot has no
    width argument, so each widget needs its own judgement.
  • Installers remain unsigned and unnotarized, and still require Python plus
    requirements.txt on the target machine.

Prerequisites

The desktop installers bundle the Electron shell and the Streamlit UI, not the Python
runtime. Install Python 3.11/3.12 and the app dependencies first:

pip install -r requirements.txt

AutoML backends (AutoGluon, PyCaret, TPOT, Lale, H2O, AutoKeras, HuggingFace) are optional
and lazy-imported; see the README for what each one needs.

Installers are not code-signed or notarized, so SmartScreen and Gatekeeper will warn
on first launch.