Document the interface in the readme
Missed by the previous commit: the shell line carrying the edit had a bad cd and never ran, while the commit after it still went through. Second time that has happened in this repo, both times because the working directory had already moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4ecfc2c parent: 2b72cef modified
spike/nim/readme.md +25 -2 | @@ -7,11 +7,23 @@ nim c -d:ssl glean.nim | ||
| 7 | 7 | ./glean |
| 8 | 8 | ``` |
| 9 | 9 | |
| 10 | -Listens on `127.0.0.1:8080` and stores its database under | |
| 11 | -`~/.local/share/glean/`. Override with `GLEAN_PORT` and `GLEAN_DB`. | |
| 10 | +Then open <http://127.0.0.1:8080>. The database lives under | |
| 11 | +`~/.local/share/glean/`; override with `GLEAN_PORT` and `GLEAN_DB`. | |
| 12 | + | |
| 13 | +## The interface | |
| 14 | + | |
| 15 | +A two-pane reader: feeds on the left, articles on the right, the article in | |
| 16 | +place. Add a feed, refresh, search, read. Opening an article marks it read; | |
| 17 | +"Fetch full text" runs the scraper over the original page, for feeds that | |
| 18 | +only ship an excerpt. | |
| 19 | + | |
| 20 | +One HTML file, no build step, no dependencies, baked into the binary with | |
| 21 | +`staticRead` so there is still only one file to copy. | |
| 12 | 22 | |
| 13 | 23 | ## API |
| 14 | 24 | |
| 25 | +The interface is built on this, and it is usable on its own. | |
| 26 | + | |
| 15 | 27 | ``` |
| 16 | 28 | GET /feeds subscriptions, with unread counts |
| 17 | 29 | POST /feeds url=… subscribe and fetch immediately |
| @@ -44,6 +56,17 @@ The one visible consequence: **do not expose it to a network.** It binds | ||
| 44 | 56 | loopback deliberately. Anything that can reach the port can read and change |
| 45 | 57 | everything, because there is nobody to distinguish from anybody else. |
| 46 | 58 | |
| 59 | +## A note on rendering feeds | |
| 60 | + | |
| 61 | +Article bodies are markup from someone else's server, and the interface puts | |
| 62 | +them in a browser. Every body the API serves -- scraped *and* straight from | |
| 63 | +the feed -- goes through the same whitelist the scraper uses: `script`, | |
| 64 | +`style` and `iframe` never survive, attributes are allowed rather than | |
| 65 | +denied, and a link that does not point at http(s) is flattened to its text. | |
| 66 | + | |
| 67 | +Titles and summaries are set with `textContent` in the page, for the same | |
| 68 | +reason. `innerHTML` is used in exactly one place: the sanitised body. | |
| 69 | + | |
| 47 | 70 | ## What it is built on |
| 48 | 71 | |
| 49 | 72 | Six modules under `reader/`: |
| @@ -7,11 +7,23 @@ nim c -d:ssl glean.nim | |||
| 7 | ./glean | 7 | ./glean |
| 8 | ``` | 8 | ``` |
| 9 | 9 | ||
| 10 | -Listens on `127.0.0.1:8080` and stores its database under | 10 | +Then open <http://127.0.0.1:8080>. The database lives under |
| 11 | -`~/.local/share/glean/`. Override with `GLEAN_PORT` and `GLEAN_DB`. | 11 | +`~/.local/share/glean/`; override with `GLEAN_PORT` and `GLEAN_DB`. |
| 12 | + | ||
| 13 | +## The interface | ||
| 14 | + | ||
| 15 | +A two-pane reader: feeds on the left, articles on the right, the article in | ||
| 16 | +place. Add a feed, refresh, search, read. Opening an article marks it read; | ||
| 17 | +"Fetch full text" runs the scraper over the original page, for feeds that | ||
| 18 | +only ship an excerpt. | ||
| 19 | + | ||
| 20 | +One HTML file, no build step, no dependencies, baked into the binary with | ||
| 21 | +`staticRead` so there is still only one file to copy. | ||
| 12 | 22 | ||
| 13 | ## API | 23 | ## API |
| 14 | 24 | ||
| 25 | +The interface is built on this, and it is usable on its own. | ||
| 26 | + | ||
| 15 | ``` | 27 | ``` |
| 16 | GET /feeds subscriptions, with unread counts | 28 | GET /feeds subscriptions, with unread counts |
| 17 | POST /feeds url=… subscribe and fetch immediately | 29 | POST /feeds url=… subscribe and fetch immediately |
| @@ -44,6 +56,17 @@ The one visible consequence: **do not expose it to a network.** It binds | |||
| 44 | loopback deliberately. Anything that can reach the port can read and change | 56 | loopback deliberately. Anything that can reach the port can read and change |
| 45 | everything, because there is nobody to distinguish from anybody else. | 57 | everything, because there is nobody to distinguish from anybody else. |
| 46 | 58 | ||
| 59 | +## A note on rendering feeds | ||
| 60 | + | ||
| 61 | +Article bodies are markup from someone else's server, and the interface puts | ||
| 62 | +them in a browser. Every body the API serves -- scraped *and* straight from | ||
| 63 | +the feed -- goes through the same whitelist the scraper uses: `script`, | ||
| 64 | +`style` and `iframe` never survive, attributes are allowed rather than | ||
| 65 | +denied, and a link that does not point at http(s) is flattened to its text. | ||
| 66 | + | ||
| 67 | +Titles and summaries are set with `textContent` in the page, for the same | ||
| 68 | +reason. `innerHTML` is used in exactly one place: the sanitised body. | ||
| 69 | + | ||
| 47 | ## What it is built on | 70 | ## What it is built on |
| 48 | 71 | ||
| 49 | Six modules under `reader/`: | 72 | Six modules under `reader/`: |