# 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=<random-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 <repo>
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
