Skip to content

Serve every lambda as a git repository - #108

Merged
Kaliumhexacyanoferrat merged 3 commits into
mainfrom
feature/git
Oct 5, 2026
Merged

Kaliumhexacyanoferrat merged 3 commits into
mainfrom
feature/git

Conversation

@Kaliumhexacyanoferrat

@Kaliumhexacyanoferrat Kaliumhexacyanoferrat commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Every lambda is now a git repository: whoever holds the editor key clones it, works on it with their own tools and coding agent, and pushes - and anybody clones a published source.

git clone https://genhttp.dev/editor/<editor key>/my-app.git     # read and write
git clone https://genhttp.dev/source/my-app.git                  # read only, while published

Runs on GenHTTP.Modules.Git 0.1.1, which includes virtual-git-server#6: it fixes a bug this work found, where a clone made before old commits were deleted could no longer fetch (shallow roots are not allowed to be updated).

How it works

The repository is the lambda's project. It is the export's project, adjusted for a repository: Project.cs is lambda.cs (the body of BuildAsync(), the method the platform runs the snippet in), the other .cs files, assets/, docs/ and tests/ (from .lambda/). Around them sit the platform's files: Program.cs, the .csproj, Platform/, Dockerfile, .gitignore, .dockerignore, AGENTS.md, CLAUDE.md (@AGENTS.md), and LICENSE while the source is published. A clone builds and runs with dotnet run, and a test clones every demo and builds it. AGENTS.md tells an agent how to work in the repository and carries the platform's rules in git's terms: relative paths, never waiting for a task, the database, secrets, docs and tests in proportion, pages meant to be found, no polling, the link back.

History. main is the newest version, every version a commit tagged v{n}, every feature a branch (features.branch, migration V19). The branch name comes from the feature's name in git's characters, or is the name that was pushed; renaming the feature keeps it. The published source serves the same commits, without the features' branches, and their commits aren't fetchable by id either.

Pushing to main. Each commit becomes the next version, with the first line of its message as the change and the rest as the specification (origin git). History is linear only: no force pushes, merge commits or deletion; a version saved meanwhile elsewhere means "fetch first". The newest commit is compiled first, and the push is refused if it doesn't compile, with errors pointing at the right place in the repository, e.g. Project.cs(47,42). -o deploy puts the newest version online. A feature whose branch is now in main counts as merged.

Pushing a branch. A push starts or replaces a feature, based on the newest version among its commits, and puts its preview online, which no visitor sees. -o merge merges it as one version, git push origin branch:main as a version per commit, and deleting the branch deletes the feature. A feature changed in the editor since the client fetched refuses the push rather than losing that change.

Refused, with the reason and the fix: changes to the platform's files, files that have no place in a lambda (a root README.md), executables and links, tags, extra members in class Project, and anything the services refuse a save for (sizes, names, demos). Every push goes through IMetaService and IFeatureService, so their rules hold.

