Auto-hébergé · Open source

Une plateforme FaaS légère
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 quatre onglets d'une fonction, son code, et le runner affichant une exécution réussie L'éditeur FaaSBox : la liste des fonctions, les quatre 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

    Tout ce qu'il faut à une fonction tient sur un seul écran — aucune chaîne d'outils locale, aucune étape de déploiement.

    • 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 : la sauvegarde les télécharge et les installe pour vous, en tâche de fond. 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.
    daily-report
    const { team } = await Bun.stdin.json();
    
    const res = await fetch(`https://api.internal/teams/${team}`);
    const members = await res.json();
    
    console.log(JSON.stringify({ sent: members.length }));
  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. Le corps de la requête devient le stdin de la fonction, et la réponse porte son résultat et sa duré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.
  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.

Stockage

La base de données est un simple fichier.

Du SQLite. Fonctions, planifications, secrets, clés et logs y vivent tous. 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.

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.

Dans la boîte

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

Un éditeur, pas un pipeline

Quatre onglets par fonction : script, package.json, triggers, secrets. 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.

Des secrets chiffrés au repos

AES-256-GCM. Éditez-les en paires clé/valeur dans l'onglet Environment, masquées jusqu'à ce que vous les révéliez, et injectées dans l'environnement du subprocess. La sauvegarde remplace l'ensemble — raison pour laquelle vous le voyez.

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

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, aucun rôle qui accorde en silence plus que prévu.
  • 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 \
  faasbox

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.