What it is
shpaste is a small Emacs client for paste.sr.ht,
the pastebin service from SourceHut. It lets me share a snippet without
leaving the editor: select some text, run a command, and the paste URL is
already in my kill-ring, ready to paste into a chat or an email.
It is built on the SourceHut GraphQL API and does just enough to be useful: create pastes, list them, open them, delete them. Nothing more.
The project lives at sr.ht/~bounga/shpaste, where you’ll find the source, the ticket tracker, and the mailing lists.
What it does
- Create a paste from the region or from the whole buffer. The resulting URL is pushed to the kill-ring and shown in the echo area.
- Browse your pastes in a dedicated list buffer, sorted and tabulated.
- From that buffer: open a paste, copy its URL, delete it, or refresh the list.
- Choose a visibility per paste —
public,unlisted, orprivate. - Point it at a self-hosted SourceHut instance if you don’t use the public one.
The list buffer looks like this:

Requirements
- Emacs 29.1 or later
plz(GNU ELPA) andcurl
Installation
shpaste is not on MELPA yet — the package is only a few weeks old. MELPA
Stable support will come later, built from the version tags. In the
meantime, install it straight from the repository.
On vanilla Emacs 29.1+, package-vc-install clones and builds it from
source:
(package-vc-install "https://git.sr.ht/~bounga/shpaste")
On Emacs 30+, you can let use-package do the same with :vc:
(use-package shpaste
:vc (:url "https://git.sr.ht/~bounga/shpaste" :rev :newest))
In Doom Emacs, declare the recipe in packages.el:
(package! shpaste
:recipe (:type git
:host nil
:repo "https://git.sr.ht/~bounga/shpaste"
:files ("*.el")))
Then configure it in config.el. This is the setup I use, with a paste
leader menu and the list-buffer bindings:
(use-package! shpaste
:commands (shpaste-list
shpaste-create-from-region
shpaste-create-from-buffer)
:init
(setq shpaste-default-visibility 'unlisted)
(map! :leader
(:prefix ("P" . "paste")
:desc "List my pastes" "l" #'shpaste-list
:desc "Paste buffer" "p" #'shpaste-create-from-buffer
:desc "Paste region" "r" #'shpaste-create-from-region))
:config
(map! :map shpaste-list-mode-map
:n "RET" #'shpaste-list-open
:n "w" #'shpaste-list-copy-url
:n "d" #'shpaste-list-delete
:n "gr" #'shpaste-list-refresh))
Getting a token
shpaste authenticates with a SourceHut OAuth2 personal access token.
Generate one at meta.sr.ht/oauth2 with the PASTES grant in read-write
mode, then store it in auth-source with the host set to your instance —
for example in ~/.authinfo.gpg:
machine paste.sr.ht password <YOUR_TOKEN>
There is no shpaste-token variable: the token is only ever read from
auth-source. If you want the details of how that lookup works and why I
chose it, I wrote a whole post about it:
Using auth-source in a Real Emacs Package.
Usage
| Command | Action |
|---|---|
shpaste-create-from-region |
Create a paste from the region; URL to kill-ring |
shpaste-create-from-buffer |
Create a paste from the whole buffer |
shpaste-list |
Browse your pastes in a list buffer |
Inside the shpaste-list buffer:
| Key | Action |
|---|---|
RET |
Open the paste |
w |
Copy its URL |
d |
Delete it |
g |
Refresh the list |
Configuration
Two options, both optional:
shpaste-instance(default"paste.sr.ht") — set it to a self-hosted host. It is used both to build the API endpoint and as theauth-sourcelookup key, so themachinefield above must match it.shpaste-default-visibility(defaultunlisted) — one ofpublic,unlisted, orprivate.
Wrapping up
That’s the whole tool. shpaste is young (currently 0.1.0) and MIT
licensed. If you live in Emacs and use SourceHut, give it a try — and if you
hit a rough edge or have an idea, the project page is at
sr.ht/~bounga/shpaste.
Share on
Twitter Facebook LinkedInHave comments or want to discuss this topic?
Send an email to ~bounga/public-inbox@lists.sr.ht