No description
  • Python 98.8%
  • Dockerfile 1.2%
Find a file
Erick Ruiz de Chavez 50ca317c41 Replace tinylog with Station (Gemini social network) mirroring
Replaces the self-hosted Gemini tinylog with real-time mirroring to
Station (gemini://station.martinrue.com) via the Gemini protocol and
TLS client certificates. Posts, self-replies, edits, and deletes are
all kept in sync. Long posts are split into continuation self-replies
to fit Station's 1024-byte URL limit. Media attachments are appended
as gemtext link lines pointing back to the GTS media URLs.

Removes tinylog.py, feed.py, all backfill/SYNC_SINCE logic, antenna
pings, and simplifies db.py schema (drops created_at and state table).

Assisted-By: Claude <noreply@anthropic.com>
2026-06-08 11:20:51 +00:00
.env.example Add feed.gmi generation and Gemini antenna pings 2026-05-16 08:33:36 -04:00
.gitignore Initial implementation of GTS → Gemini tinylog generator 2026-05-16 07:13:28 -04:00
.python-version Initial implementation of GTS → Gemini tinylog generator 2026-05-16 07:13:28 -04:00
CLAUDE.md Replace tinylog with Station (Gemini social network) mirroring 2026-06-08 11:20:51 +00:00
compose.yml fix user permissions 2026-05-24 10:06:17 -04:00
db.py Replace tinylog with Station (Gemini social network) mirroring 2026-06-08 11:20:51 +00:00
Dockerfile fix user permissions 2026-05-24 10:06:17 -04:00
gts.py Replace tinylog with Station (Gemini social network) mirroring 2026-06-08 11:20:51 +00:00
main.py Replace tinylog with Station (Gemini social network) mirroring 2026-06-08 11:20:51 +00:00
pyproject.toml Initial implementation of GTS → Gemini tinylog generator 2026-05-16 07:13:28 -04:00
README.md Update README and CLAUDE.md for feed and antenna features 2026-05-16 08:43:49 -04:00
station.py Replace tinylog with Station (Gemini social network) mirroring 2026-06-08 11:20:51 +00:00
uv.lock Initial implementation of GTS → Gemini tinylog generator 2026-05-16 07:13:28 -04:00

tinylog

Generates and keeps a tinylog-format .gmi file from public posts on a GoToSocial instance, for serving on a Gemini capsule. Connects to the GoToSocial streaming API for real-time updates and handles adds, edits, and deletes.

Features

  • Real-time streaming via GoToSocial WebSocket API (no polling)
  • Full backfill of existing public posts on first run
  • Propagates edits and deletes
  • Self-reply threads collapsed into a single tinylog entry with a [continued] separator
  • HTML-to-gemtext conversion: lists, blockquotes, preformatted blocks, footnote-style links
  • Skips posts with content warnings, replies to other accounts, non-public posts, local-only posts, and posts that mention other accounts
  • Atomically writes the .gmi file so Agate never serves a partial result
  • Regenerates the full file on every startup (safe to restart after config changes)
  • Generates a feed.gmi compatible with Gemini antenna aggregators and pings them on every update

Setup

cp .env.example .env
# fill in .env
docker compose up --build -d

Map the output file into your Agate content directory via a volume in compose.yml:

volumes:
  - ./data:/app/data
  - /var/gemini/capsule/tinylog.gmi:/app/tinylog.gmi

Configuration

All configuration is via environment variables (.env):

Variable Required Default Description
GTS_BASE_URL yes Your GoToSocial instance URL
GTS_ACCESS_TOKEN yes OAuth bearer token with read scope
TINYLOG_TITLE no Tinylog Level-1 heading in the .gmi file
AUTHOR_NAME no Author name for the author: metadata line
AUTHOR_CAPSULE no Gemini capsule URL for the author: metadata line
AUTHOR_AVATAR no Avatar emoji (optionally followed by a description)
AUTHOR_DESCRIPTION no Free-text description shown below the title
TINYLOG_PATH no /app/tinylog.gmi Output path inside the container
FEED_PATH no Output path for feed.gmi; required to enable feed generation
FEED_TITLE no Link label in feed.gmi (e.g. Microblog); required to enable feed generation
FEED_URL no Full Gemini URL of feed.gmi; sent to antenna endpoints
ANTENNA_URLS no Comma-separated Gemini antenna submit URLs to ping on each update
SYNC_SINCE no now First-run behaviour (see below)
LOG_LEVEL no INFO DEBUG, INFO, WARNING, ERROR

GTS access token

Create an application on your GoToSocial instance and complete the OAuth flow to obtain a bearer token with read scope. No write scope is needed.

SYNC_SINCE (first run only)

Controls what happens the very first time the program runs against an empty database. Ignored on subsequent starts.

Value Behaviour
now (default) Sets a watermark at the current latest post; no historical posts are included
all Backfills everything GoToSocial can return (paginated)
2024-05-01 Backfills posts from that date onward (ISO date or datetime)

Development

# Run locally (requires .env)
uv run python main.py

# Override sync behaviour
SYNC_SINCE=all uv run python main.py

There are no automated tests in this project.