GenHTTP Lambda

Como funciona

Você escreve um trecho de C#. O que ele retorna fica hospedado em um endereço público, com HTTPS, em poucos segundos. Aqui está tudo, na ordem em que você vai precisar.

O que é uma lambda

Uma lambda é um trecho de código que retorna um handler do GenHTTP. A plataforma compila, carrega e monta o que ele retornou no seu próprio endereço. Não tem projeto, nem arquivo de build, nem using. Todos os módulos do GenHTTP já vêm importados.

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

Essa é uma lambda completa. No ar em /lambda/your-key/, ela responde a toda requisição com a palavra hello.

O trecho é feito de instruções, não de uma classe. A última coisa que ele faz é retornar algo que atenda requisições: um handler ou um builder de handler.

Sua primeira lambda

  1. 1
    Clique em Criar minha lambda. Você recebe um endereço público e uma chave de edição. A chave é o único jeito de voltar, então guarde bem. Ninguém consegue recuperar para você.
  2. 2
    Você cai no painel de controle, com um pequeno serviço REST já escrito como primeira versão. É só um ponto de partida.
  3. 3
    Passe a chave de edição para um agente e diga o que criar: ele escreve novas versões via MCP. Ou abra Código e escreva você mesmo: Verificar compila sem salvar nada e mostra o que o compilador achou, com arquivo e linha.
  4. 4
    Clique em Fazer deploy. Pronto, está no ar. Antes disso, nada fica acessível, e cada novo deploy estende o tempo que ela fica no ar.

O painel de controle

O link de edição abre um painel de controle, não uma caixa de texto: a maior parte do código aqui é escrita por agentes, então a primeira coisa na tela é como anda a sua lambda. A barra lateral mostra a lambda (se está no ar, o endereço e um botão quando uma versão mais nova está esperando para ir ao ar) e as seções dela. O que se faz raramente, como mudar o endereço ou excluir a lambda, fica no menu ⋯ ali.

Visão geral
Se está no ar, quantas requisições teve hoje e quantas falharam, a última mudança e quanto espaço ainda sobra.
Mudar
Diga o que deve ficar diferente e o agente deste servidor faz isso enquanto você acompanha. Ele trabalha num rascunho, testa lá e mescla na próxima versão quando funciona. Desligue Colocar no ar quando terminar para testar o rascunho você mesmo antes.
Rascunhos
Mudanças feitas ao lado da lambda: cada uma é testada num endereço próprio e mesclada na próxima versão quando estiver tudo certo. Aberto, um rascunho tem código, dados e log próprios.
Arquivos
Os arquivos de uma versão: o código e os assets, o próprio programa. Um cadeado ou um globo mostra se o público consegue acessar.
Dados
O que a lambda guarda enquanto roda, compartilhado por todas as versões: o workspace. Veja o que tem nele, envie e exclua arquivos, ou desligue.
Versões
O que cada versão mudou, o que foi pedido e a diferença para a anterior. Faça deploy ou volte uma versão por aqui, ou comece um rascunho a partir de qualquer uma delas.
Deploys
O que esteve no ar e quando, e o que tirou do ar.
Métricas
Requisições, falhas, tempos de resposta e os caminhos mais acessados, na última hora ou nas últimas 24 horas.
Logs
As requisições, o que ela imprimiu e o stack trace de qualquer erro, em tempo real.
Código
Para escrever à mão. Verificar compila, Salvar cria uma versão, Fazer deploy coloca no ar. Num rascunho, Salvar mantém a mudança no rascunho e Fazer deploy da prévia coloca no ar no endereço do rascunho. Ctrl-S salva; F12 vai para uma declaração.

Toda seção funciona do mesmo jeito: o título, um ⓘ que explica, as ações à direita e, quando há mais de uma visualização, uma fileira de abas embaixo. No código, as abas são os arquivos.

O tráfego e o log ficam em memória, para acompanhar, não para guardar: reiniciar o servidor zera tudo. As versões e o histórico de deploys ficam salvos.

Explicando o porquê

Uma versão é o código e, se você quiser, duas notas sobre ele: a especificação, o que o usuário quer e por quê, com as palavras dele quando possível, e a mudança, uma linha sobre o que a versão faz. As duas aparecem ao lado do diff no histórico de versões. Assim, o porquê fica junto do quê: para você e para o próximo agente que ler o histórico antes de mudar alguma coisa.

