Un éditeur, pas un pipeline
Cinq onglets par fonction : script, package.json, triggers, secrets, et les fichiers que son dossier porte sur le disque. Run exécute ce que le serveur détient ; Save and run n'apparaît que lorsque votre écran en diffère.
Version Community · Open source
Écrivez vos fonctions dans le navigateur, puis appelez-les en HTTP ou planifiez-les en cron. Se déploie en un seul conteneur Docker : un binaire Go, un fichier SQLite, rien d'autre à installer.
Fonctionnement
Le code. Votre fonction lit du JSON sur stdin et écrit du JSON sur stdout. Aucun framework, aucune signature de handler à retenir, rien à importer.
package.json — optionnel. Listez vos paquets npm et sauvegardez : l'installation tourne sur le serveur et vous la suivez depuis l'onglet — installing, puis ready, ou la fin du message d'erreur si npm avait quelque chose à dire. Il n'y a rien d'autre à faire.
Run — un clic exécute la fonction et vous montre son résultat, sa sortie et sa durée, sans quitter l'onglet.
Files — les autres onglets montrent ce que la base détient ; celui-ci montre ce que la machine a. Le dossier tel qu'il est sur le disque : vos fichiers, le lockfile, et le node_modules que l'installation a produit. En lecture seule, et c'est là qu'on regarde quand une version n'est pas celle qu'on attendait.
Deux façons de lancer une exécution, et une fonction peut porter les deux à la fois.
Un appel HTTP. POST /invoke/{nom} avec une clé d'API, créée depuis la page API keys. Le corps de la requête devient le stdin de la fonction, et la réponse porte son résultat et sa durée. L'éditeur affiche cette route sous le nom de la fonction ouverte, avec un exemple curl à côté : l'appel entier, prêt à copier, la clé laissée en placeholder.
Une planification cron. Ajoutée dans l'onglet Triggers : quinze expressions toutes faites, un champ libre, et un plafond d'exécutions simultanées. Un changement prend effet immédiatement — rien ne redémarre.
Chaque exécution enregistre son déclencheur, son statut, sa durée, stdout, stderr, le payload de la requête et le code de sortie. Y compris celles qui n'ont jamais eu lieu : une planification due pendant un arrêt du serveur est journalisée en missed, avec le nombre d'occurrences et la période. Une clé qui peut gérer les fonctions relit aussi cet historique par l'API — le seul moyen de voir ce qu'un déclencheur cron a fait sans les identifiants de l'instance elle-même.
Agents IA
FaaSBox parle MCP sur POST /mcp. Un agent qui s'y connecte reçoit aussitôt le contrat d'écriture — payload sur stdin, résultat JSON sur stdout, la règle de nommage, les plafonds, le format cron — si bien que « voici une URL et une clé, débrouille-toi » devient « écris-moi une fonction qui m'envoie un mail chaque matin ».
list_functions, get_function, create_function, update_function, delete_function, invoke_function et get_function_logs. Chacun emprunte le même chemin de code que sa route HTTP : ce qu'un agent peut faire est exactement ce que son identifiant peut faire — refus compris.POST /mcp, sur l'instance que vous avez déjà déployée. Pas de variante stdio : il faudrait publier un artefact et vous demander de faire tourner un second runtime à côté du premier.Stockage
Du SQLite. Fonctions, planifications, secrets, clés et logs y vivent tous, chiffrés colonne par colonne. Aucun serveur de base à provisionner, et sauvegarder l'instance revient à copier un fichier.
remplacé à chaque déploiement
Litestream · optionnel
survit au conteneur
Le disque en dessous n'est pas permanent. Un conteneur est remplacé à chaque déploiement et emporte son système de fichiers. Litestream remet le fichier en place avant que le serveur ne démarre : le conteneur reste jetable, vos données non.
Ou simplement comme sauvegarde. Même sur un serveur dont le disque survit, cette même réplication est une copie hors site toujours à jour — aucun dump à planifier, aucune fenêtre pendant laquelle la dernière heure manque. Ce qui quitte la machine est chiffré : gardez la clé ailleurs et une copie de ce bucket n'ouvre rien.
Laissez-le désactivé et rien d'autre ne change : FaaSBox tourne sur le seul fichier local, ce qu'on veut sur sa propre machine.
Vos dépendances reviennent toutes seules, dans les deux cas. node_modules n'est pas dans le fichier — c'est un artefact de build, et un gros. Ce qui est stocké, c'est le lockfile : un conteneur neuf réinstalle exactement les versions que l'ancien faisait tourner, avant le premier appel plutôt que pendant.
Dans la boîte
Cinq onglets par fonction : script, package.json, triggers, secrets, et les fichiers que son dossier porte sur le disque. Run exécute ce que le serveur détient ; Save and run n'apparaît que lorsque votre écran en diffère.
Un appel HTTP et un tic cron empruntent le même chemin : un subprocess Bun, le même environnement, les mêmes plafonds, la même ligne de log. Ce qui diffère, c'est ce qui se passe quand la boîte est occupée — HTTP refuse avec un 429, une exécution cron attend son tour.
AES-256-GCM, colonne par colonne : votre code, son package.json et son lockfile, ses déclencheurs, la sortie de chaque exécution, et les secrets que vous éditez en paires clé/valeur dans l'onglet Environment. Une seule clé pour tout, FAASBOX_ENCRYPTION_KEY, et le serveur ne démarre pas sans elle. Les deux colonnes que le SQL doit chercher sont chiffrées elles aussi, et retrouvées par une empreinte à clé posée à côté.
Hachées à la création, révélées une fois, restreintes au besoin à des fonctions nommées et assorties d'une date d'expiration. Cochez Can manage functions et la clé les écrit autant qu'elle les appelle — créer, lire, remplacer, supprimer, et relire leur historique, sans les identifiants de l'instance. Une expiration que le serveur ne sait pas lire est refusée à la création plutôt qu'ignorée en silence.
Déclencheur, statut, durée, les deux flux, payload de la requête, code de sortie, et un drapeau quand quelque chose a été tronqué pour tenir. Lisibles depuis l'éditeur, et par l'API avec une clé qui peut gérer les fonctions. La rétention se compte en lignes, purgées toutes les heures.
Rien ne tourne sans borne : 30 secondes par exécution, 1 Mo de corps de requête, 1 Mo capturé par flux de sortie, quatre exécutions simultanées. Les tailles et la concurrence sont des défauts que vous changez par variable d'environnement ; les deux délais, eux, sont fixes. Le limiteur de débit de PocketBase est armé à chaque démarrage : un troisième mot de passe faux d'affilée répond 429 — il ne couvre pas /invoke, que borne le plafond de concurrence.
Par conception
Chaque décision ci-dessous retire une pièce mobile. Ce qui reste se comporte pareil à chaque fois, et tient dans un seul raisonnement.
Démarrer
docker run -d -p 8080:8080 \
-e SUPERUSER_EMAIL=admin@example.com \
-e SUPERUSER_PASSWORD='…' \
-e FAASBOX_ENCRYPTION_KEY=$(openssl rand -hex 32) \
-v faasbox-data:/app/data/pb_data \
ghcr.io/antoineviau/faasbox:latest
localhost:8080/ — l'éditeur. Connectez-vous avec l'adresse et le mot de passe de la commande ci-dessus ; le compte est créé au premier démarrage.
localhost:8080/_/ — l'admin PocketBase en dessous, si vous voulez les collections brutes.
Go 1.24+, Bun et Node.js, puis bash infra/dev/dev.sh construit l'éditeur et démarre le serveur.