# StarPunk Technology Stack - Quick Reference

## Project Understanding

StarPunk is a **minimal, single-user IndieWeb CMS** for publishing notes with RSS syndication. The core philosophy is radical simplicity: "Every line of code must justify its existence."

### Key Requirements
- Publish IndieWeb-compatible notes
- External IndieLogin authentication via indielogin.com
- Micropub server for publishing from any client
- RSS feed generation
- File-based note storage (markdown files)
- SQLite for metadata
- Self-hostable
- API-first architecture

## Complete Technology Stack

### Backend

| Component | Technology | Version | Justification |
|-----------|------------|---------|---------------|
| **Language** | Python | 3.11+ | User's preference, excellent ecosystem |
| **Web Framework** | Flask | 3.0+ | Minimal micro-framework, perfect for single-user |
| **Note Storage** | Markdown Files | - | Maximum portability, user owns data directly |
| **Metadata DB** | SQLite | Built-in | Single file, no server, perfect for single-user |
| **Markdown Rendering** | markdown | 3.5+ | Standard Python implementation |
| **RSS Generation** | feedgen | 1.0+ | Ensures valid RSS 2.0 output |
| **HTTP Client** | httpx | 0.27+ | Modern API, IndieLogin communication |
| **Configuration** | python-dotenv | 1.0+ | Standard .env file support |
| **Testing** | pytest | 8.0+ | Python testing standard |

**Total Direct Dependencies**: 6 packages

### Frontend

| Component | Technology | Justification |
|-----------|------------|---------------|
| **Template Engine** | Jinja2 | Included with Flask, server-side rendering |
| **CSS** | Custom CSS (~200 lines) | No framework, full control, no build tools |
| **JavaScript** | Vanilla JS (optional) | Minimal preview feature, progressive enhancement |
| **Build Tools** | NONE | Zero build process, direct file serving |

### Authentication

| Component | Technology | Approach |
|-----------|------------|----------|
| **Admin Auth** | IndieLogin.com | External OAuth 2.0 service at https://indielogin.com |
| **Session Management** | HttpOnly Cookies + SQLite | 30-day sessions, secure tokens |
| **Micropub Auth** | IndieAuth Tokens | Bearer tokens, stored in SQLite |
| **CSRF Protection** | State Tokens | Random tokens with 5-minute expiry |

**Key Point**: Authentication is delegated to indielogin.com, requiring zero auth code to maintain.

## Data Architecture

### Hybrid File + Database Storage

#### Note Content: Markdown Files
```
data/notes/
├── 2024/
│   ├── 11/
│   │   ├── my-first-note.md
│   │   └── another-note.md
│   └── 12/
│       └── december-note.md
```

- **Format**: Pure markdown, no frontmatter
- **Organization**: Year/Month subdirectories (`YYYY/MM/`)
- **Naming**: `{slug}.md`
- **Portability**: Copy anywhere, read in any editor, backup with cp/rsync/git

#### Metadata: SQLite Database
```sql
-- Note metadata (NOT content)
CREATE TABLE notes (
    id INTEGER PRIMARY KEY,
    slug TEXT UNIQUE,
    file_path TEXT UNIQUE,
    published BOOLEAN,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    content_hash TEXT
);

-- Authentication
CREATE TABLE sessions (...);    -- IndieLogin sessions
CREATE TABLE tokens (...);      -- Micropub tokens
CREATE TABLE auth_state (...);  -- CSRF protection
```

- **Location**: `data/starpunk.db`
- **Purpose**: Fast queries, indexes, referential integrity
- **Sync**: Files are authoritative for content, database for metadata

### How They Work Together

**Creating a Note**:
1. Generate slug
2. Write markdown file → `data/notes/YYYY/MM/slug.md`
3. Calculate content hash
4. Insert database record with metadata
5. If database fails: delete file, rollback

**Reading a Note**:
1. Query database by slug → get file_path
2. Read markdown from file
3. Render to HTML
4. Return content + metadata

## IndieLogin Authentication Flow

### Configuration Required
```bash
# .env file
SITE_URL=https://starpunk.example.com
ADMIN_ME=https://your-website.com  # Only this URL can authenticate
SESSION_SECRET=random-secret-key
```

### Authentication Steps

1. **User initiates login** → enters their website URL
2. **StarPunk redirects** → to https://indielogin.com/auth with:
   - `me` = user's website
   - `client_id` = StarPunk URL
   - `redirect_uri` = callback URL
   - `state` = random CSRF token
3. **IndieLogin verifies identity** → via RelMeAuth, email, etc.
4. **User authenticates** → chooses verification method
5. **IndieLogin redirects back** → with authorization code
6. **StarPunk exchanges code** → POST to indielogin.com API
7. **IndieLogin returns** → verified "me" URL
8. **StarPunk verifies** → me == ADMIN_ME (from config)
9. **Create session** → generate token, store in database, set cookie
10. **Redirect to admin** → user is now authenticated

### API Endpoint
**IndieLogin API**: https://indielogin.com/api

