πβ‘π Viperlith: the Hypermedia-Based Monolith for Python built with Datastar
Python
1
235 commits
updated Sep 16, 2026
A demo application to illustrate how Datastar works.
datastar.js is a lightweight (11kB) hypermedia framework similar to HTMX (but not).

A Discord-like chat sample application is currently implemented with real-time chat rooms, creating/removing channels and setting nicknames.
π΄ Live demo: https://starcord.fastserial.com
This benchmark measures requests per second (RPS) under different workloads in the context of a hypermedia application. To serve a read request, the server must issue 3 separate DB queries touching all 4 tables. The queries perform several joins and require at least 500 completely (pseudo-)random pages from the database. This data is then used to render a ~190 kB HTML template (using Jinja), which must then be Brotli-compressed and returned to the client.


Ths exact application was also built using FastAPI and Postgres π. Check it out here.
To see all benchmark results or to replicate them yourself, see the benchmark respository.
Viperlith consists of the following components:
sqlite3 module.CompressionConfig to minimize bandwidth for streaming HTML over the wire.multiprocessing.Queue. Used to queue commands to the single-writer process with exclusive write access to SQLite.Hypermedia-driven applications (HDA), unlike SPA frameworks (e.g. React, Vue, Angular), eliminate state and logic on the client side. Instead, interactive logic is moved to the backend and page updates are streamed to the client as server-rendered templates over a Server-Sent Events stream and morphed into the local DOM. This is a bit like streaming a movie, except the movie is HTML and you receive new content from updates and UI interactions. The client's browser is effectively reduced to a rendering viewport only capable of displaying raw HTML.
See docs/HTML_STREAMING.md for an introduction to the concept of HTML streaming.
Also see: the Tao of Datastar and Datastar: Why another framework?.
.env.example and name it .envcp .env.example .env
Generate a SESSION_KEY with openssl rand -hex 16 and change configurations if necessary.
/var/libsudo mkdir -p /var/lib/viperlith
sudo chown $USER /var/lib/viperlith
sudo chmod +x ./launch.sh
./launch.sh
This will launch the application in debug mode with hot reload.
Alternatively, it is possible to launch in production mode with multiple workers:
DEBUG=0 ./launch.sh
Before running compose, make sure environment variables such as SESSION_KEY are present in the shell environment by configuring them in CI. These will take precedence before the defaults inside .env.example.
docker compose --env-file .env.example up --build
By default, the application will bind to localhost on 127.0.0.1:8000. The application will only be accessible from the internet if you have a reverse proxy with an appropriate proxy_pass pointed there. If you have a different setup, edit the compose config as needed.
See .gitea/workflows/deploy.yml for an actual CI deploy job example.
Block device readahead makes it so when any page is read from disk, the next N sectors are also read automatically (256 by default). This triggers excess page faults and can significantly worsen performance for larger-than-RAM databases.
Readahead can be disabled by setting it to zero like so:
sudo blockdev --setra 0 /dev/sda1
Unfortunately, this setting will not persist across reboots. To make the it persist, a series of steps are required:
lsblk:$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 75G 0 disk
|-sda1 8:1 0 74.9G 0 part /
|-sda14 8:14 0 3M 0 part
`-sda15 8:15 0 124M 0 part /boot/efi
Pick your primary storage disk, in this case sda1.
sudo nano /etc/systemd/system/sda1-readahead-disable.service
sda1 with your block device)[Unit]
Description=Disable readahead on sda1
Requires=dev-sda1.device
After=dev-sda1.device
[Service]
Type=oneshot
ExecStart=/usr/sbin/blockdev --setra 0 /dev/sda1
[Install]
WantedBy=multi-user.target
Save and quit the file.
sudo systemctl daemon-reload
sudo systemctl enable --now sda1-readahead-disable.service
sudo blockdev --getra /dev/sda1
You should see:
0
Inspired by Anders Murphy's Hyperlith.
235 commits
Python
58.0%
HTML
23.8%
Shell
8.5%
Lua
6.0%
Dockerfile
2.9%
πβ‘π Viperlith: the Hypermedia-Based Monolith for Python built with Datastar
Python
1
235 commits
updated Sep 16, 2026
A demo application to illustrate how Datastar works.
datastar.js is a lightweight (11kB) hypermedia framework similar to HTMX (but not).

A Discord-like chat sample application is currently implemented with real-time chat rooms, creating/removing channels and setting nicknames.
π΄ Live demo: https://starcord.fastserial.com
This benchmark measures requests per second (RPS) under different workloads in the context of a hypermedia application. To serve a read request, the server must issue 3 separate DB queries touching all 4 tables. The queries perform several joins and require at least 500 completely (pseudo-)random pages from the database. This data is then used to render a ~190 kB HTML template (using Jinja), which must then be Brotli-compressed and returned to the client.


Ths exact application was also built using FastAPI and Postgres π. Check it out here.
To see all benchmark results or to replicate them yourself, see the benchmark respository.
Viperlith consists of the following components:
sqlite3 module.CompressionConfig to minimize bandwidth for streaming HTML over the wire.multiprocessing.Queue. Used to queue commands to the single-writer process with exclusive write access to SQLite.Hypermedia-driven applications (HDA), unlike SPA frameworks (e.g. React, Vue, Angular), eliminate state and logic on the client side. Instead, interactive logic is moved to the backend and page updates are streamed to the client as server-rendered templates over a Server-Sent Events stream and morphed into the local DOM. This is a bit like streaming a movie, except the movie is HTML and you receive new content from updates and UI interactions. The client's browser is effectively reduced to a rendering viewport only capable of displaying raw HTML.
See docs/HTML_STREAMING.md for an introduction to the concept of HTML streaming.
Also see: the Tao of Datastar and Datastar: Why another framework?.
.env.example and name it .envcp .env.example .env
Generate a SESSION_KEY with openssl rand -hex 16 and change configurations if necessary.
/var/libsudo mkdir -p /var/lib/viperlith
sudo chown $USER /var/lib/viperlith
sudo chmod +x ./launch.sh
./launch.sh
This will launch the application in debug mode with hot reload.
Alternatively, it is possible to launch in production mode with multiple workers:
DEBUG=0 ./launch.sh
Before running compose, make sure environment variables such as SESSION_KEY are present in the shell environment by configuring them in CI. These will take precedence before the defaults inside .env.example.
docker compose --env-file .env.example up --build
By default, the application will bind to localhost on 127.0.0.1:8000. The application will only be accessible from the internet if you have a reverse proxy with an appropriate proxy_pass pointed there. If you have a different setup, edit the compose config as needed.
See .gitea/workflows/deploy.yml for an actual CI deploy job example.
Block device readahead makes it so when any page is read from disk, the next N sectors are also read automatically (256 by default). This triggers excess page faults and can significantly worsen performance for larger-than-RAM databases.
Readahead can be disabled by setting it to zero like so:
sudo blockdev --setra 0 /dev/sda1
Unfortunately, this setting will not persist across reboots. To make the it persist, a series of steps are required:
lsblk:$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 75G 0 disk
|-sda1 8:1 0 74.9G 0 part /
|-sda14 8:14 0 3M 0 part
`-sda15 8:15 0 124M 0 part /boot/efi
Pick your primary storage disk, in this case sda1.
sudo nano /etc/systemd/system/sda1-readahead-disable.service
sda1 with your block device)[Unit]
Description=Disable readahead on sda1
Requires=dev-sda1.device
After=dev-sda1.device
[Service]
Type=oneshot
ExecStart=/usr/sbin/blockdev --setra 0 /dev/sda1
[Install]
WantedBy=multi-user.target
Save and quit the file.
sudo systemctl daemon-reload
sudo systemctl enable --now sda1-readahead-disable.service
sudo blockdev --getra /dev/sda1
You should see:
0
Inspired by Anders Murphy's Hyperlith.
235 commits
Python
58.0%
HTML
23.8%
Shell
8.5%
Lua
6.0%
Dockerfile
2.9%