GenHTTP Lambda

So funktioniert es

Sie schreiben ein C#-Snippet. Was es zurückgibt, ist in wenigen Sekunden unter einer öffentlichen HTTPS-Adresse erreichbar. Hier steht alles Wichtige – in der Reihenfolge, in der Sie es brauchen.

Was ein Lambda ist

Ein Lambda ist ein Snippet, das einen GenHTTP-Handler zurückgibt. Die Plattform kompiliert es, lädt es und hängt das Ergebnis unter Ihrer eigenen Adresse ein. Kein Projekt, keine Build-Datei, keine using-Anweisungen: Alle GenHTTP-Module sind schon importiert.

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

Das ist bereits ein vollständiges Lambda. Unter /lambda/your-key/ deployt, beantwortet es jeden Request mit „hello“.

Das Snippet besteht aus Anweisungen, nicht aus einer Klasse. Zum Schluss gibt es etwas zurück, das Requests beantworten kann: einen Handler oder einen Builder dafür.

Ihr erstes Lambda

  1. 1
    Klicken Sie auf Lambda erstellen. Sie bekommen eine öffentliche Adresse und einen Editor-Schlüssel. Der Schlüssel ist der einzige Weg zurück – heben Sie ihn auf. Niemand kann ihn für Sie wiederherstellen.
  2. 2
    Sie landen im Kontrollzentrum. Als erste Version ist schon ein kleiner REST-Dienst angelegt – nur als Startpunkt.
  3. 3
    Geben Sie den Editor-Schlüssel einem Agenten und sagen Sie ihm, was er bauen soll – er schreibt über MCP neue Versionen. Oder öffnen Sie Code und schreiben Sie selbst: Prüfen kompiliert, ohne etwas zu speichern, und zeigt die Meldungen des Compilers mit Datei und Zeile.
  4. 4
    Klicken Sie auf Deployen. Jetzt ist es online – vorher ist nichts erreichbar. Jedes weitere Deployment verlängert, wie lange es online bleibt.

Das Kontrollzentrum

Der Editor-Link öffnet ein Kontrollzentrum, kein Textfeld. Den meisten Code schreiben hier Agenten, also sehen Sie zuerst, wie es Ihrem Lambda geht. Die Seitenleiste zeigt, ob das Lambda online ist, seine Adresse und seine Bereiche. Wartet eine neuere Version darauf, online zu gehen, erscheint dort ein Button. Was Sie selten brauchen, etwa die Adresse ändern oder das Lambda löschen, steckt im Menü ⋯.

Übersicht
Ob es online ist, wie viele Requests es heute hatte und wie viele davon fehlschlugen, die letzte Änderung und wie viel Platz noch frei ist.
Ändern
Sagen Sie, was anders sein soll, und der Agent auf diesem Server setzt es um, während Sie zusehen. Er arbeitet in einem Entwurf, probiert die Änderung dort aus und übernimmt sie als nächste Version, sobald sie funktioniert. Schalten Sie Nach Abschluss online stellen aus, um den Entwurf zuerst selbst auszuprobieren.
Entwürfe
Änderungen, an denen neben dem Lambda gearbeitet wird: Jede wird unter einer eigenen Adresse ausprobiert und als nächste Version übernommen, sobald alles passt. Geöffnet hat ein Entwurf eigenen Code, eigene Daten und eigene Logs.
Dateien
Die Dateien einer Version: Code und Assets, das Programm selbst. Ein Schloss oder ein Globus zeigt, ob sie öffentlich erreichbar sind.
Daten
Was das Lambda zur Laufzeit aufbewahrt, für alle Versionen gemeinsam: der Workspace. Hineinsehen, Dateien hochladen und löschen oder ihn ausschalten.
Versionen
Was jede Version geändert hat, worum gebeten wurde und der Diff zur vorherigen. Von hier aus deployen oder zurückrollen – oder aus jeder von ihnen einen Entwurf beginnen.
Deployments
Was wann online war und warum es offline ging.
Statistik
Requests, Fehler, Antwortzeiten und die meistgefragten Pfade der letzten Stunde oder des letzten Tages.
Logs
Requests, Ausgaben und Stacktraces von allem, was schiefging – live.
Code
Selbst schreiben. Prüfen kompiliert, Speichern legt eine Version an, Deployen stellt sie online. In einem Entwurf behält Speichern den Code im Entwurf, und Vorschau deployen stellt ihn unter der Adresse des Entwurfs online. Strg+S speichert, F12 springt zur Deklaration.

Alle Bereiche funktionieren gleich: oben der Titel, ein ⓘ mit Erklärung, rechts die Aktionen und – wo es mehrere Ansichten gibt – darunter eine Reihe von Tabs. Beim Code sind die Tabs seine Dateien.

Traffic und Log liegen im Arbeitsspeicher. Sie sind zum Beobachten da, nicht zum Aufbewahren: Nach einem Neustart des Servers beginnen sie von vorn. Versionen und der Deployment-Verlauf werden gespeichert.

Das Warum festhalten