**Exchange Request**:
```http
POST https://indielogin.com/auth
Content-Type: application/x-www-form-urlencoded

code={authorization_code}&
client_id={starpunk_url}&
redirect_uri={starpunk_url}/auth/callback
```

**Exchange Response**:
```json
{
  "me": "https://user-website.com"
}
```

### Security Features
- State tokens prevent CSRF attacks
- Only ADMIN_ME URL can authenticate (single-user enforcement)
- Session tokens are cryptographically random (256-bit)
- HttpOnly cookies prevent XSS theft
- Secure flag requires HTTPS
- 30-day session expiry

## Frontend Stack Details

### Server-Side Rendering (Jinja2)

**Public Templates**:
- `base.html` - Base layout with HTML structure
- `index.html` - Homepage (note list)
- `note.html` - Single note permalink
- `feed.xml` - RSS feed template

**Admin Templates**:
- `admin/base.html` - Admin layout
- `admin/login.html` - Login form
- `admin/dashboard.html` - Note list
- `admin/new.html` - Create note form
- `admin/edit.html` - Edit note form

### CSS Approach

**Single stylesheet**: `static/css/style.css` (~200 lines)

```css
/* CSS custom properties for theming */
:root {
  --color-text: #333;
  --color-bg: #fff;
  --color-link: #0066cc;
  --max-width: 42rem;
  --spacing: 1rem;
}

/* Mobile-first responsive */
body { padding: 1rem; }

@media (min-width: 768px) {
  body { padding: 2rem; }
}
```

**No framework**: Custom CSS gives full control, no unused code.

### JavaScript Approach

**Single optional file**: `static/js/preview.js`

**Purpose**: Real-time markdown preview in admin editor (progressive enhancement)

**Implementation**:
- Vanilla JavaScript (no framework)
- Uses marked.js from CDN for client-side markdown
- Works without it (form submits to server)

**Why vanilla JS?**
- Core functionality works without JavaScript
- Single feature doesn't justify React/Vue/Svelte
- Modern browser APIs are sufficient
- No build tools needed

### Build Process: NONE

- No webpack, Vite, Rollup, esbuild
- No npm, package.json, node_modules
- No Babel transpilation
- No CSS preprocessing
- Direct file serving
- Instant development setup

**Advantages**:
- Zero build time
- No dependency hell
- Simple deployment
- Easy debugging

## API Routes

### Public API
```
GET  /                    Homepage (recent notes)
GET  /note/{slug}         Individual note
GET  /feed.xml            RSS feed
```

### Admin Interface
```
GET  /admin/login         Login form
POST /admin/login         Initiate IndieLogin flow
GET  /auth/callback       IndieLogin callback handler
GET  /admin               Dashboard (list notes)
GET  /admin/new           Create note form
GET  /admin/edit/{id}     Edit note form
POST /admin/logout        Destroy session
```

### Notes API (Session Auth)
```
GET    /api/notes         List published notes (JSON)
POST   /api/notes         Create note (JSON)
GET    /api/notes/{id}    Get single note (JSON)
PUT    /api/notes/{id}    Update note (JSON)
DELETE /api/notes/{id}    Delete note (JSON)
```

### Micropub API (Token Auth)
```
POST /api/micropub              Create note (h-entry)
GET  /api/micropub?q=config     Query configuration
GET  /api/micropub?q=source     Query note source
```

## File Organization

```
starpunk/
├── app.py                      # Main Flask application
├── requirements.txt            # 6 dependencies
├── .env                        # Configuration (gitignored)
├── .env.example                # Template
│
├── starpunk/                   # Application package
│   ├── __init__.py
│   ├── config.py              # Load environment
│   ├── database.py            # SQLite operations
│   ├── models.py              # Data models
│   ├── auth.py                # IndieLogin logic
│   ├── micropub.py            # Micropub endpoint
│   ├── feed.py                # RSS generation
│   └── utils.py               # Helpers
│
├── static/
│   ├── css/style.css          # Single stylesheet
│   └── js/preview.js          # Optional markdown preview
│
├── templates/
│   ├── base.html              # Public base
│   ├── index.html             # Homepage
│   ├── note.html              # Note permalink
│   └── admin/
│       ├── base.html          # Admin base
│       ├── login.html         # Login form
│       ├── dashboard.html     # Note list
│       ├── new.html           # Create form
│       └── edit.html          # Edit form
│
├── data/                      # Persistent (gitignored)
│   ├── notes/YYYY/MM/slug.md  # Markdown files
│   └── starpunk.db            # SQLite
│
├── tests/                     # pytest tests
│   ├── test_auth.py
│   ├── test_database.py
│   ├── test_micropub.py
│   └── test_feed.py
│
└── docs/                      # Architecture docs
    ├── architecture/
    │   ├── overview.md
    │   └── technology-stack.md
    └── decisions/
        ├── ADR-001-python-web-framework.md
        ├── ADR-002-flask-extensions.md
        ├── ADR-003-frontend-technology.md
        ├── ADR-004-file-based-note-storage.md
        └── ADR-005-indielogin-authentication.md
```

