This release is a pre-release and may not be stable for production use.
bloomctl
Python command-line tool for the Bloom server — download cylinder experiments
(metadata + images), write per-scan pipeline results back, and manage
credentials. Successor to the Node @salk-hpi/bloom-cli. Tracked by issue #347.
Container image
bloomctl is also published as a container image, for use as a step in
pipelines (e.g. sleap-roots-pipeline's Argo DAG) rather than a pip install:
ghcr.io/salk-harnessing-plants-initiative/bloomctl
sha-<short-git-sha>— immutable, pushed on every commit tostagingthat touchesbloomcli/**.staging— mutable, points at the most recentstagingbuild.<version>(e.g.0.1.0a2) — pushed when a matching GitHub Release is published; guaranteed to match the PyPI-published version of the same name.
The image is built directly from this repo's source at the commit being
built (not from PyPI), so it's always available immediately after a
staging push regardless of PyPI release timing — see
.github/workflows/docker-build-bloomcli.yml. Every PR touching
bloomcli/** builds and Trivy-scans the Dockerfile via pr-checks.yml's
docker-build job (the same pre-merge gate every other Bloom image gets);
the publishing workflow itself only builds and pushes, it never runs on a
pull request.
Provenance: docker/metadata-action bakes standard OCI labels into every
image, including org.opencontainers.image.revision (the full source
commit SHA) — recoverable from a running/pulled image with no other
context via docker inspect <image> | jq .Config.Labels.
docker run --rm ghcr.io/salk-harnessing-plants-initiative/bloomctl:staging \
cyl ingest-result path/to/scan.result.json
Commands
login is flat; assay-specific commands are grouped by data type (cyl). Each
command is tagged [read] or [write] — see Access & roles.
bloomctl login— bootstrap client config from the Bloom server and store credentials per profile.- [read]
bloomctl cyl download <out_dir> …— download a cylinder experiment or single scan (metadatascans.csv+ per-frame images). - [read]
bloomctl cyl download-for-predict <scan-id> <out>— stage one scan into the predict-ready layout (see below); produces a different output tree thancyl download— use this only for A4 pipeline stage-in. - [write]
bloomctl cyl ingest-result <envelope>— write a per-scan pipelineResultEnvelopeback to Bloom (see below). - [read]
bloomctl cyl datasets list— list cylinder trait datasets (--experiment-idto scope to one experiment,--jsonfor machine-readable output). - [read]
bloomctl cyl datasets get <name>— show one dataset's details and the unique traits it contains, via thecyl_dataset_trait_namesview (--jsonoutput). - [write]
bloomctl cyl datasets create <name> <experiment_id> <trait_source_name>— create a trait dataset (--qc-set-nameto exclude a QC set,--timepoints). - [read]
bloomctl cyl experiments list— list cylinder experiments (species, name, id), sorted by species then name (--jsonfor machine-readable output).
(Full login/cyl download usage docs are still forthcoming; run any command
with --help in the meantime. cyl ingest-result and cyl download-for-predict
are documented in full below.)
bloomctl cyl download-for-predict
Stage one cylinder scan into the layout the warm-predict container expects.
Unlike cyl download (which writes images/Wave{n}/… + scans.csv), this
command co-locates frames with a scan_metadata.json sidecar so
sleap_roots_predict.discover_scans can find them — use it for A4 per-scan
pipeline stage-in, not as a replacement for cyl download.
bloomctl cyl download-for-predict <scan-id> <out> [-p/--profile PROFILE]
- Writes frames to
<out>/scan_<scan_id>/<frame_number><ext>. - Authors
<out>/scan_<scan_id>/scan_<scan_id>.scan_metadata.jsonwith:scan_key—scan_<scan_id>(matches the filename stem).params—{species, mode, age}, resolved viasleap-roots-contracts(modeis always"cylinder").image_ids— realcyl_images.idvalues, required for the write-back RPC to resolve the scan (see rationale in design.md / bloom#411).images_checksum—sha256:<hex>over the downloaded frame bytes.
- Exits non-zero with a readable message if the scan isn't found, has no frames, or any frame fails to download — on a frame-download failure, no sidecar is written (successfully-downloaded frames remain on disk).
- A successful re-run reconciles away any stray frame file left by an earlier failed attempt, so the directory always matches the written sidecar exactly.
Auth: same saved login profile as other cyl commands.
Example:
bloomctl cyl download-for-predict 1 ./staged
Access & roles
Commands run as the logged-in user — every query and mutation is RLS-enforced
under the caller's role, not a service key. So the role your bloomctl login
profile maps to determines what works:
| Command tag | Required role | Intended user |
|---|---|---|
[read] (download, datasets list) |
bloom_user (any authenticated user) |
anyone with a Bloom account |
[write] (ingest-result, datasets create) |
bloom_writer / bloom_admin |
automated pipelines (e.g. the trait-extraction write-back), or users granted write access |
A read-only bloom_user can list datasets but cannot create one — the
write path (the create_cyl_dataset / insert_cyl_result_envelope RPCs and the
underlying table inserts) is granted to bloom_writer/bloom_admin. Point the
[write] commands at a profile with write access (e.g. the pipeline's service
account); a bloom_user login will get a clear permission error.
bloomctl cyl ingest-result
Ingest one per-scan ResultEnvelope (emitted by the sleap-roots trait extractor)
into Bloom by calling the insert_cyl_result_envelope RPC.
bloomctl cyl ingest-result <envelope.json | -> [-p/--profile PROFILE] [--json] [--predictions-dir DIR]
- Reads the envelope from a file path, or from stdin when the argument is
-. - Validates it against
sleap-roots-contractsbefore the call (fails fast with a readable message) and sends the original JSON unchanged. - Idempotent: re-ingesting the same envelope is a no-op (first-writer-wins on
the envelope's
idempotency_key), reported as "already ingested" — not an error. --jsonprints the RPC's result object (includingsource_id) to stdout for scripting; without it, a human-readable summary line.--predictions-dir DIR: construct and upload the envelope'sblobs. ReadsDIR/{scan_key}.predictions.json(aPredictionManifest, fromsleap-roots-contractsv0.1.0a5+), verifies each artifact's.slpbytes against its declared checksum, uploads them to thecyl-intermediatesstorage bucket, and merges the resultingBlobRefs into the envelope before ingesting. Idempotent per-blob (skips re-upload if an identical object already exists at the derived path) and fails fast — before any upload or RPC call — on a missing/malformed manifest, a missing.slpfile, a checksum mismatch, or a blob already present in the envelope. Omit to forwardblobsunchanged, exactly as before this flag existed.
The most common real-world error is inputs.image_ids not resolving to exactly
one scan on the target server — the command explains that the scan's images must
already exist in cyl_images on the Bloom you're pointed at.
Auth: uses your saved login profile, which must have write access
(bloom_writer / bloom_admin). Non-interactive / scoped credentials for
cluster/CI use are tracked separately (#398).
Examples:
bloomctl cyl ingest-result path/to/scan.result.json
cat scan.result.json | bloomctl cyl ingest-result - --json
Dev-stack smoke test
tests/test_dev_stack_smoke.py verifies the local Supabase stack is serving and
that bloomctl cyl ingest-result can round-trip against it (gateway /rest+/auth
= 200, the write-back RPC is migrated, and a seed → ingest → no-op → cleanup cycle
succeeds). It self-skips unless BLOOMCTL_DEV_SMOKE is set, and is marked
integration so the default suite and CI never run it. After make dev-up:
set -a; . ./.env.dev; set +a
BLOOMCTL_DEV_SMOKE=1 uv run --extra test --with psycopg \
--project bloomcli pytest bloomcli/tests/test_dev_stack_smoke.py -v
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bloomctl-0.1.0a2.tar.gz.
File metadata
- Download URL: bloomctl-0.1.0a2.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e1827e40836717152d2d75a47e0baccdb7f2a3e0aa53157b13c36583ff8abc2
|
|
| MD5 |
d12f3c8fd0cc09b9f55e7ce61d417095
|
|
| BLAKE2b-256 |
0db9e92f72e546174687eecd8a4ffb55cfc478a16b30d41e67c073a2f676bfa1
|
File details
Details for the file bloomctl-0.1.0a2-py3-none-any.whl.
File metadata
- Download URL: bloomctl-0.1.0a2-py3-none-any.whl
- Upload date:
- Size: 29.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fd97cd4fba98e2bfcdd7dc384d12c6dac4f639bbb8a6d240644892407190e2a
|
|
| MD5 |
46413686f3ae4d1a5800d4c6758334f2
|
|
| BLAKE2b-256 |
5598d77836c06e51d9d65fa8679f7d3cac19c89f90e20344ab34d6bb84359924
|