GenHTTP Lambda

Come funziona

Scrivi uno snippet di C#. Quello che restituisce va online a un indirizzo pubblico, in HTTPS, in pochi secondi. Qui trovi tutto, nell’ordine in cui lo incontrerai.

Cos’è una lambda

Una lambda è uno snippet che restituisce un handler GenHTTP. La piattaforma lo compila, lo carica e monta quello che ha restituito sotto il tuo indirizzo. Niente progetto, niente file di build, niente istruzioni using: tutti i moduli GenHTTP sono già importati.

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

Questa è una lambda completa. Dopo il deploy su /lambda/your-key/, risponde a ogni richiesta con la parola «hello».

Lo snippet è fatto di istruzioni, non di una classe. L’ultima cosa che fa è restituire qualcosa in grado di servire le richieste: un handler, o un builder che ne crea uno.

La tua prima lambda

  1. 1
    Premi Crea la mia lambda. Ricevi un indirizzo pubblico e una chiave di modifica. La chiave è l’unico modo per rientrare, quindi conservala. Nessuno può recuperarla per te.
  2. 2
    Arrivi nel suo pannello di controllo, con un piccolo servizio REST già scritto come prima versione. È solo un punto di partenza.
  3. 3
    Passa la chiave di modifica a un agente e digli cosa creare: scrive nuove versioni tramite MCP. Oppure apri Codice e scrivi tu il codice: Verifica compila senza salvare niente e ti mostra cosa dice il compilatore, con file e riga.
  4. 4
    Premi Deploy. Ora è online. Prima non è raggiungibile, e ogni nuovo deploy allunga il tempo in cui resta online.

Il pannello di controllo

Il link di modifica apre un pannello di controllo, non un semplice editor di testo: qui gran parte del codice la scrivono gli agenti, quindi la prima cosa che vedi è come sta andando la tua lambda. La barra laterale mostra la lambda (se è online, il suo indirizzo e un pulsante quando una versione più recente aspetta di andare online) e le sue sezioni. Le azioni che servono di rado, come cambiare l’indirizzo o eliminarla, sono nel menu ⋯.

Panoramica
Se è online, quante richieste ha ricevuto oggi e quante sono fallite, l’ultima modifica e quanto spazio resta.
Modifica
Scrivi cosa deve cambiare e l’agente di questo server lo fa sotto i tuoi occhi. Lavora su una bozza, prova lì la modifica e la integra nella prossima versione quando funziona. Disattiva Metti online a lavoro finito per provare prima tu la bozza.
Bozze
Modifiche preparate accanto alla lambda: ognuna si prova a un indirizzo tutto suo e si integra nella prossima versione quando è a posto. Una volta aperta, una bozza ha il suo codice, i suoi dati e i suoi log.
File
I file di una versione: il codice e gli asset, cioè il programma vero e proprio. Un lucchetto o un globo indica se sono pubblici.
Dati
Quello che la lambda conserva mentre gira, condiviso da tutte le versioni: il workspace. Puoi guardarci dentro, caricare ed eliminare file, o disattivarlo.
Versioni
Cosa ha cambiato ogni versione, cosa era stato chiesto e le differenze rispetto alla precedente. Da qui fai il deploy o torni indietro, oppure avvii una bozza da una qualsiasi di esse.
Deployment
Cosa è stato online e quando, e cosa l’ha fermato.
Statistiche
Richieste, errori, tempi di risposta e i percorsi più richiesti, nell’ultima ora o nelle ultime 24 ore.
Log
Le richieste, cosa ha stampato e lo stack trace di ogni errore, in tempo reale.
Codice
Per scriverlo a mano. Verifica compila, Salva crea una versione, Deploy la mette online. In una bozza, Salva lo tiene nella bozza e Deploy dell’anteprima lo mette online all’indirizzo della bozza. Ctrl-S salva; F12 va alla dichiarazione.

Ogni sezione funziona allo stesso modo: il titolo, il pulsante ⓘ che la spiega, le azioni a destra e, dove ci sono più viste, una fila di schede sotto. Nel codice, le schede sono i file.

Traffico e log restano in memoria: servono per tenere d’occhio le cose, non per conservarle. Un riavvio del server li azzera. Le versioni e la cronologia dei deployment invece vengono salvate.

Spiegare il perché