Eine Version ist der Code plus zwei optionale Notizen: die Spezifikation – was der Nutzer möchte und warum, möglichst in seinen Worten – und die Änderung, eine Zeile dazu, was die Version tut. Beide stehen im Versionsverlauf neben dem Diff. So bleibt das Warum neben dem Was erhalten – für Sie und für den nächsten Agenten, der den Verlauf liest, bevor er etwas ändert.

POST /api/v1/lambdas/{editorKey}/versions
{
  "files": [ { "name": "lambda.cs", "code": "..." } ],
  "specification": "Ein Gästebuch zum Eintragen; Einträge müssen einen Neustart überstehen",
  "change": "Speichert Einträge im Workspace, damit sie einen Neustart überstehen"
}

Agenten übergeben dieselben beiden Felder an write_code. Unter Code wird beim Speichern nach der Änderung gefragt. Beides ist optional. Zu lange Texte werden gekürzt statt abgelehnt: die Spezifikation nach 4000 Zeichen, die Änderung nach 500. Ein Entwurf hat seine eigenen beiden; wird er übernommen, gehen sie an die neue Version über.

Gefahrlos ändern

Eine Version ändert sich nie mehr, sobald sie gespeichert ist – und genau deshalb lohnt es sich, jede aufzuheben: Jede lässt sich vergleichen und genau so wieder online stellen, wie sie war. Um ein Lambda zu ändern, das Leute nutzen, beginnen Sie stattdessen einen Entwurf.

  1. 1
    Beginnen Sie ihn unter Entwürfe oder aus einer beliebigen Version. Er ist eine Kopie von Code und Assets dieser Version und der Daten des Lambdas.
  2. 2
    Ändern Sie ihn so oft wie nötig – unter Code oder indem Sie den Agenten darum bitten. Vorschau deployen stellt ihn unter einer eigenen Adresse online, /features/…/, mit einer eigenen Kopie der Daten. Besucher des Lambdas sehen nichts davon, und nichts, was er schreibt, erreicht die Daten des Lambdas.
  3. 3
    Klicken Sie auf Übernehmen, sobald alles passt: Der Entwurf wird zur nächsten Version, mit seinen Notizen, und geht auf Wunsch sofort online. Dabei verschwindet er – samt seiner Vorschau und seiner Kopie der Daten.
POST /api/v1/lambdas/{editorKey}/features
{ "name": "Bestenliste" }

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

An mehreren Entwürfen kann gleichzeitig gearbeitet werden. Übernehmen lässt sich nur einer, der auf der neuesten Version basiert – damit das Übernehmen nie eine Version rückgängig macht, die nach dem Beginn des Entwurfs gespeichert wurde. Wurde zuerst ein anderer übernommen, holen Sie dessen Änderungen herein – oder bitten Sie den Agenten darum – und geben Sie dann an, dass der Entwurf auf der neuesten Version basiert. Nichts wird von selbst übernommen; das ist Absicht.

Mehrere Dateien

Typen müssen nicht unter dem Code stehen, der sie nutzt. Klicken Sie unter Code neben den Dateien auf +: Die neue Datei wird zusammen mit dem Snippet im selben Namespace kompiliert, also müssen Sie nichts importieren. Ein Name ohne Endung gilt als 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);

Eine Seite ausliefern

Für eine Seite gibt es zwei Wege – und einen weiteren für das, was Leute daneben hochladen.

Eine Seite, direkt im Code

Gut für Kleines. Die Seite ist Teil des Snippets.

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);

Ein Ordner mit echten Dateien

Das Richtige für alles mit Stylesheet und Script. Die Dateien legen Sie genauso an wie eine C#-Datei. Sie werden ausgeliefert, wie sie sind – nichts wird kompiliert.

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

Hochgeladene Dateien, aus den Daten

Für das, was Leute hochladen oder das Lambda erzeugt – Bilder, Dokumente –, ausgeliefert neben der App. Nicht für die Seiten der App selbst: Die gehören in einen Ordner mit Dateien, damit sie in derselben Version stecken wie der Code, der sie braucht.

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

Ein Frontend, Schritt für Schritt

Der zweite Weg im Detail. Jede Demo liefert ihre Seite so aus, aus einem Ordner namens web – öffnen Sie zum Beispiel demo-crud. Demos sind schreibgeschützt; ihr Editor-Schlüssel ist ihr Name.

  1. 1
    Klicken Sie unter Code neben den Dateien auf + und geben Sie site/index.html ein. Ein Schrägstrich im Namen legt die Datei in einen Ordner. Eine Endung sagt, was für eine Datei es ist.
  2. 2
    Legen Sie site/app.css und site/app.js genauso an. Ihre Seite verweist über den Namen auf sie, etwa mit href="app.css". Denn der Ordner ist die Wurzel dessen, was ausgeliefert wird – kein Teil der Adresse.
  3. 3
    Für alles, was kein Text ist, etwa Bilder oder Schriften, öffnen Sie eine Datei in site und klicken neben den Dateien auf den Upload-Button: Die Datei landet im selben Ordner. Ein PNG kann man nicht in einen Texteditor tippen – das hier ist der Weg.
  4. 4
    Liefern Sie den Ordner in lambda.cs aus:
    return Layout.Create().Add(Assets.App("site"));
  5. 5
    Klicken Sie auf Deployen. site/index.html antwortet unter /, site/app.css unter /app.css. Jede Adresse ohne passende Datei bekommt die Seite. So funktioniert ein Frontend mit eigenem Routing auch, wenn jemand einen Deep Link neu lädt.
  6. 6
    Mit einer API daneben hat die Seite auch einen Gesprächspartner:
    var api = Inline.Create().Get("notes", () => notes);
    
    return Layout.Create()
                 .Add("api", api)
                 .Add(Assets.App("site"));

