Skip to main content

Tyrtulia TUI

Terminal client for Tyrtulia, the headless forum for small multi-user servers (tildes, pubnixes, club servers). Built with Textual.

The server, its API and how to run an instance are in the Tyrtulia repository (also on PyPI as tyrtulia).

  • On the server itself it talks to Tyrtulia through its Unix socket: the operating system tells the forum who you are, so there is nothing to log in to.
  • From anywhere else it logs in with your SSH key (a signed challenge) and keeps the session token in a private file. Before signing, it checks that the login message names the server it is talking to, so a malicious server cannot reuse your signature on another instance.
  • It only knows GET / and follows the links the forum returns: an action appears in the footer only when the forum offers it to you (reply, edit, pin, hide...). It never builds, reads or changes a URL of the API: page sizes and positions are variables of the forum's templated links, and orders, views and filters are links of their own.
  • Notifications arrive in real time over Server-Sent Events.
  • It reads several forums at once: their threads and notifications come together in one list, and search results side by side. You write only where you are registered (where your SSH key is, or through the socket); elsewhere you read as a guest.

A thread in Tyrtulia TUI

One forum:

The list of threads of one forum

Three forums at once, the last one read as a guest:

The threads of three forums in one list

Requirements

  • Python 3.14.
  • OpenSSH client (ssh-keygen), to sign the login challenge when you connect from outside the server.
  • A Tyrtulia instance, 1.5 or later for everything. Older forums still work, with less: no tree or levels view (threads show in order), no order by activity, no unread-only notifications and no search in titles; the client never rebuilds those by hand. 1.2 adds login messages bound to the instance. To set one up, follow its getting started guide for administrators.

Installation

With uv:

uv tool install tyrtulia-tui

With pip:

python3.14 -m venv ~/.local/share/tyrtulia-tui
~/.local/share/tyrtulia-tui/bin/pip install tyrtulia-tui
ln -s ~/.local/share/tyrtulia-tui/bin/tyrtulia-tui ~/.local/bin/tyrtulia-tui

Administrators can install it once for every user of the server, for example in /opt/tyrtulia-tui with a link in /usr/local/bin.

Usage

On the server, just run it. It finds the socket in /run/tyrtulia/tyrtulia.sock (or /var/run/tyrtulia/tyrtulia.sock on BSD):

tyrtulia-tui

From another machine, point it to the forum and give your username and SSH key. Your first SSH key is registered from the server itself (profile, a) or by the operator:

tyrtulia-tui --url https://forum.example.org --user alice --key ~/.ssh/id_ed25519

The key can also be the .pub file, to sign with ssh-agent. Without --user, you read as a guest and can log in later with l.

Several forums

Repeat --url (or --socket) to read several forums together:

tyrtulia-tui --url https://forum.example.org --url https://tilde.example --user alice
  • The list of threads mixes every forum into one, with a column that says where each one comes from: pinned threads first, then the newest by creation (the default) or by the last post (s). Each forum keeps its own order, and the next thread is always the newest of what every forum has left, asking a forum for its next page only when it runs out; so the order holds across pages, even when one forum is much busier than another. Forums older than 1.5 do not order by activity and are left out of that order, with a notice. Notifications are mixed the same way, newest first.
  • Search results are interleaved: the best match of each forum, then the second of each, and so on, since each forum scores against its own posts and the scores do not compare. The screen says so.
  • At start it logs in to every forum with the same username and key. Where your key is not registered, the login fails and you read that forum as a guest: no reply, answer or new thread there. l logs in to one of them later.
  • When an action needs one forum (n new thread, p profile, @ user, l log in, o log out) and more than one fits, it asks which. n only offers the forums where you can write.
  • A forum that cannot be reached at start is left out, with a message.
Option Meaning
--socket PATH Unix socket of a server. Repeatable.
--url URL URL of a server, for remote access. Repeatable.
--user NAME Username for the SSH key login.
--key FILE SSH private key (or its .pub with the agent). Defaults to ~/.ssh/id_ed25519, id_ecdsa or id_rsa.
--editor COMMAND Your editor for Ctrl+O (see Writing in your own editor). Defaults to $VISUAL, $EDITOR or vi.
--config FILE Configuration file.