POST /api/v1/lambdas/{editorKey}/versions
{
  "files": [ { "name": "lambda.cs", "code": "..." } ],
  "specification": "Um livro de visitas para as pessoas assinarem; as mensagens não podem sumir num reinício",
  "change": "Guarda as mensagens no workspace para não sumirem num reinício"
}

Os agentes passam os mesmos dois campos para write_code. Em Código, salvar pede a mudança. Os dois são opcionais. Uma especificação longa é cortada em 4.000 caracteres, e uma mudança em 500, em vez de ser recusada. Um rascunho tem os próprios dois campos, e a versão em que ele é mesclado fica com eles.

Mudando com segurança

Uma versão nunca muda depois de salva, e é isso que faz cada uma valer a pena: qualquer uma delas pode ser comparada e colocada de volta no ar exatamente como era. Para mudar uma lambda que as pessoas usam, comece um rascunho.

  1. 1
    Comece em Rascunhos, ou a partir de qualquer versão. Ele é uma cópia do código e dos assets dessa versão, e dos dados da lambda.
  2. 2
    Mude quantas vezes precisar, em Código ou pedindo ao agente. Fazer deploy da prévia coloca no ar num endereço próprio, /features/…/, com a própria cópia dos dados. Os visitantes da lambda não veem nada disso, e nada do que ele grava chega aos dados da lambda.
  3. 3
    Quando estiver tudo certo, Mesclar faz dele a próxima versão, com as notas dele, e coloca no ar na hora, se você quiser. O rascunho some junto, com a prévia e a cópia dos dados.
POST /api/v1/lambdas/{editorKey}/features
{ "name": "Ranking" }

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

Dá para trabalhar em vários rascunhos ao mesmo tempo. Só um baseado na versão mais recente pode ser mesclado, para que uma mesclagem nunca desfaça uma versão salva depois que o rascunho começou. Se outro foi mesclado antes, traga as mudanças dele (ou peça ao agente) e depois baseie o rascunho na versão mais recente. Nada é mesclado sozinho; isso é de propósito.

Mais de um arquivo

Os tipos não precisam ficar embaixo do código que os usa. Em Código, clique em + ao lado dos arquivos: o novo arquivo é compilado junto com o trecho, no mesmo namespace, então não precisa importar nada. Um nome sem extensão é tratado como 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);

Servindo uma página

Existem dois jeitos de servir uma página, e mais um para o que as pessoas enviam junto com ela.

Uma página, escrita no código

Bom para algo pequeno. A página faz parte do trecho.

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

Uma pasta de arquivos de verdade

O ideal para qualquer coisa com CSS e script. Os arquivos são adicionados do mesmo jeito que um arquivo C# e servidos exatamente como foram escritos. Nada compila esses arquivos.

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

Arquivos enviados, dos dados

Para o que as pessoas enviam ou a lambda cria (fotos, documentos), servido junto com o app. Não para as páginas do próprio app: essas ficam numa pasta de arquivos, onde entram nas versões junto com o código que depende delas.

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

Um front-end, passo a passo

O segundo jeito, completo. Toda demo serve a página assim, de uma pasta chamada web. Abra demo-crud para ver uma. As demos são somente leitura; a chave de edição delas é o próprio nome.

  1. 1
    Em Código, clique em + ao lado dos arquivos e digite site/index.html. Um nome com barra coloca o arquivo em uma pasta; um nome com extensão é tratado como o tipo de arquivo que ele indica.
  2. 2
    Adicione site/app.css e site/app.js do mesmo jeito. Sua página se refere a eles pelo nome, como em href="app.css", porque a pasta é a raiz do que é servido, e não parte do endereço.
  3. 3
    Para o que não é texto, como uma imagem ou uma fonte, abra um arquivo em site e use o botão de enviar ao lado dos arquivos: ele vai para a mesma pasta. Não dá para digitar um PNG num editor de texto, então o caminho é esse.
  4. 4
    Em lambda.cs, sirva a pasta:
    return Layout.Create().Add(Assets.App("site"));
  5. 5
    Clique em Fazer deploy. site/index.html responde em /, site/app.css em /app.css, e qualquer endereço que não bate com nenhum arquivo recebe a página. Assim, um front-end com roteamento próprio continua funcionando quando alguém recarrega a página num deep link.
  6. 6
    Adicione uma API ao lado, e a página ganha com quem conversar:
    var api = Inline.Create().Get("notes", () => notes);
    
    return Layout.Create()
                 .Add("api", api)
                 .Add(Assets.App("site"));

