GenHTTP Lambda

Comment ça marche

Vous écrivez un snippet C#. Ce qu’il renvoie est hébergé à une adresse publique, en HTTPS, en quelques secondes. Voici tout ce qu’il faut savoir, dans l’ordre où vous le découvrirez.

Qu’est-ce qu’une lambda ?

Une lambda est un snippet qui renvoie un handler GenHTTP. La plateforme le compile, le charge et monte ce qu’il renvoie sous votre propre adresse. Pas de projet, pas de fichier de build, pas d’instruction using. Tous les modules GenHTTP sont déjà importés pour vous.

return Content.From(Resource.FromString("hello"));

Voilà une lambda complète. Déployée sur /lambda/your-key/, elle répond « hello » à chaque requête.

Le snippet est une suite d’instructions, pas une classe. Il finit par renvoyer quelque chose qui sait servir des requêtes : un handler, ou un builder de handler.

Votre première lambda

  1. 1
    Cliquez sur Créer ma lambda. Vous obtenez une adresse publique et une clé d’édition. Cette clé est le seul moyen d’y revenir : gardez-la. Personne ne pourra la récupérer pour vous.
  2. 2
    Vous arrivez sur son tableau de bord, avec un petit service REST déjà écrit en guise de première version. Ce n’est qu’un point de départ.
  3. 3
    Donnez la clé d’édition à un agent et dites-lui quoi construire : il écrit de nouvelles versions via MCP. Ou ouvrez Code et écrivez le code vous-même : Vérifier compile sans rien enregistrer et vous montre ce qu’en dit le compilateur, avec le fichier et la ligne.
  4. 4
    Cliquez sur Déployer. Votre lambda est en ligne. Avant ça, rien n’est accessible. Chaque nouveau déploiement prolonge sa durée en ligne.

Le tableau de bord

Le lien d’édition ouvre un tableau de bord plutôt qu’une zone de texte : ici, l’essentiel du code est écrit par des agents, donc l’écran montre d’abord comment va votre lambda. La barre latérale contient la lambda (en ligne ou non, son adresse, et un bouton quand une version plus récente attend d’être mise en ligne) et ses sections. Tout ce qui sert rarement, comme changer l’adresse ou supprimer la lambda, se trouve dans le menu ⋯.

Vue d’ensemble
En ligne ou non, le nombre de requêtes du jour et d’échecs, la dernière modification, et la place qui reste.
Modifier
Dites ce qui doit changer, et l’agent de ce serveur s’en charge sous vos yeux. Il travaille dans un brouillon, y essaie la modification, et l’intègre à la prochaine version une fois que ça marche. Désactivez Mettre en ligne une fois terminé pour essayer vous-même le brouillon d’abord.
Brouillons
Des modifications préparées à côté de la lambda : chacune s’essaie à sa propre adresse et s’intègre à la prochaine version une fois au point. Une fois ouvert, un brouillon a son propre code, ses propres données et ses propres logs.
Fichiers
Les fichiers d’une version : son code et ses assets, le programme lui-même. Un cadenas ou un globe indique si le public peut y accéder.
Données
Ce que la lambda garde pendant qu’elle tourne, partagé par toutes les versions : le workspace. Regardez ce qu’il contient, importez et supprimez des fichiers, ou désactivez-le.
Versions
Ce que chaque version a changé, ce qui avait été demandé, et la différence avec la précédente. C’est ici qu’on déploie ou qu’on revient en arrière, ou qu’on démarre un brouillon à partir de n’importe quelle version.
Déploiements
Ce qui était en ligne, quand, et ce qui l’a arrêté.
Stats
Requêtes, échecs, temps de réponse et chemins les plus demandés, sur la dernière heure ou les dernières 24 heures.
Logs
Ses requêtes, ce qu’elle affiche, et la stack trace de tout ce qui plante, en direct.
Code
Pour écrire le code à la main. Vérifier compile, Enregistrer crée une version, Déployer met en ligne. Dans un brouillon, Enregistrer garde le code dans le brouillon et Déployer l’aperçu le met en ligne à l’adresse du brouillon. Ctrl-S enregistre ; F12 va à une déclaration.

