remoto.el: Browse GitHub repos without cloning

An Emacs package to browse Github without cloning the repository. Uses magit/ghub package for interacting with Github.

Installation:

(use-package remoto
  :vc (:url "https://github.com/agzam/remoto.el"))

Examples:

(remoto-browse "torvalds/linux") ;; hangs for a minute on a slow connection
(remoto-browse "https://github.com/agzam/remoto.el")
(find-file "/github:nex-crm/wuphf@main:/CHANGELOG.md")

Screenshot: img

Hosting static HTMLs

While I'm on vacation and Mac Mini 2024 (my home server) is turned off, I wanted to host HackerNews legible frontend (html file) on the Internet with minimum setup as possible.

Obvious choices like Cloudflare Workers, Github Pages and all their variants were ruled out as they require accounts, backed by big corps while I wanted something indy.

Playing with Surge, made me realise it's a nice way to host static html artifacts created with AI, so here it is: https://hotter.surge.sh.

Operations
(let ((default-directory "~/Developer/src/github.com/velppa/hotter.surge.sh"))
  (async-shell-command
    ;; "surge --help"
    ;; "surge files hotter.surge.sh"
    ;; "surge . hotter.surge.sh" ;; deploy
    "make deploy"
    "*hotter.surge.sh*"))

(setq surge-server (ws-file-server :dir "~/Developer/src/github.com/velppa/hotter.surge.sh" :port 9004))
(ws-stop surge-server)

textpod.el v0.3.0

Another round of improvements of textpod.el, a companion Emacs package to Textpod.

Custom ID format - attribute name and prefix

Added two defcustom options in textpod.el:

  • textpod-id-property (default "TEXTPOD_ID") - name of the Org property used to store the note id.
  • textpod-id-prefix (default "") - prefix prepended to ids when stored, stripped before sending requests to the server.

Introduced textpod--strip-prefix helper and updated all id call sites (textpod-open-current-note, textpod-org-heading-to-note, textpod-org-region-to-note) to respect these configs.

textpod-set-id sets ID without creating a node

I need a function to create texpod id property without sending note to the server. This is needed to submit old notes so they have proper timestamp.

Added textpod-set-id: an interactive command that sets textpod-id-property on the current heading (with textpod-id-prefix) derived from a time as YYYYMMDDhhmmss, without making any HTTP request.

  • No prefix arg → uses (current-time).
  • With prefix arg → prompts via org-read-date, so the note can be back-dated before later uploading with textpod-org-heading-to-note.
Quickly getting to Textpod notes

Using Ack is possible, placing point on a file-name and pressing {H-RET} will call visit-source and go directly to the line.

ack ":ID:\s*textpod_" *.org | grep -v 2026-04-22 | head -n 20

2026-04-12.org:226::ID: textpod_20260412124048
2026-04-13.org:10::ID: textpod_20260414214110
2026-04-13.org:237::ID: textpod_20260414212612
2026-04-13.org:247::ID: textpod_20260414224720
2026-04-13.org:263::ID: textpod_20260414210103
2026-04-13.org:314::ID: textpod_20260414224944
2026-04-16.org:12::ID: textpod_20260416100352
2026-04-16.org:97::ID: textpod_20260417092822
2026-04-21.org:205::ID: textpod_20260421120000

Since I made my Textpod notes to be Org-roam notes, I can query them directly from SQLite DB.

  • (sqlite-mode-open-file "~/.config/emacs/org-roam.db")

    select id, file, title from nodes n where n.id like '"textpod_%"';

textpod_20260329123259 ~/Notes/2026-03.org Standalone HTML Galleries
textpod_20260315124829 ~/Notes/2026-03.org Build HackerNews Legible Frontend
textpod_20260415130636 ~/Notes/2026-04.org Improve public notes
textpod_20260415205822 ~/Notes/2026-04.org “Why Kim Wexler Was The Real Villain of Better Call Saul” by BreakingHub (19 min)
textpod_20260415210948 ~/Notes/2026-04.org “Linus Lays down the Law” – The PrimeTime (ThePrimeagen), 13 min
textpod_20260412124048 ~/Notes/daily/2026-04-12.org Improve HackerNews Legible Frontend
textpod_20260414214110 ~/Notes/daily/2026-04-13.org Interview about k8s setup in Amazon
textpod_20260414212612 ~/Notes/daily/2026-04-13.org kumo – local AWS emulator in Go
textpod_20260414224720 ~/Notes/daily/2026-04-13.org Snowflake MCP server
textpod_20260414210103 ~/Notes/daily/2026-04-13.org Claude Code quota exhausted / degraded quality
textpod_20260414224944 ~/Notes/daily/2026-04-13.org Trying Textpod once again
textpod_20250612070826 ~/Notes/html.org I'm betting on HTML
textpod_20260421120000 ~/Notes/daily/2026-04-21.org Rewrite Textpod to Go
textpod_20260416100352 ~/Notes/daily/2026-04-16.org Advice to org-roam-dailies-goto-today
textpod_20260417092822 ~/Notes/daily/2026-04-16.org Links <2026-04-16 Thu>
textpod_20260422124810 ~/Notes/daily/2026-04-22.org textpod.el v0.3.0

[TK: :colnames yes got broken for some reason].

My textpod.el config

The config automatically makes notes published to Textpod as Org-roam nodes.