$ git push -o deploy
remote: Saved version 2: Keeps three hundred tasks
remote: Version 2 is online at https://genhttp.dev/lambda/pantry/
$ git push -u origin dark-mode
remote: Started the feature 'dark-mode' from version 2, with a copy of the lambda's data to try it on.
remote: Its preview is online at https://genhttp.dev/features/7918a495…/
remote: To make it the next version: git push origin dark-mode:main (a version for each of its commits), -o merge with its next push (one version), or merge it in the editor.
$ git push
 ! [remote rejected] main -> main ('Program.cs' is the platform's: changing it changes nothing of the lambda, so the change is refused. Undo it (git checkout origin/main -- Program.cs), and commit again.)

The answers never contain the editor key.

Decisions worth a look

All of these are recorded in CLAUDE.md (new section Git):

  • Commits are made when the repository is read, and kept. They are created lazily per lambda, then stored with their files below /data/git/{lambda} and never regenerated from the version, so a newer platform can't rewrite anybody's history. Saving in the editor, the API or MCP is untouched, and a lambda nobody clones costs nothing. Commits for pruned versions are collected; a clone is then shallow from that point on.
  • The platform's files are refused, not ignored. If they were ignored, a commit would claim something the lambda doesn't do. To keep "leave them alone" always correct, they depend on nothing a version decides: the project always references SQLite, EF Core and Evolve, and the snippet is always async. A commit whose lambda.cs didn't change keeps the previous Project.cs, so a class someone formatted their own way survives a save in the editor.
  • One version per commit on main, refusing more commits in one push than the tier keeps versions ("squash them").
  • One history for owner and public. The specification never goes into a commit the platform makes.
  • Previews go online on every branch push, because git sends -o options only with an actual update. A version already pushed is deployed in the editor or through the API, and the push answer says so.
  • Not on the reactor: commits are made on the pool, under a per-lambda semaphore. The module packs fetches and applies pushes while it writes the response, so that response is written on the pool into a pipe (Infrastructure/OffloadedContent). At most two pushes are applied at once, each up to LAMBDA_GIT_MAX_PUSH_BYTES (256 MB).
  • Git is for whoever works with code: the clone popover is in the full view only, and the simple view and /build say nothing about it.

UI

The full view has a Clone popover on the overview (the repository's front page), beside the code, and in a draft, which also shows its branch. Versions pushed with git get a small branch mark. /source gets a GitHub-like Code menu next to the star: clone, then the version as ZIP, then how to run it. On phones the popover keeps inside the screen. /docs gains Working on it with git, and /ship the question Can I push to it with git?. All 12 languages are translated through the usual pipeline, with glossary entries for clone, commit, branch and push.

Overview with the clone popover Code section, dark
A draft and its branch Versions, one pushed with git
/source Code menu /docs, working with git

/source Code menu on a phone, dark

For agents

  • create_lambda and read_lambda answer with gitUrl, and features name their branch.
  • platform_guide has a git section, and the MCP instructions add one line.
  • The REST API has gitPath on the lambda and on a published source, and branch on a feature.
  • The README covers it under Working with git, the new service (Services/Git), the routes and the setting.

Tests

The 41 new tests in tests/.../Git use the real git client, which checks every object it receives (transfer.fsckObjects):

  • cloning, stable commit ids, asset-only commits, features as branches, a base moved forward as a merge commit, pruned versions leaving a shallow history that earlier clones still fetch into;
  • pushes becoming versions to the byte, refusals, -o deploy, branches with previews, -o merge, branch:main, deletion, races with the editor;
  • the published source being read only, carrying no feature commits, getting its LICENSE, and never holding data;
  • every demo's clone building with the .NET SDK, and Project.cs round-tripping for every demo.

The full suite passes against the released 0.1.1: 727 tests. The frontend build and the translations check pass too; the one remark is one that main already has.

Follow-ups

  • The /source code view still shows the export's Project.cs (Build()), while a clone has BuildAsync(). Serving the source pages from the commits would make them identical.
  • LICENSE is in the commits made while the source is published, so commits made before that don't carry it.

🤖 Generated with Claude Code

Kaliumhexacyanoferrat and others added 3 commits October 5, 2026 10:34
A lambda is cloned from /editor/{privateKey}/{name}.git by whoever holds
its editor key, and from /source/{publicKey}.git by anybody while its
source is published. The repository is the project the lambda is exported
as, made for a repository: every version a commit of main tagged v{n},
every feature a branch, AGENTS.md telling an agent how to work in it.

A commit pushed to main becomes the next version - compiled first and
refused when it does not compile - and a branch pushed starts or replaces
a feature with its preview online; -o deploy and -o merge put it online.
Changes to the files the platform puts around the lambda are refused.

Commits are made when the repository is read and kept below
/data/git/{lambda}, so they never change once anybody saw them. Served by
GenHTTP.Modules.Git, the answer written away from the reactor.

The editor offers the clone address in the full view, the /source page in
a Code menu beside the star; docs, README, guide and MCP say how.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…low pruned versions

A branch whose first push was refused after its feature was started -
over the tier's allowance, say - removes that feature again. A version
committed after the ones before it were pruned unread follows the newest
commit made before them, so a clone made earlier fetches it as the
continuation of its history; GenHTTP.Modules.Git 0.1.1 keeps such a clone
complete rather than cutting it off as shallow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Kaliumhexacyanoferrat
Kaliumhexacyanoferrat merged commit 8b0b440 into main Oct 5, 2026
5 of 6 checks passed
@Kaliumhexacyanoferrat
Kaliumhexacyanoferrat deleted the feature/git branch October 5, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant