This document describes the current BioModStack runtime/service model and the shells that sit on top of it.
BioModStack shells are clients, not supervisors.
The browser, Electron shell, GTK panel, tray, and optional Android thin-shell
path all sit on top of one shared runtime/service layer. They should open the
hosted /bms/ UI, inspect status, and request start/stop/restart actions
through the shared control plane. They should not own long-lived API/frontend
processes directly.
Dev mode is still supported for explicit repository-first work:
biomodstack-api.servicebiomodstack-frontend.service
Those units own the dev API/frontend process shape and write to:
~/.local/state/biomodstack/logs/api.log~/.local/state/biomodstack/logs/frontend.log
The default runtime mode is now container.
Container mode uses:
biomodstack-core-runtime.servicecompose.core-runtime.ymlscripts/run_biomodstack_core_runtime.sh
That service owns the hosted API/web pair and writes to:
~/.local/state/biomodstack/logs/core-runtime.log
Container mode does not make Nextflow execution container-owned. The honest runtime boundary is:
- API/web runtime is containerized
- workflow launch/cancel/running-job ownership remains host-native
- the bridge is
biomodstack-workflow-adapter.service - the API reaches it through
BMS_WORKFLOW_ADAPTER_URL - the local health endpoint is
http://127.0.0.1:8001/api/workflow-adapter/health
This is the current production stance for BioModStack container mode.
The container runtime is robust enough to be the default control-plane/runtime shape, but it is intentionally bounded. The stable pieces are:
bms-api,bms-web, and the CPU-power helper are managed throughcompose.core-runtime.yml- API health checks hit
/api/health; web health checks hit/bms/ - startup readiness uses a container-mode wait budget rather than a short dev process timeout, which avoids false failures on first image builds/recreates
- the service manager recognizes the active Compose-backed containers by labels before cleaning up legacy dev listeners on the same ports
BMS_CORE_RUNTIME_MODE=1guards the API/web container from directly owning workflow launches; workflow launch/cancel/running-job calls are either forwarded toBMS_WORKFLOW_ADAPTER_URLor rejected clearly- mounted state/cache roots are injected for database, inputs, weights, ColabFold DB, MSA cache, and SAbDab cache paths so the API does not silently create home-directory fallback stores inside a recreated container
The bounded pieces are just as important:
- this is not full end-to-end workflow containerization; Nextflow/Apptainer execution remains host-native through the workflow adapter
- GPU and workstation hardware telemetry can degrade if the container cannot see the host NVIDIA/tooling stack; host-native adapter/proxy surfaces remain the safer ownership boundary for workstation hardware
- BioXP is a local hardware integration, not a generic core-runtime dependency; the dashboard should degrade when linkage is absent rather than fail startup
A general Linux host should be able to start the core dashboard/API/web stack and show explicit degraded capability messages. It should not be expected to run the full scientific workflow catalog until the host-side assets and adapters are installed.
Runtime selection resolves in this order:
- explicit
--runtime ... BMS_RUNTIME_MODEcontainer
That behavior is implemented in biomodstack_services.py and surfaced by
scripts/manage_desktop_services.py.
Launch-surface selection is separate from runtime selection:
- default surface: browser
- optional additive surface: Electron
- explicit
--surface noneis allowed for service-only startup
Launch preferences live at:
~/.config/biomodstack/launch_preferences.json
The current service/runtime/shell split lives in these files:
biomodstack_services.py- runtime-mode resolution
- systemd unit rendering/install
- service lifecycle and readiness checks
- browser/Electron launch preference handling
biomodstack_runtime_profile.py- install-profile persistence
- generated compatibility env exports
- generated container-runtime env file
- runtime-path resolution and precedence rules
scripts/manage_desktop_services.py- shared desktop/runtime control-plane CLI
scripts/launch_biomodstack_ui.py- raises browser or Electron after ensuring services are running
start_ui.sh- stable service-control entrypoint
start_ui_electron.sh- additive Electron wrapper around
launch_biomodstack_ui.py
- additive Electron wrapper around
scripts/run_biomodstack_core_runtime.sh- compose wrapper for the containerized API/web runtime
platform/api/routers/system.py- runtime-state and install-profile API routes
platform/api/routers/workflow_adapter.py- host-native workflow launch/cancel/running-jobs surface
platform/api/routers/mobile_ui_updates.py- manifest/bundle/file endpoints for optional mobile shell updates
platform/desktop-electron/src/main.ts- Electron shell bootstrap, tray/menu wiring, and IPC registration
platform/desktop-electron/src/serviceControl.ts- Electron bridge to
scripts/manage_desktop_services.py
- Electron bridge to
platform/desktop-electron/src/shellPaths.ts- Electron-side path resolution using env, install profile, and heuristics
platform/frontend/src/runtime/navigation.ts- router basename handling for hosted/browser/Electron shells
platform/frontend/src/runtime/cordovaShell.ts- optional Cordova shell readiness hook
biomodstack_panel.py,biomodstack_tray.py- GTK control surfaces that remain clients of the shared service layer
Runtime/data-path state is now centered around the install profile and generated env files.
Primary files:
~/.config/biomodstack/install_profile.json~/.config/biomodstack/core-runtime.env~/.biomodstack/env.sh~/.config/biomodstack/launch_preferences.json
Path precedence is:
- explicit environment variables
- install profile
- repo/workstation heuristics
Important install-profile outputs include:
data_rootinputs_dirdb_pathcontainer_dirweights_rootcolabfold_dbmsa_cache_dirsabdab_cache_dirworkflow_adapter_url- API/web host ports
- container-state paths
core_runtime_mode
The API system routes expose this through local-only endpoints such as:
GET /api/system/runtime-stateGET /api/system/install-profilePUT /api/system/install-profile
These routes are intentionally limited to localhost/testclient callers.
The browser remains the default operator surface.
GTK panel/tray remain useful local helpers for:
- start/stop/restart
- status inspection
- log access
- opening the hosted UI
They should never become the owner of backend lifetime.
The Electron shell is now a real optional launch surface rather than a future placeholder.
Current Electron behavior includes:
- launching the same hosted
/bms/UI as the browser path - persistent shell storage partition via
persist:biomodstack-shell - preload-provided shell context including router basename/runtime mode
- tray and application menu integration
- open-in-browser support
- runtime status/start/stop/restart/restart-api through the shared Python service-control layer
- open results folder, logs, and shell-data folder helpers
- zoom controls and always-on-top toggle
- safe fallback to browser when Electron is only a persisted preference and the shell runtime is unavailable
Important guardrail:
start_ui_electron.shand the Electron app do not own API/frontend process lifetime directly; they call the shared service layer
BioModStack does not move the runtime onto the phone.
The current Android/mobile contract is additive:
- the hosted
/bms/UI remains the product truth - the frontend exposes
signalCordovaAppReady()so a Cordova-style shell can confirm readiness exactly once through__BMS_CORDOVA_CONFIRM_READY__ - the API exposes mobile update endpoints under
/api/mobile-ui/* - update assets default to
${BMS_DATA}/mobile-ui-updatesunlessBMS_MOBILE_UI_UPDATES_DIRoverrides the location
The update surface currently serves:
- channel manifests
- versioned ZIP bundles
- versioned extracted asset files
That gives BioModStack an update/feed contract for an optional APK shell without pretending the repo's primary runtime now lives on Android.
- shells may request service actions, but must not become backend supervisors
- container mode must stay honest about workflow ownership through the adapter
- browser/Electron/mobile shells should all point at the same hosted UI contract
- explicit Electron launch requests should fail clearly when the shell runtime is absent
- persisted Electron preferences should degrade safely to browser when needed
- local admin/runtime mutation routes stay localhost-only unless the scope is intentionally widened later
Default startup:
./start_ui.sh start
./start_ui.sh statusExplicit container mode:
./start_ui.sh start --runtime container
./start_ui.sh restart-api --runtime container
./start_ui.sh stop --runtime containerBrowser or Electron shell launch:
python3 scripts/launch_biomodstack_ui.py --surface browser --runtime container
./start_ui_electron.sh --runtime containerDirect compose wrapper:
./scripts/run_biomodstack_core_runtime.sh up
./scripts/run_biomodstack_core_runtime.sh ps
./scripts/run_biomodstack_core_runtime.sh downThe current BioModStack architecture is:
- service-layer-owned runtime
- containerized core API/web surface by default
- host-native workflow execution through the workflow adapter
- browser as the default shell
- Electron as an additive packaged desktop shell
- optional Android thin-shell/update support around the same hosted UI
That separation is what keeps the runtime honest while still allowing multiple operator surfaces to coexist.