Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b8b6347
docs: GitHub's palette, and a landing page that shows the thing
lestex Aug 7, 2026
6f7a0b3
docs: correct the TUI layout, palette duplication and release ritual
lestex Aug 7, 2026
c443694
docs: center the landing page and drop its stray toolbar row
lestex Aug 7, 2026
4f2280f
docs: the get started button is not a shell prompt
lestex Aug 7, 2026
269baeb
docs: no copy button over the terminal frame, and a solid border
lestex Aug 7, 2026
366be51
docs: a real screenshot in the hero, not ASCII art
lestex Aug 7, 2026
18b7572
docs: a denser hero screenshot, with a live download in it
lestex Aug 7, 2026
af6033a
docs: the site wears the same theme the TUI does
lestex Aug 7, 2026
130b1b0
docs: drop the window chrome and widen the screenshot
lestex Aug 7, 2026
cea6023
docs: the hero glow fades again instead of ending in a box
lestex Aug 7, 2026
3b1ef99
docs: the header repo block sizes to the repo name
lestex Aug 7, 2026
f89bad5
docs: tidy the footer
lestex Aug 7, 2026
cbbed56
docs: credit @lestex in the footer, linked to the profile
lestex Aug 7, 2026
461a330
docs: link the dependencies in the footer, drop the hover underline
lestex Aug 7, 2026
aeddc3f
docs: tighten the header spacing
lestex Aug 7, 2026
e58b898
feat: a logo, drawn once and used in three places
lestex Aug 7, 2026
91243a3
fix(docs): the logo was rendering black in the header
lestex Aug 7, 2026
1dc4204
docs: close the gap between the mark and the wordmark
lestex Aug 7, 2026
9aee327
docs: the header keeps saying torrnado when you scroll
lestex Aug 7, 2026
a8636e2
docs: the wordmark is set in the code face
lestex Aug 7, 2026
43115ae
docs: separate the footer, and even out the header gaps
lestex Aug 7, 2026
13d3b5d
docs: the footer credit runs over two lines
lestex Aug 7, 2026
22a8208
docs: the light footer is light
lestex Aug 7, 2026
54cbfa8
docs: apply the house dash and spelling rules to the restyle
lestex Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/assets/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/tui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 17 additions & 37 deletions docs/guide/tui.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Three panes plus a status line. The focused pane is the one with the
highlighted border, and it's where `j`/`k` go:

```
┌ sidebar ─┐┌ torrent list ─────────────────────────────────┐
│ torrnado ││ Name Size Status ↓ Speed ETA │
│ ││> ubuntu-24.04.iso 5.9GiB downl… ↓ 21M/s 3m56│
│ Status ││ ━━━━━━━━━━━───────────
┌ sidebar ─┐┌ torrent list ─────────────────────────────────
│ torrnado ││ Name Progress Size Status ETA │
│ ││> ubuntu-24.04… ━━━━━──── 62% 5.9GiB downl… 3m56│
│ Status ││
│ All ││ │
│ Downl… │└────────────────────────────────────────────────┘
│ Seeding │┌ detail ────────────────────────────────────────┐
Expand All @@ -24,8 +24,10 @@ highlighted border, and it's where `j`/`k` go:

- **Sidebar** filters the list by status. It intersects with `/` search
rather than replacing it.
- **List** shows one torrent per two lines: the data columns, and a thin
progress underline beneath the name (absent once complete).
- **List** shows one torrent per line. Progress is a column - a bar
followed by its percentage - rather than an underline beneath the name.
A wide enough pane also shows size, status, both speeds and the ETA; a
narrow one drops those and keeps the name and progress.
- **Detail pane** always tracks the cursor torrent - there is no separate
full-screen detail view. Its three tabs are the piece completion map,
the connected-peer table, and the file list.
Expand Down Expand Up @@ -58,7 +60,8 @@ mode, just the movement/action idioms.
| `q` | quit the TUI (the daemon keeps running) |

With the detail pane focused on its Files tab, `j`/`k` move between files
and `+`/`-` raise/lower the selected file's priority. On the other tabs
and `+`/`-` raise/lower the selected file's priority (`=` and `_` do the
same, so neither needs shift). On the other tabs
`j`/`k` scroll. Actions (`p`, `r`, `x`, `D`, `:`) work from any pane and
always apply to the list's selection or cursor row.

Expand All @@ -69,7 +72,7 @@ always apply to the list's selection or cursor row.
| Command | Effect |
|-------------------------------------------------------|--------------------------------------------|
| `:add <magnet\|file\|url\|dir\|glob\|magnet-list-file> ...` | add one or more torrents (see Batch add) |
| `:remove` / `:remove!` | remove (without/with data); acts on the selection, or the cursor row |
| `:remove` / `:remove!` (`:rm` / `:rm!`) | remove (without/with data); acts on the selection, or the cursor row |
| `:purge` | delete the data, keep the torrent; selection or cursor row |
| `:pause` / `:resume` | absolute pause/resume; selection or cursor row |
| `:recheck` | force recheck on selection or cursor row |
Expand All @@ -79,6 +82,12 @@ always apply to the list's selection or cursor row.
| `:theme [name]` | open the theme picker, or switch straight to a named theme |
| `:q` / `:quit` | quit the TUI |

Arguments may be quoted with `'` or `"`, which is what makes an argument
containing a space possible (`:move '/media/big disk'`). Quoting a magnet
is unnecessary here - the palette is not a shell, so nothing expands -
but harmless, which matters because quoting one *is* necessary in zsh and
the habit follows you into the palette.