Os dois lugares onde ficam os arquivos

Uma lambda guarda arquivos em dois lugares, e o editor mostra cada um separado: Arquivos tem os arquivos de uma versão (o programa), e Dados tem o workspace (o que o programa guarda). A diferença é de quem eles são. Os arquivos de uma versão pertencem a essa versão; os dados pertencem à lambda, e todas as versões usam os mesmos.

Numa versãoNos dados
o que guardao código e os assets: o programa, inclusive o front-endtudo o que a lambda grava ou alguém envia
quando mudanunca: uma mudança é uma nova versãono momento em que algo é gravado
um deploycoloca exatamente esses arquivos no arnunca mexe neles
voltar uma versãotraz os arquivos antigos de voltanenhum efeito: são os mesmos para todas as versões
um rascunhocomeça como uma cópia delestrabalha numa cópia deles
quando somemcom as versões antigas, depois do limitecom a lambda, ou quando você desliga
acessado no código comoAssetsWorkspace

Os dois não podem ser um lugar só. Se fossem, um deploy ou apagaria tudo o que a lambda gravou desde então, ou nada nunca poderia sair dos arquivos que vão com ela. Um jogo com ranking quer a segunda opção; a página que ele serve quer a primeira. Então a página fica na versão, e o ranking nos dados.

Guardando dados

Workspace é um diretório privado que sua lambda pode ler e gravar. É o lugar para tudo o que precisa durar mais que uma requisição, ou que um 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;
             });

Também tem ReadBytes, WriteBytes, Delete, List, CreateFolder e Tree/Files/App para servir o conteúdo. Nada mais no sistema de arquivos é acessível.

WebSockets

Suporte completo, pensado desde o início. A demo demo-game forma pares de jogadores e roda cada partida no servidor. A forma mais simples são três 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);

Uma coisa pega todo mundo: o navegador não consegue definir headers no handshake de um WebSocket. Passe o que o handler precisa na query, que ele lê de connection.Request.Header.Query, ou mande segredos na primeira mensagem.

O que não é permitido

Seu código roda em um servidor compartilhado, então parte do C# é recusada antes de compilar: iniciar processos, abrir seus próprios sockets, carregar assemblies, acessar o sistema de arquivos fora do seu workspace e usar reflection para contornar qualquer uma dessas regras.

Todo o resto está disponível, incluindo a API de módulos do GenHTTP inteira. Se algo for recusado, você fica sabendo a linha e o motivo, e não só que falhou.

Levando seu código embora

Baixar como projeto .NET, no editor, entrega tudo pronto: uma solução que você pode abrir, rodar com dotnet run e guardar. Ela tem uma única referência de pacote e nenhum rastro desta plataforma.

Seu trecho vira o corpo do Program.cs, dentro de um host que serve o que ele retorna. Seus outros arquivos vão exatamente como você escreveu. Workspace e Assets viram duas pastas ao lado do código, com os mesmos métodos, então nada no seu código precisa mudar.

Bom saber antes de criar qualquer coisa aqui: o que você escreve é seu e sai inteiro. Rodar nesta máquina não prende seu código a ela.

Deixando um agente fazer

Existe um endpoint MCP em /mcp. Conecte um agente a ele, e o agente pode fazer tudo o que o editor faz: ler o guia, ler uma demo inteira, escrever arquivos, compilar e fazer deploy. Por baixo, é a mesma API.

O agente explica o porquê enquanto trabalha (write_code recebe a especificação e a mudança) e pode ver o que colocou no ar: read_logs responde com as requisições recentes da lambda, o que ela imprimiu e o stack trace de qualquer exceção. É assim que ele descobre que o código funciona, em vez de supor. Você acompanha tudo isso no painel de controle.

Saiba mais →