---
title: 'Feedia BBS'
url: 'https://wiki.teletypegames.org/services/feedia-bbs'
markdown: 'https://wiki.teletypegames.org/services/feedia-bbs.md'
lang: en
date: '2026-10-27'
description: 'Pick a feed from the list, and Feedia fetches it and shows its items in a form a terminal can read: the title, the date, the text wrapped to the screen, its links numbered under it. Live at teletypegames.org:2327, and in the browser on bbsweb, and through TTG Gate BBS Overview A Ruby app on the r…'
taxonomy:
  tag:
    - howto
---

# Feedia BBS

> Pick a feed from the list, and Feedia fetches it and shows its items in a form a terminal can read: the title, the date, the text wrapped to the screen, its links numbered under it.
> 
> **Live at `teletypegames.org:2327`**, and in the browser on [bbsweb](https://bbsweb.teletypegames.org), and through [TTG Gate BBS](https://wiki.teletypegames.org/services/ttg-gate-bbs)

# Overview

A Ruby app on the [**rubbs**](https://git.teletypegames.org/engines/rubbs) gem, like the [Teletype BBS](https://wiki.teletypegames.org/services/teletype-bbs) and [Alexandria BBS](https://wiki.teletypegames.org/services/alexandria-bbs). It has two faces on one port:

- **Modern** — for ANSI terminals (SyncTERM, NetRunner, PuTTY, bbsweb): a desktop under a menubar, windows, mouse.
- **Commodore 64** — PETSCII on a 40×25 screen with single-key menus, for a C64 terminal program (CCGMS, StrikeTerm, SyncTERM in C64 mode) behind a WiFi modem.

A call starts with one question: which terminal is calling. Feedia guesses first from the telnet answers (a window wider than 40 columns is a modern terminal, 40 or no answer a C64); RETURN takes the guess, `1` picks the modern face, `2` the C64.

# The feeds

The list is in [`config/feeds.yml`](https://git.teletypegames.org/bbs/feedia-bbs/src/branch/master/config/feeds.yml): tech (Hacker News, Lobsters, Ars Technica, The Register, OSNews, Hackaday, HUP), retro (Indie Retro News, Vintage Computing and Gaming, RetroRGB), science (NASA) and news (BBC World, Telex, 444, HVG). Each entry has a name and an address, and optionally a group, a language and a line about it. A new feed is a commit to that file: a push to master deploys it. RSS 0.9x/2.0, RSS 1.0 (RDF) and Atom all work.

A feed is fetched when it is opened and kept for 10 minutes; up to 60 items are shown. The item's HTML becomes plain text: paragraphs, headings, lists and quotes stay, a link keeps its text with a number (`Comments[1]`) and its address is listed under the text, an image keeps its alt text. Most feeds carry only a summary: the item's own address is under it.

## Modern face

| Key | Action |
|---|---|
| `F2` | Feed list |
| `F7` | Suggest a feed… |
| `F8` | Send feedback… |
| `Enter` | Open the feed |
| `Alt-S` | Send (in the forms) |
| `F5` / `Alt-Enter` | Full screen and back |
| `Esc` | Close the window |
| `Alt-X` | Log off |

The feed list has the feeds on the left and the one picked on the right; a feed opens in its own window, the items on the left and the text on the right. `Reload` fetches the feed again, past the cache.

## C64 face

The menu: `F` feeds, `S` suggest a feed, `W` write to the admins, `Q` log off. Feeds and items are numbered lists: the number and RETURN opens one, RETURN alone turns the page, `Q` or `←` goes back. An item stops at every screenful.

# The Admins menu

Callers can **suggest a feed** (its address, name, why) or **send feedback**. Each message is a JSON line in the stack's `data/inbox.jsonl`, and a message on the admins' Discord webhook (`FEEDIA_BBS_DISCORD_WEBHOOK` in the stack's `.env`; mentions off). One call can send five. Feedia never fetches a suggested address itself: an admin looks at it and adds it to `config/feeds.yml` if it stays. The inbox on the server:

```bash
docker exec feedia-bbs bin/inbox        # the newest 20
```

# Under the hood

Every request names Feedia in its User-Agent; a feed may be 4 MB at most and redirect four times. Control characters are dropped from everything a feed sends, so it cannot send escape sequences to the terminal. The C64 face writes the same text as the modern one; rubbs' PETSCII line turns it into the C64's character set (accented letters become their nearest ASCII).

A push to master deploys it from Woodpecker (`ci-deploy feedia-bbs`); the container is `feedia-bbs`, in `/srv/stacks/feedia-bbs`.

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [Services](https://wiki.teletypegames.org/services.md)
- Previous: [Discord bots](https://wiki.teletypegames.org/services/discord-bots.md)
- Next: [Impostor BBS](https://wiki.teletypegames.org/services/impostor-bbs.md)