### Batch add

`:add` (and `torrnado add` on the CLI) accepts any mix of:
Expand All @@ -102,32 +111,3 @@ torrnado add ~/torrents/*.torrent
torrnado add ~/torrents/ # every .torrent file in the directory
torrnado add magnets.txt # one magnet uri per line
```

## Command palette

`:`-prefixed, vim ex-mode style:

| Command | Effect |
|-------------------------------------------------------|--------------------------------------------|
| `:add <magnet\|file\|url\|dir\|glob\|magnet-list-file> ...` | add one or more torrents (see Batch add) |
| `:remove` / `:remove!` | remove (without/with data); acts on the selection, or the cursor row |
| `:purge` | delete the data, keep the torrent; selection or cursor row |
| `:pause` / `:resume` | absolute pause/resume; selection or cursor row |
| `:recheck` | force recheck on selection or cursor row |
| `:limit-up <rate>` / `:limit-down <rate>` | set the *global* rate limit (`500k`, `2M`, `unlimited`) |
| `:move <dir>` | move the cursor row's data to a new directory |
| `:sort name\|size\|progress\|ratio\|eta\|added\|down\|up [desc]` | change list sort order |
| `:theme [name]` | open the theme picker, or switch straight to a named theme |
| `:q` / `:quit` | quit the TUI |

Arguments may be quoted with `'` or `"`, which is what makes an argument
containing a space possible (`:move '/media/big disk'`). Quoting a magnet
is unnecessary here - the palette is not a shell, so nothing expands -
but harmless, which matters because quoting one *is* necessary in zsh and
the habit follows you into the palette.

Arguments may be quoted with `'` or `"`, which is what makes an argument
containing a space possible (`:move '/media/big disk'`). Quoting a magnet
is unnecessary here - the palette is not a shell, so nothing expands -
but harmless, which matters because quoting one *is* necessary in zsh and
the habit follows you into the palette.
156 changes: 119 additions & 37 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
# torrnado

A terminal BitTorrent client: a vim-like TUI on top of a torrent engine
that runs as a background daemon.

```
┌──────────────────┐┌────────────────────────────────────────────────────────────────────┐
│ torrnado ││ Name Progress Size Status │
│ ││ > archlinux-2026.08.01… ━━━━━━━────── 42% 1.5GiB downloading │
│ Status ││ Fedora-KDE-Desktop-L… ━━━━━━━━━━━━ 100% 3.1GiB seeding │
│ All ││ * ubuntu-26.04-desktop… ━━────────── 11% 6.1GiB paused │
│ Downloading ││ │
│ Seeding │└────────────────────────────────────────────────────────────────────┘
│ Completed │┌────────────────────────────────────────────────────────────────────┐
│ Stopped ││ ─ [Pieces] Peers Files │
│ ││ 1950/24208 pieces verified × 256.0KiB │
└──────────────────┘└────────────────────────────────────────────────────────────────────┘
↓ 21.4MiB/s ↑ 0B/s │ 3 torrents added 1 torrent(s)
```
---
hide:
- navigation
- toc
---

<div class="tn-hero" markdown>

<h1 class="tn-hero__title">torrnado</h1>

<p class="tn-hero__tagline">
A terminal BitTorrent client with a vim-like TUI.<br>
The engine runs as a daemon, so closing the terminal doesn't stop the download.
</p>

<div class="tn-cta" markdown>
[get started](getting-started/installation.md){ .tn-btn .tn-btn--primary }
[view on github](https://github.com/lestex/torrnado){ .tn-btn .tn-btn--ghost }
</div>

</div>

<div class="tn-terminal">
<img src="assets/tui.png" width="1760" height="713"
alt="The torrnado TUI: a sidebar filtering by status, four torrents listed with
progress, size, state and speed, one downloading at 19% and 35.5MiB/s, and
a docked Pieces tab below showing the verified piece map.">
</div>

<div class="tn-stats">
<span>macOS</span>
<span>Linux</span>
<span>MIT License</span>
<span>Go 1.25+</span>
<span><b>v0.1.0</b></span>
</div>

## The idea

Expand All @@ -29,6 +46,89 @@ the CLI are both thin clients that talk to the engine over a local Unix
socket, so neither of them owns the torrents; quitting either one is not
an event the daemon notices.

<p class="tn-label">// features</p>

<div class="tn-features" markdown>

<div class="tn-feature" markdown>
### runs detached
One daemon; the TUI and the CLI attach and detach freely. Quitting a
client is not something the engine notices.
</div>

<div class="tn-feature" markdown>
### survives restarts
The torrent list, paused state, save paths, rate limits and per-file
priorities are written to disk and restored on start.
</div>

<div class="tn-feature" markdown>
### streams while downloading
Press ++v++ on a video and it opens in your player at once, seeking
included - the read position drives which pieces are fetched.
</div>

<div class="tn-feature" markdown>
### three panes, vim keys
A status sidebar, the torrent list and a docked Pieces/Peers/Files pane.
++colon++ opens a command palette; ++h++ lists every key.
</div>

<div class="tn-feature" markdown>
### fully scriptable
Every action is a subcommand, so `torrnado add`, `torrnado list` and
friends work in a shell script or a cron job.
</div>

<div class="tn-feature" markdown>
### waits for your VPN
Optionally holds every transfer until the system's traffic leaves through
a tunnel, and lets them go again when it reconnects.
</div>

</div>

<p class="tn-label">$ install torrnado</p>

<div class="tn-install" markdown>

=== "Released binary"

```sh
tar xzf torrnado_0.1.0_linux_amd64.tar.gz
./torrnado version
```

Archives for Linux and macOS on both architectures, plus
`checksums.txt`, are on the [releases
page](https://github.com/lestex/torrnado/releases).

=== "From source"

```sh
go build -o torrnado ./cmd/torrnado
```

Requires Go 1.25+. `make build` instead stamps the version, commit and
date in, so `torrnado version` says more than "dev".

=== "Docker"

```sh
docker build -t torrnado .
docker run --rm -v "$PWD/downloads:/downloads" torrnado version
```

For leaving it running on a box - see [Docker](server/docker.md).

</div>

<p class="tn-after-install" markdown>
then run `torrnado` to start - [quick start →](getting-started/quick-start.md)
</p>

## Where to go next

<div class="grid cards" markdown>

- __Start here__
Expand Down Expand Up @@ -67,26 +167,8 @@ an event the daemon notices.

</div>

## What it does

- **Runs detached.** One daemon; the TUI and CLI attach and detach freely.
- **Survives restarts.** The torrent list, paused state, save paths, rate
limits and per-file priorities are written to disk and restored.
- **Streams while downloading.** Press ++v++ on a video and it opens in
your player at once, seeking included - the read position drives which
pieces are fetched.
- **Scripts.** Every action is a subcommand, so `torrnado add`,
`torrnado list` and friends work in a shell script or a cron job.
- **Stays out of the way.** Vim-like keys, no mouse, no configuration
required to start.

## What it does not do

No remote control protocol, no web UI, no Windows. The socket is local by
construction and SSH already solves the remote problem properly - see
[non-goals](reference/caveats.md) for the full list and the reasoning.

## Requirements

Go 1.25+ to build, a POSIX system to run (Linux and macOS are tested), and
a media player if you want the streaming preview.
11 changes: 10 additions & 1 deletion docs/reference/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,17 @@ The tag is the version; nothing is committed anywhere to forget to bump.
make check && make e2e
make changelog TAG=v0.1.0 # files the pending commits under that version
git commit -am "chore: changelog for v0.1.0"

# main is protected: the changelog commit lands through a pull request.
git checkout -b release-v0.1.0 && git push -u origin release-v0.1.0
gh pr create --base main --fill && gh pr merge --merge

# Tag the commit that actually landed, not the local one you wrote -
# otherwise the archives and `torrnado version` name a commit that is not
# on the branch. Tags are not protected and push directly.
git checkout main && git pull
git tag -a v0.1.0 -m "v0.1.0"
git push --follow-tags
git push origin v0.1.0
```

The tag triggers `.github/workflows/release.yml`: it reruns `make check`
Expand Down
Loading
Loading