Skip to content

About

Unofficial terminal UI for /dev/null (dev-null.sh) — the feed, threads and inbox in any terminal, or as a Herdr pane

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

devnull-tui

An unofficial terminal UI for /dev/null, the developers' commit social network — in any terminal, or as a Herdr pane.

Unofficial: not affiliated with /dev/null. It reads the same pages the website serves its guests.

Every post on /dev/null is a git commit drawn on a rail, coloured by its author's lane: a small circle before the name, coloured from the author's real picture, the short hash on the right, and bodies with inline code, links, quotes, polls, link previews and replies. A body longer than five lines is clamped in the log, replies included; enter opens the commit with its whole conversation. ← goes back, and c / R commit and reply as you, signed with your SSH key: the composer takes @ mentions and dropped images, the way the website does.

⬤ legecha @legecha · 4m                    40dd829
│ I haven't done social media for years.
│ But this feels different.
│ Hello, World!
│ ★ 3   ↳ 1
○

Each author gets a dot in the lane colour, tinted by a colour sampled from their real GitHub photo, so the rail still reads as a person and costs two columns. a turns the dots off and back on.

A commit's attached images stay a one-line ▣ 2 images marker until you ask for them: i draws the pictures under the commit and i again puts them away, so the log is text unless you want to look. Where the terminal speaks the Kitty graphics protocol — Ghostty, Kitty, WezTerm, and Herdr forwards it from a pane — the real picture is drawn; everywhere else it falls back to half blocks, which need no protocol and survive any redraw.

Screenshots

The log, latest or trending, 20 commits a page:

The /dev/null log as a timeline in a Herdr pane: author dots, bodies with link previews, the short hash on the right

A commit opened with its replies, and the hint bar for the keys that work there:

A commit and its replies in the Herdr pane, with the key hint bar at the bottom

Requirements

  • Node.js 20 or newer (fetch, Intl.Segmenter).
  • Herdr 0.9.0 or newer only for the plugin pane; the CLI needs no Herdr.
  • One WebP converter for attached images — dwebp, sips (macOS), ffmpeg, ImageMagick or vips. /dev/null serves its images as WebP and Node cannot read that; without a converter the marker says so and the rest of the pane is unaffected. Reading, committing and replying need none of this.
  • OpenSSH 8.2+ with ssh-keygen -Y sign, and an Ed25519 or RSA key that is on your GitHub account — held by your agent (1Password, ssh-agent) or as a file without a passphrase — for committing and replying. FIDO sk-* keys and ECDSA keys are not accepted by /dev/null. Reading needs none of this.
  • Following and the inbox are the user's own, so they need an approved device (the same one-time approval stars and deletes use: press s). The pane catches the approval code itself on macOS (osacompile) and Linux (xdg-mime), and falls back to pasting it when neither is available.

Install

As a CLI, with a package manager:

npm install -g devnull-tui
bun add -g devnull-tui
pnpm add -g devnull-tui

npx devnull-tui --help      # without installing anything
bunx devnull-tui --help

Without one, no build step:

curl -fsSL https://raw.githubusercontent.com/arhen/devnull-tui/main/install.sh | sh

The script needs curl and Node 20+, puts the files in ~/.local/share/devnull and points ~/.local/bin/devnull at them (DEVNULL_HOME, DEVNULL_PREFIX and DEVNULL_REF override that). Run from a checkout, sh install.sh installs that checkout instead. To remove it, delete both paths.

As a Herdr plugin:

herdr plugin install arhen/devnull-tui
herdr server reload-config

The old arhen/herdr-devnull-plugin entry still installs: it runs the current code from here, so listings and keybindings made before the rename keep working.

From a checkout, while developing:

herdr plugin link ~/Code/devnull-tui

Open it

From a pane, beside that pane:

herdr plugin pane open --plugin devnull.feed --entrypoint feed \
  --direction right --target-pane "$HERDR_PANE_ID" --focus

From the command palette or a keybinding, the action opens or focuses a feed in the workspace the user is looking at:

herdr plugin action invoke devnull.feed.open

Bind a key in ~/.config/herdr/config.toml:

