# Phase 5 Quick Reference Guide **Phase**: 5 - RSS Feed & Production Container **Version**: 0.6.0 **Status**: Implementation Ready ## Pre-Implementation Setup ### Version Numbering **Decision**: Go directly from 0.5.1 → 0.6.0 - Phase 5 introduces significant new functionality (RSS feeds and container deployment) - Skip intermediate versions (e.g., 0.5.2) - go straight to 0.6.0 - This follows semantic versioning for new feature additions ### Git Workflow **Decision**: Use feature branch `feature/phase-5-rss-container` 1. Create and checkout feature branch: ```bash git checkout -b feature/phase-5-rss-container ``` 2. Implement all Phase 5 features on this branch 3. Create PR to merge into main when complete 4. This provides cleaner history and easier rollback if needed ## Overview Phase 5 implements: 1. RSS 2.0 feed generation for syndicating published notes 2. Production-ready container for deployment with HTTPS/IndieAuth testing ## Implementation Checklist ### Part 1: RSS Feed (Estimated: 3-4 hours) #### Step 1: Create Feed Module - [ ] Create `starpunk/feed.py` - [ ] Implement `generate_feed()` using feedgen - [ ] Implement `format_rfc822_date()` for date formatting - [ ] Implement `get_note_title()` for title extraction - [ ] Implement `clean_html_for_rss()` for CDATA safety #### Step 2: Add Feed Route - [ ] Update `starpunk/routes/public.py` - [ ] Add `@bp.route("/feed.xml")` handler - [ ] Implement in-memory caching (5 minutes) - [ ] Add ETag generation and support - [ ] Set proper Content-Type and Cache-Control headers #### Step 3: Update Templates - [ ] Add RSS discovery link to `templates/base.html` - [ ] Add RSS link to navigation in `templates/index.html` #### Step 4: Configuration - [ ] Update `starpunk/config.py` with feed settings - [ ] Add FEED_MAX_ITEMS (default: 50) - [ ] Add FEED_CACHE_SECONDS (default: 300) - [ ] Update `.env.example` with feed variables #### Step 5: RSS Testing - [ ] Create `tests/test_feed.py` for unit tests - [ ] Create `tests/test_routes_feed.py` for route tests - [ ] Test feed generation with various note counts - [ ] Test caching behavior - [ ] Test ETag validation - [ ] Validate with W3C Feed Validator ### Part 2: Production Container (Estimated: 3-4 hours) #### Step 6: Create Container Files - [ ] Create `Containerfile` with multi-stage build - [ ] Create `compose.yaml` for orchestration - [ ] Create `.containerignore` to exclude unnecessary files - [ ] Create `Caddyfile.example` for reverse proxy - [ ] Create `nginx.conf.example` as alternative #### Step 7: Add Health Check - [ ] Add `/health` endpoint to `starpunk/__init__.py` - [ ] Check database connectivity - [ ] Check filesystem access - [ ] Return JSON with status and version #### Step 8: Container Configuration - [ ] Update `.env.example` with container variables - [ ] Add VERSION=0.6.0 - [ ] Add WORKERS=4 - [ ] Add WORKER_TIMEOUT=30 - [ ] Document environment variables #### Step 9: Container Testing - [ ] Build container with Podman - [ ] Build container with Docker - [ ] Test container startup - [ ] Test health endpoint - [ ] Test data persistence - [ ] Test with compose orchestration #### Step 10: Production Deployment Testing - [ ] Deploy container to public server - [ ] Configure reverse proxy (Caddy or Nginx) - [ ] Set up HTTPS with Let's Encrypt - [ ] Test IndieAuth authentication flow - [ ] Verify callback URLs work - [ ] Test session creation and persistence ### Part 3: Documentation (Estimated: 1-2 hours) #### Step 11: Update Documentation - [ ] Update CHANGELOG.md for v0.6.0 - [ ] Increment version in `starpunk/__init__.py` from 0.5.1 to 0.6.0 - [ ] Create deployment guide - [ ] Document RSS feed usage - [ ] Document container deployment - [ ] Document IndieAuth testing with HTTPS ## File Locations ### New Files ``` starpunk/feed.py # RSS generation module Containerfile # Container build definition compose.yaml # Container orchestration .containerignore # Container build exclusions Caddyfile.example # Caddy reverse proxy config nginx.conf.example # Nginx reverse proxy config tests/test_feed.py # Feed unit tests tests/test_routes_feed.py # Feed route tests docs/designs/phase-5-rss-and-container.md # This phase design docs/designs/phase-5-quick-reference.md # This guide docs/decisions/ADR-014-rss-feed-implementation.md # RSS ADR ``` ### Modified Files ``` starpunk/routes/public.py # Add /feed.xml route starpunk/__init__.py # Add /health endpoint starpunk/config.py # Add feed configuration templates/base.html # Add RSS discovery link templates/index.html # Add RSS nav link .env.example # Add feed/container vars CHANGELOG.md # Document v0.6.0 ``` ## Key Implementation Details ### RSS Feed Module **File**: `starpunk/feed.py` **Core Function**: ```python from feedgen.feed import FeedGenerator from starpunk.notes import list_notes def generate_feed(site_url, site_name, site_description, notes, limit=50): """Generate RSS 2.0 XML feed""" fg = FeedGenerator() # Set channel metadata fg.title(site_name) fg.link(href=site_url, rel='alternate') fg.description(site_description) fg.language('en') fg.link(href=f'{site_url}/feed.xml', rel='self') # Add items for note in notes[:limit]: fe = fg.add_entry() fe.title(get_note_title(note)) fe.link(href=f'{site_url}/note/{note.slug}') fe.guid(f'{site_url}/note/{note.slug}', permalink=True) fe.pubDate(note.created_at.replace(tzinfo=timezone.utc)) fe.description(note.html) # HTML content return fg.rss_str(pretty=True).decode('utf-8') ``` ### Feed Route **File**: `starpunk/routes/public.py` **Add to existing blueprint**: ```python @bp.route("/feed.xml") def feed(): """RSS 2.0 feed endpoint with caching""" # Check cache (implementation in design doc) # Generate feed if cache expired # Return XML with proper headers pass ``` ### Health Check Endpoint **File**: `starpunk/__init__.py` **Add before return app**: ```python @app.route('/health') def health_check(): """Container health check""" try: # Check database and filesystem return jsonify({'status': 'healthy', 'version': '0.6.0'}), 200 except Exception as e: return jsonify({'status': 'unhealthy', 'error': str(e)}), 500 ``` ### Containerfile **Key Sections**: ```dockerfile # Multi-stage build for smaller image FROM python:3.11-slim AS builder # ... install dependencies in venv ... FROM python:3.11-slim # ... copy venv, run as non-root ... CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:app"] ``` ## Testing Commands ### RSS Feed Testing ```bash # Unit tests uv run pytest tests/test_feed.py -v # Route tests uv run pytest tests/test_routes_feed.py -v # Manual test curl http://localhost:5000/feed.xml # Validate XML curl http://localhost:5000/feed.xml | xmllint --noout - # W3C Validation (manual) # Visit: https://validator.w3.org/feed/ # Enter: http://your-domain.com/feed.xml ``` ### Container Testing ```bash # Build with Podman podman build -t starpunk:0.6.0 -f Containerfile . # Build with Docker docker build -t starpunk:0.6.0 -f Containerfile . # Run with Podman mkdir -p container-data/notes podman run -d --name starpunk \ -p 127.0.0.1:8000:8000 \ -v $(pwd)/container-data:/data:rw,Z \ --env-file .env \ starpunk:0.6.0 # Check health curl http://localhost:8000/health # Check feed curl http://localhost:8000/feed.xml # View logs podman logs starpunk # Test with compose podman-compose up -d podman-compose logs -f ``` ## Configuration Examples ### .env for Container ```bash # Required SITE_URL=https://your-domain.com SITE_NAME=My StarPunk Site ADMIN_ME=https://your-identity.com SESSION_SECRET= # Feed configuration FEED_MAX_ITEMS=50 FEED_CACHE_SECONDS=300 # Container configuration VERSION=0.6.0 ENVIRONMENT=production WORKERS=4 FLASK_ENV=production FLASK_DEBUG=0 ``` ### Caddy Reverse Proxy ```caddy your-domain.com { reverse_proxy localhost:8000 log { output file /var/log/caddy/starpunk.log } encode gzip zstd } ``` ### Nginx Reverse Proxy ```nginx upstream starpunk { server localhost:8000; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { proxy_pass http://starpunk; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; } } ``` ## Common Issues & Solutions ### Issue: Feed not updating **Solution**: Check cache duration (5 minutes default), force refresh by restarting ### Issue: Container won't start **Solution**: Check logs (`podman logs starpunk`), verify .env file exists ### Issue: IndieAuth callback fails **Solution**: Verify SITE_URL matches public URL exactly (no trailing slash) ### Issue: Data not persisting **Solution**: Check volume mount is correct, verify permissions ### Issue: RSS validation errors **Solution**: Check date formatting (RFC-822), verify XML structure ## Deployment Workflow ### 1. Local Testing ```bash # Test feed locally uv run flask --app app.py run --debug curl http://localhost:5000/feed.xml ``` ### 2. Container Testing ```bash # Build and test container podman build -t starpunk:0.6.0 . podman run -d -p 8000:8000 --name starpunk-test starpunk:0.6.0 curl http://localhost:8000/health ``` ### 3. Production Deployment ```bash # On server git clone cd starpunk cp .env.example .env # Edit .env with production values # Build and run podman-compose up -d # Configure reverse proxy (Caddy or Nginx) # Set up HTTPS with certbot or Caddy auto-HTTPS # Test IndieAuth # Visit https://your-domain.com/admin/login ``` ## Success Criteria Phase 5 complete when: - [ ] RSS feed validates with W3C validator - [ ] Feed appears correctly in RSS readers - [ ] Container builds and runs successfully - [ ] Health check endpoint responds - [ ] Data persists across container restarts - [ ] IndieAuth works with public HTTPS URL - [ ] All tests pass (>90% coverage) - [ ] Documentation complete - [ ] Version incremented from 0.5.1 to 0.6.0 in `starpunk/__init__.py` - [ ] Feature branch `feature/phase-5-rss-container` merged to main ## Time Estimate - RSS Feed Implementation: 3-4 hours - Container Implementation: 3-4 hours - Testing: 2-3 hours - Documentation: 1-2 hours **Total**: 9-13 hours ## Next Steps After Completion 1. Ensure all changes committed on feature branch: ```bash git add . git commit -m "feat: implement RSS feed and production container (v0.6.0)" ``` 2. Create PR to merge `feature/phase-5-rss-container` into main 3. After merge, tag release on main: ```bash git checkout main git pull git tag -a v0.6.0 -m "Release 0.6.0: RSS feed and production container" git push --tags ``` 4. Create implementation report in `docs/reports/` 5. Begin Phase 6 planning (Micropub implementation) ## Reference Documents - [Phase 5 Full Design](/home/phil/Projects/starpunk/docs/designs/phase-5-rss-and-container.md) - [ADR-014: RSS Implementation](/home/phil/Projects/starpunk/docs/decisions/ADR-014-rss-feed-implementation.md) - [Versioning Strategy](/home/phil/Projects/starpunk/docs/standards/versioning-strategy.md) - [Git Branching Strategy](/home/phil/Projects/starpunk/docs/standards/git-branching-strategy.md) --- **Phase**: 5 **Version**: 0.6.0 **Date**: 2025-11-18 **Status**: Ready for Implementation