API Reference
JSON over HTTPS. Bearer-token auth via API keys you create on
your account page.
All endpoints live under https://scsipub.com/api/.
Quickstart
Three commands from "I have an API key" to "/dev/sdX mounted on this Linux box". Uses the public-catalog
debian-12 image, so no upload step. Requires
open-iscsi (apt install open-iscsi on Debian/Ubuntu) and
jq.
# 1. Provision a session against a public-catalog image. No upload
# needed — `debian-12` lives in the global catalog.
RESP=$(curl -fsSL -X POST https://scsipub.com/api/sessions \
-H "Authorization: Bearer $SCSIPUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image": "debian-12"}')
IQN=$(echo "$RESP" | jq -r .iqn)
PORTAL=$(echo "$RESP" | jq -r .portal)
USER=$(echo "$RESP" | jq -r .chap_user)
SECRET=$(echo "$RESP" | jq -r .chap_secret)
# 2. Point the open-iscsi initiator at the new target. CHAP
# creds are unique to this session — they don't authorize
# anything else.
sudo iscsiadm -m node -T "$IQN" -p "$PORTAL" -o new
sudo iscsiadm -m node -T "$IQN" -p "$PORTAL" \
-o update -n node.session.auth.authmethod -v CHAP
sudo iscsiadm -m node -T "$IQN" -p "$PORTAL" \
-o update -n node.session.auth.username -v "$USER"
sudo iscsiadm -m node -T "$IQN" -p "$PORTAL" \
-o update -n node.session.auth.password -v "$SECRET"
sudo iscsiadm -m node -T "$IQN" -p "$PORTAL" --login
# 3. The disk shows up as a real /dev/sdX. Find it, mount it,
# use it. `lsblk` makes the new device obvious.
lsblk
sudo mount /dev/sdX1 /mnt
What you have now: a real block device backed by an ephemeral writable overlay over the catalog image. Disconnect (iscsiadm --logout) and the overlay vanishes. The reference below covers every endpoint, error code, and the upload-your-own-image flow.
Authentication
Pass an API key in the Authorization header:
Authorization: Bearer $SCSIPUB_API_KEY
Anonymous requests are allowed for the few endpoints where it makes sense (listing the global image catalog). Anything that touches user state — creating a session, uploading an image, deleting either — requires a key.
Images
GET /api/images
List the global image catalog. With a key, also includes images you
own. Each row's owned field tells you which is which.
curl https://scsipub.com/api/images \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
{"images":[
{"id":"…","name":"debian-12","format":"qcow2","size_bytes":2147483648,
"sha256":"…","owned":false},
{"id":"…","name":"ci-pi-abc1234","format":"raw","size_bytes":4294967296,
"sha256":"…","owned":true}
]}
POST /api/images
Tell scsipub to fetch a remote artifact and register it as your
private image. Synchronous —
returns 201 once the fetch,
decompression, checksum, and DB insert have all succeeded.
Body:
{
"name": "ci-pi-abc1234", // [A-Za-z0-9._-]+
"url": "https://gitlab.example.com/.../build.img.xz",
"sha256": "abcdef0123…" // optional, fail if mismatch
}
Example:
curl -X POST https://scsipub.com/api/images \
-H "Authorization: Bearer $SCSIPUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-pi-abc1234",
"url": "https://gitlab.example.com/.../build.img.xz",
"sha256": "deadbeef…"
}'
Response (201):
{
"id": "…",
"name": "ci-pi-abc1234",
"iqn": "iqn.2025-01.pub.scsipub:image.ci-pi-abc1234",
"format": "raw",
"size_bytes": 4294967296,
"sha256": "deadbeef…",
"source_url": "https://gitlab.example.com/.../build.img.xz"
}
Errors:
401 unauthorized— no API key sent.402 payment_required— over your tier'smax_storagebudget. Free tiers always 402 since their budget is 0.422 unprocessable_entity— bad name format.502 bad_gateway— fetch failed. Body'serrorfield has the underlying reason (DNS, HTTP status, checksum mismatch, decompress error, …).
DELETE /api/images/:id
Drop a private image. :id can be the UUID or, if you authenticate, the image name. Owner-only — global catalog images can't be deleted via the API.
curl -X DELETE https://scsipub.com/api/images/ci-pi-abc1234 \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
Sessions
POST /api/sessions
Provision a new iSCSI session against an image. Returns a unique IQN
and CHAP credentials; mount with iscsiadm from there.
Body:
{
"image": "ci-pi-abc1234", // image name or UUID
"write_limit": null // optional, override per-session byte cap
}
Example:
curl -X POST https://scsipub.com/api/sessions \
-H "Authorization: Bearer $SCSIPUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image": "ci-pi-abc1234"}'
Response (201):
{
"id": "…",
"iqn": "iqn.2025-01.pub.scsipub:…",
"chap_user": "u-12abcd34",
"chap_secret": "…",
"portal": "scsipub.com:3260",
"status": "provisioned"
}
GET /api/sessions/:id
curl https://scsipub.com/api/sessions/<id-or-iqn> \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
DELETE /api/sessions/:id
Tear down a session immediately, releasing its overlay and write-budget quota. Useful for CI cleanup jobs.
curl -X DELETE https://scsipub.com/api/sessions/<id> \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
End-to-end: a CI build
The pattern most paid users hit: build something in CI, register it with scsipub, mount it from a runner, run tests, clean up.
# 1. publish — register your build artifact as a private image
curl -fsSL -X POST https://scsipub.com/api/images \
-H "Authorization: Bearer $SCSIPUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-build-'"$CI_COMMIT_SHORT_SHA"'",
"url": "https://artifacts.example.com/builds/'"$CI_PIPELINE_ID"'/build.img.xz",
"sha256": "'"$(sha256sum build.img.xz | cut -d' ' -f1)"'"
}' | tee image.json
# 2. provision a session against it
curl -fsSL -X POST https://scsipub.com/api/sessions \
-H "Authorization: Bearer $SCSIPUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image": "ci-build-'"$CI_COMMIT_SHORT_SHA"'"}' | tee session.json
# 3. on the runner, mount + test (using the IQN + CHAP returned above)
IQN=$(jq -r .iqn session.json)
PORTAL=$(jq -r .portal session.json)
USER=$(jq -r .chap_user session.json)
SECRET=$(jq -r .chap_secret session.json)
ssh runner "iscsiadm -m node -T $IQN -p $PORTAL -o new
iscsiadm -m node -T $IQN -p $PORTAL \
-o update -n node.session.auth.authmethod -v CHAP
iscsiadm -m node -T $IQN -p $PORTAL \
-o update -n node.session.auth.username -v $USER
iscsiadm -m node -T $IQN -p $PORTAL \
-o update -n node.session.auth.password -v $SECRET
iscsiadm -m node -T $IQN -p $PORTAL --login
/opt/runtests.sh"
# 4. cleanup (always, even on test failure)
curl -fsSL -X DELETE \
"https://scsipub.com/api/sessions/$(jq -r .id session.json)" \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
curl -fsSL -X DELETE \
"https://scsipub.com/api/images/ci-build-$CI_COMMIT_SHORT_SHA" \
-H "Authorization: Bearer $SCSIPUB_API_KEY"
The Pi-fleet post walks through this end-to-end with a real GitLab CI pipeline.
Limits
- Image uploads: 20/hour per IP, on top of your tier's
max_storagebyte budget. - Session creation: 10/hour per IP, plus your tier's
max_sessionsconcurrent cap. - Login (web): 5/30 min per IP — only relevant if you're scripting against the cookie-session web flow rather than the API.
- Tier limits: see the pricing page for write-limit, session-cap, and storage numbers per tier.
Found something missing or wrong? Email support@defensiblelogic.com.