[[keys.command]]
key = "prefix+d"
type = "plugin_action"
command = "devnull.feed.open"
description = "open the /dev/null feed"

The pane opens as a split beside the calling pane. Agents that install the dev-null skill run that action when you say "open dev-null".

CLI

devnull is the same program without Herdr, in any terminal. From a checkout, node index.js.

Flag
--once print the log and exit
--log <name> latest (default), trending, following, notifications
--interval <sec> refresh interval, at least 15 (default 60)
--width <cols> width for --once (default: the terminal's)
--help / -h usage
--version / -v the version

Everything works without Herdr: reading, following, the inbox, stars, deletes, committing and replying with your SSH key, device approval, dropping images into a draft. The only difference is o: a Herdr browser tab when Herdr is there, the default browser when it is not. A pipe is treated as a reader — devnull | less and devnull > feed.txt print once and exit.

Keys

Key
j / k next / previous commit
→ / ← open the selected commit / go back one level
g / G first / last commit
ctrl+d / ctrl+u half a page
enter open the commit and its conversation (same as →)
e expand the selected reply's own replies, or a group in the inbox
R reply to the selected commit
c commit something new
o open the commit in a browser (Herdr browser tab, else the default browser)
a avatars: colour dots on / off
i images of the selected commit: show / hide
s star or unstar the selected commit · approve this pane as a device
d delete one of your own commits, twice to confirm
r refresh the open tab
1 / 2 / 3 / 4 latest · trending · following · notifications
n jump to notifications from anywhere
t latest / trending
? keys
esc back one level: reply → its parent → timeline
q close the pane; in a commit, back to the timeline

The tab row numbers the tabs with the keys that open them, so 1 latest, 2 trending, 3 following, 4 notifications — the pane opens on 1, so a fresh install reads the public log before it asks for anything. Following and the inbox are read from the signed API: without a device the tab says so and s starts the approval. The inbox refreshes only while it is on screen, because listing notifications marks them read on /dev/null, as it does on the web.

The composer

c and R open a composer that works like the one on the website:

  • @ tags people. The names on screen come first, then /dev/null's own people search; ↑/↓ (or ctrl+p/ctrl+n) pick one, tab or enter puts the @handle in, esc closes the list.
  • Drop an image into the composer, or paste its path. Herdr pastes a dropped file as its path, so a drop, a Finder drag, or a typed --image-style path all attach the same way: JPEG, PNG or WebP up to 10 MB, at most 3 a commit; videos, GIFs, SVG and HEIC are refused with the reason, as /dev/null would refuse them. On an empty draft, backspace removes the last image.
  • enter sends, shift+enter starts a new line, esc cancels, ctrl+u clears the draft.
  • The composer is two lines tall. ←/→ move the caret, ctrl+←/ctrl+→ (or alt+←/alt+→, which Herdr sends as alt+b/alt+f) move by word, ctrl+a/ctrl+e (cmd+←/→) jump to the start or end of the line, ctrl+k deletes to its end, delete deletes forward. ↑/↓ walk the caret through a longer draft, and the hint says how many lines are out of view.

Starring and deleting

Stars, pastéis, follows, bookmarks and deletes are not part of the commit signature API: /dev/null only accepts them from a device key it has approved. Press s once and the UI walks you through that approval, the same PKCE flow the mobile app uses:

  1. It makes an Ed25519 key in its state directory (HERDR_PLUGIN_STATE_DIR inside Herdr, ~/.cache/devnull-tui otherwise) and opens /mobile/authorize in your browser.
  2. You approve it there. The page answers on devnull://device?code=…, which a desktop has no handler for, so for the few minutes the approval takes the pane registers one: an AppleScript applet in ~/Applications on macOS (made with osacompile, so no compiler or binary is needed), an x-scheme-handler/devnull entry in ~/.local/share/applications on Linux. It writes the code where the app can read it, and both are removed as soon as the approval is done.
  3. When no handler can be installed, it asks for the code instead: esc switches to pasting it, and a code stays good for five minutes.

From then on s stars and unstars, d deletes your own commits (twice to confirm), and the Following tab and the inbox open. Every call is signed as timestamp\nnonce\nMETHOD\npath\nbody with ssh-keygen -Y sign -n devnull-api and sent with X-Devnull-Timestamp, X-Devnull-Nonce and X-SSH-Signature.

Writing

c and R post as your /dev/null account the same way dev-null's own curl -fsSL https://dev-null.sh/commit | bash does, with no login:

  1. It lists your public keys (ssh-add -L and ~/.ssh/*.pub) and asks /api/cli/key-lookups which of them is on the GitHub account of a /dev/null user. Public keys are public; nothing else leaves your machine.
  2. It signs timestamp\nnonce\nreply-to\nbody with ssh-keygen -Y sign -n devnull-commit, so your private key — held by 1Password or ssh-agent — never leaves the agent. With images the payload gains a line of their SHA-256s and the signature moves to devnull-commit-images; the images themselves go up as a multipart form next to the signed payload, exactly like the script's --image.
  3. It posts the payload, the images and the signature to /api/cli/commits.

Stars, pastéis, follows and bookmarks are not available here: /dev/null only accepts them from an approved device (a key you approve under /settings/devices from the mobile app), and this UI does not pretend otherwise.

Data

It reads what the website renders for guests, so there is no API key, no cookie jar, and no scraping of private state:

  • https://dev-null.sh/log/latest and /log/trending: the Inertia data-page JSON with 20 commits.
  • https://dev-null.sh/@user/commits/{id} with Cookie: replies_per_page=25: a commit and its replies, one page at a time.

Following and the inbox are the user's own, so they come from the signed API the mobile app uses, with the device key the app already has:

  • /api/v1/log/following: the commits of the people you follow, plus /dev/null's suggestions while the log is empty.
  • /api/v1/notifications?type=all: the inbox grouped and filtered the way the web groups it — follows, stars, replies, quotes, mentions, moderation and level-ups, each group with its actors, its event and unread counts, and the commit it happened to.
  • /api/v1/notifications/{id}/group?type=all: the events e opens under a group.

Listing notifications marks them read, as on the web, so the pane only asks while the inbox tab is open. The open tab refreshes every 60 seconds; --interval changes that.

Text mode

For agents and scripts, and for a quick look without a TUI. Piped output has no colour codes and no key hints:

devnull --once --log latest --width 100
devnull --once --log notifications   # the inbox, as text
devnull --once                       # one text frame, when piped

From a checkout, node index.js takes the same flags. The Herdr actions call the same mode: herdr plugin action invoke devnull.feed.latest prints it into the plugin log (herdr plugin log list --plugin devnull.feed); following and notifications are actions too.

Development

npm install          # typescript and @types/node, for checking only
npm run typecheck    # tsc --noEmit over the JSDoc-checked sources
node index.js        # run it by hand

There is no build step and no runtime dependency. devnull is node index.js with a shebang; inside Herdr, the manifest launches /bin/sh run.sh, which execs the same file.

Layout

Path
herdr-plugin.toml pane, actions, keybinding target
install.sh the curl installer: files under ~/.local/share/devnull, a link in ~/.local/bin
run.sh, open.sh, find-node.sh the Herdr entry points
index.js state machine: timeline, thread, composer, keys, polling
lib/draw.js rows, rails, avatars, links, thread, inbox, composer and chrome
lib/devnull.js page JSON → commits, threads
lib/api.js device-signed Following and notifications → the same shape
lib/catcher.js the temporary devnull:// handler that catches the approval code
lib/avatars.js GitHub avatar photo → one colour sample for the dot, cached in HERDR_PLUGIN_STATE_DIR
lib/attachments.js dropped paths → readable images, by sniffed content, under /dev/null's limits
lib/pictures.js attached images → WebP converted to PNG, then half-block rows, cached in HERDR_PLUGIN_STATE_DIR
lib/auth.js SSH key lookup, ssh-keygen -Y sign, commit/reply with images
lib/jpeg.js the baseline JPEG decoder GitHub's photo avatars need
lib/pane-id.js finds an already-open feed pane for open.sh
lib/text.js display width, wrapping, link detection

Licence

MIT.

About

Unofficial terminal UI for /dev/null (dev-null.sh) — the feed, threads and inbox in any terminal, or as a Herdr pane

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages