Open workflow steering platform (alternative to Seqera Platform): exploration synthesis¶
Date: 23/10/2026. Based on five parallel explorations (Nextflow, Snakemake, GA4GH and existing projects, viewers and QC, architecture and HPC). Points marked [to be validated] could not be confirmed in any source that was read.
1. Verdict¶
It is feasible, and the niche appears to be open. Both engines now expose native, structured mechanisms for emitting events (plugins), without parsing logs or going through subprocess for tracking. Monitoring is already covered by partial tools. What is missing is steering: QC gates, relaunching with new parameters, stage-by-stage execution, all independent of the engine. No project found targets exactly this (non-exhaustive search).
Three decisions shape everything else:
- An in-house, engine-independent event log as the source of truth (append-only). Each engine has its own small agent that writes to it.
- Implement the 5 ingestion routes of the Tower protocol on the server side (revised on 23/10 after reading the code, see section 2bis). The
nf-towerplugin ships with Nextflow: you just pointtower.endpointat our server, with nothing to install, as nf-shard does. The protocol remains undocumented and accepts no commands from the server, so steering goes through our launcher, not through this protocol. - The QC gate is modelled in our application, not in the engine. Neither engine has a native pause: the reliable pattern is "stop cleanly after step N, then relaunch with resume".
2. Integration per engine (without log parsing)¶
Nextflow¶
- In-house plugin based on
TraceObserverV2(interface marked@Beta, verified). Available hooks:onFlowCreate,onFlowBegin,onFlowComplete,onFlowError,onProcessCreate,onProcessTerminate,onTaskPending,onTaskSubmit,onTaskStart,onTaskComplete,onTaskCached,onWorkflowOutput,onFilePublish. - This gives us control over the event schema and the transport (HTTP with retry, or a buffered JSONL file).
- Quick prototype: the official
nf-weblog1.2.0 plugin (Apache-2.0, Nextflow ≥ 25.10, verified) sends JSON POSTs. According to the reports,-with-weblogis deprecated in 26.10 in favour of the plugin [to be validated]. Announced events:started,process_submitted,process_started,process_completed,error,completed. nf-tower(shipped with Nextflow) calls/trace/create,/begin,/progress,/heartbeat,/completeviatower.endpoint. Reimplementing this server would be fragile reverse engineering.- Plugin distribution: official registry (since 25.10) or a private JSON repository.
- Points of attention: the
-resumecache depends on the launch directory; changing a parameter that feeds into the hash invalidates tasks; in 26.04 the first-resumeafter an upgrade may re-run everything.
Snakemake¶
- Logger plugin (
snakemake-interface-logger-plugins, enabled with--logger <name>, verified).--log-handler-scriptand--wms-monitorhave disappeared from the recent CLI (read in the code, not in the docs). - The handler runs inside the Snakemake process and receives typed
LogRecords:WORKFLOW_STARTED,RUN_INFO,JOB_INFO,JOB_STARTED,JOB_FINISHED,JOB_ERROR,PROGRESS,ERROR, etc. Fields may change between versions: read defensively and pin the version. - Clean steering: one Python worker per run that calls the API (
SnakemakeApi,DAGSettings(until=…),ConfigSettings) rather than asubprocesson the CLI. The logger and the working directory are global to the process, hence one process per run. - Existing tools:
panoptes(HTTP server + dashboard, MIT, plugin 0.3),snkmt(SQLite + TUI),flowo,stomp,prometheus. Mature for tracking, nothing for steering.
2bis. Code reading: nf-tower, nf-shard, Snakemake (23/10/2026)¶
nf-tower protocol (Nextflow master code, 22/10/2026)¶
TowerObserver now implements TraceObserverV2.
| Hook | Call | Content |
|---|---|---|
onFlowCreate |
POST /trace/create |
sessionId, runName, projectName, repository, workflowId (read from TOWER_WORKFLOW_ID), instant. Expected response: workflowId (+ optional watchUrl) |
onFlowBegin |
PUT /trace/{id}/begin |
workflow (full metadata, params, configText, manifest…), processNames, towerLaunch, dag (new, optional) |
| sender thread | PUT /trace/{id}/progress |
batches of tasks (all trace record fields in camelCase, plus executor, numAttempts, gpuMetrics…), progress, containers. Sent every 1 s or per 100 tasks |
| same | PUT /trace/{id}/heartbeat |
progress, every 1 min when there is no activity |
onFlowComplete |
PUT /trace/{id}/complete |
final workflow, metrics aggregated per process, progress |
progress:succeeded,failed,ignored,cached,pending,submitted,running,retries,aborted, CPU/memory load and peaks, per-process breakdown.- An error on
create,beginorprogressaborts the run (AbortRunException): our server must be available and respond quickly. TOWER_WORKFLOW_IDlets our launcher set the identifier before launch, to link the run to our record.- Reports:
tower.yml(reportssection) describes the published files to display. The plugin writes a local TSV (key, path, size, display, mime_type) without sending it over HTTP. A good model for declaring QC artefacts.
nf-shard¶
- Next.js + Prisma + PostgreSQL, MIT. It implements exactly these 5 routes, in about 200 lines.
creategenerates an identifier,begincreates the workflow,progressdoes an upsert per(workflowId, taskId)with the raw task JSON,completestores stats and metrics.- No token verification (any non-empty string). No steering apart from a beta submission feature via a separate "shard-worker" project.
- Last commit: 19/09/2024. Little maintained, but it proves the approach works.
Snakemake: no built-in equivalent¶
- No built-in monitoring protocol, no bridge to the Tower protocol found. A logger plugin must be installed in the environment (
pip install snakemake-logger-plugin-<name>) and enabled with--logger. - Good news:
--loggeris not passed on to remote jobs (verified inspawn_jobs.py). Events are therefore sent only from the main process, not from the compute nodes. The same applies to Nextflow, where the observer runs in the main JVM. - Pitfall: if the plugin does not declare
has_filter = True, Snakemake applies its default filter to it, and--quietsuppresses events. - Events are emitted via a
QueueListener: a slow handler does not block jobs, but delays subsequent events. JOB_FINISHEDdoes not include resource usage: thebenchmarkdirective is required (an asymmetry with Nextflow, which sends%cpu,rss, etc.).- The panoptes plugin makes one synchronous HTTP call per event, without retry, with a JSON message encoded in a form. Not to be reused. Better to copy the Tower model: batches every second, heartbeat, local buffer.
- Useful Python API on the launcher side:
printd3dag,printrulegraph,summary,list_changes,unlock,create_report. The logger must be a registered package (an instance cannot be passed directly).
Consequence for the architecture¶
Two ingestion gateways that write to the same event log:
/api/tower/*: Tower compatibility for Nextflow, with no installation, storing the raw JSON and reading fields tolerantly./api/v1/ingest/events: our native format, for the Snakemake plugin (and possibly an in-house Nextflow plugin later, if the Tower protocol changes or if extra hooks such asonWorkflowOutputare needed).
2ter. Advanced scenarios: what each engine exposes (code read on 23/10/2026)¶
Versions read: Snakemake 9.27 (main), Snakemake SLURM plugin (main), Nextflow 26.10.3 (main).
Matrix¶
| Scenario | Snakemake (logger plugin) | Nextflow (Tower protocol) | How to fill the gap |
|---|---|---|---|
| Conditional execution / dynamic DAG | is_checkpoint on the job; <TBD> inputs; no "DAG updated" event; the new total only appears at the next PROGRESS |
Full static DAG in begin; a task filtered out by when: produces no event |
Display "at least N" while a checkpoint is open; keep a history of totals. On the Nextflow side, a companion plugin to report unscheduled tasks |
| Temporary files | temp() invisible in events; deletion = a plain text message "Removing temporary output"; deletions deferred while a checkpoint is open |
No temporary files in the Snakemake sense; cleanup = true only at the end of a successful run, and it breaks -resume; no "eager" cleanup in the code |
Snakemake: flags read via the API at launch + message turned into a temp_removed event. "Deleted" status ≠ "missing" |
| SLURM job arrays | Supported (--slurm-array-jobs); the SLURM ID (123 or 123_4) reaches the logger only on error |
array directive: one record per child task, nativeId = 123_4 on SLURM (other formats for LSF, SGE, PBS, AWS, Google); the parent is never reported |
Snakemake: read .snakemake/incomplete/ or the persistence database, then sacct --name <run_uuid>. Nextflow: parse nativeId per executor |
| Retries | Attempt number not sent; same jobid re-emitted after "Trying to restart job N" |
attempt present; a retry = new taskId and new work directory; raw failed includes retries and ignored tasks |
Count attempts in the Snakemake plugin; link Nextflow attempts by process + tag |
| Executors / backends | Executor name absent from events; only local (true/false) |
executor per task (per-process override visible), queue, nativeId, machineType/cloudZone in the cloud; no hostname or effective partition on HPC |
Snakemake: read --executor from the command line. HPC: sacct from the native ID |
| Storage | Visible in paths (retrieve from storage, send to storage); transfers as text messages; credentials masked |
workDir, outputDir, workdir per task; no published files sent over HTTP |
Snakemake: URI scheme = provider. Nextflow: companion plugin (onFilePublish, onWorkflowOutput) |
| Wrappers / modules | Nothing in events; renamed rule visible (as M_*), originating module absent |
process = qualified name (NFCORE_RNASEQ:RNASEQ:FASTQC); nothing on module version or source; the module field = Lmod |
Snakemake: API (rule.wrapper, script, software_env_specs). Nextflow: companion plugin (meta.yml, module registry ≥ 26.04) + modules.json on the server side |
| Software environment | Absent from events; present in .snakemake metadata after execution (software[], hash) |
containers[] (rich with Wave, otherwise only the image); conda and spack absent; no digest |
Snakemake: metadata. Nextflow: companion plugin |
| Job groups | GROUP_INFO + instance UUID in JOB_STARTED: 1 group = 1 scheduler job |
Not applicable | n:m model between attempts and scheduler jobs |
| Measured resources | Absent; TSV from the benchmark directive |
%cpu, rss, durations, etc. in each task |
Read the benchmark TSV; sacct for efficiency |
Consequences¶
- Nextflow: hybrid approach. The Tower protocol covers basic tracking with no installation. A
TraceObserverV2companion plugin is still needed for modules and versions, published files, conda/spack,storeDirand tasks filtered out bywhen:. It pushes to an extension route. - Snakemake: three sources. The logger plugin (with
has_filter = True), a few text messages turned into typed events (versioned regexes, raw message kept), and a collector that reads.snakemake(or the--persistence-backend dbdatabase) andsacct. - Never assume every transition is seen: Nextflow only sends the latest state per task every second. Upsert per
(run, task_id). - Keep the raw JSON of each event alongside the normalised columns.
Extended data model¶
- Lineage: chain of linked runs (resume, new parameters, next stage).
- Run: engine and version, workflow source (repo, revision, commit), directories, parameters, resolved config, default executor, state.
- DagSnapshot: successive versions of the DAG; "known" total or "at least N".
- Component: Nextflow process, module or subworkflow; Snakemake rule, wrapper or module; qualified name, source, version, sha.
- SoftwareEnv: container (image, digest if known, Wave), conda, spack, envmodules.
- Task: logical instance (hash or wildcards), reason for execution, normalised status.
- Attempt: number, requested and measured resources, exit code, error action.
- SchedulerJob: executor, native ID, array parent ID and index, partition, node, machine type. n:m relationship with Attempt (groups, arrays).
- ComputeEnv: cluster or cloud, executor type, partition, region.
- Artifact: URI, storage provider, role (input, output, published, report, QC), lifecycle (created → published | temporary → deleted, and by whom).
- Event: append-only log, raw + normalised payload.
Normalised statuses: QUEUED, SUBMITTED, RUNNING, SUCCEEDED, RETRIED, FAILED, FAILED_IGNORED, CANCELLED, SKIPPED_CACHED, STORED, NOT_SCHEDULED.
3. Standards and existing projects¶
| Item | Verdict |
|---|---|
| GA4GH WES 1.1.0 | A good facade: /runs, /runs/{id}/status, /tasks, cancel. No pause, no gate, no resume, poor task detail. To be exposed as a thin layer, with extensions for the rest. |
| GA4GH TES 1.1.0 | Task-centred model. Useful as an execution target later, not as the core. |
| DRS / TRS | Periphery: DRS for artefacts, TRS for resolving a workflow. |
| Sapporo (DDBJ, Apache-2.0) | The best reference for a multi-engine WES (Nextflow, Snakemake, CWL, Toil, Cromwell). To be studied before deciding what we reimplement. |
| Arvados, Galaxy, Seqera Platform | Too monolithic to serve as a base. Arvados for provenance, Galaxy as an example of a minimal WES. |
| nf-shard (MIT) | Community nf-tower-compatible server. Maturity to be checked. |
| Cromwell, Toil, Funnel, TESK | Engines or task servers: future targets for WDL/CWL/TES. |
The internal model could be aligned with OpenLineage (lineage, via an optional exporter) and OpenTelemetry (traces and metrics), without putting them in the core.
4. Engine-agnostic data model (proposal)¶
- Run: engine, workflow, typed parameters, state, dates,
stages. - Task (Nextflow process / Snakemake rule), Attempt (attempt, with retry), ResourceUsage (requested and measured), Artifact (URI, type, checksum).
- Event:
(run_id, seq, event_id ULID, ts, type, payload), append-only, uniqueness constraint on(run_id, event_id)for idempotent ingestion. Therunsandtaskstables are projections updated in the same transaction. Theseqalso serves as the SSE identifier for resuming after a disconnection. - States: a common subset with WES (11 states), plus internal states:
AWAITING_QC,PAUSED.
5. QC gates and stage-by-stage execution¶
Engine-independent model:
- Stage: a logical unit (alignment, segmentation…) linked to a set of tasks.
- Checkpoint: placed after a stage, with the QC artefacts to display, a policy (mandatory, auto-approval on threshold, timeout) and a lifecycle
pending → awaiting_review → approved | rejected | params_changed → rerun | continue. - Decision: author, date, comment, modified parameters, stage to resume from. Append-only audit trail.
The three desired scenarios:
- Change the parameters and relaunch everything: a new run linked to the previous one.
- Continue to the end: relaunch with resume, with no stage limit.
- Iterative, in parts 1, 2, 3, 4: a chain of short, idempotent runs.
An adapter per engine translates "stop after N" and "resume from X":
- Snakemake:
until/omit_from, then a new run with the modified config. The.snakemakemetadata avoids redoing everything. Snakemakecheckpointsare reserved for data-dependent DAGs. - Nextflow:
--stageparameter (or separate workflows) plus-params-file, then-resumewith the session preserved.-entryhas been deprecated since 24.10. Keep control parameters out of the task hash. A task that waits for approval in a loop would block an executor slot: to be avoided on HPC. - Prior art: Airflow 3.1 (approval operators), Argo (
suspend), Prefect 3 (pause with input).
5bis. Intervention points: humans, AI agents and external systems (added 23/10)¶
Goal: the same stopping point can be resolved by a human, by an AI agent, or by an external system (lab robot, instrument, LIMS, script). The pipeline does not need to know who decides.
The concept: Intervention (generalises the Checkpoint)¶
-
Type:
approval: approve or reject;choice: choose a branch;parameter_edit: modify parameters via a form generated from a JSON Schema;data_input: provide a file or a value;external_wait: wait for a signal from an external system, for example "plate ready";external_action: the platform triggers an external action and waits for it to finish.
-
Evidence: QC artefacts (thumbnails, reports), machine-readable QC metrics in JSON, a text summary. An agent does not read a 20 MB HTML report; it reads the metrics and the thumbnails.
-
Policy:
- who can resolve (human roles, agent identity, robot service account);
- mode:
human_only,agent_proposes_human_confirms,agent_autounder thresholds,system; - optional quorum, timeout, default action on expiry, escalation.
-
Decision: actor type (
human,agent,system), identifier, model and version for an agent, justification, evidence consulted (artefact IDs and hashes), parameter diff, idempotency key. Append-only, hence a complete audit.
One API, three facades¶
- REST API + OpenAPI: the single source; everything the UI does goes through it.
- Web UI (humans): intervention queue, viewers, parameter form, decision history.
-
MCP server (AI agents):
- tools
list_interventions,get_intervention(evidence + thumbnails as images),propose_decision,submit_decision,get_run,compare_runs,relaunch; - the MCP 2026-07-28 specification is stateless, with handles passed as arguments, which fits intervention IDs well;
- it also defines
input_requiredrequests and atasksextension for long-running operations.
- tools
-
Webhooks + events (SSE) (systems and robots):
- outgoing notification when an intervention opens;
- authenticated incoming signal (
POST /interventions/{id}/signal) when the system has finished; - SiLA 2 adapter (lab automation standard, Python library
sila2) in a later phase.
An agent can also be called by the platform: a "reviewer agent" configured per pipeline, which receives the intervention, reads the evidence via MCP and proposes a decision.
Engine-side mechanics¶
- Long wait (hours, days, a wet-lab step): the pipeline stops after step N (
untilfor Snakemake,--stageparameter for Nextflow). The platform holds theAWAITING_INTERVENTIONstate, then the launcher relaunches the next stage with the new parameters or new data. Nothing runs during the wait. - Short wait (minutes): option of a local wait task (Snakemake
localrulerule, Nextflowexecutor 'local'process) that polls the API. It does not consume a cluster slot, but keeps the main process alive. - External intervention within the flow: part 1 →
external_action(the robot prepares, the instrument acquires) → "done" signal with the path to the new data → part 2. This is the generalised "iterative in parts" mode.
Safeguards for AI¶
- Limited-scope agent tokens: an agent can propose anywhere, but only approves on its own where the policy allows it. Costly or irreversible actions (deletion, large launch) require a human.
- AI decisions are flagged as such, with a justification, and a human can revert them as long as the next stage has not started.
- Artefact content (logs, HTML reports, metadata) is untrusted for an agent: it is presented as data, never as instructions (prompt injection risk).
- External signals are idempotent; a late signal after expiry is logged without changing the decision.
Prior art: Temporal (approval pattern: structured signal, timeout, escalation, idempotency), Airflow 3.1 (HITL operators), Argo (suspend).
5ter. Implementing pause, check and resume¶
Principle: an engine is never frozen mid-run. A pause is a clean stop at a stage boundary. Resuming is a new launch that reuses the engine's cache. Each launch is a Run within the same Lineage.
1. Declare the stages: a manifest next to the pipeline (spectio.yml, modelled on tower.yml)¶
stages:
- id: preprocess
snakemake: { until: [qc_report] } # rule or target file
nextflow: { params: { stage: preprocess } }
intervention:
type: parameter_edit # approval | choice | parameter_edit | external_wait …
evidence: ["results/qc/*.json", "results/qc/*.png", "results/multiqc.html"]
params_schema: schema/segmentation.json
policy: { mode: agent_proposes_human_confirms, timeout: 48h, on_timeout: stop }
- id: analysis
snakemake: { until: [] } # until the end
nextflow: { params: { stage: analysis } }
2. Platform-side state machine¶
QUEUED → RUNNING(stage k) → STAGE_DONE → AWAITING_INTERVENTION → decision →
continue→RUNNING(k+1);params_changed→RUNNING(k or chosen stage)with the new parameters;rerun_all→ new run without cache;reject→STOPPED.
For an unplanned pause ("Pause" button): PAUSING → PAUSED, then resume.
3. Pause¶
-
Planned (stage boundary):
- Snakemake launches the stage with
DAGSettings(until=[…])via the Python API. - Nextflow launches with the
--stageparameter, and the pipeline testsparams.stagein itsworkflowblock. Nextflow has no native equivalent tountil, so the pipeline must cooperate.
- Snakemake launches the stage with
-
Unplanned: SIGINT to Snakemake (which cancels cluster jobs via
cancel_jobs), SIGTERM to Nextflow (orscancelof the head job). Running tasks are lost; finished ones remain in the cache. No native "finish running tasks without submitting new ones" mode [to be validated].
4. Check¶
At the end of a stage (last Snakemake PROGRESS, Nextflow /trace/complete), the platform:
- collects the declared evidence (globs) and the QC metrics in JSON;
- creates the
Intervention, notifies the UI, the MCP server and the webhooks; - applies the policy (auto-approval under threshold, timeout, escalation).
5. Resume¶
| Decision | Snakemake | Nextflow |
|---|---|---|
| Continue | same directory, next stage; .snakemake avoids redoing finished jobs |
same launch directory, -resume <sessionId>, next --stage |
| Change the parameters and restart from stage X | new config (ConfigSettings) + forcerun=[rule X]; downstream is invalidated automatically |
new -params-file + -resume; only tasks whose hash changes are relaunched. Forcing a specific stage requires a "salt" parameter read by that process [to be validated] |
| Relaunch everything | forceall or new directory |
without -resume, or new directory |
Settings to fix:
- Snakemake: explicit
rerun_triggers;unlock+rerun_incompleteafter an abrupt stop. - Nextflow:
cleanup = false; same Nextflow version across stages (the hash changes in 26.04); control parameters such asstagemust not enter the task hash;cache 'lenient'on NFS.
6. Launcher¶
- One supervisor per run, persisted in the database. Snakemake: a Python worker process that calls the API. Nextflow:
nextflow run(JVM) launched as a process or as ansbatchhead job, withTOWER_WORKFLOW_IDset by us. Tracking is always done through events, never by reading the process output. - On server restart: reconciliation with the database, PIDs and
sacct. - Short wait (minutes): option of a local wait task that polls the API (section 5bis).
6. Results visualisation¶
- PNG/JPEG: WebP thumbnails generated by the pipeline,
loading="lazy". - PDF:
pdf.jsin a worker with range loading. - HTML (MultiQC, reports):
iframe sandbox="allow-scripts"withoutallow-same-origin, ideally from a separate origin without cookies, with a strict CSP header on the server side. - Tables: server-side pagination and virtualisation.
- Imaging: OME-Zarr (NGFF 0.6 current, 0.5 widely supported; read 0.4 to 0.6). Viv (deck.gl components, Zarr v3 since 0.19) for fine-grained channel control, Vizarr for speed (iframe
?source=), Neuroglancer as an "open in" link for 3D. Only one active WebGL viewer at a time; on tablet and mobile, fall back to a thumbnail and an "open on desktop" link. - Serving the data: a small read-only proxy (FastAPI or nginx) that checks the token and an allowed prefix, forbids paths outside the root, and handles
Rangeand CORS. S3 presigned URLs only cover a single object, so they are unsuitable for a Zarr.
7. Recommended stack¶
- Backend: FastAPI (native SSE since 0.135, 15 s keep-alive,
Last-Event-ID), shared Pydantic, SQLAlchemy 2, uvicorn with a single worker for the MVP. - Frontend: React + Vite + Mantine 7 (shared with depictio) + TanStack Router and Query. An SSE event updates the cache. Tables become cards below 768 px. The static bundle is served by FastAPI: a single process, no Node in production.
- Real time: SSE (unidirectional, passes through proxies and SSH tunnels), one multiplexed stream per tab.
- Storage: SQLite WAL for the MVP, Postgres later via
DATABASE_URL(portable SQLAlchemy Core, Alembic withrender_as_batch). Require SQLite ≥ 3.51.3 (WAL-reset bug fix); backup viaVACUUM INTO. - Critical constraint: WAL does not work on NFS/Lustre/GPFS. The file must be on the local disk of the service node.
7bis. Third-party integration (trigger and track via our API)¶
High priority: a third-party tool (LIMS, portal, orchestrator, AI agent, script) must be able to launch and track a run without going through the UI.
- Native
/api/v1API: stable, versioned contract, published OpenAPI, generated Python and TypeScript SDKs. Launch with an idempotency key (Idempotency-Key) to avoid duplicates on retry. Cursor-based pagination, structured errors. - GA4GH WES 1.1 facade:
POST /runs(multipart:workflow_url,workflow_type,workflow_params,workflow_engine_parameters,tags),GET /runs,/status,/tasks,/cancel,/service-info. The spec mentions CWL and WDL but allows other types declared by the instance: we will declare Nextflow and Snakemake inservice-info. Pause, interventions and resume go into extensions. - Outgoing events: signed webhooks (HMAC) with retry, subscriptions filtered by project and event type; SSE stream for real time. Standard envelope format (CloudEvents, to be decided).
- Scoped tokens (
runs:launch,runs:read,interventions:resolve…) per user, service or agent. - MCP for AI agents (section 5bis), on the same API.
- Partial compatibility with the Seqera API (to reuse
twor seqerakit): to be studied later. Public OpenAPI specification, but subject to Seqera's terms of use: legal check first.
7quinquies. Spectio as a headless execution layer: plugins in other interfaces, LabID, RO-Crate (25/10)¶
Goal: Spectio must also work without its own UI, as an engine-agnostic layer that other systems use to launch and monitor Nextflow and Snakemake runs (later CWL and WDL). The web UI is one client among others; everything it does goes through the public API (principle of §7bis).
Headless mode¶
spectio serve --no-ui(or simply never opening the UI): same server, same runners, same API. Typical callers: LIMS and ELNs (LabID at EMBL), portals, core-facility dashboards, Jupyter, CI, AI agents.- Stable public resources:
Workflow,WorkflowVersion,Environment,Run,Task,Gate/Intervention,Artifact,Event. Each carriesexternal_refs(e.g.labid:sample/123,labid:dataset/456, project or order IDs), at run level and per samplesheet row, so the caller can map results back to its own records. - Launch contract: workflow + version + params file + samplesheet (rows may reference external IDs that the caller resolves to paths, or that a resolver plugin resolves through the runner), environment, optional gates policy;
Idempotency-Key; response = run ID + links. Everything else (states, tasks, gates, artefacts) is read or subscribed to.
Surfaces for other interfaces¶
| Surface | For | Notes |
|---|---|---|
REST /api/v1 + OpenAPI, Python / TypeScript SDKs, spectio CLI |
any system | the reference contract |
| GA4GH WES 1.1 facade (later TRS for the catalogue, DRS for artefacts) | standard clients (Sapporo-style, workflow portals) | no gates in WES: exposed as extensions |
| Webhooks (signed, CloudEvents envelope) + SSE stream | systems that react | run state, gate opened / decided, artefact published |
| MCP server | AI agents | same scopes as tokens |
| Embeddable elements (web components, iframe fallback) | other web UIs | <spectio-run-card run=…>, <spectio-run-timeline>, <spectio-launch-form workflow=… prefill=…>, <spectio-review gate=…>, <spectio-artifact-viewer>; scoped short-lived token; events via postMessage; themeable with the design tokens |
| Host plugins | specific platforms | thin adapters on the host side (e.g. a LabID plugin) that call the API and embed the elements |
LabID (EMBL)¶
What is known (from the RO-Crate project page on LabID): LabID is EMBL Heidelberg's FAIR data-management platform (MODIS group) for samples, datasets, inventory and an ELN; it imports and exports workflows and workflow runs from Galaxy, WorkflowHub and Git using the Workflow RO-Crate and Workflow Run RO-Crate profiles. Its public API surface is not documented on that page: to confirm with the MODIS team.
Proposed integration, in both directions:
- LabID → Spectio: from a LabID project, select samples / datasets, pick a registered workflow, fill the embedded launch form (or call the API); Spectio receives the samplesheet with LabID IDs in
external_refs; the runner resolves dataset locations on EMBL storage. - During the run: LabID shows the embedded run card; gate notifications can be routed to LabID users; review can be done in Spectio or in the embedded review element.
- Spectio → LabID: on completion (or at each gate), Spectio produces a Workflow Run RO-Crate; LabID imports it, registers outputs as datasets linked to the input samples, and keeps the provenance, including the review decisions.
RO-Crate compatibility¶
- Import a workflow from a crate: nf-core requires every pipeline to ship its own RO-Crate (describing the pipeline, not runs); WorkflowHub entries are Workflow RO-Crates. Spectio registers a workflow from such a crate (name, version, repository, licence, inputs).
- Export every run as a crate:
GET /api/v1/runs/{id}/ro-crate(JSON-LD or zip). Target the Workflow Run Crate profile, and the Provenance Run Crate when step-level detail is available (it is: Spectio stores tasks). Content: workflow version (repository + commit), engine and version, params file, samplesheet (withexternal_refs), environment and containers / conda environments, inputs and outputs with checksums, timings. - Spectio-specific provenance in the crate: gates, checks and their results, decisions with their actor (person, AI agent with model, robot), excluded items with reasons, edited samplesheet / parameter / file versions. Encoded as additional actions in the crate so a generic RO-Crate reader still understands the run.
- Engines: Nextflow's
nf-provplugin can already write a Workflow Run RO-Crate (WRROC, since 1.4.0); Spectio can enable it through its generated config and merge its output with the gate provenance. For Snakemake no native exporter was found, so Spectio builds the crate from its own event store. - Reproduce from a crate: importing a run crate pre-fills a launch (same workflow version, params, samplesheet).
Full design (deployment modes, plugins, endpoint catalogue, identity and tenancy, database schema, events): see api-and-data-model.md. Plugin contract and packaging (Python bundle, Docker Compose, Helm): see architecture-plugins-and-packaging.md. Administration and operations (users and groups, branding, tasks, logs, health, backups, restore, migrations, maintenance): admin-and-operations.md. CLI and libraries shared with depictio and LabID: cli-and-shared-libraries.md. What is reused from depictio: depictio-ecosystem-reuse.md. HPC schedulers, Kubernetes and monitoring (pending reasons, accounting, capacity, pre-flight): schedulers-and-monitoring.md.
Phasing¶
MVP: native API, SDK, webhooks (already planned). Phase 1b: RO-Crate export and import, WES facade. Phase 2: embeddable elements, LabID plugin (after confirming LabID's API with MODIS), TRS / DRS.
Sources: LabID on researchobject.org, Workflow Run RO-Crate profiles, nf-prov, nf-core RO-Crate requirement.
7quater. Register, launch and track without a terminal (24/10)¶
Goal: the end user no longer touches the terminal. They register a workflow, launch it on their machine or on a remote cluster, track it, debug it and relaunch it from the interface.
Two coexisting modes¶
- Observed (Tower/Panoptes model): the user launches from the command line, and the plugin reports the events. Useful for experts, but not sufficient.
- Driven (Seqera Launchpad, Sapporo/WES model): the platform does the launching. This is the default mode for the end user.
Three building blocks¶
-
Workflow catalogue. A workflow is registered once: git URL + revision (pinned to a commit), or a path on the cluster. Spectio reads its description:
- Nextflow:
nextflow_schema.json(nf-core convention) → generated form; - Snakemake:
config/config.yaml+workflow/schemas/config.schema.yaml(Snakemake workflow catalogue convention) → generated form; - optional:
spectio.yml(stages, interventions, QC evidence, parameter presets). Can later be exposed as GA4GH TRS, the registry standard (WES has no registry).
- Nextflow:
-
Execution environments + runner. A Spectio runner (
spectio runner, same pip package, a single logical binary) runs on the login node, under the user's account. It opens an outgoing connection to the server, so no inbound port on the cluster. It receives orders (launch, cancel, resume, read a log, browse a directory), submits the head job to the scheduler and configures the plugins automatically (tower.endpoint,--logger). This is the same principle as Seqera's Tower Agent (outgoing WebSocket, user's account, Slurm/LSF/PBS/SGE). Locally, server and agent run in the same process (perspectio serve). - Launching from the interface. Choose the workflow → the environment → fill in the form (sample sheet uploaded or selected via the agent) → Launch. Then: tracking, per-task debugging page (command, stdout/stderr, exit code, working directory files read on demand via the agent), relaunch with a prefilled form, resume, interventions.
Comparison¶
| Registration | Remote launch | Tracking | Steering (pause, QC, relaunch) | |
|---|---|---|---|---|
| Tower / Panoptes (plugin mode) | none | no (terminal) | yes | no |
| Seqera Platform | Launchpad | yes, Tower Agent | rich | relaunch, no interventions |
| Sapporo (WES) | list of executable workflows in the config | POST /runs, engines in Docker containers; HPC schedulers not mentioned |
run state, logs | cancellation |
| Spectio | catalogue + generated forms | outgoing agent, local or cluster | rich (events) | pause, interventions, relaunch, resume |
Spectio also exposes the WES facade: a Sapporo or WES client can launch and track runs.
The terminal, just once¶
Install the runner on the cluster: pip install spectio, then spectio runner connect <token> (token generated in the interface). An administrator can do this for everyone. After that, no more terminal for the user.
7ter. Distribution¶
- Server = pip-installable Python package (
pip install,uv tool installorpixi), with aservecommand. The React build is embedded in the wheel; Node is only used in CI. Compiled dependencies (pydantic-core…) are available as manylinux wheels. - SQLite: the uv-managed Python embeds SQLite 3.53.1, tested on 23/10 (compliant). The system Python in this container has SQLite 3.45.1, which is affected by the WAL-reset bug. Possible fallback with the
apswwheel (SQLite 3.54.1 embedded). Version check at startup. - Engine plugins distributed separately: Snakemake logger plugin = pip package
snakemake-logger-plugin-<name>in the Snakemake environment; Nextflow companion plugin (Groovy) via the Nextflow plugin registry, downloaded automatically if declared in the config. - No PR required at Snakemake or Nextflow: both engines provide for these extensions. Upstream PRs are useful but optional, especially on the Snakemake side:
external_jobidandattemptin events, typed events for temporary file deletion and for DAG updates after a checkpoint, an end-of-workflow event, the executor name. They would reduce text message parsing.
8. HPC constraints¶
- Delivery without root:
uvorpixienvironment, or Apptainer image (user namespaces required). - Host on a service node or a dedicated VM rather than on a login node (often forbidden for long-running services). To be validated with the admins.
- Access by listening on
127.0.0.1and an SSH tunnel, or via the site's reverse proxy (proxy_buffering offfor SSE). - Egress from compute nodes is often filtered: test early. Fallback: the agent writes JSONL to the shared file system, and a server-side forwarder reads it.
- Restart: state replayed from the database, reconciliation with
sacctorsqueue. Launched viasystemd --user(iflingeris allowed) or a loop intmux. - Agents: disk buffer, batched sending, retry with exponential backoff, never block the workflow if the server is unreachable.
- Launching runs (later stage):
sbatchof a head job rather than-bgon the login node. Cancellation:scancelor SIGTERM [to be validated in practice].
9. Proposed roadmap¶
| Phase | Content | Indicative duration |
|---|---|---|
| 0 | Network reachability test on the target cluster, database schema, idempotent ingestion | 2 to 3 weeks |
| 1 (MVP) | Snakemake agent, run list and detail, SSE, responsive UI, uv and Apptainer deployment, one ingestion token |
4 to 6 weeks |
| 1b | Nextflow agent (V2 plugin), sacct reconciliation, documented SSH tunnel |
|
| 1c | Stages, checkpoints and decisions: QC gates, relaunch with new parameters, standard viewers | |
| 2 | Users, groups, projects, RBAC, CLI tokens generated from the UI (hashed, scoped, expiring, revocable), OIDC/LDAP, audit log | |
| 3 | Postgres, WES facade, advanced OME-Zarr viewers, notifications, SGE, WDL/CWL |
Durations come from an agent report and should be taken as orders of magnitude.
I propose to start with read-only tracking of both engines, then add steering (phase 1c). QC gates are what sets the project apart, but they rely on a reliable event log.
10. Main risks¶
- SQLite WAL on a shared file system: corruption. Enforce local disk and check it at startup.
- Network access: only the engine's main process needs to reach the server (verified for Snakemake and Nextflow). The risk therefore concerns the node where that process runs (login node or
sbatchhead job), not the compute nodes. Keep the spool fallback. On the Nextflow side, a server outage duringcreate,beginorprogressaborts the run. - Site policy on long-running services.
- Unstable engine APIs:
TraceObserverV2is beta, Snakemake logger fields may change,-with-weblogis deprecated. Pin versions, test in CI, version the event schema. - Different granularity between engines (Nextflow process/task, Snakemake jobs and grouped jobs): document the mapping.
- The
-resumecache can lead to inconsistent resumes after parameter changes: validate case by case. - A single SQLite writer under heavy load: batched inserts, migration to Postgres if needed.
11. Open questions¶
- Target cluster: SLURM, SGE, Kubernetes? Login node or dedicated machine?
- Launching: does the interface submit runs, or does it only track those launched from the CLI with the plugin?
- Imaging: formats (OME-Zarr, OME-TIFF…) and typical sizes?
- Project name and licence (MIT, Apache, AGPL)?
- Is a WES facade needed from the MVP, or later?
12. Main sources¶
- Nextflow: plugins, TraceObserverV2, nf-weblog, cache and resume, tower config
- Snakemake: monitoring, logger interface, API, panoptes, plugin catalogue
- GA4GH: WES, TES, Sapporo, nf-shard
- Viewers: NGFF, Viv, Vizarr, Neuroglancer, iframe sandbox (MDN), Airflow HITL, Argo suspend
- Architecture: FastAPI SSE, SQLite WAL, Alembic batch, Apptainer userns, Slurm REST