Mountable
Sign in

Agents

Everything an agent needs works without the console: the mountable CLI, the skill that teaches this workflow, and the MCP server.

Who holds which credential

WhoHowUsed for
A personmountable logineverything
A backend or agentMOUNTABLE_API_KEY=mtbl_… in the environmentmanaging filesystems and tickets
A sandboxa one-time ticket on stdinmounting only

An owner or admin creates the API key in the console. A key belongs to one organisation and is never broader than its creator: it sees only filesystems it has a grant on (a filesystem it creates gets a read-write grant), and it cannot delete filesystems or change grants, members or keys. See Permissions.

The workflow

Backend: create (or pick) a filesystem, then a one-time ticket, with an idempotency key you choose for this mount, such as the job ID:

export MOUNTABLE_API_KEY=mtbl_…
FS_ID=$(mountable fs create agent-workspace)
mountable ticket create "$FS_ID" --idempotency-key job-42 --json
# → {"id": SESSION_ID, "state": "requested", "ticket": "mtbltk_…", "ticket_expires_at": …}

Add --ro for a read-only mount. The ticket works once and expires within minutes, so hand it to the sandbox right away.

Sandbox: mount onto an existing, writable directory, with the ticket on stdin, and wait for the mounted event:

mkdir -p /mnt/work
printf '%s\n' "$MOUNTABLE_TICKET" | mountable mount --ticket-stdin --json /mnt/work &

mount keeps running while the filesystem is mounted and writes JSON lines:

{"event":"mounted","path":"/mnt/work","filesystem_id":"…","mode":"rw"}
{"event":"unmounted","reason":"signal|unmount|revoked|expired|error","detail":"…"}

Use the directory only after mounted. If the command exits first, its last line is {"error": …}. When done, run mountable unmount /mnt/work (or send SIGTERM to the mount process) so pending writes are committed.

Backend, optionally: end access early with mountable sessions list FS_ID and mountable sessions revoke SESSION_ID. A revoked mount stops within seconds.

The sandbox needs the CLI and FUSE (/dev/fuse); see Mount from a sandbox. Never put an API key or a ticket in a command-line argument or a log.

JSON output and exit codes

  • --json on every command writes one JSON document to stdout, with the API's field names. Diagnostics go to stderr, and nothing ever prompts. mountable mcp is the exception: its stdout is the MCP protocol.
  • Exit codes: 0 success; 1 an error from the API, network, mount or credentials; 2 wrong usage.
  • Errors carry a stable code and a next step: on stderr, or with --json as
{"error": {"code": "not_found", "message": "…", "hint": "check the ID with `mountable fs list`"}}

Error codes

CodeWhat to do
unauthenticatedRun mountable login or set MOUNTABLE_API_KEY.
org_requiredPass --org ORG_ID (see mountable orgs list).
not_foundCheck the ID with mountable fs list; an API key sees only filesystems it has a grant on.
no_grantAsk an owner or admin for a grant, or use --ro.
api_key_forbiddenA person must do this, in the console or with mountable login.
payment_requiredThe hint is the console's billing link; give it to a person.
rate_limitedWait retry_after seconds, then retry.
ticket_already_usedUse a new --idempotency-key for another mount.
ticket_invalidThe ticket was used or expired; create a new one.
idempotency_conflictAn identical request is in progress; retry shortly with the same key.
network_error, outcome_unknownRetry reads; for ticket create, retry with the error's idempotency_key.

Retries

  • Reads (orgs list, fs list, fs usage, sessions list) are safe to retry.
  • ticket create sends an idempotency key: the one given with --idempotency-key, or a random one it prints to stderr before sending. When the outcome is unknown (network_error or outcome_unknown), the error's idempotency_key is the key that request was sent with: retry with exactly that key. A replay returns the existing session without a ticket, and the CLI handles it by the session's state:
    • still requested: its ticket was lost. The CLI revokes that session, creates a new one with a fresh key, and returns it with replaced_session_id naming the old one.
    • active or later: the ticket was used. Nothing is revoked and the command fails with ticket_already_used. Keep the working mount, and use a new key for another mount.
  • fs create has no idempotency key, and names need not be unique. After an unknown outcome, run fs list and look for the name before creating again. Never retry blindly.
  • sessions revoke is idempotent.
  • HTTP 429 (rate_limited): wait retry_after seconds.