Capability : runtime

Implémentez un runtime de sandboxes — démarrer une boîte qui fait tourner l’agent de Spunto, garder son /workspace d’un arrêt à l’autre, et dire dans quel état elle est.

runtime, c’est là où tournent les sandboxes. Spunto Cloud, Google Cloud et les machines qu’une organisation apporte elle-même sont chacun une app runtime ; tout ce que vous implémentez ici l’est aussi. À la création d’une sandbox, un membre choisit parmi les runtimes que son organisation a installés.

Le contrat est petit, et c’est voulu. Un fournisseur a la charge du cycle de vie d’une boîte : la démarrer, l’arrêter, la redémarrer, la supprimer, et dire dans quel état elle est.

Il ne transporte pas le terminal, l’exécution de commandes, l’IDE ni la redirection de ports. Chaque boîte fait tourner l’agent de Spunto, qui rappelle la plateforme par une connexion sortante et transporte tout cela lui-même — quel que soit le fournisseur. Vos boîtes n’ont besoin d’aucun port entrant ouvert sur Internet.

Ce qu’une boîte doit faire :

  1. Faire tourner l’image qu’on lui donne, avec l’environnement qu’on lui donne.
  2. Exécuter la commande bootstrap comme processus principal. Elle récupère et démarre l’agent (curl ou wget, et sh, doivent être dans l’image).
  3. Joindre SPUNTO_API_URL en HTTPS, en sortie.
  4. Garder /workspace d’un stop à un start.

OpérationObligatoireDébloque
createOuiCréer une sandbox
startOuiDémarrer une sandbox arrêtée
stopOuiEn arrêter une, /workspace conservé
destroyOuiEn supprimer une
describeOuiConnaître son état
listMachineTypesUn sélecteur de taille, avec les prix
listSizesUn sélecteur de taille plus simple, quand il n’y a qu’un type de machine
listLocationsChoisir où tourne une boîte (une région, une machine)
featuresDire aux membres ce qu’une boîte peut faire ici, et ce qu’elle ne peut pas faire
pausePause : un arrêt qui garde la mémoire de la boîte, processus compris
forkFork : une copie d’une boîte en marche, mémoire comprise
growAgrandir les disques d’une boîte
logs, build, buildStatus, snapshot…Démarrer des boîtes depuis une image de projet préconstruite au lieu de construire sur place

L’entrée de chaque opération porte un handle — ce qu’a renvoyé create —, sauf pour create lui-même et les listes.

// input
{
  "sandboxId": "sbx_8f2c…",
  "name": "fix-login",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22",
  "size": "small",
  "machineType": "small",
  "location": "europe-west1-b",
  "needs": { "dind": false },
  "volumes": [{ "name": "workspace", "path": "/workspace" }],
  "bootstrap": "if command -v curl …; fi",
  "env": {
    "SPUNTO_API_URL": "https://spunto.net",
    "SPUNTO_SANDBOX_ID": "sbx_8f2c…",
    "SPUNTO_SANDBOX_TOKEN": "…"
  },
  "labels": {
    "spunto.sandboxId": "sbx_8f2c…",
    "spunto.organizationId": "org_…",
    "spunto.projectId": "prj_…",
    "spunto.userId": "usr_…"
  }
}
 
// result
{ "handle": "vm-8f2c" }
  • Répondez en moins de 10 secondes. Acceptez la requête, lancez le démarrage en arrière-plan, et renvoyez le handle. C’est la connexion de l’agent qui apprend à Spunto que la boîte est prête ; describe lui apprend tout le reste.
  • Soyez idempotent sur sandboxId. Si create est appelé pour une sandbox dont vous avez déjà la boîte, renvoyez le handle de cette boîte. Spunto rejoue create quand votre app était injoignable ou n’a pas répondu à temps, et une boîte démarrée deux fois est une boîte facturée deux fois.
  • env contient un secret (SPUNTO_SANDBOX_TOKEN) : l’identité de la boîte auprès de la plateforme. Transmettez-le à la boîte et nulle part ailleurs — ni dans les logs, ni dans les labels.
  • labels, ce sont des ids et des liens, jamais des secrets : gardez-les avec la boîte (labels d’instance, labels de conteneur) pour qui exploite les machines. Ignorez-les si vous n’avez nulle part où les mettre.
  • size, machineType et location sont des ids que votre app a donnés dans ses propres listes. machineType l’emporte sur size.
  • needs.dind : le projet a besoin de Docker dans la boîte. Refusez si vous ne pouvez pas le fournir, plutôt que de démarrer une boîte qui échouera plus tard.
  • Optionnel dans le résultat : idePort, si l’IDE ne peut pas écouter sur le 8080 dans vos boîtes.

// input
{ "handle": "vm-8f2c", "sandboxId": "sbx_8f2c…" }
 
// result
{ "ok": true }
  • stop garde /workspace et rien d’autre : le start suivant redémarre la boîte. La facturation doit s’arrêter avec elle.
  • stop peut répondre avant que la boîte soit complètement arrêtée — describe dit alors "transition": "stopping" jusqu’à ce qu’elle le soit.
  • destroy supprime la boîte et son stockage, et est idempotent : détruire une boîte qui n’existe déjà plus réussit.

// input
{ "handle": "vm-8f2c", "sandboxId": "sbx_8f2c…" }
 
// result
{ "state": "running" }
state
creatingEn cours de démarrage
runningDémarrée — l’agent ne s’est peut-être pas encore connecté
stoppedArrêtée, /workspace conservé
missingVous n’avez aucune boîte pour ce handle
errorElle a échoué — dites pourquoi dans detail
unreachableVous ne pouvez pas le dire pour l’instant (la machine est hors ligne). Il ne faut rien en conclure

Champs optionnels : detail (une ligne, montrée au membre), transition (stopping, pausing) pendant qu’un arrêt ou une pause est en cours, ram (saved / none) pour un fournisseur qui implémente pause.

Ce que le membre choisit comme boîte — une taille sur un type de machine.

// result
{
  "machineTypes": [
    { "id": "small", "label": "Small", "size": "small", "cpus": 2, "memoryGb": 4, "diskGb": 30, "pricePerHour": 0.04 }
  ]
}

L’id revient à create sous la forme de machineType. pricePerHour est affiché tel quel ; omettez-le si vous ne facturez pas à l’heure.

Ce que vos boîtes garantissent, montré aux membres pour qu’ils sachent ce qu’ils choisissent.

// result
{
  "dind": true,
  "suspendKeepsDisk": true,
  "suspendKeepsProcesses": false,
  "location": "Your own machines, in Frankfurt",
  "billing": "A stopped box costs nothing"
}

La plupart des runtimes ont besoin d’un credential — une clé d’API, un token de cluster. Déclarez "auth": "apiToken" et servez sur votre installPath un formulaire, là où une app OAuth mettrait son écran de consentement : vérifiez le jeton de lancement, demandez le credential, vérifiez-le auprès de votre backend, puis mettez en attente et redirigez exactement comme dans Installer. Un runtime qui n’a besoin de rien ("auth": "none") peut mettre en attente et rediriger tout de suite.

Images préconstruites

Construire l’image d’un projet à l’avance, puis démarrer des boîtes depuis elle en quelques secondes, passe par logs, build ou snapshot — des opérations qui bougent encore à mesure que nous ajoutons des fournisseurs. Si votre runtime doit démarrer depuis des images préconstruites, parlez-nous avant de les implémenter.