mimiry-softlaunch
Mimiry Softlaunch — GPU Compute (Early Beta)
Softlaunch notice: This skill targets the Mimiry softlaunch environment (
softlaunch.mimiry.com). It is an early beta — APIs, pricing, and features may change without notice. Report issues to the Mimiry team.
Locating Skill Scripts
This skill bundles helper scripts in its scripts/ subdirectory. Before
running any commands, locate the skill installation directory by checking
these paths (in order):
~/.agents/skills/mimiry-softlaunch/(npx skills standard location)~/.claude/skills/mimiry-softlaunch/(Claude Code symlink)
Use whichever exists. In all command examples below, SKILL_DIR is a
placeholder for this resolved path — substitute the actual path when
executing commands.
This skill covers three concerns:
- API operations (always available) — auth, list sessions/volumes, get status, logs, terminate, check balance/quota
- Job building (when creating something new) — choose an image strategy, pick an implementation language, generate the job script, and launch it
- Volumes — provision and manage persistent block storage for sessions
Prerequisites
Before any API operation, verify the user has a registered SSH key. Ask:
Do you have an SSH key registered on your Mimiry account?
If yes — ask for the path to the key (there is no default). The auth
script accepts either the private key path or the .pub path — it
normalizes automatically.
If no — walk them through setup:
- Generate a key (if they don't have one):
ssh-keygen -t ed25519 -f ~/.ssh/mimiry -C "mimiry-softlaunch" - Register it on Mimiry — the user must add the public key
(
~/.ssh/mimiry.pub) through the Mimiry portal. There is currently no API for key registration. Direct them to:https://softlaunch.mimiry.com→ Profile → SSH Keys → Add Key - Once the key is registered, proceed with the key path they chose.
The same SSH key serves two purposes: authenticating with the API (signature exchange) and SSH access into running sessions.
Other requirements: curl, jq, ssh-keygen, and openssl must be
available on the user's system.
Part 1: API Operations (Always Available)
These are the building blocks used by everything else.
Authentication
The auth algorithm uses SSH signature exchange (not a simple API key). Always use the bundled auth script — do not re-implement the signing logic inline. The algorithm has several non-obvious details (message format, namespace, file-based signing) that are easy to get wrong.
source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path>
# Exports: MIMIRY_TOKEN, MIMIRY_API
Tokens expire after 1 hour — re-authenticate if you get 401s.
API base: https://softlaunch.mimiry.com
Compute prefix: /api/compute/v1
CRITICAL — All commands MUST be wrapped in
bash -c. The Bash tool loads the user's shell profile, which can interfere with command execution. Always wrap the entire command inbash -c '...'to run in a clean, non-interactive shell.Shell state does not persist between Bash tool calls. Each call spawns a new shell. You MUST include
sourcein everybash -cinvocation. Never runsourcein one Bash call and use$MIMIRY_API/$MIMIRY_TOKENin a separate one.
Common Operations
These commands are for the agent's own shell. Always include the
sourceline. Never copy these verbatim into user-facing output — the variables won't exist in the user's terminal. When printing commands for the user, follow the "After Session Creation" section.
Every API call must be wrapped in bash -c with source included.
Check GPU availability (public — no auth required):
bash -c 'curl -s "https://softlaunch.mimiry.com/api/compute/v1/availability" | jq .'
Returns all available GPU models with per-provider pricing and locations.
Each model includes a providers array showing which providers offer it
and at what locations (e.g. "verda" at "FIN-01"). Use this as a
pre-flight check before creating sessions, and to discover valid
provider and location values for session creation hints.
Optional query params: provider=verda, gpu_family=H100,T4,
min_vram_gb=40, location=FIN-01, available_only=false, detail=full.
Response structure (abbreviated):
{
"gpu_models": [
{
"name": "T4",
"family": "T4",
"vram_gb": 16,
"available": true,
"currency": "EUR",
"providers": [
{ "provider": "verda", "hourly_rate": 0.32, "locations": ["FIN-01"] }
]
},
{
"name": "H100_SXM",
"family": "H100",
"vram_gb": 80,
"available": true,
"currency": "EUR",
"providers": [
{ "provider": "verda", "hourly_rate": 2.10, "locations": ["FIN-01"] },
{ "provider": "acme", "hourly_rate": 2.50, "locations": ["US-EAST-1"] }
]
}
]
}
Finding the cheapest GPU from a specific provider: Filter gpu_models
to entries where the providers array contains the target provider, then
sort by that provider's hourly_rate. Example using jq:
bash -c 'curl -s "https://softlaunch.mimiry.com/api/compute/v1/availability" | jq --arg p "verda" '"'"'[.gpu_models[] | select(.available) | {name, hourly_rate: ([.providers[] | select(.provider == $p) | .hourly_rate] | first)} | select(.hourly_rate)] | sort_by(.hourly_rate) | first'"'"''
This returns the cheapest available GPU offered by the given provider
(name + rate). Use that name as gpu.types[0] and the provider as
gpu.provider when creating the session.
Check balance (do this before creating sessions):
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/balance" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
List sessions:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/sessions" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
Optional filter query params (all comma-separated where lists apply):
state=started,provisioned— durable state inclusionstate_not=terminated,completed— durable state exclusionoperation=starting,stopping— primary operation inclusion (matches all<primary>__*compounds, e.g.operation=startingmatchesstarting__pulling_image)operation_not=...— primary operation exclusionupdated_after=2026-05-01T00:00:00Z— RFC3339, gates on durable state changes only (sub-operation flips do NOT bumpupdated_at)updated_before=2026-05-03T00:00:00Z
Get session details:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/sessions/$SESSION_ID" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
Get logs (session must have state=started):
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/sessions/$SESSION_ID/logs?tail=50" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq -r .logs'
- HTTP 503 → VM still setting up, retry after
retry_after_seconds - HTTP 409 → session not running, check status first
Terminate session:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s -X DELETE "${MIMIRY_API}/sessions/$SESSION_ID" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
Check quota:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/quota" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
Transaction history:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s "${MIMIRY_API}/transactions" -H "Authorization: Bearer $MIMIRY_TOKEN" | jq .'
Part 2: Job Building (When Creating Something New)
When the user wants to create a new compute job, gather the required
information through natural conversation. Do not use AskUserQuestion
with fixed option lists — instead, ask plain questions and let the user
respond in their own words. This produces a better experience because
users often provide multiple answers at once, clarify context, or ask
follow-up questions that rigid option menus can't accommodate.
What to gather
Work through these in conversation, adapting to what the user has already told you (they may have answered several in their initial request):
- SSH key path — needed for auth and session SSH access
- Image — what container to run. Suggest common options if they're
unsure:
- PyTorch:
nvcr.io/nvidia/pytorch:24.01-py3 - TensorFlow:
nvcr.io/nvidia/tensorflow:24.01-tf2-py3 - Plain CUDA:
nvcr.io/nvidia/cuda:12.3.1-devel-ubuntu22.04 - Or any public image URI / custom Dockerfile
- PyTorch:
- GPU type — check the
/availabilityendpoint to see what's currently available. Multiple providers may offer different GPU models at different locations. Default to the cheapest available option unless the user needs more power. Common types includeT4,V100,A100,H100_SXM— but always verify via/availabilityrather than hardcoding - Provider/location preference (optional) — if the user has a preference
for a specific provider (e.g.
verda) or location (e.g.FIN-01), these can be passed as hints ingpu.providerandgpu.location. They are not guaranteed — the system falls back to best available if the preference can't be satisfied. Show available providers/locations from/availabilityif the user asks - What to run — a command/script, or interactive SSH access?
- Session name — suggest a sensible default based on the image/task
Optional (only ask if relevant to what the user described):
- Environment variables
- Auto-terminate behavior (default:
trueif command,falseif interactive)
Once you have enough information, authenticate, create the session, poll until running, and print management commands per "After Session Creation".
Creating the Session
Agent-internal code — do not print this block to the user. All commands MUST be wrapped in
bash -c '...'.
Regardless of how decisions were made, the session creation call looks like:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && PUB_KEY=$(cat "<ssh_key_path>.pub") && curl -s -X POST "${MIMIRY_API}/sessions" -H "Authorization: Bearer $MIMIRY_TOKEN" -H "Content-Type: application/json" -d '"'"'{"name": "<session_name>", "image": {"uri": "<image_uri>"}, "gpu": {"types": ["<gpu_type>"], "count": 1}, "ssh_enabled": true, "ssh_public_key": "'"'"'"'"'"'"'"'"'$PUB_KEY'"'"'"'"'"'"'"'"'", "command": "<command_or_null>", "auto_terminate": <true|false>}'"'"' | jq .'
Tip: The JSON body quoting is complex. A cleaner approach is to build the JSON with jq:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && PUB_KEY=$(cat "<ssh_key_path>.pub") && JSON=$(jq -n --arg name "<session_name>" --arg image "<image_uri>" --arg gpu "<gpu_type>" --arg key "$PUB_KEY" --arg cmd "<command>" '"'"'{name: $name, image: {uri: $image}, gpu: {types: [$gpu], count: 1}, ssh_enabled: true, ssh_public_key: $key, command: $cmd, auto_terminate: false}'"'"') && curl -s -X POST "${MIMIRY_API}/sessions" -H "Authorization: Bearer $MIMIRY_TOKEN" -H "Content-Type: application/json" -d "$JSON" | jq .'
To include provider/location preferences, add them to the gpu object:
# ... same as above but with provider/location hints in the jq call:
JSON=$(jq -n --arg name "..." --arg image "..." --arg gpu "T4" --arg key "$PUB_KEY" --arg provider "verda" --arg location "FIN-01" \
'{name: $name, image: {uri: $image}, gpu: {types: [$gpu], count: 1, provider: $provider, location: $location}, ssh_enabled: true, ssh_public_key: $key, auto_terminate: false}')
Only include provider and location when the user explicitly requests
them — they are optional hints.
Field guide:
| Field | When | Value |
|---|---|---|
command |
User has a script to run | The command string |
command |
Interactive access | Omit entirely |
auto_terminate |
Has a command | true or {"mode":"on_complete"} |
auto_terminate |
Interactive / long-running | false or {"mode":"never"} |
auto_terminate |
Only terminate on success | {"mode":"on_success"} |
ssh_public_key |
Always required | Contents of <key>.pub |
gpu.types |
User doesn't specify | Use cheapest available from /availability |
gpu.provider |
User prefers a provider | Provider name string (e.g. "verda"). Optional hint |
gpu.location |
User prefers a location | Location string (e.g. "FIN-01"). Optional hint |
environment_vars |
User needs env config | {"KEY": "value", ...} |
Session Lifecycle
Two dimensions:
state(durable milestone — nouns / past-tense):submitted → provisioned → started → completed/failed/stopped/provision_failed → terminatedoperation(current activity — present-continuous, may be empty when resting). Format is bare<primary>or compound<primary>__<sub_step>:provisioning— duringstate=submittedstarting__setting_up,starting__pulling_image,starting__starting_container— duringstate=provisioned, transitioning tostarted""(empty) — atstate=started, container is up and running (resting)stopping__stopping_container— transitioning fromstartedtostoppedterminating— transitioning towardterminated
Filters accept primaries only.
?operation=startingmatches all threestarting__*compounds. The compound value is what the response field contains; the primary is what the filter accepts.
POST /sessions → state:submitted → state:provisioned → state:started → state:completed
operation:provisioning operation:starting__setting_up operation:starting__pulling_image operation:starting__starting_container operation:"" ↓
state:terminated
DELETE /sessions/{id}
↓
state:stopped → state:terminated
On error at any stage → state:failed or state:provision_failed
updated_at is bumped only on durable state transitions. Sub-operation flips
inside the same state (e.g. starting__setting_up → starting__pulling_image)
do NOT move the session into ?updated_after=NOW-5s results.
Poll every 5 seconds until started (agent-internal, not user-facing).
The entire loop MUST be in a single bash -c call:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && while true; do RESP=$(curl -s "${MIMIRY_API}/sessions/$SESSION_ID" -H "Authorization: Bearer $MIMIRY_TOKEN"); STATE=$(echo "$RESP" | jq -r .state); OP=$(echo "$RESP" | jq -r ".operation // \"\""); echo "State: $STATE | Operation: $OP"; case "$STATE" in started) break ;; failed|provision_failed) echo "FAILED: $(echo $RESP | jq -r .error)"; break ;; completed|terminated|stopped) echo "Session ended unexpectedly"; break ;; esac; sleep 5; done'
Once running, extract SSH details from $RESP (still in the same shell)
and then print user-facing management commands per the "After Session
Creation" section below. Include the SSH command with resolved values:
# Extract from $RESP (in the same bash -c call as the polling loop above):
SSH_HOST=$(echo "$RESP" | jq -r '.ssh.host')
# Then print for the user (with actual values substituted):
# ssh -i <key_path> -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR <host>
# Plus the mirc helper commands — see "After Session Creation"
Note: SSH sessions connect through the ssh-proxy on port 22 (the default SSH port), which lands users directly inside their container. No special port or username is needed — just
ssh -i <key> <host>.
After Session Creation
Once a session is running, print management commands the user can copy-paste
into their terminal. There is a critical rule: never print commands that
contain unresolved shell variables like ${MIMIRY_API} or $MIMIRY_TOKEN —
those only exist inside the agent's shell, not in the user's terminal. Either
use the mirc helper (which handles auth internally) or print fully
resolved URLs with the auth source command included.
Default: Quick Commands (mirc helper)
Print these by default. Replace <session_id> and <key_path> with the
actual values from the session that was just created:
# Manage your session (run in any terminal):
mirc session status <session_id> --key <key_path>
mirc session logs <session_id>
mirc session ssh <session_id>
mirc session terminate <session_id>
The --key flag is only needed on the first command — the path is cached
for subsequent calls. If the user has already run a mirc command in this
terminal session, --key can be omitted entirely.
The helper script lives at:
SKILL_DIR/scripts/mirc.sh
If the user hasn't added it to their PATH, print the full path on first use. For convenience, suggest:
alias mirc='SKILL_DIR/scripts/mirc.sh'
Alternative: Create via mirc CLI
Sessions can also be created directly from the command line using
mirc session create:
mirc session create --name training --image nvcr.io/nvidia/pytorch:24.01-py3 --gpu T4 --key ~/.ssh/mimiry
mirc session create --name job1 --image myimage:latest --gpu A100 --command "python train.py" --provider verda
This is the CLI equivalent of the agent's POST /sessions curl approach.
Run mirc --help for all top-level commands and subcommands.
Alternative: Raw API Commands
Print these when the user asks about automation, pipelines, CI/CD, or wants to understand what's happening under the hood. Always include the auth source command so the variables are defined:
# Authenticate (sets $MIMIRY_TOKEN for 1 hour, re-run to refresh):
source SKILL_DIR/scripts/mimiry-auth.sh <key_path>
# Check status:
curl -s "https://softlaunch.mimiry.com/api/compute/v1/sessions/<session_id>" \
-H "Authorization: Bearer $MIMIRY_TOKEN" | jq .
# Get logs:
curl -s "https://softlaunch.mimiry.com/api/compute/v1/sessions/<session_id>/logs?tail=50" \
-H "Authorization: Bearer $MIMIRY_TOKEN" | jq -r '.logs'
# Terminate:
curl -s -X DELETE "https://softlaunch.mimiry.com/api/compute/v1/sessions/<session_id>" \
-H "Authorization: Bearer $MIMIRY_TOKEN" | jq .
Note: in raw commands, always use the fully resolved URL
(https://softlaunch.mimiry.com/api/compute/v1/...), never ${MIMIRY_API}.
The only variable that's acceptable is $MIMIRY_TOKEN because the source
command on the line above defines it.
Token Expiry Guidance
Tokens last 1 hour. When printing post-session commands, mention:
- The
mirchelper auto-refreshes tokens (no action needed) - For raw API commands, re-run the
sourcecommand to get a fresh token - If a command returns HTTP 401, the token has expired
Part 3: Block Volumes
Persistent block storage that survives session termination. A volume is created once, can be attached to a session at create time, and reused across sessions. Volumes belong to an organization (auto-picked from the user's membership when not specified).
Volume Lifecycle
Two dimensions, mirroring sessions:
state(durable):submitted → provisioned → deleted, withfailedas the terminal error state.operation(current activity, may be empty when resting):provisioning— duringstate=submitted""— atstate=provisioned, ready to attach (resting)resizing— during in-place extension (state staysprovisioned)deleting__detaching_volume,deleting__deleting_volume— transitioning towardstate=deleted
Filters accept primaries only:
?operation=deletingmatches bothdeleting__detaching_volumeanddeleting__deleting_volume.
attached_to is a property, not a state. A volume in state=provisioned
with attached_to="<session-id>" is in use by that session; with
attached_to="" it's free to attach.
Volume Operations
Create a volume:
mirc volume create --name data1 --size-gb 100
mirc volume create --name big-cache --size-gb 500 --provider verda --location FIN-01
Returns 202 with {state: "submitted", operation: "provisioning"} initially.
Within seconds it transitions to {state: "provisioned", operation: ""}.
Raw API equivalent:
bash -c 'source SKILL_DIR/scripts/mimiry-auth.sh <ssh_key_path> && curl -s -X POST "${MIMIRY_API}/volumes" -H "Authorization: Bearer $MIMIRY_TOKEN" -H "Content-Type: application/json" -d '"'"'{"name":"data1","size_gb":100}'"'"' | jq .'
List volumes (same filter shape as sessions):
mirc volume list
mirc volume list --state provisioned
mirc volume list --state-not deleted --operation-not deleting
Get details:
mirc volume status <volume_id>
Note: deleted volumes are still returned by GET /volumes/{id} with
state=deleted (cache is preserved). They are excluded from the default
GET /volumes list — use --state deleted to include them.
Extend (resize) a volume — must be larger than current size:
mirc volume extend <volume_id> --size-gb 200
Operation flips to resizing and back to "" on completion. State stays
provisioned (resize is in-place; no durable state transition, so
updated_at does not bump).
Delete a volume — the volume must not be attached to a running session:
mirc volume delete <volume_id>
Operation walks deleting__detaching_volume → deleting__deleting_volume
before settling in state=deleted, operation="".
Attaching a Volume to a Session
Volume attachment happens at session-create time via the volumes field on
POST /sessions. Once a session terminates, the volume is automatically
detached and remains in state=provisioned ready for the next session.
A volume can only be attached to one running session at a time. If the user wants to attach to a new session, the previous session must already be terminated (or use a different volume).
Error Reference
| Error | Meaning | Fix |
|---|---|---|
Invalid SSH signature |
Auth algorithm wrong | Ensure the bundled auth script is being used |
Timestamp expired |
Clock drift > 5 min | Sync system clock |
SSH key not registered |
Key not on account | Register key in Mimiry portal |
insufficient_balance |
No credits | Ask admin to add credits |
no_balance |
No billing account | Ask admin to create one |
vm_setup_in_progress (503) |
VM booting | Retry after retry_after_seconds |
invalid_state (409) |
Wrong session state for operation | Check session status |
Tips
- ALWAYS wrap commands in
bash -c '...'— the Bash tool's default shell loads user profile scripts that can interfere with command execution.bash -cruns in a clean non-interactive shell, avoiding these issues. - The same SSH key authenticates with the API and provides session SSH access
- Billing runs from provisioning to termination — remind users to terminate idle sessions
- Available GPUs vary by provider — always check
/availabilitybefore suggesting GPU types. Multiple providers may offer different models at different locations and prices. When the user doesn't specify, default to the cheapest available option - When generating scripts for the user, include error handling for common failure modes (402 insufficient balance, 429 quota exceeded)
- Exploration vs. automation: Default to
mirchelper commands for interactive use. Switch to raw curl commands when the user mentions pipelines, CI/CD, scripting, or automation — and always include the full authsourcecommand so the commands are self-contained - Never print unresolved variables in user-facing commands.
${MIMIRY_API}and$MIMIRY_TOKENonly exist inside the agent's shell session. See the "After Session Creation" section for the correct patterns