XUtils

algernon

HTTP/2 web server with built-in support for Lua, Markdown, GCSS and Amber.


Arch Linux
  • pacman -S algernon
Run Algernon in “dev” mode

This enables debug mode, uses the internal Bolt database, uses regular HTTP instead of HTTPS+HTTP/2 and enables caching for all files except: Pongo2, Amber, Lua, Teal, Sass, GCSS, Markdown and JSX.

  • algernon -e

Then try creating an index.lua file with print("Hello, World!") and visit the served web page in a browser.

Enable HTTP/2 in the browser (for older browsers)
  • Chrome: go to chrome://flags/#enable-spdy4, enable, save and restart the browser.
  • Firefox: go to about:config, set network.http.spdy.enabled.http2draft to true. You might need the nightly version of Firefox.
Configure the required ports for local use
  • You may need to change the firewall settings for port 3000, if you wish to use the default port for exploring the samples.
  • For the auto-refresh feature to work, port 5553 must be available (or another host/port of your choosing, if configured otherwise).
Prepare for running the samples
git clone https://github.com/xyproto/algernon
make -C algernon
Launch the “welcome” page
  • Run ./welcome.sh to start serving the “welcome” sample.
  • Visit http://localhost:3000/
Create your own Algernon application, for regular HTTP
  • mkdir mypage
  • cd mypage
  • Create a file named index.lua, with the following contents: print("Hello, Algernon")
  • Start algernon --httponly --autorefresh.
  • Visit http://localhost:3000/.
  • Edit index.lua and refresh the browser to see the new result.
  • If there were errors, the page will automatically refresh when index.lua is changed.
  • Markdown, Pongo2 and Amber pages will also refresh automatically, as long as -autorefresh is used.
Create your own Algernon application, for HTTP/2 + HTTPS
  • mkdir mypage
  • cd mypage
  • Create a file named index.lua, with the following contents: print("Hello, Algernon")
  • Create a self-signed certificate, just for testing:
    • openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3000 -nodes
    • Press return at all the prompts, but enter localhost at Common Name.
    • For production, store the keys in a directory with as strict permissions as possible, then specify them with the --cert and --key flags.
  • Start algernon.
  • Visit https://localhost:3000/.
  • If you have not imported the certificates into the browser, nor used certificates that are signed by trusted certificate authorities, perform the necessary clicks to confirm that you wish to visit this page.
  • Edit index.lua and refresh the browser to see the result (or a Lua error message, if the script had a problem).

There is also a small tutorial.

Using AI / LLMs / Ollama

  • The ollama server must be running locally, or a host:port must be set in the OLLAMA_HOST environment variable.

Example use, using the default tinyllama model (will be downloaded at first use, the size is 637 MiB and it should run anywhere).

lua> ollama()
Autumn leaves, crisp air, poetry flowing - this is what comes to mind when I think of Algernon.

lua> ollama("Write a haiku about software developers")
The software developer,
In silence, tapping at keys,
Creating digital worlds.

Using OllamaClient and the mixtral model (will be downloaded at first use, the size is 26 GiB and it might require quite a bit of RAM and also a fast CPU and/or GPU).

”` lua> oc = OllamaClient(“mixtral”) lua> oc:ask(“Write a quicksort function in OCaml”) Sure! Here’s an implementation of the quicksort algorithm in OCaml:

let rec qsort = function | [] -> [] | pivot :: rest ->

  let smaller, greater = List.partition (fun x -> x < pivot) rest in
  qsort smaller @ [pivot] @ qsort greater

This function takes a list as input and returns a new list with the same elements but sorted in ascending order using the quicksort algorithm. The qsort funct.

Here are some examples of using the qsort function:

qsort [5; 2; 9; 1; 3];;

  • : int list = [1; 2; 3; 5; 9]

qsort [“apple”; “banana”; “cherry”];;

  • : string list = [“apple”; “banana”; “cherry”]
Set
// Get or create a database-backed Set (takes a name, returns a set object)
Set(string) -> userdata

// Add an element to the set
set:add(string)

// Remove an element from the set
set:del(string)

// Check if a set contains a value
// Returns true only if the value exists and there were no errors.
set:has(string) -> bool

// Get all members of the set
set:getall() -> table

// Remove the set itself. Returns true on success.
set:remove() -> bool

// Clear the set
set:clear() -> bool
List
// Get or create a database-backed List (takes a name, returns a list object)
List(string) -> userdata

// Add an element to the list
list:add(string)

// Get all members of the list
list:getall() -> table

// Get the last element of the list
// The returned value can be empty
list:getlast() -> string

// Get the N last elements of the list
list:getlastn(number) -> table

// Remove the list itself. Returns true on success.
list:remove() -> bool

// Clear the list. Returns true on success.
list:clear() -> bool

// Return all list elements (expected to be JSON strings) as a JSON list
list:json() -> string
HashMap
// Get or create a database-backed HashMap (takes a name, returns a hash map object)
HashMap(string) -> userdata

// For a given element id (for instance a user id), set a key
// (for instance "password") and a value.
// Returns true on success.
hash:set(string, string, string) -> bool

// For a given element id (for instance a user id), and a key
// (for instance "password"), return a value.
// Returns a value only if they key was found and if there were no errors.
hash:get(string, string) -> string

// For a given element id (for instance a user id), and a key
// (for instance "password"), check if the key exists in the hash map.
// Returns true only if it exists and there were no errors.
hash:has(string, string) -> bool

// For a given element id (for instance a user id), check if it exists.
// Returns true only if it exists and there were no errors.
hash:exists(string) -> bool

// Get all keys of the hash map
hash:getall() -> table

// Remove a key for an entry in a hash map
// (for instance the email field for a user)
// Returns true on success
hash:delkey(string, string) -> bool

// Remove an element (for instance a user)
// Returns true on success
hash:del(string) -> bool

// Remove the hash map itself. Returns true on success.
hash:remove() -> bool

// Clear the hash map. Returns true on success.
hash:clear() -> bool

Method 1

Follow the guide at certbot.eff.org for the “None of the above” web server, then start algernon with --cert=/etc/letsencrypt/live/myhappydomain.com/cert.pem --key=/etc/letsencrypt/live/myhappydomain.com/privkey.pem where myhappydomain.com is replaced with your own domain name.

First make Algernon serve a directory for the domain, like /srv/myhappydomain.com, then use that as the webroot when configuring certbot with the certbot certonly command.

Remember to set up a cron-job or something similar to run certbot renew every once in a while (every 12 hours is suggested by certbot.eff.org). Also remember to restart the algernon service after updating the certificates.


Articles

  • coming soon...