Chaque section fonctionne de la même façon : son titre, un ⓘ qui l’explique, ses actions à droite et, quand elle a plusieurs vues, une rangée d’onglets en dessous. Pour le code, les onglets sont ses fichiers.

Le trafic et les logs sont gardés en mémoire, pour suivre ce qui se passe, pas pour archiver : un redémarrage du serveur les remet à zéro. Les versions et l’historique des déploiements, eux, sont enregistrés.

Dire pourquoi

Une version, c’est le code, plus deux notes facultatives : la spécification, ce que veut l’utilisateur et pourquoi, si possible avec ses mots, et le changement, une ligne sur ce que fait la version. Elles s’affichent à côté du diff dans l’historique des versions. Le pourquoi reste ainsi à côté du quoi, pour vous, et pour le prochain agent qui lira l’historique avant de toucher à quoi que ce soit.

POST /api/v1/lambdas/{editorKey}/versions
{
  "files": [ { "name": "lambda.cs", "code": "..." } ],
  "specification": "Un livre d’or que les gens peuvent signer ; les messages doivent survivre à un redémarrage",
  "change": "Garde les messages dans le workspace pour qu’ils survivent à un redémarrage"
}

Les agents passent les deux mêmes champs à write_code. Dans Code, l’enregistrement vous demande le changement. Les deux sont facultatifs. Trop longs, ils ne sont pas refusés mais coupés : à 4 000 caractères pour la spécification, à 500 pour le changement. Un brouillon a ses deux notes à lui, et la version dans laquelle il est intégré les reprend.

Modifier sans risque

Une version ne change plus une fois enregistrée, et c’est ce qui fait que chacune vaut la peine d’être gardée : on peut comparer n’importe laquelle, et la remettre en ligne exactement telle qu’elle était. Pour modifier une lambda que des gens utilisent, démarrez plutôt un brouillon.

  1. 1
    Démarrez-le dans Brouillons, ou à partir de n’importe quelle version. C’est une copie du code et des assets de cette version, et des données de la lambda.
  2. 2
    Modifiez-le autant de fois qu’il le faut, dans Code ou en le demandant à l’agent. Déployer l’aperçu le met en ligne à sa propre adresse, /features/…/, avec sa propre copie des données. Les visiteurs de la lambda n’en voient rien, et rien de ce qu’il écrit n’atteint les données de la lambda.
  3. 3
    Une fois au point, cliquez sur Intégrer : il devient la prochaine version, avec ses notes, et passe en ligne tout de suite si vous le voulez. Le brouillon disparaît alors, avec son aperçu et sa copie des données.
POST /api/v1/lambdas/{editorKey}/features
{ "name": "Classement" }

PUT  /api/v1/lambdas/{editorKey}/features/{feature}/files?deploy=true
POST /api/v1/lambdas/{editorKey}/features/{feature}/merge
{ "deploy": true }

On peut travailler sur plusieurs brouillons à la fois. Seul un brouillon basé sur la version la plus récente peut être intégré : ainsi, une intégration n’annule jamais une version enregistrée après le démarrage du brouillon. Si un autre a été intégré avant, reportez ses changements (ou demandez-le à l’agent), puis basez le brouillon sur la version la plus récente. Rien ne s’intègre tout seul, et c’est voulu.

Plusieurs fichiers

Les types n’ont pas besoin d’être sous le code qui les utilise. Dans Code, cliquez sur + à côté des fichiers : le nouveau fichier est compilé avec le snippet, dans le même namespace, donc rien à importer pour y accéder. Un nom sans extension est considéré comme du C#.

lambda.cs
var shelf = new Shelf();

return Inline.Create()
             .Get(() => shelf.All())
             .Post((Book book) => shelf.Add(book));
Shelf.cs
public sealed class Shelf
{
    private readonly List<Book> _books = [];

