- Python 90.1%
- JavaScript 7.2%
- HTML 1.4%
- CSS 1.2%
| .woodpecker | fix: batch embeds | |
| deploy | feat(mcp): SSE support | |
| docs | Parse HTML instead of regex | |
| scripts | More signed headers | |
| src/backend | feat: remove food analysis | |
| stubs | Parse HTML instead of regex | |
| tests | Add coverage | |
| .gitignore | feat: gemini server | |
| LICENSE.md | Add license | |
| Makefile | feat: commands for rebuilding FTS/embeddings | |
| package-lock.json | chore: JS tests | |
| package.json | chore: JS tests | |
| prek.toml | fix: add missing conf | |
| pyproject.toml | feat: show pronouns | |
| README.md | fix: lints | |
| uv.lock | feat: show pronouns | |
| vitest.config.js | More tests | |
Robida
A personal IndieWeb site that serves as identity, blog, social reader, and quantified-self hub. Built with Python (Litestar) and vanilla JS, backed by SQLite.
This is the software that powers robida.net. It's a single-user site — not a framework or CMS — but the code is public in case it's useful to anyone building their own IndieWeb tools.
What it does
Identity & Auth — The site is its own OAuth2/OIDC provider. I sign in to IndieWeb services with my domain. Implements IndieAuth (both client and server), RelMeAuth with GitHub/Codeberg, and dynamic client registration (RFC 7591).
Publishing — Posts are stored as microformats2 JSON in SQLite and rendered as semantic HTML with mf2dom. Supports notes, articles, photos, bookmarks, likes, replies, RSVPs, checkins, and more. Publishing happens via Micropub.
Social Reader — A built-in feed reader that subscribes to RSS, Atom, JSONFeed, JF2, and h-feed sources. Implements Microsub for channel management and timeline reading. Guest users can like and reply to posts, stored locally.
Webmentions — Sends and receives Webmentions for cross-site replies, likes, and reposts. Outgoing mentions are queued and processed asynchronously.
Feeds — Publishes content as Atom, RSS, JSONFeed, JF2, and h-feed.
Offline-first sync — The frontend runs a local SQLite database (sql-wasm.js persisted to IndexedDB) that syncs bidirectionally with the server via WebSocket using Hybrid Logical Clocks. The site works offline — you can compose posts, like things, and browse your feed without a connection.
Quantified Self — Integrates with external services and creates h-entry posts for each data point:
- Oura Ring — sleep, activity, readiness scores
- Withings — weight, body composition
- Strava — workouts
- Trakt — TV shows and movies watched
- Steam — game playtime sessions
- ListenBrainz — music scrobbles
Semantic Search & MCP — Entry content is embedded using fastembed (all-MiniLM-L6-v2, 384 dimensions) and searched via cosine similarity. Exposed as a Model Context Protocol server so Claude can search my posts and query health data.
Architecture
┌──────────────┐ WebSocket ┌──────────────┐
│ Browser │◄──────────────────►│ Litestar │
│ sql-wasm.js │ HLC sync │ (async) │
│ IndexedDB │ │ SQLite │
└──────────────┘ └──────┬───────┘
│
┌───────────┼───────────┐
│ │ │
Micropub Microsub Webmention
(publish) (read) (social)
- Backend: Litestar (async ASGI), aiosqlite, SQLite in WAL mode
- Frontend: Vanilla JS modules, no bundler. Local SQLite via sql-wasm.js
- Sync: Hybrid Logical Clocks — 48-bit physical ms + 16-bit logical counter. Preserves causality without a centralized clock
- Content model: Everything is microformats2 JSON in a single
microformats2table. Rich column types (URL,HLC,TIMESTAMP,JSON) with registered adapters - Rendering: mf2dom — isomorphic mf2-to-HTML renderer (Python for server, TypeScript for client)
Points of interest
If you're exploring the code, these might be the most interesting parts:
| Area | Files | What's there |
|---|---|---|
| HLC sync | hlc.py, sync.py, sync.js |
Bidirectional CRDT sync between server and browser SQLite |
| IndieAuth/OIDC | auth/ |
Full OAuth2 + OIDC provider with PKCE, refresh tokens, dynamic client registration |
| Micropub | micropub/ |
Create/update/delete posts, media uploads, slug generation |
| Microsub | microsub/ |
Channel management, timeline, feed subscriptions |
| Webmentions | webmention/, sender.py |
Receiving, sending, and processing cross-site interactions |
| Feed parsing | feeds/ |
RSS, Atom, JSONFeed, JF2, h-feed, h-card discovery |
| Semantic search | embeddings.py |
fastembed vectors, brute-force cosine similarity |
| MCP server | mcp/ |
JSON-RPC over HTTP, tool registry with per-tool scope enforcement |
| Offline-first UI | static/js/ |
Local DB, sync client, SPA router, entry rendering |
Running
This is a personal site, not a product — but if you want to poke around:
# Install dependencies
uv sync --dev
# Run the dev server
make dev
# Run tests (100% coverage enforced)
make pr
Requires Python 3.11+ and Node.js (for JS tests via Vitest).
License
GNU Affero General Public License 3.