Una versione è il codice più, se vuoi, due note: la specifica, cioè cosa vuole l’utente e perché, se possibile con parole sue, e la modifica, una riga su cosa fa la versione. Compaiono accanto al diff nella cronologia delle versioni, così il perché resta vicino al cosa: per te e per il prossimo agente che leggerà la cronologia prima di cambiare qualcosa.

POST /api/v1/lambdas/{editorKey}/versions
{
  "files": [ { "name": "lambda.cs", "code": "..." } ],
  "specification": "Un guestbook che la gente può firmare; le voci devono sopravvivere a un riavvio",
  "change": "Salva le voci nel workspace così sopravvivono a un riavvio"
}

Gli agenti passano gli stessi due campi a write_code. In Codice, quando salvi ti viene chiesta la modifica. Sono entrambi facoltativi: una specifica lunga viene tagliata a 4000 caratteri e una modifica a 500, invece di essere rifiutata. Una bozza ha le sue due note, e la versione in cui viene integrata le riprende.

Cambiarla senza rischi

Una versione, una volta salvata, non cambia più, ed è per questo che vale la pena conservarle tutte: ognuna si può confrontare e rimettere online esattamente com’era. Per cambiare una lambda che la gente usa, avvia invece una bozza.

  1. 1
    Avviala in Bozze, o da una versione qualsiasi. È una copia del codice e degli asset di quella versione, e dei dati della lambda.
  2. 2
    Modificala tutte le volte che serve, in Codice o chiedendolo all’agente. Deploy dell’anteprima la mette online a un indirizzo tutto suo, /features/…/, con la sua copia dei dati. I visitatori della lambda non ne vedono niente, e niente di quello che scrive arriva ai dati della lambda.
  3. 3
    Quando è a posto, premi Integra: diventa la prossima versione, con le sue note, e se vuoi va subito online. La bozza sparisce, insieme alla sua anteprima e alla sua copia dei dati.
POST /api/v1/lambdas/{editorKey}/features
{ "name": "Classifica" }

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

Si può lavorare a più bozze insieme. Si può integrare solo una bozza basata sulla versione più recente, così un’integrazione non annulla mai una versione salvata dopo l’avvio della bozza. Se prima ne è stata integrata un’altra, porta dentro le sue modifiche (o chiedilo all’agente), poi basa la bozza sulla versione più recente. Niente viene integrato da solo, ed è voluto.

Più di un file

I tipi non devono per forza stare sotto il codice che li usa. In Codice, premi + accanto ai file: il nuovo file viene compilato insieme allo snippet, nello stesso namespace, quindi non devi importare niente per usarlo. Un nome senza estensione viene considerato 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);

Servire una pagina

Ci sono due modi per servire una pagina, più uno per quello che la gente carica accanto.

Una pagina, scritta nel codice

Va bene per le cose piccole. La pagina fa parte dello 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);

Una cartella di file veri

Quello che ti serve per qualsiasi cosa con un foglio di stile e uno script. I file si aggiungono come un file C# e vengono serviti esattamente come li hai scritti. Niente li compila.

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

File caricati, dai dati

Per quello che la gente carica o che la lambda crea (foto, documenti), servito accanto all’app. Non per le pagine dell’app stessa: quelle vanno in una cartella di file, dove seguono le versioni insieme al codice che le usa.

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

Un front-end, passo per passo

Il secondo modo, per intero. Ogni demo serve la sua pagina così, da una cartella chiamata web: apri demo-crud per vederne una. Le demo sono in sola lettura, e la loro chiave di modifica è il loro nome.

  1. 1
    In Codice, premi + accanto ai file e scrivi site/index.html. Un nome con una barra mette il file in una cartella; un nome con un’estensione viene trattato come il tipo di file che indica.
  2. 2
    Aggiungi site/app.css e site/app.js allo stesso modo. La pagina li richiama per nome, come in href="app.css", perché la cartella è la radice di ciò che viene servito, non una parte dell’indirizzo.
  3. 3
    Per tutto ciò che non è testo, come un’immagine o un font, apri un file in site e premi il pulsante di caricamento accanto ai file: finisce nella stessa cartella. Un PNG non si può scrivere in un editor di testo, quindi si passa da lì.
  4. 4
    In lambda.cs, servi la cartella:
    return Layout.Create().Add(Assets.App("site"));
  5. 5
    Premi Deploy. site/index.html risponde su /, site/app.css su /app.css, e qualsiasi indirizzo che non corrisponde a un file riceve la pagina: così un front-end con un suo routing funziona anche quando qualcuno ricarica la pagina su un deep link.
  6. 6
    Aggiungi un’API accanto e la pagina avrà qualcosa con cui parlare:
    var api = Inline.Create().Get("notes", () => notes);
    
    return Layout.Create()
                 .Add("api", api)
                 .Add(Assets.App("site"));