    public IEnumerable<Book> All() => _books;

    public Book Add(Book book)
    {
        _books.Add(book);
        return book;
    }
}

public record Book(string Title, string Author);

Servir une page

Il y a deux façons de servir une page, et une troisième pour ce que les gens importent à côté.

Une page, écrite dans le code

Parfait pour quelque chose de petit. La page fait partie du snippet.

var page = Resource.FromString("""
                              <!doctype html>
                              <title>Mine</title>
                              <h1>It works</h1>
                              """)
                   .Type(new ContentType("text/html; charset=utf-8"));

return Content.From(page);

Un dossier de vrais fichiers

Ce qu’il vous faut dès qu’il y a une feuille de style et un script. Les fichiers s’ajoutent comme un fichier C#, et sont servis exactement tels que vous les avez écrits. Rien ne les compile.

return Layout.Create()
             .Add("api", api)
             .Add(Assets.App("site"));

Des fichiers importés, depuis les données

Pour ce que les gens importent ou ce que la lambda crée (photos, documents), servi à côté de l’app. Pas pour les pages de l’app elle-même : leur place est dans un dossier de fichiers, où elles sont versionnées avec le code qui en a besoin.

return Layout.Create()
             .Add("api", api)
             .Add("uploads", Workspace.Files("uploads"))
             .Add(Assets.App("site"));

Un front-end, étape par étape

La deuxième façon, en entier. Chaque démo sert sa page ainsi, depuis un dossier nommé web : ouvrez demo-crud pour en lire une. Les démos sont en lecture seule ; leur clé d’édition est leur nom.

  1. 1
    Dans Code, cliquez sur + à côté des fichiers et tapez site/index.html. Un nom qui contient un slash place le fichier dans un dossier ; un nom avec une extension donne le type du fichier.
  2. 2
    Ajoutez site/app.css et site/app.js de la même façon. Votre page les appelle par leur nom, comme dans href="app.css", car le dossier est la racine de ce qui est servi, pas une partie de l’adresse.
  3. 3
    Pour tout ce qui n’est pas du texte, comme une image ou une police, ouvrez un fichier dans site et cliquez sur le bouton d’import à côté des fichiers : le fichier arrive dans le même dossier. Un PNG ne se tape pas dans un éditeur de texte, c’est donc par là qu’il faut passer.
  4. 4
    Dans lambda.cs, servez le dossier :
    return Layout.Create().Add(Assets.App("site"));
  5. 5
    Cliquez sur Déployer. site/index.html répond sur /, site/app.css sur /app.css, et toute adresse qui ne correspond à aucun fichier renvoie la page. Un front-end qui gère son propre routage marche donc aussi quand quelqu’un recharge un lien profond.
  6. 6
    Ajoutez une API à côté, pour que la page ait à qui parler :
    var api = Inline.Create().Get("notes", () => notes);
    
    return Layout.Create()
                 .Add("api", api)
                 .Add(Assets.App("site"));

Les deux endroits où vivent les fichiers

Une lambda garde des fichiers à deux endroits, et l’éditeur les montre séparément : Fichiers contient les fichiers d’une version (le programme), et Données contient le workspace (ce que le programme garde). La différence, c’est à qui ils appartiennent. Les fichiers d’une version appartiennent à cette version ; les données appartiennent à la lambda, et toutes les versions les partagent.

Dans une versionDans les données
ce qu’il contientle code et les assets : le programme, front-end compristout ce que la lambda écrit, ou que quelqu’un importe
quand il changejamais : une modification donne une nouvelle versiondès que quelque chose y est écrit
un déploiementmet exactement ces fichiers en lignen’y touche jamais
revenir en arrièrerestaure les anciens fichiersaucun effet : toutes les versions les partagent
un brouilloncommence par une copie de ces fichierstravaille sur une copie de ces données
quand il disparaîtavec les anciennes versions, au-delà de la limiteavec la lambda, ou quand vous le désactivez
accessible dans le code viaAssetsWorkspace

