Version Community · Open source

Lambda sans la complexitéUne plateforme légère de Function-as-a-Service
pour TypeScript. Zéro dépendance.

É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.

Licence MIT · Go · Bun · SQLite

L'éditeur FaaSBox : la liste des fonctions, les cinq onglets d'une fonction, son code, et le runner affichant une exécution réussie L'éditeur FaaSBox : la liste des fonctions, les cinq onglets d'une fonction, son code, et le runner affichant une exécution réussie

Fonctionnement

Trois étapes, et ça tourne.

  1. 1

    Écrivez, installez et testez dans le navigateur

    • Le code. Votre fonction lit du JSON sur stdin et écrit du JSON sur stdout. Aucun framework, aucune signature de handler à retenir, rien à importer.

      L'onglet script d'une fonction, avec son code L'onglet script d'une fonction, avec son code
    • 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.

      L'onglet package.json d'une fonction, avec une dépendance npm et son état d'installation L'onglet package.json d'une fonction, avec une dépendance npm et son état d'installation
    • Run — un clic exécute la fonction et vous montre son résultat, sa sortie et sa durée, sans quitter l'onglet.

      La barre du runner et son bouton Run La barre du runner et son bouton Run
    • 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.

      L'onglet Files d'une fonction, avec le contenu de son dossier sur le disque L'onglet Files d'une fonction, avec le contenu de son dossier sur le disque
  2. 2

    Posez-lui un déclencheur

    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.

      La page des clés d'API, avec le formulaire de création et le choix de la portée La page des clés d'API, avec le formulaire de création et le choix de la portée
    • 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.

      L'onglet Triggers d'une fonction, avec une planification et son payload L'onglet Triggers d'une fonction, avec une planification et son payload
  3. 3

    Lisez le log

    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

Un agent peut écrire vos fonctions.

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 ».

  • Deux façons d'entrer. Un clic : l'agent ouvre un navigateur sur un écran de consentement qui le nomme et dit ce qu'il demande, vous approuvez, et aucun secret n'est collé nulle part. Ou une clé d'API cochée Can manage functions, portée par un en-tête — la forme d'une intégration non interactive, et la seule qui marche sur une instance qui ne connaît pas sa propre adresse publique.
  • Les deux accordent la même chose, et cette chose est tout. Lire, remplacer, exécuter et supprimer toutes les fonctions de l'instance, et lire leur historique d'exécution. Il n'existe pas d'octroi plus étroit aujourd'hui : une clé à portée restreinte ne crée rien, donc un agent qui doit créer des fonctions en tient une qui les atteint toutes. Perdre l'une ou l'autre revient à perdre un shell sur la machine.
  • Sept outils, un par verbe. 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.
  • Révocable, depuis l'éditeur. La page AI MCP liste chaque agent qui s'est autorisé, le jour où vous l'avez approuvé et celui où son accès expire de lui-même. Revoke prend effet à l'appel suivant de cet agent — il n'y a pas de session à attendre.
  • Streamable HTTP, et rien à installer. Un seul point d'entrée, 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

La base de données est un simple fichier.

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.

Conteneur
data.db toute votre instance

remplacé à chaque déploiement

Litestream · optionnel

chaque écriture, au fil de l'eau restauré avant le démarrage
Bucket S3
data.db le même fichier, tenu à jour

survit au conteneur

Deux raisons de l'activer

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

Tout ce qu'il faut, rien à câbler.

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.

Deux déclencheurs, un seul moteur

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.

Chiffré au repos, et pas que les secrets

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é.

Des clés dont vous fixez la portée

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.

Des logs qui répondent

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.

Borné par conception

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

Assez simple pour tenir en tête.

Chaque décision ci-dessous retire une pièce mobile. Ce qui reste se comporte pareil à chaque fois, et tient dans un seul raisonnement.

  • Une seule frontière à sécuriser. Un unique superuser administre l'instance. Aucune matrice de permissions à auditer et aucun rôle : une clé d'API porte deux interrupteurs — les fonctions qu'elle atteint, et si elle peut les écrire — tous deux posés sur la page qui l'a créée.
  • Les exécutions sont atomiques. Un appel rend un résultat complet ou une erreur. Rien à réassembler côté client, aucune réponse à moitié écrite à détecter.
  • Les fonctions restent indépendantes. Chacune s'écrit, s'invoque et se teste seule. Quand quelque chose casse, aucun graphe d'appels caché à démêler d'abord.
  • Rien ne part deux fois à votre insu. Une planification manquée pendant un arrêt est signalée, jamais rejouée — la nuance compte quand la fonction facture un client ou envoie du courrier.
  • Une seule pièce mobile. Pas de control plane, pas de registre, pas de broker, pas de VPC, pas de serverless.yml. La plateforme est le conteneur que vous avez lancé, et c'est tout.

Démarrer

Un conteneur, une commande.

docker
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

Ensuite

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.

Pour travailler en local

Go 1.24+, Bun et Node.js, puis bash infra/dev/dev.sh construit l'éditeur et démarre le serveur.