Zwei Orte für Dateien

Ein Lambda hat Dateien an zwei Orten, und der Editor zeigt sie getrennt: Dateien enthält die Dateien einer Version – das Programm –, Daten den Workspace – was das Programm aufbewahrt. Der Unterschied ist, wem sie gehören. Die Dateien einer Version gehören zu dieser Version; die Daten gehören dem Lambda, und alle Versionen teilen sie.

In einer VersionIn den Daten
InhaltCode und Assets: das Programm, samt Frontendalles, was das Lambda schreibt oder jemand hochlädt
Ändert sichnie – eine Änderung ist eine neue Versionsobald etwas hineingeschrieben wird
Ein Deploymentstellt genau diese Dateien onlinelässt sie unberührt
Zurückrollenbringt die alten Dateien zurückkeine Wirkung: Alle Versionen teilen sie
Ein Entwurfbeginnt als Kopie davonarbeitet mit einer Kopie davon
Wird gelöschtmit alten Versionen, sobald das Limit überschritten istmit dem Lambda oder wenn Sie sie ausschalten
im Code erreichbar alsAssetsWorkspace

Ein gemeinsamer Ort geht nicht. Sonst würde ein Deployment entweder alles löschen, was Ihr Lambda seitdem geschrieben hat – oder aus dem, was es ausliefert, ließe sich nie etwas entfernen. Ein Spiel mit Bestenliste braucht das Zweite, die Seite dazu das Erste. Also gehört die Seite in die Version und die Bestenliste in die Daten.

Daten speichern

Workspace ist ein privates Verzeichnis, in dem Ihr Lambda lesen und schreiben darf. Hier gehört alles hin, was einen Request oder ein Deployment überdauern soll.

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;
             });

Dazu gibt es ReadBytes, WriteBytes, Delete, List, CreateFolder und zum Ausliefern Tree/Files/App. Sonst ist nichts im Dateisystem erreichbar.

Websockets

Unterstützt – und von Anfang an mitgedacht. Die Demo demo-game bringt Spieler zusammen und führt jede Partie auf dem Server aus. Die einfachste Form sind drei 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);

In diese Falle tappt jeder: Browser können beim Websocket-Handshake keine Header setzen. Übergeben Sie, was der Handler braucht, in der Query – er liest sie aus connection.Request.Header.Query. Secrets schicken Sie als erste Nachricht.

Was nicht erlaubt ist

Ihr Code läuft auf einem gemeinsam genutzten Server. Deshalb wird manches in C# schon vor dem Kompilieren abgelehnt: Prozesse starten, eigene Sockets öffnen, Assemblies laden, auf das Dateisystem außerhalb Ihres Workspace zugreifen – und Reflection, die all das umgehen soll.

Alles andere ist da, auch die komplette API der GenHTTP-Module. Wird etwas abgelehnt, sehen Sie, in welcher Zeile und warum – nicht nur, dass es fehlschlug.

Alles mitnehmen

Mit Als .NET-Projekt herunterladen im Editor bekommen Sie alles als Solution, die Sie öffnen, mit dotnet run starten und behalten können. Sie hat genau eine Paketreferenz und keine Spur dieser Plattform.

Ihr Snippet wird zum Inhalt von Program.cs, in einem Host, der ausliefert, was es zurückgibt. Ihre anderen Dateien kommen genau so mit, wie Sie sie geschrieben haben. Workspace und Assets werden zu zwei Ordnern neben dem Code, mit denselben Methoden – an Ihrem Code ändert sich nichts.

Gut zu wissen, bevor Sie hier etwas bauen: Was Sie schreiben, gehört Ihnen, und Sie können es komplett mitnehmen. Dass es auf unserem Server läuft, bindet es nicht an unseren Server.

Mit einem Agenten arbeiten

Unter /mcp gibt es einen MCP-Endpunkt. Verbinden Sie einen Agenten damit, und er kann alles, was der Editor kann: die Anleitung lesen, eine Demo komplett lesen, Dateien schreiben, kompilieren und deployen. Darunter liegt dieselbe API.

Dabei sagt er, warum er etwas tut – write_code nimmt Spezifikation und Änderung entgegen. Und er kann prüfen, was er deployt hat: read_logs liefert die letzten Requests des Lambdas, seine Ausgaben und die Stacktraces aller Exceptions. So weiß ein Agent, dass sein Code funktioniert, statt es nur anzunehmen. Dasselbe sehen Sie im Kontrollzentrum.

Mehr dazu →