GenHTTP Pages

Reference

Kaliumhexacyanoferrat/genhttp-pages@v1 takes every input of actions/deploy-pages, so a workflow keeps working as it is, and answers with the same page_url.

How activation works

Activating a repository makes a lambda (or takes yours, by its editor key), gives you the link to its editor and an activation code, and remembers the repository by name. The first run that presents the code with a GitHub OIDC token of that repository binds the code to the repository's id, which outlives renames. From then on the action asks GitHub for a token of the run, and pages.genhttp.run hands it the editor key of the lambda to publish to.

Inputs

The inputs of deploy-pages

InputDefaultWhat it does here
artifact_namegithub-pagesThe artifact to publish, as actions/upload-pages-artifact made it in an earlier job of the run.
timeout600000Milliseconds to wait for the upload.
previewfalsePublishes to a feature of the lambda instead, with an address of its own, named after the pull request. Delete it in the editor when you are done.
tokengithub.tokenUsed only to check out a branch.
error_count, reporting_intervalAccepted and not used: a lambda answers at once, so there is nothing to poll.

Its own inputs

InputDefaultWhat it does
activationThe code the assistant gave for this repository. Needed for the first run; after that, a run is recognised by its GitHub OIDC token. With several sites in one repository, keep each code to say which lambda it goes to.
keyInstead of an activation: the editor key of the lambda, from a secret. Left out, the GENHTTP_KEY environment variable is read.
pathPublish this folder rather than an artifact.
branchPublish this branch, as GitHub's “Deploy from a branch” does.
folder/With branch: the folder to publish, / or /docs.
jekyllautoWith branch: build with Jekyll unless there is a .nojekyll (auto), always (true) or never (false).
include_hidden_filesfalseWith path or branch: publish files starting with a dot, such as .well-known/. An artifact holds what its upload included.
base_pathautoWhere else the site answers. auto serves a project site at / and at /repository/, where it was built for; none serves the root only.
backendA folder of C# published beside the site, see below.
serverhttps://genhttp.devAnother installation of GenHTTP Lambda.
brokerhttps://pages.genhttp.runWhere activations are kept and tokens exchanged for the key.

Outputs

OutputWhat it holds
page_urlThe address the site answers at, such as https://my-site.genhttp.run/.
versionThe version of the lambda that is online.

Configuring the build

actions/configure-pages keeps working while GitHub Pages is enabled for the repository. To switch Pages off, use its drop-in, which answers with the lambda's address instead, without a base path:

- id: pages
  uses: Kaliumhexacyanoferrat/genhttp-pages/configure@v1
  with:
    activation: gp-…
# ${{ steps.pages.outputs.base_url }} is https://my-site.genhttp.run

It does not rewrite the configuration of a static site generator as configure-pages does with static_site_generator; a site at the root of its address needs no base path.

How the site is served

The way GitHub Pages serves it:

A backend beside the site

A static site sometimes needs code on the server: a form that stores what it is sent, a counter, a proxy that keeps an API key from the browser, something live. A lambda runs it beside the site, on the same address, so the pages call it with fetch('api/…').

Ask for it

GenHTTP Lambda is built to be changed by agents. Open the lambda's editor, go to Change and say what you need: "Add an API at api/signups that stores the e-mail addresses my newsletter form posts." Its agent writes the routes, switches on a database if they need one, tries them in a draft and puts them online.

Or use your own agent - Claude Code, Codex, Cursor - through MCP, and give it the editor link:

claude mcp add --transport http genhttp https://genhttp.dev/mcp

Why a push keeps it

A push replaces the site and nothing else: resources/site/, resources/blobs/, resources/pages.json, pages/PagesSite.cs and docs/pages.md. Everything else - the routes, resources/migrations/, the documentation - stays as the agent or you left it, and the database is never touched by a deployment. docs/pages.md says so to the next agent that reads the lambda.

The routes go in front of the site in lambda.cs, which keeps serving the site last. A lambda.cs without PagesSite.Create() is replaced by the next push.

return Layout.Create()
             .Add("api", api)
             .Add(PagesSite.Create());

A form or a button that uses the API belongs to the site, so it is changed in the repository; what the agent changes in resources/site/ is gone with the next push.

Or keep it in the repository

To have the C# in the repository instead, name its folder in backend: its lambda.cs replaces the lambda's, the other files go to backend/, and its resources/ - migrations, say - become the lambda's, all replaced on every push.

This site does it: its backend activates repositories and exchanges the tokens of their runs, with a database of activations.

Compared with GitHub Pages

GitHub PagesGenHTTP Pages
Static files, Jekyll, your buildYesYes, from the same artifact
Code on the serverNoC# routes beside the site: a form, an API, a proxy that keeps a key from the browser
A databaseNoSQLite, one per site
Live updates, multiplayerNoWebsockets and server-sent events
Secrets while it runsNo, only during the buildRead by your code, never shown again
Traffic statisticsOnly with a third-party scriptCounted on the server: no script, no cookie
Request log and errorsNoEvery request, what your code printed, errors with stack traces
Going backRun an old workflow againEvery deployment is a version, one click to put it back
Previews of pull requestsNot available to the publicpreview: true
AccountYour GitHub accountNone; the key is the access
Your own domainFreeWith the premium tier
Size1 GB32 MB on the free tier
How long it staysAs long as the repositoryAs long as it is used, on the free tier

What to know when you switch

The address
Your site answers at https://name.genhttp.run/. A project site built for owner.github.io/repository/ still works: it is served at the root and below /repository/.
How long it stays online
A free lambda stays online as long as it is used. One that nobody visits and nobody deploys to for a month goes offline, and is removed two months after that. The premium tier keeps it online.
Size
A free lambda holds 32 MB per deployment, against GitHub's 1 GB. The action says so before it uploads, and how large the site is.
Your own domain
Comes with the premium tier, set in the lambda's editor. A CNAME file is published like any other file.
Permissions
None needed: pages: write, id-token: write and the github-pages environment do no harm and can stay.
History
Every deployment is a version you can read, compare and put back online in the lambda's editor.