Impossible d’en faire un seul endroit. Sinon, soit un déploiement effacerait tout ce que votre lambda a écrit depuis, soit rien ne pourrait jamais être retiré de ce qu’elle publie. Un jeu qui tient un classement a besoin du second cas ; la page qu’il sert, du premier. La page va donc dans la version, et le classement dans les données.

Garder des données

Workspace est un dossier privé que votre lambda peut lire et écrire. C’est là que va tout ce qui doit survivre à une requête, ou à un déploiement.

var notes = new List<string>();

if (Workspace.Exists("notes.json"))
{
    notes.AddRange(JsonSerializer.Deserialize<List<string>>(Workspace.ReadText("notes.json")) ?? []);
}

return Inline.Create()
             .Get("notes", () => notes)
             .Post("notes", (string text) =>
             {
                 notes.Add(text);
                 Workspace.WriteText("notes.json", JsonSerializer.Serialize(notes));
                 return notes.Count;
             });

Il y a aussi ReadBytes, WriteBytes, Delete, List, CreateFolder, et Tree/Files/App pour le servir. Rien d’autre du système de fichiers n’est accessible.

WebSockets

Pris en charge, et pas à moitié. La démo demo-game forme des paires de joueurs et fait tourner chaque partie sur le serveur. La forme la plus simple tient en trois callbacks :

var room = new ConcurrentDictionary<IReactiveConnection, string>();

var socket = Websocket.Functional()
                      .OnOpen(c => { room[c] = "someone"; return ValueTask.CompletedTask; })
                      .OnMessage(async (c, text) =>
                      {
                          foreach (var other in room.Keys)
                          {
                              await other.WritePayloadAsync(text);
                          }
                      })
                      .OnClose((c, _) => { room.TryRemove(c, out _); return ValueTask.CompletedTask; });

return Layout.Create().Add("chat", socket);

Un piège où tout le monde tombe : un navigateur ne peut pas définir d’en-têtes lors du handshake WebSocket. Passez ce dont le handler a besoin dans la query string, où il le lit via connection.Request.Header.Query, ou envoyez les secrets dans le premier message.

Ce que vous ne pouvez pas faire

Votre code tourne sur un serveur partagé. Une partie de C# est donc refusée avant même la compilation : lancer des processus, ouvrir vos propres sockets, charger des assemblies, accéder au système de fichiers en dehors de votre workspace, et la réflexion utilisée pour contourner tout ça.

Tout le reste est là, y compris toute l’API des modules GenHTTP. Si quelque chose est refusé, on vous dit quelle ligne et pourquoi, pas simplement que ça a échoué.

Repartir avec votre code

Dans l’éditeur, Télécharger en projet .NET vous donne le tout : une solution que vous pouvez ouvrir, lancer avec dotnet run, et garder. Elle contient une seule référence de package, et aucune trace de cette plateforme.

Votre snippet devient le corps de Program.cs, dans un hôte qui sert ce qu’il renvoie. Vos autres fichiers sont repris exactement tels quels. Workspace et Assets deviennent deux dossiers à côté du code, avec les mêmes méthodes : rien à changer dans votre code.

Bon à savoir avant de construire quoi que ce soit ici : ce que vous écrivez vous appartient, et repart avec vous en entier. Le faire tourner sur cette machine ne vous enferme pas sur cette machine.

Laisser faire un agent

Un endpoint MCP est disponible sur /mcp. Branchez-y un agent, et il peut faire tout ce que fait l’éditeur : lire le guide, lire une démo en entier, écrire des fichiers, les compiler et déployer. C’est la même API en dessous.

Il explique ses choix au fur et à mesure (write_code prend la spécification et le changement) et il peut regarder ce qu’il a déployé : read_logs renvoie les requêtes récentes de la lambda, ce qu’elle a affiché, et la stack trace de chaque exception levée. C’est comme ça qu’un agent vérifie que son code marche, au lieu de le supposer. Vous voyez la même chose dans le tableau de bord.

En savoir plus →