pyrtan / sourcev0.13.0 · main

pyrtan / deploy

..
openmediavault/No seeds: pyrtan.com is GitHub Pages and a hub list, nothing more2026-10-27
systemd/Initial public release2026-10-25
README.mdNo seeds: pyrtan.com is GitHub Pages and a hub list, nothing more2026-10-27

Running the demo on OpenMediaVault

Two ways. Docker through OMV's Compose plugin is the one that fits the box; the systemd unit is for running it bare on the host.

Replace <nas-ip> below with your NAS's address on the LAN. Both end with the demo page at http://<nas-ip>:8700/ (Docker) or http://<nas-ip>:8701/ (systemd): a sentence box, the answer, the output, and the trace, plus the list of everything the swarm can answer.

Docker, through the Compose plugin

The stack in openmediavault/docker-compose.yml runs four containers: a hub on port 8700, two example services (news: Hacker News and GitHub; weather: Open-Meteo and site status) that register with the hub over the compose network, and mine, which serves a folder of your own trees from a shared folder and reloads them when you edit a file.

  1. Install the plugin, if it is not there: in OMV, *System › Plugins*, install openmediavault-compose (needs omv-extras on OMV 6/7). It installs Docker.
  2. Pick a shared folder for the files. Say the share is appdata on the data disk; on the host that is a path like /srv/dev-disk-by-uuid-…/appdata. Find it under *Storage › Shared Folders*, or run ls /srv over SSH.
  3. Clone the repository there, over SSH on the NAS:

`` cd /srv/dev-disk-by-uuid-…/appdata git clone https://github.com/b-i-t-8/pyrtan.git mkdir -p pyrtan/trees ``

pyrtan/trees is the folder mine watches. Copy an example in to start: cp pyrtan/examples/site-status/site-status.toml pyrtan/trees/.

  1. Add the stack: *Services › Compose › Files › Add*. Name it pyrtan, paste the compose file, and replace both CHANGE-ME paths with the path from step 2 (the checkout at …/appdata/pyrtan, the trees folder at …/appdata/pyrtan/trees). Save.
  2. Up: select the file, press *Up*. The first time it builds the image on the NAS, which takes a minute or two.
  3. Open http://<nas-ip>:8700/. The page should say 4/4 peers up within ten seconds (the hub polls every ten). Try "top 3 stories on hacker news".

To update: git pull in the checkout, then *Down* and *Up* with *Build* ticked (or docker compose -p pyrtan up -d --build over SSH). To check a tree you dropped into trees/ before it is live: docker compose -p pyrtan exec mine pyrtan check /trees. To see gaps the demo has met: http://<nas-ip>:8700/gaps.

The services do not expose their own ports; everything goes through the hub on 8700. As shipped nothing authenticates: this is a demo for your LAN. Before forwarding 8700 anywhere, set a token: uncomment PYRTAN_TOKEN in the compose file's x-pyrtan block (every container reads it, so the swarm shares it), put TLS in front, and open https://pyrtan.com/console/ with the hub's URL and the token; the compose file already passes --cors https://pyrtan.com to the hub. The mine service keeps its gaps in trees/.gaps.jsonl so they survive a restart; the container runs as uid 10001, so chown 10001 trees (or chmod o+w trees) on the NAS, else the gaps stay in memory and /health reports gaps_file_error.

To let this swarm answer for another (a friend's hub, or any hub in the directory), give the hub a public address, with a tunnel that opens no port (cloudflared tunnel --url http://127.0.0.1:8700, or tailscale funnel 8700) or a port forward behind TLS, then set in the compose file's environment PYRTAN_ADVERTISE (that address), PYRTAN_JOIN: default and PYRTAN_LISTED: "true", with PYRTAN_TOKEN set and PYRTAN_EGRESS: public (hubs that follow yours may ask it to run trees, and any would let them point a tree at your LAN). The hub then joins the default network pyrtan.com publishes, follows the first hub in it that answers (from pyrtan.com's hub list, then its snapshot), routes to it, and is listed in the directory every hub gossips; pyrtan follow URL --at <your hub> --token … follows any other. For a network of your own, pyrtan network new home --at <your hub> --token … prints an invite; a friend's hub joins with pyrtan join <invite>, or by adding it to its PYRTAN_JOIN. Following is one-directional trust: your hub routes to the hubs you follow, and hubs that follow yours may ask it. See ../_meta/federation.md.

Your phone is part of it too: open the hub's console on the phone, enter the token, and in the Member tab join; the phone then keeps the hub's blobs and runs the hub's trees when asked. Trees meant for the phones rather than the NAS (they need the internet the NAS is kept from, or the network the person is on) go in a handout directory next to trees, with PYRTAN_HANDOUT: /handout in the compose file.

Trees that read the box (a file node over /var/log, a shell node running df, a serial node on a USB modem) see the container, not the NAS: mount the paths and pass the devices through in the compose file (there are commented lines for it), and the tree's own paths, commands and devices lists decide what it may touch. Trees that drive a browser need Playwright, which the image does not carry; run those from a checkout with uv sync --extra browser.

Without the plugin

Over SSH, from the checkout:

cd /srv/dev-disk-by-uuid-…/appdata/pyrtan
cp deploy/openmediavault/docker-compose.yml .
sed -i "s#/srv/dev-disk-by-uuid-CHANGE-ME/pyrtan#$PWD#g" docker-compose.yml
docker compose up -d --build

One container only

If you want a single process and no swarm:

docker build -t pyrtan:local .
docker run -d --name pyrtan -p 8701:8701 --restart unless-stopped pyrtan:local

It serves the examples at http://<nas-ip>:8701/.

Bare, with systemd

For a host with no Docker. Pyrtan needs Python 3.12+ and OMV 7 ships 3.11, so uv fetches an interpreter. Everything is installed under /opt/pyrtan and run by a system user with no home directory.

curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR=/usr/local/bin sh
sudo env UV_TOOL_DIR=/opt/pyrtan/tools UV_TOOL_BIN_DIR=/opt/pyrtan/bin \
  UV_PYTHON_INSTALL_DIR=/opt/pyrtan/python \
  uv tool install --python 3.12 git+https://github.com/b-i-t-8/pyrtan
sudo useradd --system --no-create-home --shell /usr/sbin/nologin pyrtan
sudo mkdir -p /var/lib/pyrtan/trees
sudo cp -r /path/to/pyrtan/examples/* /var/lib/pyrtan/trees/
sudo chown -R pyrtan:pyrtan /var/lib/pyrtan
sudo cp deploy/systemd/pyrtan-demo.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now pyrtan-demo
curl http://127.0.0.1:8701/health

The unit runs one process holding every tree in /var/lib/pyrtan/trees on port 8701, hardened to read its trees and reach the network and nothing else. Logs: journalctl -u pyrtan-demo -f. To update, run the same uv tool install line with --force, then sudo systemctl restart pyrtan-demo.

What the demo shows