## Recommended Architectural Patterns

### 1. API-First Design
All functionality exposed via API, web interface consumes it.

### 2. Progressive Enhancement
Core works without JavaScript, JS adds optional enhancements.

### 3. File-Database Sync
Write files first, then database. Rollback on failure.

### 4. Atomic Operations
Use temp files and atomic renames to prevent corruption.

### 5. Token-Based Auth
Sessions for humans (cookies), tokens for APIs (bearer).

## Potential Risks & Considerations

### Risk 1: IndieLogin.com Dependency
**Impact**: Cannot authenticate if service is down
**Mitigation**:
- Sessions last 30 days (brief outages don't lock out user)
- IndieLogin.com is stable, community-run service
- V2: Consider fallback auth method

### Risk 2: File/Database Sync Issues
**Impact**: Data inconsistency between files and database
**Mitigation**:
- Atomic operations (write file → insert DB, rollback on error)
- Content hashing detects external modifications
- Optional integrity check on startup

### Risk 3: SQLite Limitations
**Impact**: Limited concurrency (but this is single-user)
**Consideration**: SQLite is perfect for single-user, would need PostgreSQL for multi-user

### Risk 4: No Built-in Backup
**Impact**: User must manage backups
**Mitigation**:
- Document backup procedures clearly
- Backup is simple (cp -r data/ backup/)
- Consider adding automated backup script

## Deployment Stack

### Development
```bash
# Setup
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Configure
cp .env.example .env
# Edit .env with your settings

# Run
flask run

# Test
pytest
```

### Production

**WSGI Server**: Gunicorn
```bash
gunicorn -w 4 -b 127.0.0.1:8000 app:app
```

**Reverse Proxy**: Nginx or Caddy
- HTTPS termination (Let's Encrypt)
- Static file serving
- Rate limiting (optional)

**Process Manager**: systemd
- Auto-restart on failure
- Log management
- Run on boot

**Backup**: Cron job
```bash
# Daily backup via rsync
rsync -av /opt/starpunk/data /backup/starpunk-$(date +%Y%m%d)
```

## Standards Compliance

### IndieWeb
- **Microformats2**: h-entry, h-card, e-content, dt-published, u-url
- **IndieAuth**: OAuth 2.0 flow (delegated to indielogin.com)
- **Micropub**: JSON and form-encoded, 201 Created responses

**Validation**:
- https://indiewebify.me/ (microformats)
- https://micropub.rocks/ (Micropub compliance)

### Web Standards
- **RSS 2.0**: Valid XML, RFC-822 dates, CDATA for HTML
- **HTML5**: Semantic elements, accessible, mobile-responsive
- **HTTP**: Proper status codes (200, 201, 400, 401, 404)

**Validation**:
- https://validator.w3.org/feed/ (RSS)
- https://validator.w3.org/ (HTML)

## Performance Targets

- **API responses**: < 100ms
- **Page loads**: < 200ms
- **RSS generation**: < 300ms
- **Memory usage**: < 100MB
- **Startup time**: < 1 second

## Quick Start

```bash
# 1. Clone and setup
git clone <repo> && cd starpunk
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt

# 2. Configure
cp .env.example .env
# Edit .env:
#   SITE_URL=https://your-domain.com
#   ADMIN_ME=https://your-website.com
#   SESSION_SECRET=$(python -c "import secrets; print(secrets.token_hex(32))")

# 3. Run
flask run

# 4. Visit http://localhost:5000/admin/login
#    Enter your website URL (must match ADMIN_ME)
#    Authenticate via indielogin.com
#    Start publishing!
```

## Summary

StarPunk uses a **radically simple** technology stack:

- **Backend**: Flask + Python stdlib + 5 small libraries
- **Storage**: Markdown files (content) + SQLite (metadata)
- **Frontend**: Jinja2 templates + custom CSS + optional vanilla JS
- **Auth**: Delegated to indielogin.com (zero maintenance)
- **Build**: None (zero build tools)
- **Deploy**: Gunicorn + nginx/Caddy + systemd

**Total Dependencies**: 6 direct packages
**Lines of Code**: ~1500 LOC estimate for V1
**Setup Time**: < 5 minutes
**Build Time**: 0 seconds (no build process)

This stack embodies the project philosophy: every technology choice is justified by simplicity, fitness for purpose, and maintainability.

## Further Reading

- **Project Requirements**: `/home/phil/Projects/starpunk/CLAUDE.MD`
- **Full Tech Stack**: `/home/phil/Projects/starpunk/docs/architecture/technology-stack.md`
- **Architecture Overview**: `/home/phil/Projects/starpunk/docs/architecture/overview.md`
- **All ADRs**: `/home/phil/Projects/starpunk/docs/decisions/ADR-*.md`
- **IndieLogin API**: https://indielogin.com/api
- **IndieWeb**: https://indieweb.org/
