Quickstart for platform users
Provision isolated storage for your customers' runtimes end to end: tenant, grant, signed mount ticket, inject, mount, revoke.
For platforms whose backends provision storage on behalf of their own customers: sandbox runtimes (Daytona, Modal, your own), AI agent platforms, ML training services, anything where each of your customers gets isolated storage they don't sign up to Tonbo for. End-to-end: sign up, model a tenant, grant a runtime id workspace access, create a short-lived mount ticket, inject the config into the customer runtime, mount, run, clean up.
Read this if your backend is provisioning storage per customer task. For the single-machine "I just want to mount my workspace" path, see Quickstart instead.
Prerequisites: a Node.js backend, an API key from your profile at user.tonbo.dev. Your sandbox VM image needs the artifacts CLI installed (one line at image-build time).
1. Sign up and get an API key
Register at user.tonbo.dev. From your profile, generate an API key.
export TONBO_API_KEY=<your key>
This key authenticates your backend. Never inject it into customer-facing runtimes.
2. Install the SDK
npm install @tonbo/artifacts
import { Artifacts } from '@tonbo/artifacts';
const artifacts = new Artifacts();
3. The mental model
A short chain: your root user (this API key) owns tenants (your customers); each tenant scopes runtimes (your tasks or sandboxes, identified by an opaque id you choose); an access grant lets a tenant or runtime mount a workspace; and a mount ticket is the short-lived signed file a runtime mounts with. Your root credential stays on your backend; runtimes receive tickets only.
See Multi-tenancy model for the full diagram and lifecycle, and Temporary mount credentials for the authorization detail.
4. Choose a runtime id for the task
const tenantSlug = 'acme';
const runtimeId = taskId;
The tenant is your customer or account boundary; the runtime id is the access scope for one container, VM, task, or agent run. Both are plain strings you choose. You do not pre-create a runtime row.
5. Provision or choose a workspace
Workspace provisioning runs through the CLI today: the create step initializes the workspace's storage from the machine that runs it, so it isn't available over the plain HTTP API. Run the CLI from your backend host with the same API key (no browser login involved), or pre-provision a pool of workspaces ahead of task time:
export TONBO_API_KEY=<your key>
artifacts workspace create task-4711
Workspaces are the filesystem namespaces. Some platforms create one workspace per task; others reuse a workspace across several runtimes under the same tenant. If your platform needs to provision workspaces over the HTTP API, reach out.
6. Create a short-lived mount ticket
This is the one call that matters. createMountTicket auto-creates the tenant (if new) and a runtime-scoped grant, then signs a short-lived ticket — all in one request.
const { mount_ticket } = await artifacts.platform.createMountTicket(tenantSlug, runtimeId, {
workspace: `<your-handle>/task-${taskId}`,
mode: 'rw',
ttlSeconds: 3600,
});
What you should see: a signed mount ticket expiring one hour from now. It authorizes this runtime to mount that one workspace only, in rw mode. Your account API key never leaves your backend.
That single call is the fast path. You only need explicit createTenant / createAccessGrant calls for tenant metadata, a tenant-wide grant shared by every runtime, or to authorize a runtime without issuing a ticket yet. See Tenants and grants.
7. Inject the mount ticket into your runtime
The runtime needs the mount ticket available as a secret-backed file before artifacts mount runs:
/run/tonbo/artifacts/mount-ticket.json
How you inject depends on your orchestration. The common shapes are a
Kubernetes secret volume, an init-time file write, or a microVM/containerd
secret mount. The mount-ticket.json file must contain the JSON-serialized
mount ticket returned by Orchestrator.
volumes:
- name: tonbo-artifacts
secret:
secretName: tonbo-mount
defaultMode: 0400
items:
- key: mount-ticket.json
path: mount-ticket.json
containers:
- name: agent
volumeMounts:
- name: tonbo-artifacts
mountPath: /run/tonbo/artifacts
readOnly: true# Whatever your launcher is, place the mount ticket file before it runs `artifacts mount`.
mkdir -p /run/tonbo/artifacts
chmod 0700 /run/tonbo/artifacts
cp mount-ticket.json /run/tonbo/artifacts/mount-ticket.json
chmod 0600 /run/tonbo/artifacts/mount-ticket.jsonThe Orchestrator returns { mount_ticket } to your backend. Your platform
control plane delivers the config to the worker; Orchestrator does not relay
or supervise the worker command.
8. Mount and use it in the VM
In your VM template, install the CLI once at image-build time (see Install). At VM boot, mount the workspace:
artifacts mount --ticket-file /run/tonbo/artifacts/mount-ticket.json /mnt/work
The customer's agent code uses /mnt/work like a regular local directory. No SDK in their code, no Tonbo awareness, no special API:
import fs from 'node:fs/promises';
await fs.writeFile('/mnt/work/output.txt', 'hello');
This is the value-prop your platform delivers: persistent shared filesystem across your customers' runtimes, using their existing code.
9. Tear down when the task ends
Revoke the runtime's grant, and delete the workspace if it was task-scoped:
await artifacts.platform.revokeAccessGrants({ tenantSlug, runtimeId });
await artifacts.workspaces.delete(`<your-handle>/task-${taskId}`);
Revocation propagates within about one second; the mount starts erroring on the next syscall. For revoking by grant id, tearing down a whole tenant, tenant-wide grants, and authorize-without-issue, see Tenants and grants.
Next steps
The explicit path: pre-create tenants, tenant-wide grants, authorize without issuing, and revoke.
Concurrent writesWhat happens when multiple customer VMs mount the same workspace or write the same file.
File locks and coordinationPatterns to give your customers when they need cross-mount mutual exclusion.