
Web server with built-in support for HTTP/2, HTTP/3 (QUIC), Lua, Teal, Markdown, Pongo2, HyperApp, Amber, Sass(SCSS), GCSS, JSX, TypeScript, Ollama (LLMs), BoltDB (built-in, stores the database in a file, like SQLite), Redis, Valkey, PostgreSQL, SQLite, MariaDB, MySQL, MSSQL, IPv6, rate limiting, graceful shutdown, plugins, users and permissions.
All in one small self-contained executable.
Requires Go 1.26 or later.
go install github.com/xyproto/algernon@latest
Or manually (development version):
git clone https://github.com/xyproto/algernon
cd algernon
go build -mod=vendor
./welcome.sh
See the release page for releases for a variety of platforms and architectures.
See TUTORIAL.md.
The Docker image is less than 12MB and can be tried out (on x86_64) with:
mkdir localhost
echo 'hi!' > localhost/index.md
docker run -it -p4000:4000 -v .:/srv/algernon xyproto/algernon
And then visiting http://localhost:4000 in a browser.
Written in Go. Uses Bolt (built-in), MySQL, PostgreSQL, SQLite or Valkey/Redis (recommended) for the database backend, permissions2 for handling users and permissions, gopher-lua for interpreting and running Lua, optional Teal for type-safe Lua scripting, http2 for serving HTTP/2, quic-go for serving QUIC, gomarkdown/markdown for Markdown rendering, amber for Amber templates, Pongo2 for Pongo2 templates, Sass(SCSS) and GCSS for CSS preprocessing. logrus is used for logging, goja-babel for converting from JSX to JavaScript, tollbooth for rate limiting, pie for plugins and graceful for graceful shutdowns.
.zip or .alg) and be loaded at start.title: Title goes here. This is a subset of MultiMarkdown.-autorefresh is enabled, the browser will automatically refresh pages when the source files are changed. Works for Markdown, Lua error pages and Amber (including Sass, GCSS and data.lua). This only works on Linux and macOS, for now. If listening for changes on too many files, the OS limit for the number of open files may be reached.README.md files locally. Use -m to display it in a browser and only serve it once.help command is available at the Lua REPL, for a quick overview of the available Lua functions.Lua.Code and Lua.Help functions and talk JSON-RPC over stderr+stdin. See pie for more information. Sample plugins for Go and Python are in the plugins directory.gccgo that supports Go 1.26.package.loadlib.When a directory contains an index.jsx or index.tsx file, Algernon serves it as a complete React application. The JSX/TSX is bundled with esbuild, wrapped in an HTML page with the React runtime loaded, and served with the correct content type.
style.css or style.gcss is present in the same directory, it is used. Otherwise, a built-in default stylesheet is applied.The React version defaults to 19. A different major version (if it is available and built-in to Algernon) can be selected by placing a // React: <N> comment at the top of index.jsx or index.tsx:
// React: 19
Lua scripts in the same directory (e.g. login.lua, data.lua) can serve as API endpoints, making it possible to build full-stack applications with JSX for the frontend and Lua for the backend.
When serving index.jsx or index.tsx, the following helper functions are available in the browser:
postForm(url, fields) — POST form data to a Lua/Teal endpoint and return the parsed JSON response as a promise. The fields argument is an object whose keys and values are URL-encoded and sent as application/x-www-form-urlencoded.bufferToBase64URL(buffer) — convert an ArrayBuffer to a base64url-encoded string. Useful for WebAuthn credential responses.base64URLToBuffer(str) — convert a base64url-encoded string to an ArrayBuffer. Useful for WebAuthn challenge and credential ID fields.Example usage in JSX:
postForm("login.lua", { username, password }).then((data) => {
if (data.ok) {
console.log("Logged in");
} else {
console.log(data.error);
}
});
formjson() — Read a JSON request body and return a Lua table with the decoded fields. Useful when the client sends application/json instead of form data.formdata() — Read form-encoded or URL query data and return a Lua table.json(table) — Convert a Lua table to a JSON string.Example Lua endpoint:
content("application/json")
local fields = formdata()
local username = sanhtml(fields["username"] or "")
if username == "" then
print(json({error = "Missing username"}))
return
end
print(json({ok = true}))
Algernon has built-in support for WebAuthn/FIDO2 passwordless authentication. Credentials are stored in the database alongside regular user data. Four Lua/Teal functions are available:
WebAuthnBeginRegister(username) — Begin a registration ceremony. Writes JSON options to the response. Returns true if successful.WebAuthnFinishRegister(username) — Finish registration. Reads the attestation response from the request body and stores the credential. Returns true if successful.WebAuthnBeginLogin(username) — Begin a login ceremony. Writes JSON options to the response. Returns true if successful.WebAuthnFinishLogin(username) — Finish login. Reads the assertion response from the request body. On success, also logs the user in via the standard session mechanism. Returns true if successful.The relying party ID and origin are automatically derived from the request. Example Teal endpoints:
```lua -- webauthn_regist
$ claude mcp add algernon \
-- python -m otcore.mcp_server <graph>