I due posti dove stanno i file

Una lambda tiene i file in due posti, e l’editor li mostra separati: File contiene i file di una versione (il programma) e Dati contiene il workspace (quello che il programma conserva). La differenza è di chi sono. I file di una versione appartengono a quella versione; i dati appartengono alla lambda, e tutte le versioni li condividono.

In una versioneNei dati
cosa contieneil codice e gli asset: il programma, front-end compresoquello che scrive la lambda o che carica qualcuno
quando cambiamai: una modifica è una nuova versioneappena ci viene scritto qualcosa
un deploymette online esattamente questi filenon li tocca mai
tornare indietroriporta i vecchi filenessun effetto: tutte le versioni li condividono
una bozzaparte da una copia di questi filelavora su una copia dei dati
quando spariscecon le versioni vecchie, oltre il limitecon la lambda, o quando disattivi il workspace
dal codice si raggiunge conAssetsWorkspace

Non possono stare in un unico posto. Se ci stessero, un deploy cancellerebbe tutto ciò che la lambda ha scritto nel frattempo, oppure non si potrebbe mai togliere niente da ciò che pubblica. Un gioco con una classifica vuole la seconda cosa; la pagina che serve vuole la prima. Quindi la pagina va nella versione, e la classifica nei dati.

Salvare i dati

Workspace è una cartella privata che la tua lambda può leggere e scrivere. È il posto per tutto ciò che deve sopravvivere a una richiesta, o a un deploy.

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

Ci sono anche ReadBytes, WriteBytes, Delete, List, CreateFolder, e Tree/Files/App per servirlo. Il resto del file system non è raggiungibile.

WebSocket

Supportati sul serio, non aggiunti all’ultimo momento. La demo demo-game abbina i giocatori e gestisce ogni partita sul server. La forma più semplice sono tre callback:

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

C’è un tranello in cui cadono tutti: un browser non può impostare header nell’handshake di un websocket. Passa quello che serve all’handler nella query, dove lo legge da connection.Request.Header.Query, oppure manda i dati segreti come primo messaggio.

Cosa non puoi fare

Il tuo codice gira su un server condiviso, quindi una parte di C# viene rifiutata prima ancora della compilazione: avviare processi, aprire socket propri, caricare assembly, accedere al file system fuori dal workspace e usare la reflection per aggirare uno di questi limiti.

Tutto il resto c’è, compresa l’intera API dei moduli GenHTTP. Se qualcosa viene rifiutato, vedi quale riga e perché, non solo che non ha funzionato.

Portarla via

Con Scarica come progetto .NET, nell’editor, ti porti a casa tutto: una solution da aprire, avviare con dotnet run e tenere. Ha un solo riferimento a un pacchetto e nessuna traccia di questa piattaforma.

Il tuo snippet diventa il corpo di Program.cs, dentro un host che serve ciò che restituisce. Gli altri file arrivano esattamente come li hai scritti. Workspace e Assets diventano due cartelle accanto al codice, con gli stessi metodi, quindi nel tuo codice non devi cambiare niente.

Meglio saperlo prima di creare qualsiasi cosa qui: quello che scrivi è tuo e te lo porti via intero. Farlo girare su questo server non ti lega a questo server.

Lasciar fare a un agente

C’è un endpoint MCP su /mcp. Collegaci un agente e potrà fare tutto quello che fa l’editor: leggere la guida, leggere una demo per intero, scrivere file, compilarli e fare il deploy. Sotto c’è la stessa API.

Mentre lavora spiega il perché (write_code riceve la specifica e la modifica) e può controllare quello che ha pubblicato: read_logs restituisce le richieste recenti della lambda, cosa ha stampato e lo stack trace di ogni eccezione. È così che un agente scopre che il suo codice funziona, invece di darlo per scontato. Tu vedi le stesse cose nel pannello di controllo.

Scopri di più →