Cómo funciona
Escribes un fragmento de C#. Lo que devuelva queda alojado en una dirección pública, con HTTPS, en unos segundos. Aquí tienes todo, en el orden en que te lo vas a encontrar.
Qué es una lambda
Una lambda es un fragmento de código que devuelve un handler de GenHTTP. La plataforma lo compila, lo carga y monta lo que devuelve en tu propia dirección. No hay proyecto, ni archivo de build, ni sentencias using. Todos los módulos de GenHTTP ya vienen importados.
return Content.From(Resource.FromString("hello"));
Eso ya es una lambda completa. Desplegada en /lambda/your-key/, responde a cada petición con la palabra «hello».
El fragmento son sentencias, no una clase. Lo último que hace es devolver algo que pueda atender peticiones: un handler o un builder que cree uno.
Tu primera lambda
- 1Haz clic en Crear mi lambda. Recibes una dirección pública y una clave de edición. La clave es la única forma de volver a entrar, así que guárdala. Nadie puede recuperarla por ti.
- 2Llegas a su centro de control, con un pequeño servicio REST ya escrito como primera versión. Es solo un punto de partida.
- 3Pásale la clave de edición a un agente y dile qué construir: escribe versiones nuevas a través de MCP. O abre Código y escríbelo tú: Comprobar compila sin guardar nada y te dice qué opina el compilador, con archivo y línea.
- 4Haz clic en Desplegar. Ya está en línea. Antes de eso no se puede acceder a nada, y cada vez que vuelves a desplegar alargas el tiempo que sigue en línea.
El centro de control
El enlace de edición abre un centro de control, no un cuadro de texto: aquí casi todo el código lo escriben agentes, así que lo primero que ves es cómo está tu lambda. La barra lateral muestra la lambda (si está en línea, su dirección y un botón cuando hay una versión más nueva esperando para ponerse en línea) y sus secciones. Lo que se hace pocas veces, como cambiar la dirección o eliminarla, está en el menú ⋯ de esa barra.
- Resumen
- Si está en línea, cuántas peticiones tuvo hoy y cuántas fallaron, el último cambio y cuánto espacio le queda.
- Cambiar
- Di qué debería ser distinto y el agente de este servidor lo hace mientras miras. Trabaja en un borrador, lo prueba ahí y lo fusiona en la siguiente versión cuando funciona. Desactiva Ponerlo en línea al terminar para probar tú el borrador antes.
- Borradores
- Cambios en los que se trabaja al lado de la lambda: cada uno se prueba en su propia dirección y se fusiona en la siguiente versión cuando está bien. Al abrirlo, un borrador tiene su propio código, sus datos y sus logs.
- Archivos
- Los archivos de una versión: su código y sus recursos, el programa en sí. Un candado o un globo indica si el público puede acceder a ellos.
- Datos
- Lo que la lambda guarda mientras se ejecuta, compartido por todas las versiones: el workspace. Puedes ver lo que contiene, subir y eliminar archivos, o desactivarlo.
- Versiones
- Qué cambió cada versión, qué se pidió y la diferencia con la anterior. Desde aquí despliegas o vuelves atrás, o empiezas un borrador a partir de cualquiera de ellas.
- Despliegues
- Qué estuvo en línea y cuándo, y qué lo desconectó.
- Estadísticas
- Peticiones, fallos, tiempos de respuesta y las rutas más pedidas, en la última hora o el último día.
- Logs
- Sus peticiones, lo que imprimió y el stack trace de cualquier error, en tiempo real.
- Código
- Para escribirlo a mano. Comprobar compila, Guardar crea una versión y Desplegar la pone en línea. En un borrador, Guardar lo guarda en el borrador y Desplegar la vista previa lo pone en línea en la dirección del borrador.
Ctrl-Sguarda;F12va a una declaración.
Todas las secciones funcionan igual: su título, un ⓘ que la explica, sus acciones a la derecha y, si tiene más de una vista, una fila de pestañas debajo. En el código, las pestañas son sus archivos.
El tráfico y los logs se guardan en memoria, para mirarlos, no para conservarlos: si el servidor se reinicia, empiezan de cero. Las versiones y el historial de despliegues sí se guardan.
Explicar el porqué
Una versión es el código y, si quieres, dos notas sobre él: la especificación, qué quiere el usuario y por qué, con sus palabras si se puede, y el cambio, una línea sobre lo que hace la versión. Se muestran junto al diff en el historial de versiones, así que el porqué se queda junto al qué: para ti y para el próximo agente que lea el historial antes de tocar nada.
POST /api/v1/lambdas/{editorKey}/versions { "files": [ { "name": "lambda.cs", "code": "..." } ], "specification": "Un libro de visitas que la gente pueda firmar; las entradas tienen que sobrevivir a un reinicio", "change": "Guarda las entradas en el workspace para que sobrevivan a un reinicio" }
Los agentes pasan esos mismos dos campos a write_code. En Código, al guardar se te pide el cambio. Los dos son opcionales; una especificación larga se recorta a 4000 caracteres y un cambio a 500, en vez de rechazarse. Un borrador tiene sus propios dos campos, y la versión en la que se fusiona se queda con ellos.
Cambiarla sin riesgo
Una versión nunca cambia una vez guardada, y eso es lo que hace que valga la pena conservarlas todas: cualquiera de ellas se puede comparar y volver a poner en línea exactamente como estaba. Para cambiar una lambda que la gente usa, empieza un borrador.
- 1Empiézalo en Borradores o desde cualquier versión. Es una copia del código y los recursos de esa versión, y de los datos de la lambda.
- 2Cámbialo tantas veces como haga falta, en Código o pidiéndoselo al agente. Desplegar la vista previa lo pone en línea en su propia dirección,
/features/…/, con su propia copia de los datos. Los visitantes de la lambda no ven nada de esto, y nada de lo que escribe llega a los datos de la lambda. - 3Cuando esté bien, Fusionar lo convierte en la siguiente versión, con sus notas, y lo pone en línea enseguida si quieres. El borrador desaparece con ello, junto con su vista previa y su copia de los datos.
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 }
Se puede trabajar en varios borradores a la vez. Solo se puede fusionar uno basado en la versión más nueva, para que una fusión nunca deshaga una versión guardada después de que empezara el borrador. Si antes se fusionó otro, lleva sus cambios a este (o pídeselo al agente) y después basa el borrador en la versión más nueva. Nada se fusiona solo; es a propósito.
Más de un archivo
Los tipos no tienen por qué estar debajo del código que los usa. En Código, haz clic en + junto a los archivos: el archivo nuevo se compila junto al fragmento, en el mismo namespace, así que no hay que importar nada para usarlo. Un nombre sin extensión se toma como C#.
var shelf = new Shelf(); return Inline.Create() .Get(() => shelf.All()) .Post((Book book) => shelf.Add(book));
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 una página
Hay dos formas de servir una página, y una más para lo que la gente sube junto a ella.
Una página escrita en el código
Sirve para algo pequeño. La página forma parte del fragmento.
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 carpeta de archivos reales
Lo que necesitas para cualquier cosa con hoja de estilos y script. Los archivos se añaden igual que un archivo C# y se sirven tal como están escritos. Nada los compila.
return Layout.Create() .Add("api", api) .Add(Assets.App("site"));
Archivos subidos, desde los datos
Para lo que la gente sube o la lambda crea (fotos, documentos), servido junto a la app. No para las páginas de la propia app: esas van en una carpeta de archivos, donde se versionan junto con el código que las necesita.
return Layout.Create() .Add("api", api) .Add("uploads", Workspace.Files("uploads")) .Add(Assets.App("site"));
Un frontend, paso a paso
La segunda forma, completa. Todas las demos sirven su página así, desde una carpeta llamada web: abre demo-crud para ver una. Las demos son de solo lectura; su clave de edición es su nombre.
- 1En Código, haz clic en + junto a los archivos y escribe
site/index.html. Un nombre con barra pone el archivo en una carpeta; un nombre con extensión se toma como el tipo de archivo que indica. - 2Añade
site/app.cssysite/app.jsde la misma forma. Tu página los enlaza por su nombre, como enhref="app.css", porque la carpeta es la raíz de lo que se sirve, no parte de la dirección. - 3Para todo lo que no sea texto, como una imagen o una fuente, abre un archivo de
sitey usa el botón de subir que está junto a los archivos: va a parar a la misma carpeta. Un PNG no se puede escribir en un editor de texto, así que esa es la forma de meterlo. - 4En
lambda.cs, sirve la carpeta:return Layout.Create().Add(Assets.App("site"));
- 5Haz clic en Desplegar.
site/index.htmlresponde en/,site/app.cssen/app.css, y cualquier dirección que no coincida con un archivo se responde con la página. Así, un frontend con su propio enrutamiento sigue funcionando cuando alguien recarga en un enlace profundo. - 6Añade una API al lado y la página tendrá con quién hablar:
var api = Inline.Create().Get("notes", () => notes); return Layout.Create() .Add("api", api) .Add(Assets.App("site"));
Los dos lugares donde viven los archivos
Una lambda guarda archivos en dos lugares, y el editor los muestra por separado: Archivos contiene los archivos de una versión (el programa) y Datos contiene el workspace (lo que el programa guarda). La diferencia está en de quién son. Los archivos de una versión pertenecen a esa versión; los datos pertenecen a la lambda, y todas las versiones los comparten.
| En una versión | En los datos | |
|---|---|---|
| qué contiene | el código y los recursos: el programa, frontend incluido | lo que escribe la lambda o sube alguien |
| cuándo cambia | nunca: un cambio es una versión nueva | en cuanto se escribe algo en ellos |
| un despliegue | pone en línea exactamente estos archivos | nunca los toca |
| volver atrás | trae de vuelta los archivos anteriores | no les afecta: todas las versiones los comparten |
| un borrador | empieza como una copia de ellos | trabaja con una copia de ellos |
| cuándo desaparece | con las versiones antiguas, al pasar el límite | con la lambda, o cuando desactivas el workspace |
| cómo se accede desde el código | Assets | Workspace |
No pueden estar en un solo lugar. Si lo estuvieran, un despliegue borraría todo lo que tu lambda haya escrito desde entonces, o nunca se podría quitar nada de lo que publica. Un juego con ranking quiere lo segundo; la página que sirve, lo primero. Por eso la página va en la versión y el ranking, en los datos.
Guardar datos
Workspace es un directorio privado que tu lambda puede leer y escribir. Es el lugar para todo lo que tenga que sobrevivir a una petición o a un despliegue.
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; });
También tienes ReadBytes, WriteBytes, Delete, List, CreateFolder y Tree/Files/App para servirlo. No se puede acceder a nada más del sistema de archivos.
WebSockets
Funcionan, y no son un añadido de última hora. La demo demo-game empareja jugadores y ejecuta cada partida en el servidor. La forma más simple son tres 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);
Hay un detalle que sorprende a todo el mundo: un navegador no puede poner cabeceras en el handshake de un websocket. Pasa lo que necesite el handler en la query, donde lo lee desde connection.Request.Header.Query, o envía los secretos en el primer mensaje.
Lo que no te deja hacer
Tu código se ejecuta en un servidor compartido, así que parte de C# se rechaza antes de compilar: iniciar procesos, abrir tus propios sockets, cargar ensamblados, acceder al sistema de archivos fuera de tu workspace y usar reflexión para saltarte cualquiera de esas reglas.
Todo lo demás está disponible, incluida toda la API de módulos de GenHTTP. Si algo se rechaza, te decimos en qué línea y por qué, no solo que falló.
Llévate tu código
En el editor, Descargar como proyecto .NET te da todo listo para llevártelo: una solución que puedes abrir, ejecutar con dotnet run y conservar. Tiene una sola referencia a un paquete y ningún rastro de esta plataforma.
Tu fragmento pasa a ser el cuerpo de Program.cs, dentro de un host que sirve lo que devuelve. Tus otros archivos llegan exactamente como los escribiste. Workspace y Assets se convierten en dos carpetas junto al código, con los mismos métodos, así que no tienes que cambiar nada de tu código.
Conviene saberlo antes de crear nada aquí: lo que escribes es tuyo y te lo llevas entero. Ejecutarlo en esta máquina no te ata a esta máquina.
Que lo haga un agente
Hay un endpoint MCP en /mcp. Conecta un agente y podrá hacer todo lo que hace el editor: leer la guía, leer una demo completa, escribir archivos, compilarlos y desplegar. Por debajo es la misma API.
El agente explica el porqué sobre la marcha (write_code recibe la especificación y el cambio) y puede revisar lo que desplegó: read_logs devuelve las peticiones recientes de la lambda, lo que imprimió y el stack trace de cualquier excepción. Así un agente comprueba que su código funciona en vez de suponerlo. Tú ves lo mismo en el centro de control.