rss email digests over ssh because you're a cool kid
herald.dunkirk.sh
go
rss
rss-reader
ssh
charm
1# Herald 🎏
2
3
4
5This was inspired by the sunsetting of [pico.sh/feeds](https://blog.pico.sh/ann-033-moving-rss-to-email-pico-plus) being available outside of `pico+`. It is a totally understandable move from them as their email costs were skyrocketing and they needed to pay for it somehow. This was created to allow me to still get my rss feeds delivered to me each day by email which I have grown quite accustomed to. The config is completely compatible with the `pico.sh` format as of `2026-01-09` and should stay fairly stable. It is also configured over ssh with the slight addition that you can view your feeds on a website as well as I found myself wanting to hot load my feeds into my website :)
6
7The canonical repo for this is hosted on tangled over at [`dunkirk.sh/herald`](https://tangled.org/@dunkirk.sh/herald)
8
9## Quick Start
10
11```bash
12# Build
13go build -ldflags "-X main.commitHash=$(git log -1 --format=%H)" -o herald .
14
15# Run the server
16./herald serve
17
18# Or with a config file
19./herald serve -c config.yaml
20```
21
22> **Note:** The commit hash is automatically detected at runtime if not embedded at build time.
23
24## Usage
25
26### Upload a config
27
28Create a `feeds.txt` file:
29
30```text
31=: email you@example.com
32=: cron 0 8 * * *
33=: digest true
34=> https://dunkirk.sh/atom.xml
35=> https://news.ycombinator.com/rss
36=> https://lobste.rs/rss "Lobsters"
37```
38
39Upload via SCP:
40
41```bash
42scp feeds.txt user@herald.dunkirk.sh:
43```
44
45### SSH Configuration
46
47Add this to your `~/.ssh/config` for easier access:
48
49```ssh-config
50Host herald
51 HostName herald.dunkirk.sh
52 Port 2223
53 User herald
54```
55
56Then use: `scp feeds.txt herald:` and `ssh herald ls`
57
58### SSH Commands
59
60```bash
61# Get your fingerprint (for web dashboard)
62ssh herald.dunkirk.sh
63
64# List your configs
65ssh herald.dunkirk.sh ls
66
67# Show config contents
68ssh herald.dunkirk.sh cat feeds.txt
69
70# Delete a config
71ssh herald.dunkirk.sh rm feeds.txt
72
73# Activate/deactivate configs
74ssh herald.dunkirk.sh activate feeds.txt
75ssh herald.dunkirk.sh deactivate feeds.txt
76
77# Run immediately (don't wait for cron)
78ssh herald.dunkirk.sh run feeds.txt
79
80# Show recent activity
81ssh herald.dunkirk.sh logs
82```
83
84### Web Interface
85
86Visit `http://localhost:8080` for the landing page.
87
88After uploading a config, run `ssh herald.dunkirk.sh` to get your fingerprint, then visit:
89
90- `http://localhost:8080/{fingerprint}` - Your dashboard with config status
91- `http://localhost:8080/{fingerprint}/feeds.xml` - RSS feed for feeds.txt
92- `http://localhost:8080/{fingerprint}/feeds.json` - JSON feed for feeds.txt
93
94## Config Format
95
96### Directives
97
98| Directive | Required | Description |
99| ------------------- | -------- | ------------------------------------------------- |
100| `=: email <addr>` | Yes | Recipient email address |
101| `=: cron <expr>` | Yes | Standard cron expression (5 fields) |
102| `=: digest <bool>` | No | Combine all items into one email (default: true) |
103| `=: inline <bool>` | No | Include article content in email (default: false) |
104| `=> <url> ["name"]` | Yes (1+) | RSS/Atom feed URL, optional display name |
105
106## Configuration
107
108Create a `config.yaml`:
109
110```yaml
111host: 0.0.0.0
112ssh_port: 2222
113http_port: 8080
114
115host_key_path: ./host_key
116db_path: ./herald.db
117
118smtp:
119 host: smtp.example.com
120 port: 587
121 user: sender@example.com
122 pass: ${SMTP_PASS}
123 from: herald@example.com
124
125allow_all_keys: true
126```
127
128Environment variables can also be used:
129
130- `HERALD_HOST`
131- `HERALD_SSH_PORT`
132- `HERALD_HTTP_PORT`
133- `HERALD_DB_PATH`
134- `HERALD_SMTP_HOST`
135- `HERALD_SMTP_PORT`
136- `HERALD_SMTP_USER`
137- `HERALD_SMTP_PASS`
138- `HERALD_SMTP_FROM`
139
140## Screenshots
141
142here is an example of what an email digest looks like:
143
144
145
146<p align="center">
147 <img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/main/.github/images/line-break.svg" />
148</p>
149
150<p align="center">
151 <i><code>© 2025-present <a href="https://dunkirk.sh">Kieran Klukas</a></code></i>
152</p>
153
154<p align="center">
155 <a href="https://tangled.org/dunkirk.sh/herald/blob/main/LICENSE.md"><img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=O'Saasy&logoColor=d9e0ee&colorA=363a4f&colorB=b7bdf8"/></a>
156 <a href="https://codecov.io/gh/taciturnaxolotl/harold"><img src="https://img.shields.io/codecov/c/github/taciturnaxolotl/harold?style=for-the-badge&logoColor=d9e0ee&colorA=363a4f&colorB=a6da95"/></a>
157</p>