(use-package textpod
  :config
  (setq textpod-id-property "ID"
        textpod-id-prefix "textpod_"
        textpod-token (cadr (auth-source-user-and-password "finita.myaddr.dev" "textpod"))
        ;;textpod-url "/notes"
        textpod-url "http://localhost:3000/notes"
        )
  :bind (("C-c b x" . textpod-set-id)
         ("C-c b b" . textpod-org-heading-to-note)
         ("C-c b o" . textpod-open-current-note)))

Updates:

  • <2026-04-23 Thu>: add keybindings

Rewrite Textpod to Go

On <2026-04-21 Tue> I rewritten Textpod from Rust to Go.

While I'm on vacation with the new MacBook Pro M4 Pro and I have installed only bare minimum on it, I don't have Rust toolchain but do have Go. So, asked Claude to do rewrite. The only external dependency is goldmark package to render Markdown as HTMLs.

Updated Elisp helpers:

(use-package textpod
  :config
  (setq textpod-token (cadr (auth-source-user-and-password "finita.myaddr.dev" "textpod"))
        ;;textpod-url "/notes"
        textpod-url "http://localhost:3000/notes"
        ))

(let ((default-directory "~/Developer/src/github.com/velppa/textpod"))
  (async-shell-command "go build -o ~/.local/bin/textpod ." "*textpod-build*"))

(let ((default-directory "~/Developer/src/github.com/velppa/finita-la-comedia/textpod")
      (buffer-name "*textpod-server*"))
  (when (get-buffer buffer-name)
    (kill-buffer buffer-name))
  (async-shell-command (format "textpod --token %s --base-path notes" textpod-token) buffer-name))

(browse-url (format "%s?token=%s" textpod-url textpod-token))

Links <2026-04-16 Thu>

Improve public notes

Next round of improving Textpod on <2026-04-15 Wed>.

Deploy publicly –

Introduced --base-path flag, to serve notes under sub-path.

To start Textpod with sub-path, use:

textpod --token $TEXTPOD_TOKEN --base-path notes
Publish textpod.el to the repository

textpod.el is a companion Emacs package for quick publishing notes from Org Mode documents.

Install it like this:

(use-package textpod
  :config
  (setq textpod-token (cadr (auth-source-user-and-password "finita.myaddr.dev" "textpod")))
  (setq textpod-url "/notes"))

Then these helper functions do the heavy-lifting:

  • textpod-org-region-to-note: Convert the Org region between BEG and END to Markdown and send to Textpod.
  • textpod-org-heading-to-note: Send the current heading’s subtree to Textpod.
  • textpod-open-current-note: Open the current heading’s Textpod note in a browser.

When creating a note, textpod.el sets TEXTPOD_ID property that's equal to node'id.

Change separator in notes.md to ^L

Default separator \n\n---\n\n is fragile as it requires exactly two newlines before / after the note. I often messed up notes when editing notes.md file manually. To fix that, I replaced it with ^L character (insert in Emacs via C-q C-l) which usually represents new pages.

Fix attachments links

Attachement links need to respect --base-path flag.

Handling attachments

My current workflow:

  • Assets are stored next to Notes under assets/ directory

  • I can quickly search for them, then open in Emacs/qView:

    find ../assets -name "*prefix-in-emacs*"
    
    ../assets/m-g-prefix-in-emacs.png
    ../assets/m-s-prefix-in-emacs.png
    
  • I can reference an asset as an image: img

  • An image is a link to an image file that does not have a description part (Images (The Org Manual)).

  • Textpod already support images. What happens when I drag-n-drop an image file to the textarea? Textpod copies them under attachments directory.

What I want:

  • When creating a public note containing an image via API (using textpod.el), copy the asset to assets/ directory, mimicing the directory structure in the notes.
  • I need another API endpoint - POST /notes/assets/<name>, accepting binary as payload.

Okay, I implemented it, let's test.

textpod-org-heading-to-note

Prompt: Implement textpod-org-heading-to-note function, it should select current (top) heading - the one that has TEXTPOD_ID parameter, and call textpod-org-region-to-note on it.

The command {M-x textpod-org-heading-to-note} works!

Should the textpod id be just id?

Currently I use TEXTPOD_ID property. If using ID property, notes will automatically become Org-roam nodes. Currently IDs are generated as timestamps via (setq org-id-method 'ts) with nanoseconds precision. Then it will come with seconds precision. I will need to a prefix, to be able to quickly find them (e.g. with {M-x deadgrep}).

Wrap h3 into details on index page

To declutter the index page, h3 markdown headings (h2 Org headings) are wrapped into <details> tag. The note page renders normally, showing table of contents on the left sidebar.

Trying Textpod once again

On <2026-04-13 Mon> I installed Textpod on Mac Book Pro M1 using Cargo (Rust tooling).

Good:

Open questions:

I ended up forking it to https://github.com/velppa/textpod and greatly improving it for my needs. You're reading notes served by Textpod v0.2.0. It is my project.

Notable changes:

Later changes to setup will go into separate notes.

Snowflake MCP server

MCP server for Snowflake.

kumo – local AWS emulator in Go

Similar to Openstack. A lightweight AWS service emulator written in Go.

Claude Code quota exhausted / degraded quality

Claude Code apparently degraded in coding tasks.