specimux-cloud 0.1.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.
Files changed (46) hide show
  1. specimux_cloud-0.1.0/LICENSE +28 -0
  2. specimux_cloud-0.1.0/PKG-INFO +462 -0
  3. specimux_cloud-0.1.0/README.md +424 -0
  4. specimux_cloud-0.1.0/pyproject.toml +55 -0
  5. specimux_cloud-0.1.0/setup.cfg +4 -0
  6. specimux_cloud-0.1.0/src/specimux_cloud/__init__.py +19 -0
  7. specimux_cloud-0.1.0/src/specimux_cloud/backends/__init__.py +5 -0
  8. specimux_cloud-0.1.0/src/specimux_cloud/backends/aws.py +487 -0
  9. specimux_cloud-0.1.0/src/specimux_cloud/backends/base.py +196 -0
  10. specimux_cloud-0.1.0/src/specimux_cloud/backends/local.py +463 -0
  11. specimux_cloud-0.1.0/src/specimux_cloud/cli.py +225 -0
  12. specimux_cloud-0.1.0/src/specimux_cloud/console/__init__.py +0 -0
  13. specimux_cloud-0.1.0/src/specimux_cloud/console/app.py +555 -0
  14. specimux_cloud-0.1.0/src/specimux_cloud/dorado/__init__.py +0 -0
  15. specimux_cloud-0.1.0/src/specimux_cloud/dorado/wrapper.py +270 -0
  16. specimux_cloud-0.1.0/src/specimux_cloud/engine/__init__.py +0 -0
  17. specimux_cloud-0.1.0/src/specimux_cloud/engine/plugin.py +119 -0
  18. specimux_cloud-0.1.0/src/specimux_cloud/engine/wrapper.py +452 -0
  19. specimux_cloud-0.1.0/src/specimux_cloud/packages.py +50 -0
  20. specimux_cloud-0.1.0/src/specimux_cloud/runapi/__init__.py +0 -0
  21. specimux_cloud-0.1.0/src/specimux_cloud/runapi/app.py +547 -0
  22. specimux_cloud-0.1.0/src/specimux_cloud/runapi/auth.py +95 -0
  23. specimux_cloud-0.1.0/src/specimux_cloud/runapi/events.py +120 -0
  24. specimux_cloud-0.1.0/src/specimux_cloud/runapi/service.py +1727 -0
  25. specimux_cloud-0.1.0/src/specimux_cloud/stopsignal.py +49 -0
  26. specimux_cloud-0.1.0/src/specimux_cloud/uploader/__init__.py +0 -0
  27. specimux_cloud-0.1.0/src/specimux_cloud/uploader/cli.py +228 -0
  28. specimux_cloud-0.1.0/src/specimux_cloud/uploader/runctl.py +71 -0
  29. specimux_cloud-0.1.0/src/specimux_cloud/uploader/submit.py +169 -0
  30. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/PKG-INFO +462 -0
  31. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/SOURCES.txt +44 -0
  32. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/dependency_links.txt +1 -0
  33. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/entry_points.txt +5 -0
  34. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/requires.txt +16 -0
  35. specimux_cloud-0.1.0/src/specimux_cloud.egg-info/top_level.txt +1 -0
  36. specimux_cloud-0.1.0/tests/test_backends_aws.py +202 -0
  37. specimux_cloud-0.1.0/tests/test_backends_local.py +161 -0
  38. specimux_cloud-0.1.0/tests/test_console.py +271 -0
  39. specimux_cloud-0.1.0/tests/test_contract.py +133 -0
  40. specimux_cloud-0.1.0/tests/test_dorado_wrapper.py +44 -0
  41. specimux_cloud-0.1.0/tests/test_ingest_log.py +54 -0
  42. specimux_cloud-0.1.0/tests/test_local_stack.py +366 -0
  43. specimux_cloud-0.1.0/tests/test_runapi.py +1255 -0
  44. specimux_cloud-0.1.0/tests/test_stopsignal.py +42 -0
  45. specimux_cloud-0.1.0/tests/test_uploader.py +144 -0
  46. specimux_cloud-0.1.0/tests/test_wrapper.py +98 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Josh Walker
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,462 @@
1
+ Metadata-Version: 2.4
2
+ Name: specimux-cloud
3
+ Version: 0.1.0
4
+ Summary: Hosted specimux-suite: the uploader, run API, console and compute jobs for sequencing runs in the cloud
5
+ Author-email: Josh Walker <joshowalker@yahoo.com>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/joshuaowalker/specimux-cloud
8
+ Project-URL: Repository, https://github.com/joshuaowalker/specimux-cloud
9
+ Project-URL: Issues, https://github.com/joshuaowalker/specimux-cloud/issues
10
+ Keywords: bioinformatics,nanopore,sequencing,barcoding,cloud
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: specimux-suite>=0.3.5
24
+ Requires-Dist: fastapi>=0.104
25
+ Requires-Dist: uvicorn>=0.24
26
+ Requires-Dist: sse-starlette>=1.8
27
+ Requires-Dist: httpx>=0.27
28
+ Requires-Dist: cryptography>=41
29
+ Requires-Dist: python-multipart>=0.0.9
30
+ Provides-Extra: aws
31
+ Requires-Dist: boto3>=1.34; extra == "aws"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.4; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
35
+ Requires-Dist: boto3>=1.34; extra == "dev"
36
+ Requires-Dist: moto[dynamodb,s3,sqs]>=5; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ # specimux-cloud
40
+
41
+ The hosted form of [specimux-suite](https://github.com/joshuaowalker/specimux-suite).
42
+ A lab sends a nanopore sequencing run to the cloud (raw POD5, which the
43
+ service basecalls on a GPU, or FASTQ already basecalled by MinKNOW) and
44
+ gets the same live dashboard and results the suite produces on a laptop:
45
+ demultiplexing, consensus sequences per specimen, and identification
46
+ against a reference database.
47
+
48
+ - **[Uploading a run](#uploading-a-run):** you have a job code and a run
49
+ folder. Start here.
50
+ - **[Creating runs](#creating-runs):** the console, sharing a dashboard
51
+ publicly, and the command line.
52
+ - **[Running a deployment](#running-a-deployment):** for operators and
53
+ developers: the AWS stack, hosts and keys, the local stack, and how the
54
+ pieces fit together.
55
+
56
+ ## Uploading a run
57
+
58
+ Every run has a **job code** (it looks like `r1a2b3c4d.<secret>`), which
59
+ you get from whoever created the run: a website such as mycomap.org, or
60
+ the service's console. The job code is all the uploader needs; there is no
61
+ account or login. Treat it like a password until the upload is done.
62
+
63
+ ### Install
64
+
65
+ On the computer that has the reads (the sequencing laptop, a lab server,
66
+ anything with Python 3.11 or later and an internet connection):
67
+
68
+ ```bash
69
+ pip install specimux-cloud
70
+ ```
71
+
72
+ ### A finished run folder
73
+
74
+ When sequencing is over, or the reads are already on disk:
75
+
76
+ ```bash
77
+ specimux-cloud upload --run-api https://runs.specimux.com --job-code <code> --once <folder>
78
+ ```
79
+
80
+ The folder can be a MinKNOW run folder or any folder of `.pod5` or
81
+ `.fastq` / `.fastq.gz` files; subfolders are included. `--once` uploads
82
+ what is there and tells the service the upload is complete, and
83
+ processing starts. The run's type (POD5 or FASTQ) was chosen when the run
84
+ was created, so upload the matching files.
85
+
86
+ ### While sequencing
87
+
88
+ Start the uploader on the MinKNOW run folder while MinKNOW is still
89
+ writing it, without `--once`:
90
+
91
+ ```bash
92
+ specimux-cloud upload --run-api https://runs.specimux.com --job-code <code> <MinKNOW run folder>
93
+ ```
94
+
95
+ It uploads each file once it has stopped growing (30 seconds unchanged;
96
+ `--settle` changes that) and finishes by itself when MinKNOW writes its
97
+ `final_summary_*.txt` at the end of the run. For a **live** run the
98
+ service processes files as they arrive, so the dashboard fills while you
99
+ sequence; for a batch run, uploading during sequencing just means the
100
+ upload is done when sequencing is.
101
+
102
+ Reads under MinKNOW's `fastq_fail` and `pod5_fail` folders are left out;
103
+ `--include-failed` sends them too.
104
+
105
+ ### When something goes wrong
106
+
107
+ - **The upload was interrupted** (network drop, laptop asleep, Ctrl+C):
108
+ run the same command again. Files already uploaded are checked and
109
+ skipped; nothing is sent twice. Network errors are retried on their own.
110
+ - **You forgot `--once`** and sequencing is over: the uploader keeps
111
+ waiting for MinKNOW's final summary. After ten quiet minutes it prints a
112
+ reminder. Press Ctrl+C and run the same command with `--once`, or click
113
+ **Upload is complete** on the run page; a running uploader notices
114
+ within a minute and exits.
115
+ - **"stopped taking uploads" or "no longer taking uploads"**: the upload
116
+ was already completed (from the run page, say), the run was cancelled,
117
+ or it expired. A run with no upload activity for 24 hours (or none at
118
+ all within 7 days of creation) is closed; what it had received is
119
+ deleted a week later. Upload the folder again to a new run.
120
+ - **Lost the job code**, or it may have leaked: the run page's **New job
121
+ code** issues another while the run is still taking uploads, and the
122
+ old one stops working at once.
123
+ - **A bad job code** fails at once with the server's reason; check that
124
+ the whole code was copied.
125
+
126
+ ### After the upload
127
+
128
+ Processing starts on its own: basecalling first for POD5, then the
129
+ pipeline. The run page shows the stage and progress and links to the
130
+ live **dashboard**. For scale: a full MinION run of about a million reads
131
+ basecalled with dorado's SUP model in about 47 minutes on one GPU, and
132
+ the pipeline then took about 25 minutes. When a run is done its page
133
+ offers three downloads: `results.zip` (the summary package and the run's
134
+ event log), `output.zip` (the full output) and `reads.zip`
135
+ (demultiplexed reads per specimen).
136
+
137
+ ## Creating runs
138
+
139
+ A deployment is one URL, the **run API** (for example
140
+ `https://runs.specimux.com`). Runs are created by a **host**: a website
141
+ that knows its users, or the built-in **console** at `<run API>/console/`,
142
+ which you log into with a **service key** from the operator. A service
143
+ key creates runs and sees every run of its host; keep it out of email,
144
+ chat and git repositories.
145
+
146
+ ### In the console
147
+
148
+ 1. Open `<run API>/console/` and log in with your service key.
149
+ 2. **New run:** upload the primers FASTA and the specimens file
150
+ (Index.txt), optionally a reference FASTA (`name="..."` headers;
151
+ without one there is no identification; references used before are
152
+ offered again), and choose the input:
153
+ - **FASTQ**, uploaded after or during sequencing;
154
+ - **Live** FASTQ, processed as it arrives (see
155
+ [While sequencing](#while-sequencing));
156
+ - **POD5**, basecalled by the service. The defaults are dorado's
157
+ `sup@v5.0.0` model and reads 400–2000 bases long, the full ITS
158
+ amplicon.
159
+ 3. The console shows the job code once, with the upload command to run
160
+ where the reads are.
161
+ 4. The run page follows the run from there: stage, basecalling
162
+ progress, the dashboard link and, at the end, the downloads. It also
163
+ has **Upload is complete**, **Cancel** (stops a run at whatever stage
164
+ it is in) and **Retry** (runs a failed run's failed stage again;
165
+ basecalling skips the files it already delivered).
166
+
167
+ The runs list shows the whole service's load (runs busy and queued per
168
+ stage, and any waiting for a machine), which explains a wait before
169
+ basecalling or the pipeline starts. Each stage runs two runs at a time.
170
+
171
+ ### Sharing a dashboard publicly
172
+
173
+ **Share publicly** on the run page makes a link that opens the run's
174
+ dashboard for anyone who has it, with no login: for example an audience
175
+ watching a live run. The dashboard shows the link as its QR code. Public
176
+ viewers can watch and star specimens (starring only raises a specimen's
177
+ processing priority; the run page can block it) but cannot finalize,
178
+ correct, or download unless you allow downloads. **Stop sharing** or
179
+ **New link** ends every public session at once.
180
+
181
+ ### From the command line
182
+
183
+ With a service key, one command creates the run, uploads, waits and
184
+ downloads `results.zip`:
185
+
186
+ ```bash
187
+ specimux-cloud submit --run-api https://runs.specimux.com --service-key <key> \
188
+ --primers primers.fasta --specimens Index.txt [--reference refs.fasta] \
189
+ --wait <folder or files>
190
+ ```
191
+
192
+ POD5 input is detected from the files; `--model`, `--min-length`,
193
+ `--max-length` and `--min-qscore` set basecalling, and `--profile` and
194
+ `--min-reads` the pipeline. `--live` makes a live run of a folder the
195
+ uploader keeps watching. The service keeps each reference database once,
196
+ by its SHA-256, so `submit` sends a reference only the first time your
197
+ host uses it (another host's copy is never lent: each host sends a
198
+ reference once itself). One run
199
+ at a time:
200
+
201
+ ```bash
202
+ specimux-cloud run status|cancel|retry <run id> --run-api URL --service-key KEY [--reason TEXT]
203
+ ```
204
+
205
+ `submit` and `run` also take the key from `SPECIMUX_SERVICE_KEY`, which
206
+ keeps it out of your shell history.
207
+
208
+ ## Running a deployment
209
+
210
+ The rest of this README is for whoever runs a deployment or works on the
211
+ code. The design and the reasoning behind it are in
212
+ [docs/DESIGN.md](docs/DESIGN.md).
213
+
214
+ ### For a website that wants to be a host
215
+
216
+ A host creates runs with its service key over a small HTTP API, shows its
217
+ users the job code, and has one **authorize route** that vouches for a
218
+ logged-in user when they open a run's dashboard: the run API mints a
219
+ short-lived token at the host's request, so the host never signs
220
+ anything. The console (`src/specimux_cloud/console/app.py`) is a complete
221
+ host built only on that API, and `tests/test_contract.py` checks a host's
222
+ side of it against any deployment. The API is under "The run API" in
223
+ [docs/DESIGN.md](docs/DESIGN.md).
224
+
225
+ ### Deploying on AWS
226
+
227
+ `infra/` is a CDK app (Python) for one AWS account and region:
228
+
229
+ - a VPC with public subnets only (no NAT gateway);
230
+ - an S3 bucket for uploads, results and finished runs, a DynamoDB table
231
+ for runs, hosts and keys, and EFS for the mirrors of runs in progress,
232
+ which the dashboard reads;
233
+ - AWS Batch: a CPU environment for the pipeline (c6i, m6i, c5 or m5, up
234
+ to 64 vCPUs) and a GPU environment for dorado (g6, g5 or g6e xlarge),
235
+ both at zero instances when idle, two runs at a time on each;
236
+ - the run API on Fargate behind an application load balancer at
237
+ `https://runs.<domain>`, with an ACM certificate;
238
+ - ECR repositories for the three images, and the session secret in
239
+ Secrets Manager.
240
+
241
+ Idle cost is the Fargate task, the load balancer and storage, about $1.50
242
+ a day; instances exist only while a run executes.
243
+
244
+ Before deploying you need a Route 53 hosted zone for your domain in the
245
+ account, and a quota of at least 4 vCPUs for "Running On-Demand G and VT
246
+ instances" for basecalling. Then:
247
+
248
+ ```bash
249
+ export AWS_PROFILE=<your profile>
250
+ cd infra
251
+ npx aws-cdk@2 bootstrap # once per account and region
252
+ npx aws-cdk@2 deploy -c domain=example.org --outputs-file cdk-outputs.json
253
+ cd ..
254
+ docker/build-push.sh all # engine and run API images
255
+ docker/build-push.sh dorado # dorado plus its models, several GB
256
+ cd infra
257
+ npx aws-cdk@2 deploy -c domain=example.org -c runapiDesired=1 --outputs-file cdk-outputs.json
258
+ ```
259
+
260
+ The first deploy creates everything with the run API at zero tasks; the
261
+ second starts it once its image exists. `-c domain` is required on every
262
+ deploy; `-c domain=` with no value
263
+ deploys without the load balancer and certificate (the run API is then
264
+ reachable only inside the VPC). The region is the profile's, or
265
+ `us-west-2`. `build-push.sh` reads the repository URIs from
266
+ `cdk-outputs.json`. A new engine image takes effect on the next run
267
+ (Batch pulls `:latest` per job); a new run API image needs the ECS
268
+ service rolled (`aws ecs update-service --force-new-deployment`).
269
+
270
+ Dorado models are baked into the dorado image (`DORADO_MODELS` in
271
+ `docker/dorado.Dockerfile`), and the model names the run API offers are
272
+ `SPECIMUX_DORADO_MODELS` in `infra/stack.py`; keep the two in step.
273
+
274
+ Then register the hosts that may use the deployment. A key is printed
275
+ once:
276
+
277
+ ```bash
278
+ TABLE=$(jq -r '."specimux-cloud".TableName' infra/cdk-outputs.json)
279
+ specimux-cloud hosts --backend aws --table $TABLE add lab --name "Our lab" --label lab-staff \
280
+ --console --base-url https://runs.example.org
281
+ specimux-cloud hosts --backend aws --table $TABLE add partner --name "Partner site" --label server \
282
+ --authorize-url https://partner.example.org/specimux/authorize
283
+ ```
284
+
285
+ `--console` makes the console the host's authorize route, for a host with
286
+ no website. Hosts have labelled keys (`hosts key`, `hosts rotate` with a
287
+ day's grace for the old key, `hosts revoke`, `hosts disable`, `hosts
288
+ list`, `hosts set`) and see only their own runs.
289
+
290
+ ## Development
291
+
292
+ ```bash
293
+ pip install -e '.[dev]'
294
+ pytest
295
+ ```
296
+
297
+ The suite comes from PyPI (0.3.3 or later); to work against an
298
+ unreleased suite, install the sibling checkout editable first
299
+ (`pip install -e ../specimux-suite`).
300
+
301
+ The package has four parts:
302
+
303
+ - `specimux_cloud.runapi`: the always-on service the browser, the
304
+ uploader and hosts talk to: event store and fan-out, a dashboard per
305
+ run, job control, uploads, results, run sessions.
306
+ - `specimux_cloud.console`: the built-in host, mounted at `/console/`
307
+ beside the run API. It talks to the run API only over HTTP with the key,
308
+ exactly as a host's website does.
309
+ - `specimux_cloud.engine` and `specimux_cloud.dorado`: what runs inside
310
+ the compute jobs: a wrapper around `specimux-suite` plus the cloud
311
+ plugin that forwards events to the run API and applies commands from
312
+ it, and the basecalling job around `dorado`.
313
+ - `specimux_cloud.backends`: storage, command queue, job launcher and
314
+ control-plane store behind small interfaces. Local implementations (a
315
+ directory, memory, subprocesses, SQLite) run the whole system on a
316
+ laptop and in CI; AWS implementations (S3, SQS, Batch, DynamoDB) are a
317
+ backend swap.
318
+
319
+ ### The local stack
320
+
321
+ ```bash
322
+ specimux-cloud runapi --data-dir data --port 8090
323
+ ```
324
+
325
+ starts the run API over the local backends: storage under `data/storage`,
326
+ control-plane state in `data/control-plane.sqlite`, engine work dirs under
327
+ `data/work`, engine logs under `data/logs`, and the console at
328
+ `http://127.0.0.1:8090/console/`. A host `dev` exists with the key
329
+ `dev.dev-service-key` (`--dev-key` changes it, `--dev-key ''` removes it).
330
+ Everything under [Uploading a run](#uploading-a-run) and [Creating runs](#creating-runs) works against it with
331
+ `--run-api http://127.0.0.1:8090`. The run API launches the engine as a
332
+ subprocess (`specimux-cloud engine`, which runs `specimux-suite batch`
333
+ with the `cloud` plugin), and a POD5 run's dorado job the same way; that
334
+ runs whatever `dorado` is on `PATH` on the device
335
+ `SPECIMUX_DORADO_DEVICE` (default `cuda:all`; `cpu` works, slowly).
336
+
337
+ `tests/test_local_stack.py` does whole runs end to end with stand-in
338
+ bioinformatics tools (`tests/fake_tools/`); `tests/test_contract.py` is
339
+ the job API as a host sees it, and runs against a deployment with
340
+ `SPECIMUX_CONTRACT_RUN_API` and `SPECIMUX_CONTRACT_KEY` set.
341
+
342
+ ## How it works
343
+
344
+ ### Run lifecycle
345
+
346
+ `created` → `uploading` → `input_complete` → (`basecalling` for POD5) →
347
+ `running` → `finalizing` → `sealing` → `sealed`, or `failed`; an
348
+ abandoned upload ends `incomplete` (no upload request for 24 hours, or
349
+ nothing uploaded within 7 days of creation; the uploader's status checks
350
+ don't count as activity), which closes its job code. The upload of a
351
+ cancelled or abandoned run is deleted a week after it ended; a finished
352
+ run's EFS directory an hour after, once its `view.zip` is in S3, and its
353
+ dashboard is served from that (see docs/DESIGN.md, "Storage"). Each stage
354
+ runs at most two runs at once (`SPECIMUX_STAGE_SLOTS`, default
355
+ `engine=2,dorado=2`; two dorado slots are what an 8-vCPU G-instance quota
356
+ allows): a run whose stage is full waits in `input_complete` and takes
357
+ the next free slot, oldest first. Concurrent runs share nothing: each has
358
+ its own EFS directory, SQS queue and scratch directory (per run and
359
+ generation). `GET /v1/load` (any service key) reports the whole
360
+ service's load in counts, cached for 15 seconds, and the console shows it
361
+ above the runs list. The run API reconciles every two minutes
362
+ (`SPECIMUX_RECONCILE_S`), so a job that dies without reporting fails its
363
+ run within minutes.
364
+
365
+ A live run takes uploads while its engine runs: the engine launches with
366
+ the first upload request (or the next free slot), the wrapper polls
367
+ `POST /v1/runs/{id}/inputs` for new files and renames each into the
368
+ engine's watch directory on local disk, and once the upload is complete
369
+ and every file has been demultiplexed (its `specimux.completed`, seen
370
+ through ingest) it sends the engine SIGINT, the suite's "finalize and
371
+ exit". A live engine job may run 96 hours (batch: 12). A relaunch
372
+ replays every uploaded file into a fresh engine.
373
+
374
+ A run can be shared publicly (`POST /v1/runs/{id}/public`, the owner's
375
+ service key): the link is the dashboard URL with a share token in the
376
+ fragment (`#token=s1.<run>.<secret>`), which the page exchanges at
377
+ `POST /v1/session` like a host's run token, for a `public`-scope session
378
+ of seven days (a public viewer has no host to return to). Every request
379
+ of a public session checks, through a five-second cache, that sharing is
380
+ still on under the same link; public sessions may send `watch`/`unwatch`
381
+ unless the owner blocked starring, nothing else, and download only if
382
+ allowed. The run's viewer carries the link in `/api/state`'s `share`, so
383
+ the dashboard's QR code shows it; event streams are capped at 200 per
384
+ run.
385
+
386
+ Each stage has its own job identity on the run (`stages.<stage>`:
387
+ generation, job secret, active), so two stages' jobs can run at once; a
388
+ job's secret opens only its own stage's routes, and a finished job's
389
+ only its exit report.
390
+
391
+ ### POD5 input: the dorado stage
392
+
393
+ A run created with `"input": "pod5"` is basecalled by the service before
394
+ the engine runs. Its spec carries a `basecall` object, defaults filled in
395
+ from the published protocol:
396
+
397
+ ```json
398
+ {"model": "sup@v5.0.0", "min_length": 400, "max_length": 2000, "min_qscore": null}
399
+ ```
400
+
401
+ `model` is a dorado model complex from `GET /v1/options` (`dorado_models`,
402
+ the ones the dorado image bakes); the length window keeps the full ITS
403
+ amplicon (100 to 700 for ITS2 alone); `min_qscore` is dorado's own floor,
404
+ off by default. On `complete` the run enters `basecalling`: the dorado
405
+ job (`specimux-cloud dorado`, `dorado basecaller <model> <file>
406
+ --emit-fastq --no-trim`) takes each POD5 file of the manifest, writes the
407
+ reads inside the window to `runs/<user>/<run>/fastq/<name>.fastq` and
408
+ reports it; when the job exits with every file present the engine
409
+ launches over those FASTQs. Progress (`basecalling`: files done, reads
410
+ called and kept) is on the run record and the console's run page.
411
+
412
+ ### Hosts, keys and dashboard sessions
413
+
414
+ The run API knows hosts, not users: a host creates runs and vouches for
415
+ its users. Each host has labelled service keys (`<host>.<random>`, stored
416
+ hashed) and an authorize URL, and sees only its own runs. A browser gets
417
+ at a run's dashboard through its host: the page, served by the run API,
418
+ sends the browser to the host's authorize URL, the host checks its own
419
+ session and asks the run API for a run token (`POST
420
+ /v1/runs/{id}/tokens`, service key), and redirects back with the token in
421
+ the URL fragment; the page exchanges it at `POST /v1/session` for a
422
+ cookie scoped to the run. The console is a host whose authorize route
423
+ works the same way.
424
+
425
+ ### How the engine talks to the run API
426
+
427
+ Events go out through the suite's HTTP event forwarder to
428
+ `POST /v1/runs/{id}/ingest`, batched, in order, with the job secret and
429
+ the run's generation; the log in the work dir is the buffer. Commands
430
+ come in by long-polling `GET /v1/runs/{id}/commands/next`: the run API
431
+ fronts the queue backend (memory locally, SQS in AWS), the plugin applies
432
+ each command through the suite's commands facade with the id the run API
433
+ assigned, acknowledges it, and the outcome event travels back through
434
+ ingest. Fronting the queue with the run API keeps one engine code path
435
+ over both backends and one credential (the job secret) per job.
436
+
437
+ The engine works on the job's local disk (`SPECIMUX_SCRATCH`) and
438
+ mirrors what the dashboard reads (the event log, consensus and summary
439
+ FASTAs, photos) into the run's EFS directory with the suite's
440
+ `--mirror-dir`; after the engine exits the wrapper builds the three
441
+ downloads locally and uploads them (`POST /v1/runs/{id}/package-uploads`).
442
+ EFS made the demux about 200 times slower; see docs/DESIGN.md, "Engine
443
+ storage". On SIGTERM (a cancel, a timeout) both wrappers stop their child
444
+ and report exit 143 within Batch's 30-second window.
445
+
446
+ ## License
447
+
448
+ specimux-cloud's own code is under the BSD 3-Clause License (`LICENSE`).
449
+ That license covers this repository only, not the software the Docker
450
+ images download when you build them:
451
+
452
+ - **Dorado** and its basecalling models (`docker/dorado.Dockerfile`) come
453
+ from Oxford Nanopore Technologies under their own licence, the Oxford
454
+ Nanopore Technologies PLC. Public License (Version 1.0 at dorado 2.1.2),
455
+ which limits use to research purposes and sets conditions on
456
+ redistribution. This repository does not include or redistribute Dorado;
457
+ whoever builds and runs the dorado image accepts Oxford Nanopore's terms,
458
+ and should read them (the `LICENCE.txt` in the dorado release) before
459
+ distributing the image or offering basecalling to others.
460
+ - **The engine image** (`docker/engine.Dockerfile`) installs specimux-suite
461
+ and its tools (specimux, speconsense) from PyPI and spoa, MCL and vsearch
462
+ from Bioconda, each under its own license.