Rewrite README for current Responder-based architecture

Was still describing Flask blueprints, gunicorn, and old project
structure. Now reflects the single-file Responder app, Fly.io
deployment, cached search, bot detection, and actual feature set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 18:42:13 -04:00
parent f28356a3ac
commit 3ca77dfa19
+67 -294
View File
@@ -1,320 +1,93 @@
# TufteCMS
# kennethreitz.org
A Flask-based content management system designed for thoughtful writing, inspired by Edward Tufte's design principles. Built for digital gardens, essay collections, and content-rich websites that prioritize readability and deep engagement.
Kenneth Reitz's personal website and digital garden. A living exploration of how technology can serve human consciousness rather than exploit it.
> **Note**: This repository is a hybrid containing both the TufteCMS framework (in `tuftecms/`) and Kenneth Reitz's personal website content (in `data/`). The CMS powers kennethreitz.org and serves as a living example of the system in production.
Built with [Responder](https://github.com/kennethreitz/responder), powered by markdown content, deployed on [Fly.io](https://fly.io).
## Quick Start
### Option 1: Docker Compose (Recommended)
The easiest way to get started with all dependencies included:
```bash
# Clone the repository
git clone https://github.com/kennethreitz/tuftecms.git
cd tuftecms
# Start with Docker Compose
docker compose up
```
The site will be available at `http://localhost:8000` with hot-reloading enabled. All dependencies, including PDF generation, are pre-configured.
### Option 2: Local Development with uv
For local development without Docker:
```bash
# Clone the repository
git clone https://github.com/kennethreitz/tuftecms.git
cd tuftecms
# Install dependencies with uv (recommended)
# Install dependencies
uv sync
# Run development server
uv run python engine.py
```
The site will be available at `http://localhost:8000`
**Note**: PDF generation requires additional system libraries (see below). The site works fully without them - PDFs just won't be available.
### Installing uv
If you don't have `uv` installed:
```bash
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# Or via pip
pip install uv
```
## Philosophy
TufteCMS embraces **human-first design** - content presentation that serves readers' mental models rather than forcing adaptation to machine logic. It provides powerful features like sidenotes, content indexing, and cross-referencing while maintaining simplicity and elegance.
## Features
### Content & Typography
- **Tufte-style sidenotes** - Margin commentary without disrupting reading flow
- **Responsive typography** - Optimized for reading across all devices
- **Markdown rendering** - Custom Mistune renderer with philosophical code blocks
- **File-based content** - Simple directory structure mirrors URL routing
### Discovery & Navigation
- **Full-text search** - Relevance-scored search with contextual snippets
- **Content indexes** - Automatic extraction of sidenotes, quotes, outlines, connections, terms
- **Cross-referencing** - Track incoming/outgoing links between content
- **Theme detection** - Automatic categorization by content patterns
- **HTML & XML sitemaps** - Both human and machine-readable navigation
### Visual Identity
- **Generated SVG icons** - Unique algorithmic icons for every piece of content
- **Deterministic design** - Same title always produces the same icon
- **Dynamic folder icons** - Color variations based on content titles
- **Reading progress** - Visual indicator for longer essays
### Performance
- **Intelligent caching** - LRU caches for blog posts, indexes, and metadata
- **Background cache warming** - Async startup loading for instant response
- **Lazy loading** - Images load via IntersectionObserver
- **HTTP caching headers** - Aggressive caching for static assets
- **Cache debugging** - API endpoint to monitor cache performance
### Developer Experience
- **Flask blueprints** - Modular architecture with clear separation
- **API endpoints** - JSON APIs for search, icons, and debugging
- **RSS/Atom feeds** - Standard feed formats for syndication
- **Template system** - Jinja2 templates with extensible blocks
## PDF Generation Setup (Optional)
TufteCMS includes server-side PDF generation using WeasyPrint. This requires system libraries:
**macOS (via Homebrew):**
```bash
brew install cairo pango gdk-pixbuf libffi
```
**Ubuntu/Debian:**
```bash
sudo apt-get install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
```
**Docker:**
The provided Dockerfile includes these dependencies automatically.
**Note:** If you skip this step, the site will work fine but `.pdf` URLs will not function. All other features work without these libraries.
## Project Structure
```
tuftecms/
├── tuftecms/
│ ├── app.py # Application factory
│ ├── config.py # Configuration management
│ ├── blueprints/ # Route handlers
│ │ ├── main.py # Homepage and core routes
│ │ ├── content.py # Content rendering
│ │ ├── api.py # JSON API endpoints
│ │ └── feeds.py # RSS/sitemap generation
│ ├── core/ # Core functionality
│ │ ├── cache.py # Caching system
│ │ ├── content.py # Content processing
│ │ └── markdown.py # Markdown rendering
│ ├── utils/ # Utilities
│ │ ├── content.py # Content helpers
│ │ └── svg_icons.py # Icon generation
│ ├── templates/ # Jinja2 templates
│ └── static/ # CSS, images, fonts
├── data/ # Content directory
│ └── essays/ # Your content here
├── engine.py # Development server
└── pyproject.toml # Dependencies
```
## Content Organization
### Directory Structure
Content is organized in a simple directory structure under `data/`:
```
data/
├── essays/
│ ├── 2025-01-15-example-essay.md
│ └── 2025-02-20-another-essay.md
├── notes/
└── index.md
```
URL paths mirror the file system: `/essays/2025-01-15-example-essay`
### Writing with Sidenotes
Sidenotes provide commentary without disrupting reading flow:
```markdown
This is the main text.<label for="sn-example" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-example" class="margin-toggle"/><span class="sidenote">This is a sidenote that adds depth without breaking the narrative.</span> The text continues naturally.
```
**Critical formatting rules:**
- Sidenotes must be inline (attached to sentence end with NO line breaks)
- Use descriptive IDs like `sn-consciousness`, `sn-recursion`
- Keep away from code blocks to prevent layout issues
### Markdown Features
```markdown
# Essay Title
*Published: January 2025*
Standard markdown with [links](/other-essay) and **emphasis**.
Code blocks with philosophy:
\`\`\`python
def consciousness():
# TODO: Model the unmappable
pass
\`\`\`
> Blockquotes for memorable passages
```
## Configuration
Edit `tuftecms/config.py`:
```python
class Config:
DISABLE_ANALYTICS = True # Privacy-first by default
SEARCH_CACHE_TIMEOUT = 60
DATA_DIR = "data"
MIN_SEARCH_QUERY_LENGTH = 2
MAX_SEARCH_RESULTS = 50
```
## API Endpoints
### Search
```
GET /api/search?q=query
```
Returns JSON with relevance-scored results, snippets, and match locations.
### Cache Debugging
```
GET /api/debug-cache
```
Returns cache statistics and LRU cache performance metrics.
### Icon Generation
```
GET /api/icon/<path:article_path>
```
Returns generated SVG icon data for any content path.
## Content Indexes
TufteCMS automatically builds indexes:
- **Sidenotes** - All margin notes with context
- **Outlines** - Heading structure across content
- **Quotes** - Blockquoted passages
- **Connections** - Internal link graph
- **Terms** - Key terms appearing across multiple articles
- **Themes** - Pattern-based content categorization
Access via routes: `/sidenotes`, `/outlines`, `/quotes`, `/connections`, `/terms`
## Deployment
The site will be available at `http://localhost:8000`.
### Docker
```bash
docker build -t tuftecms .
docker run -p 8000:8000 tuftecms
docker compose up
```
### Production with Gunicorn
## Architecture
This is a single-file Responder application (`engine.py`) serving markdown content from `data/`.
```
kennethreitz.org/
├── engine.py # The whole application
├── data/ # All content (markdown)
│ ├── essays/ # 250+ essays (2008-2026)
│ ├── software/ # Project pages
│ ├── themes/ # Thematic collections
│ ├── artificial-intelligence/
│ ├── poetry/
│ ├── photography/
│ ├── music/
│ └── *.md # Standalone pages
├── tuftecms/
│ ├── templates/ # Jinja2 templates
│ ├── static/ # CSS, fonts, images
│ ├── core/ # Cache, markdown rendering
│ └── utils/ # Content helpers, SVG icons
├── Dockerfile
├── fly.toml # Fly.io deployment config
└── pyproject.toml
```
URL paths mirror the filesystem: `data/essays/2025-08-26-example.md``/essays/2025-08-26-example`
## Features
- **Tufte-style sidenotes** — margin commentary without disrupting flow.
- **Full-site search** — cached index built at startup, server-side autocomplete.
- **Content indexes** — automatic extraction of sidenotes, quotes, outlines, connections, terms, themes.
- **Legacy URL redirects** — intelligent matching for old URL patterns (301 permanent).
- **Bot detection** — logs scraper activity with User-Agent identification.
- **Structured logging** — request-scoped context (ID, method, path, client IP) on every log line.
- **PDF generation** — server-side via WeasyPrint.
- **Generated SVG icons** — unique algorithmic icons for every piece of content.
- **RSS feed** — at `/feed.xml`.
- **Knowledge graph** — interactive D3 visualization of content connections.
- **Dark mode** — system-preference and manual toggle.
## API Endpoints
```
GET /api/search?q=query # Full-site search with scoring
GET /api/search/autocomplete?q=q # Title-based autocomplete (8 results)
GET /api/blog # Essay listing
GET /api/themes # Theme listing with icons
GET /api/directory-tree # Site directory structure
GET /api/icon/<path> # Generated SVG icon
GET /api/cache-stats # Cache performance metrics
```
## Deployment
Deployed on Fly.io with 2 shared CPUs, 2GB RAM, 2 uvicorn workers.
```bash
uv run gunicorn -w 4 -b 0.0.0.0:8000 'tuftecms.app:create_app()'
fly deploy
```
### Environment Variables
## Content
```bash
DISABLE_ANALYTICS=true # Disable analytics
DEBUG=1 # Enable debug mode
```
## Development
### Debug Mode
```bash
DEBUG=1 uv run python engine.py
```
Enables verbose logging and development features.
### Cache Management
Clear all caches:
```python
from tuftecms.core.cache import clear_all_caches
clear_all_caches()
```
### Adding New Features
1. Create new blueprint in `tuftecms/blueprints/`
2. Register in `app.py`
3. Add templates to `tuftecms/templates/`
4. Update cache logic if needed in `core/cache.py`
## Philosophy & Design
TufteCMS is built on principles of **contemplative pragmatism**:
- **Human-first design** - Optimize for people having difficult days
- **Simple over clever** - Tools should feel natural to use
- **Content-focused** - Design serves the writing, not the ego
- **Respectful reading** - No dark patterns, no engagement manipulation
- **Open and inspectable** - Clean code, clear architecture
The name honors Edward Tufte's work on information design - presenting complex information with clarity and respect for the reader's intelligence.
## Use Cases
- **Digital gardens** - Personal knowledge bases that grow organically
- **Essay collections** - Long-form writing with depth and cross-references
- **Academic writing** - Papers with extensive sidenotes and citations
- **Documentation sites** - Technical docs with commentary
- **Philosophy blogs** - Contemplative writing with layered meaning
The `data/` directory contains 15+ years of writing — essays, software documentation, poetry, photography, AI explorations, and personal pages. Content is plain markdown with optional YAML frontmatter and Tufte-style HTML sidenotes.
## License
MIT License - see LICENSE file for details.
## Credits
Created by Kenneth Reitz as part of kennethreitz.org. Built with Flask, Mistune, and care for human consciousness.
---
*"Technology should serve human mental models, not force humans to adapt to machine logic."*
MIT