mimiry-softlaunch

Installation
SKILL.md

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):

  1. ~/.agents/skills/mimiry-softlaunch/ (npx skills standard location)
  2. ~/.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:

  1. API operations (always available) — auth, list sessions/volumes, get status, logs, terminate, check balance/quota
  2. Job building (when creating something new) — choose an image strategy, pick an implementation language, generate the job script, and launch it
  3. 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:

  1. Generate a key (if they don't have one):
    ssh-keygen -t ed25519 -f ~/.ssh/mimiry -C "mimiry-softlaunch"
    
  2. 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
  3. 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 in bash -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 source in every bash -c invocation. Never run source in one Bash call and use $MIMIRY_API / $MIMIRY_TOKEN in a separate one.

Common Operations

These commands are for the agent's own shell. Always include the source line. 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 inclusion
  • state_not=terminated,completed — durable state exclusion
  • operation=starting,stopping — primary operation inclusion (matches all <primary>__* compounds, e.g. operation=starting matches starting__pulling_image)
  • operation_not=... — primary operation exclusion
  • updated_after=2026-05-01T00:00:00Z — RFC3339, gates on durable state changes only (sub-operation flips do NOT bump updated_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):

  1. SSH key path — needed for auth and session SSH access
  2. 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
  3. GPU type — check the /availability endpoint 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 include T4, V100, A100, H100_SXM — but always verify via /availability rather than hardcoding
  4. 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 in gpu.provider and gpu.location. They are not guaranteed — the system falls back to best available if the preference can't be satisfied. Show available providers/locations from /availability if the user asks
  5. What to run — a command/script, or interactive SSH access?
  6. 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: true if command, false if 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 → terminated
  • operation (current activity — present-continuous, may be empty when resting). Format is bare <primary> or compound <primary>__<sub_step>:
    • provisioning — during state=submitted
    • starting__setting_up, starting__pulling_image, starting__starting_container — during state=provisioned, transitioning to started
    • "" (empty) — at state=started, container is up and running (resting)
    • stopping__stopping_container — transitioning from started to stopped
    • terminating — transitioning toward terminated

Filters accept primaries only. ?operation=starting matches all three starting__* 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_upstarting__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 mirc helper auto-refreshes tokens (no action needed)
  • For raw API commands, re-run the source command 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, with failed as the terminal error state.
  • operation (current activity, may be empty when resting):
    • provisioning — during state=submitted
    • "" — at state=provisioned, ready to attach (resting)
    • resizing — during in-place extension (state stays provisioned)
    • deleting__detaching_volume, deleting__deleting_volume — transitioning toward state=deleted

Filters accept primaries only: ?operation=deleting matches both deleting__detaching_volume and deleting__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 -c runs 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 /availability before 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 mirc helper commands for interactive use. Switch to raw curl commands when the user mentions pipelines, CI/CD, scripting, or automation — and always include the full auth source command so the commands are self-contained
  • Never print unresolved variables in user-facing commands. ${MIMIRY_API} and $MIMIRY_TOKEN only exist inside the agent's shell session. See the "After Session Creation" section for the correct patterns
Installs
12
First Seen
Apr 8, 2026