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 :
- Faire tourner l’image qu’on lui donne, avec l’environnement qu’on lui donne.
- Exécuter la commande
bootstrapcomme processus principal. Elle récupère et démarre l’agent (curlouwget, etsh, doivent être dans l’image). - Joindre
SPUNTO_API_URLen HTTPS, en sortie. - Garder
/workspaced’unstopà unstart.
| Opération | Obligatoire | Débloque |
|---|---|---|
create | Oui | Créer une sandbox |
start | Oui | Démarrer une sandbox arrêtée |
stop | Oui | En arrêter une, /workspace conservé |
destroy | Oui | En supprimer une |
describe | Oui | Connaître son état |
listMachineTypes | Un sélecteur de taille, avec les prix | |
listSizes | Un sélecteur de taille plus simple, quand il n’y a qu’un type de machine | |
listLocations | Choisir où tourne une boîte (une région, une machine) | |
features | Dire aux membres ce qu’une boîte peut faire ici, et ce qu’elle ne peut pas faire | |
pause | Pause : un arrêt qui garde la mémoire de la boîte, processus compris | |
fork | Fork : une copie d’une boîte en marche, mémoire comprise | |
grow | Agrandir 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 ;
describelui apprend tout le reste. - Soyez idempotent sur
sandboxId. Sicreateest appelé pour une sandbox dont vous avez déjà la boîte, renvoyez le handle de cette boîte. Spunto rejouecreatequand 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. envcontient 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,machineTypeetlocationsont des ids que votre app a donnés dans ses propres listes.machineTypel’emporte sursize.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 }stopgarde/workspaceet rien d’autre : lestartsuivant redémarre la boîte. La facturation doit s’arrêter avec elle.stoppeut répondre avant que la boîte soit complètement arrêtée —describedit alors"transition": "stopping"jusqu’à ce qu’elle le soit.destroysupprime 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 | |
|---|---|
creating | En cours de démarrage |
running | Démarrée — l’agent ne s’est peut-être pas encore connecté |
stopped | Arrêtée, /workspace conservé |
missing | Vous n’avez aucune boîte pour ce handle |
error | Elle a échoué — dites pourquoi dans detail |
unreachable | Vous 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.