Configuration

Every key is optional; command line options win:

# ~/.config/tyrtulia-tui/config.toml
url = "https://forum.example.org"
username = "alice"
key = "~/.ssh/id_ed25519"
editor = "nvim"            # for Ctrl+O; also "emacs -nw", "nano", "code --wait"...
# socket = "/run/tyrtulia/tyrtulia.sock"

For several forums, add a [[forum]] table for each one. The top-level username and key apply to every forum that does not set its own:

username = "alice"
key = "~/.ssh/id_ed25519"

[[forum]]
url = "https://forum.example.org"

[[forum]]
url = "https://tilde.example"
username = "alice_t"       # another name on this one

[[forum]]
socket = "/run/tyrtulia/tyrtulia.sock"

Without --url or --socket on the command line, it opens the forums of the configuration and, if there is one, the local socket of the server you are on. --url and --socket replace them all.

Session tokens are kept in ~/.local/state/tyrtulia-tui/tokens.json (mode 0600), and the last post you read in each thread in read.json next to it, to show how many posts are new. Both follow XDG_STATE_HOME.

Keys

Where Key Action
Everywhere q Quit
i Notifications
/ Search (words, "a phrase", OR, NOT, prefix*)
p Your profile and SSH keys
@ Open a user by name
l / o Log in / log out (remote access)
Lists ↑ ↓ (k j) Move up and down (threads, notifications, search results, SSH keys)
Threads Enter Open
n New thread (asks in which forum, if you can write in several)
w Latest or watching
s Order: by creation (default) or by the last post
r Refresh
] / [ Next / previous page
Thread ↑ ↓ (k j) Move between posts (the selected one has an orange bar)
u Go to the post the selected one answers; in the levels view, from the post on top, go up one level
w Open the profile of the selected post's author
v View: tree, levels or linear
Enter Levels view: open the selected post with its answers (or click ▸ N replies)
r Reply to the thread
a Answer the selected post
e / d Edit / delete the selected post
s Subscription: watching, normal, muted
t Change the title
f Report the selected post
h, P, C Moderators: hide or unhide, pin, close
Esc Back
Editor Ctrl+S Send
Ctrl+O Continue in your own editor
F7 Select all
Esc Cancel
Notifications Enter Open what it points to: the post, or for reports the post, thread or user
a Mark all as read
u Only unread, or all
Search Ctrl+T Only in titles, or in titles and posts
Profile e Edit your about
a / x Add an SSH key from a .pub file / delete the selected key
User f Report the user
B, R Moderators: ban or unban, make read-only or remove it (a reason is asked and is public in the moderation log)
Esc Back

Only the actions the forum allows you appear in the footer.

Thread views

v cycles through three ways of reading a thread; the title shows the current one.

  • Tree: the whole conversation, each answer indented under the post it answers.
  • Levels: one level at a time, for long threads such as wiki pages. It starts with the posts that answer no other post. Enter (or a click on ▸ N replies) opens a post with its direct answers, and so on; u on the post on top goes back up. The title shows where you are, such as levels: #2 > #3. Only the level on screen is fetched, so it works on threads of any size.
  • Linear: every post in the order it was written.

Tree and levels need Tyrtulia 1.5: on older forums the thread shows in order, and the title says why. A thread shows up to 500 posts; m asks for more.

Writing in your own editor

You can write posts in your usual terminal editor instead of the built-in one:

  1. Open the editor of the TUI as usual: n for a new thread, r to reply, a to answer a post or e to edit one. For a new thread, type the title first.
  2. Press Ctrl+O. The TUI steps aside and your editor opens with the draft.
  3. Write, save and quit (:wq in vim, C-x C-s and C-x C-c in Emacs, C-x C-s and C-x # with emacsclient, Ctrl+O and Ctrl+X in nano).
  4. You are back in the TUI with the text. Check it and press Ctrl+S to send, or Ctrl+O again to keep editing.

The editor is, in this order: --editor on the command line, editor in the configuration, $VISUAL, $EDITOR and finally vi. Arguments are allowed:

# ~/.config/tyrtulia-tui/config.toml
editor = "nvim"
# editor = "emacs -nw"
# editor = "emacsclient"     # already inside Emacs (vterm, eat, shell): finish with C-x #
# editor = "nano"
# editor = "hx"
# editor = "code --wait"     # graphical editors need their "wait" option
tyrtulia-tui --editor "emacs -nw"
  • The draft is a temporary .md file only you can read, removed when you come back.
  • If the editor exits with an error or cannot be found, the text in the TUI stays as it was and the reason is shown.
  • The rules of the forum (lengths, too many mentions...) are checked when you send, as with the built-in editor; if the forum rejects the post, the editor opens again with your text.
  • Notifications keep arriving while you write; you see them when you come back.

Development

Everything runs in Docker:

docker compose run --rm dev sh -c 'ruff format src tests && ruff check src tests && mypy && pytest'

The tests drive the client and the interface (Textual's Pilot) against a real Tyrtulia instance running in-process, plus a real tyrtulia serve with Unix users inside the container. The server is the one locked from PyPI (1.4 for now, which exercises the degraded paths; tests that need 1.5 are skipped); to test 1.5 before it is published, install its wheel over it:

docker compose run --rm -v /path/to/wheels:/wheels dev sh -c 'uv pip install --python /opt/venv/bin/python /wheels/tyrtulia-*.whl && pytest'

tests/test_no_paths.py fails if the client writes a path of the API or a query parameter by hand: the contract is the link relations and the representations, so URLs are never built, read or changed.

e2e/run.sh runs the end-to-end test: on Debian with systemd, ten people use the real TUI in tmux against one Tyrtulia 1.5 instance installed from PyPI (until 1.5.0 is published, pass a wheel: TYRTULIA_WHEEL=/path/to/tyrtulia-1.5.0-py3-none-any.whl e2e/run.sh). Five Unix users go through the socket and five SSH key accounts log in over HTTP; they post, answer, mention, edit, moderate, report and search, and see each other's notifications in real time.

Contributing

Contributions are welcome! Please see the contribution guidelines for instructions on how to submit issues or pull requests. Changes to the forum itself (the API, the server) belong in the Tyrtulia repository.

License

Tyrtulia TUI is free software, released under the GNU General Public License v3.0 or later.

Metadata

Release files for tyrtulia-tui 1.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tyrtulia-tui 1.6.0
File Size Uploaded Provenance Actions
tyrtulia_tui-1.6.0.tar.gz 55.5 kB Without provenance Details for tyrtulia_tui-1.6.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tyrtulia-tui 1.6.0
File Interpreter ABI Platform Actions
tyrtulia_tui-1.6.0-py3-none-any.whl Python 3 none any Details for tyrtulia_tui-1.6.0-py3-none-any.whl

Total release size: 116.6 kB

Release files / tyrtulia_tui-1.6.0.tar.gz

Download URL tyrtulia_tui-1.6.0.tar.gz
Size 55.5 kB
Tags Source
SHA-256 checksum
How to use checksums
8a876b17d3dc833f7edf4178c4d053ac4f6b87cd52326a75d17afa11eb26711c
BLAKE2b-256 checksum
How to use checksums
dd92d701aed5ba8f7733f082001dab5acc9e4c6b270a5685d9870e33cd786a0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / tyrtulia_tui-1.6.0-py3-none-any.whl

Download URL tyrtulia_tui-1.6.0-py3-none-any.whl
Size 61.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
af727cb22f9df64bbc9bd43a0a9c1f3bd8bc5fd7a280d717e2399f7ee45105e6
BLAKE2b-256 checksum
How to use checksums
a2338665e44b02a7023194c1c20673ae0c411eea92564e0bd49d23564c15547b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for tyrtulia-tui 1.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page