Building slop.at: A Minimal Semantic Web Publisher

What We Built

A complete publishing system that takes markdown, extracts entities, builds a knowledge graph, and renders beautiful HTML - all with zero manual tagging.

The Stack

Frontend:

Backend:

Security:

The Flow

  1. Write markdown in Claude or Obsidian
  2. MCP extracts entities using GLiNER2 (37 entities from our longest doc!)
  3. Build RDF graph with know.dev ontology
  4. Push to GitHub for permanent storage
  5. POST to web server which renders + stores in Oxigraph
  6. View at slop.at/s/{hash} with color-coded entity highlighting

Key Design Decisions

GitHub as Storage

User owns their data, not us. If slop.at goes down, their content is safe in their repo. Also: free hosting, version control, and Obsidian integration.

8-Character Slop IDs

Changed from full UUIDs to just the first 8 characters. Cleaner URLs, still collision-resistant for any realistic usage.

Frontmatter at Bottom

Spent way too many iterations getting the YAML frontmatter spacing right. Final solution: hide it with display: none initially, then moved to bottom with minimal styling when user requested it back.

No API Keys

Rejected environment variable approach. Too much friction, leaks into logs. Went with "deploy it and see if it gets abused" strategy. Can always add GitHub webhook auth later.

Opacity = Confidence

Entity highlighting uses opacity to show GLiNER2's confidence:

Lets you see which extractions are solid vs tentative.

What Worked Surprisingly Well

GLiNER2 entity extraction - Caught "Poincaré Embeddings", "Hyperbolic Space", "GraphCodeBERT", and tons of technical terms with zero training on our docs. Just works.

Mistune markdown rendering - Fast, supports plugins, has HTML escaping built in. Perfect fit.

FastAPI + Oxigraph - Simple architecture: POST receives data, stores JSON + pushes to graph. GET reads JSON, highlights entities, renders HTML. That's it.

American Typewriter font - Looks way better than sans-serif for reading long technical docs. Personality matters.

The Iteration That Took Forever

Frontmatter spacing. Multiple rounds of:

Final fix: margin: 0; padding: 0; everywhere + display: none. Then brought it back at the bottom with margin-top: 2rem; padding-top: 2rem;. Perfectly balanced.

What We Tested

Posted 5 dense technical documents (ontology gardening, malware detection, SPARQL explorations, parser testing, LSP integration). Total entity extractions:

All rendered cleanly with color-coded highlights.

Security Hardening

Quick wins we added:

  1. HTML escaping in mistune (escape=True)
  2. Content size limits (1MB default)
  3. Rate limiting (20/minute per IP)

What we explicitly rejected:

What's Next

From TODO.md:

The Philosophy

Boring technology that works.

No blockchain, no AI hype, no unnecessary complexity. Just: write markdown → get knowledge graph → publish beautiful HTML.

Lessons Learned

1. Start with hardening, not auth HTML escaping and size limits are easy wins. Auth is complex and can wait.

2. User owns data = better UX GitHub storage means users trust the system more. They can clone, backup, edit in any tool.

3. Opacity for confidence is intuitive Bright colors = high confidence. Faded = uncertain. No numbers needed.

4. 8 characters is plenty UUIDs are overkill. Short hashes are friendlier and collision-resistant enough.

5. Fonts matter American Typewriter makes slop.at feel different from every other markdown renderer. Character counts.

The Best Part

Spent hours fighting CSS spacing, but when it clicked: zero padding achieved! 🐐✨

That moment when the frontmatter finally tucked itself nicely at the bottom with perfect spacing... chef's kiss.


Built in one session. From "let's start fresh" to "this is actually pretty solid." The power of simple tools and clear constraints.

Building slop.at: A Minimal Semantic Web Publisher
by @goodlux with claude-sonnet-4-5-20250929
2026-01-17T07:10:07+00:00