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.
- specimux_cloud-0.1.0/LICENSE +28 -0
- specimux_cloud-0.1.0/PKG-INFO +462 -0
- specimux_cloud-0.1.0/README.md +424 -0
- specimux_cloud-0.1.0/pyproject.toml +55 -0
- specimux_cloud-0.1.0/setup.cfg +4 -0
- specimux_cloud-0.1.0/src/specimux_cloud/__init__.py +19 -0
- specimux_cloud-0.1.0/src/specimux_cloud/backends/__init__.py +5 -0
- specimux_cloud-0.1.0/src/specimux_cloud/backends/aws.py +487 -0
- specimux_cloud-0.1.0/src/specimux_cloud/backends/base.py +196 -0
- specimux_cloud-0.1.0/src/specimux_cloud/backends/local.py +463 -0
- specimux_cloud-0.1.0/src/specimux_cloud/cli.py +225 -0
- specimux_cloud-0.1.0/src/specimux_cloud/console/__init__.py +0 -0
- specimux_cloud-0.1.0/src/specimux_cloud/console/app.py +555 -0
- specimux_cloud-0.1.0/src/specimux_cloud/dorado/__init__.py +0 -0
- specimux_cloud-0.1.0/src/specimux_cloud/dorado/wrapper.py +270 -0
- specimux_cloud-0.1.0/src/specimux_cloud/engine/__init__.py +0 -0
- specimux_cloud-0.1.0/src/specimux_cloud/engine/plugin.py +119 -0
- specimux_cloud-0.1.0/src/specimux_cloud/engine/wrapper.py +452 -0
- specimux_cloud-0.1.0/src/specimux_cloud/packages.py +50 -0
- specimux_cloud-0.1.0/src/specimux_cloud/runapi/__init__.py +0 -0
- specimux_cloud-0.1.0/src/specimux_cloud/runapi/app.py +547 -0
- specimux_cloud-0.1.0/src/specimux_cloud/runapi/auth.py +95 -0
- specimux_cloud-0.1.0/src/specimux_cloud/runapi/events.py +120 -0
- specimux_cloud-0.1.0/src/specimux_cloud/runapi/service.py +1727 -0
- specimux_cloud-0.1.0/src/specimux_cloud/stopsignal.py +49 -0
- specimux_cloud-0.1.0/src/specimux_cloud/uploader/__init__.py +0 -0
- specimux_cloud-0.1.0/src/specimux_cloud/uploader/cli.py +228 -0
- specimux_cloud-0.1.0/src/specimux_cloud/uploader/runctl.py +71 -0
- specimux_cloud-0.1.0/src/specimux_cloud/uploader/submit.py +169 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/PKG-INFO +462 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/SOURCES.txt +44 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/dependency_links.txt +1 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/entry_points.txt +5 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/requires.txt +16 -0
- specimux_cloud-0.1.0/src/specimux_cloud.egg-info/top_level.txt +1 -0
- specimux_cloud-0.1.0/tests/test_backends_aws.py +202 -0
- specimux_cloud-0.1.0/tests/test_backends_local.py +161 -0
- specimux_cloud-0.1.0/tests/test_console.py +271 -0
- specimux_cloud-0.1.0/tests/test_contract.py +133 -0
- specimux_cloud-0.1.0/tests/test_dorado_wrapper.py +44 -0
- specimux_cloud-0.1.0/tests/test_ingest_log.py +54 -0
- specimux_cloud-0.1.0/tests/test_local_stack.py +366 -0
- specimux_cloud-0.1.0/tests/test_runapi.py +1255 -0
- specimux_cloud-0.1.0/tests/test_stopsignal.py +42 -0
- specimux_cloud-0.1.0/tests/test_uploader.py +144 -0
- 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.
|