33 KiB
StarPunk Technology Stack
Project Summary
StarPunk is a minimal, single-user IndieWeb CMS for publishing notes with RSS syndication. The project emphasizes radical simplicity, standards compliance, and user data ownership. Every technology choice is driven by the principle: "Every line of code must justify its existence. When in doubt, leave it out."
Core Requirements
- Publish IndieWeb-compatible notes (https://indieweb.org/note)
- IndieAuth authentication using external provider (indielogin.com)
- Micropub server endpoint for publishing from any client
- RSS feed generation for syndication
- File-based note storage for maximum portability
- SQLite for metadata and structured data
- Self-hostable single-user system
- API-first architecture
- Markdown support
Complete Technology Stack
Backend Stack
Web Framework: Flask 3.0+
Purpose: HTTP server, routing, templating Justification:
- Minimal micro-framework (< 1000 lines core code)
- Perfect for single-user applications
- Native support for both JSON APIs and HTML rendering
- Mature, stable, well-documented (13+ years)
- Built-in Jinja2 templating for server-side rendering
- Standard WSGI interface for deployment flexibility
Alternatives Rejected:
- FastAPI: Async complexity unnecessary for single-user CMS
- Django: Massive framework with ORM, admin, multi-user features we don't need
- Bottle: Too minimal, smaller ecosystem
Reference: ADR-001
Python Version: 3.11+
Purpose: Programming language Justification:
- User's preferred language
- Excellent standard library (sqlite3, hashlib, secrets, etc.)
- Rich ecosystem for web development
- Strong typing support (type hints)
- Mature dependency management (pip, venv)
Data Persistence: Hybrid File + Database
Note Storage: Markdown Files on Disk
Purpose: Store note content Format: Plain markdown files (.md) Structure:
data/notes/
├── 2024/
│ ├── 11/
│ │ ├── my-first-note.md
│ │ └── another-note.md
│ └── 12/
│ └── december-note.md
└── 2025/
└── 01/
└── new-year-note.md
Naming Convention: {slug}.md
Organization: Year/Month subdirectories (YYYY/MM/)
File Format: Pure markdown, no frontmatter
Justification:
- Maximum portability (user requirement)
- Human-readable, editable in any text editor
- Easy backup (cp, rsync, git)
- User owns data directly
- No vendor lock-in
- Future-proof format
Reference: ADR-004
Metadata Storage: SQLite
Purpose: Store note metadata, sessions, tokens
Database: data/starpunk.db
Schema:
-- Note metadata (NOT content)
CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
slug TEXT UNIQUE NOT NULL,
file_path TEXT UNIQUE NOT NULL,
published BOOLEAN DEFAULT 0,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
content_hash TEXT
);
-- Authentication sessions (IndieLogin)
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_token TEXT UNIQUE NOT NULL,
me TEXT NOT NULL,
created_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP NOT NULL,
last_used_at TIMESTAMP
);
-- Micropub access tokens
CREATE TABLE tokens (
token TEXT PRIMARY KEY,
me TEXT NOT NULL,
client_id TEXT,
scope TEXT,
created_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP
);
-- CSRF state tokens
CREATE TABLE auth_state (
state TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP NOT NULL
);
Justification:
- Single file, perfect for single-user
- No separate server process
- Excellent for read-heavy workloads (blog)
- Fast indexing and querying
- Built into Python standard library
- Enables efficient metadata queries without parsing files
- Atomic transactions for data integrity
Hybrid Strategy: Files are authoritative for content; database is authoritative for metadata. This gives us portability AND performance.
Reference: ADR-004
Core Dependencies
markdown (3.5+)
Purpose: Convert markdown to HTML Usage: Render note content for display and RSS feed Justification:
- Pure Python, standard markdown implementation
- Simple API:
markdown.markdown(text) - Sufficient performance for single-user system
- More standard than alternatives (mistune)
feedgen (1.0+)
Purpose: Generate RSS 2.0 feeds Usage: Create valid RSS feed from published notes Justification:
- High-level API ensures RSS 2.0 compliance
- Handles date formatting (RFC-822) automatically
- CDATA wrapping for HTML content
- Better than manual XML generation (error-prone)
httpx (0.27+)
Purpose: HTTP client library Usage:
- Communication with indielogin.com API
- Verify Micropub client metadata
- Fetch remote URLs for verification Justification:
- Modern, clean API
- Synchronous and async support
- Better than requests (async capability) and urllib (too low-level)
- Proper timeout handling
- SSL verification built-in
python-dotenv (1.0+)
Purpose: Environment configuration
Usage: Load settings from .env file
Justification:
- Industry standard for configuration
- Keeps secrets out of code
- Simple API:
load_dotenv() - Minimal overhead
pytest (8.0+)
Purpose: Testing framework Usage: Unit and integration tests Justification:
- Current Python testing standard
- Minimal boilerplate
- Clear assertions
- Built-in fixtures
- Better than unittest (verbose) and nose2 (unmaintained)
Reference: ADR-002
Dependencies Explicitly REJECTED
- Flask-SQLAlchemy: ORM abstraction unnecessary, adds complexity
- Flask-Login: Session-based auth, we need token-based for Micropub
- Flask-CORS: Single decorator, don't need full extension (5 lines of code)
- Flask-WTF: Form library overkill for simple note creation
- Flask-Limiter: Rate limiting deferred to V2 or reverse proxy
Decision: Use Python standard library and explicit code instead of extensions where possible. Each dependency must justify its existence.
Reference: ADR-002
Frontend Stack
Template Engine: Jinja2
Purpose: Server-side HTML rendering Included With: Flask (no additional dependency) Usage:
- Public interface (homepage, note permalinks)
- Admin interface (dashboard, note editor)
- Microformats markup (h-entry, h-card)
Justification:
- Zero build process
- Server-side rendering for better performance
- Works without JavaScript (progressive enhancement)
- Easy microformats implementation
- Familiar syntax
- Stable and mature
Reference: ADR-003
CSS: Custom Stylesheet
Purpose: Visual styling
Approach: Single custom CSS file, no framework
File: static/css/style.css
Size: ~200 lines for entire site
Features:
- CSS custom properties (variables) for theming
- Mobile-first responsive design
- Simple media queries for tablet/desktop
- Semantic HTML5 + minimal classes
Justification:
- No framework overhead (Bootstrap, Tailwind, etc.)
- No build tools required
- Full control over appearance
- Minimal single theme fits project scope
- Faster than loading framework CSS
Example:
:root {
--color-text: #333;
--color-bg: #fff;
--max-width: 42rem;
--spacing: 1rem;
}
Frameworks Rejected:
- Tailwind: Requires build process, utility-first doesn't fit
- Bootstrap/Bulma: Too many unused features
- PicoCSS: Good but custom CSS gives more control
Reference: ADR-003
JavaScript: Minimal Vanilla JS
Purpose: Markdown preview in admin (optional enhancement)
Approach: Single vanilla JavaScript file, no framework
File: static/js/preview.js
Dependency: marked.js via CDN (client-side markdown)
Usage:
- Optional real-time markdown preview in note editor
- Progressive enhancement (works without JS)
Justification:
- Core functionality works without JavaScript
- Single optional feature doesn't justify framework
- Vanilla JS sufficient for simple preview
- Modern browser APIs (fetch, DOM manipulation) are enough
- No build tools required
Frameworks Rejected:
- React/Vue/Svelte: Massive overkill for one preview feature
- htmx: Interesting but not needed for V1
- Alpine.js: Too much for minimal JS needs
Reference: ADR-003
Build Tools: NONE
Decision: No build process whatsoever Justification:
- Server-side rendering eliminates need for bundling
- Custom CSS served directly
- Vanilla JS served directly
- Modern browsers support ES6+ natively
- Zero npm dependencies
- Instant development setup
This means:
- No webpack, Vite, Rollup, esbuild
- No Babel transpilation
- No PostCSS processing
- No minification (premature optimization)
- No asset pipeline
Reference: ADR-003
Authentication Stack
Admin Authentication: IndieLogin.com
Purpose: Authenticate the admin user via their personal website Provider: External service at https://indielogin.com API: https://indielogin.com/api Protocol: OAuth 2.0 / IndieAuth
Flow:
- User enters their website URL
- StarPunk redirects to indielogin.com with state token
- indielogin.com verifies user's identity (RelMeAuth, email, etc.)
- indielogin.com redirects back with authorization code
- StarPunk exchanges code for verified identity
- StarPunk creates session cookie
Session Management:
- HttpOnly, Secure cookies
- 30-day expiry
- Stored in SQLite sessions table
- CSRF protection via state tokens
Configuration:
ADMIN_ME=https://your-website.com # Only this URL can authenticate
SESSION_SECRET=random-secret-key
Justification:
- Extremely simple (< 100 lines of code)
- No authentication code to maintain
- No password management needed
- True IndieWeb authentication (user owns identity)
- Secure by default (delegated to trusted service)
- Community-maintained, stable service
Alternatives Rejected:
- Self-hosted IndieAuth: Too complex for V1
- Password auth: Not IndieWeb-compatible, security burden
- OAuth (GitHub/Google): User doesn't own identity
Reference: ADR-005
Micropub Authentication: IndieAuth Tokens
Purpose: Authenticate Micropub API clients Protocol: IndieAuth bearer tokens Flow: Standard IndieAuth authorization code grant Storage: Tokens table in SQLite
Note: Micropub token endpoint is separate from admin authentication. Users authenticate their Micropub clients (e.g., mobile apps) separately via IndieAuth flow. This will be detailed in a future ADR for Micropub implementation.
Development Tools
Code Quality
pytest-cov # Test coverage reporting
black # Code formatting (standard: 88 char line length)
flake8 # Linting
mypy # Type checking (optional but recommended)
Justification:
- Automated formatting prevents style debates
- Linting catches common errors
- Test coverage ensures quality
- Type hints improve maintainability
Development Workflow
# Setup
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Run
flask run
# Test
pytest
# Format
black .
flake8 .
No additional tools required: No npm, no build scripts, no containers (optional for deployment).
Deployment Stack
WSGI Server: Gunicorn (Production)
Purpose: Production HTTP server Justification:
- Standard Python WSGI server
- Production-ready
- Better performance than Flask dev server
- Simple configuration
Alternative: uWSGI (more complex, not needed for single-user)
Reverse Proxy: Nginx or Caddy (Recommended)
Purpose: HTTPS termination, static file serving Justification:
- Handle SSL/TLS certificates
- Serve static files efficiently
- Rate limiting (optional)
- Proven deployment pattern
Process Manager: systemd (Recommended)
Purpose: Keep application running Justification:
- Standard on modern Linux
- Auto-restart on failure
- Log management
Deployment Package: Single Unit
Structure:
starpunk/
├── app.py # Main application
├── requirements.txt # Dependencies
├── .env.example # Configuration template
├── static/ # CSS, JS
├── templates/ # Jinja2 templates
├── data/ # Notes + SQLite (persistent)
│ ├── notes/
│ └── starpunk.db
└── README.md # Setup instructions
Deployment:
- Clone repository
- Create virtual environment
- Install dependencies
- Configure .env file
- Run with Gunicorn + systemd
- (Optional) Nginx for HTTPS
Justification: Single self-contained package, easy to deploy and backup.
File Organization
Project Structure
starpunk/
├── app.py # Main Flask application
├── requirements.txt # Python dependencies
├── .env # Environment configuration (gitignored)
├── .env.example # Configuration template
├── README.md # Setup documentation
├── CLAUDE.MD # Project requirements
│
├── starpunk/ # Application package
│ ├── __init__.py
│ ├── config.py # Configuration loading
│ ├── database.py # SQLite operations
│ ├── models.py # Data models
│ ├── auth.py # Authentication logic
│ ├── micropub.py # Micropub endpoint
│ ├── feed.py # RSS generation
│ └── utils.py # Helper functions
│
├── static/ # Static assets
│ ├── css/
│ │ └── style.css # Single stylesheet
│ └── js/
│ └── preview.js # Optional markdown preview
│
├── templates/ # Jinja2 templates
│ ├── base.html # Base layout
│ ├── index.html # Homepage (note list)
│ ├── note.html # Single note
│ ├── feed.xml # RSS template
│ └── admin/
│ ├── base.html # Admin layout
│ ├── login.html # Login form
│ ├── dashboard.html # Admin dashboard
│ ├── new.html # Create note
│ └── edit.html # Edit note
│
├── data/ # Persistent data (gitignored)
│ ├── notes/ # Markdown files
│ │ └── YYYY/MM/
│ │ └── slug.md
│ └── starpunk.db # SQLite database
│
├── tests/ # Test suite
│ ├── test_auth.py
│ ├── test_database.py
│ ├── test_micropub.py
│ ├── test_feed.py
│ └── test_notes.py
│
└── docs/ # Architecture documentation
├── architecture/
│ ├── overview.md
│ ├── components.md
│ ├── data-flow.md
│ ├── security.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
Architecture Patterns
API-First Design
Pattern: All functionality exposed via API, web interface consumes API Routes:
# Public API
GET /api/notes # List published notes
GET /api/notes/{slug} # Get single note
GET /feed.xml # RSS feed
# Admin API (session auth)
POST /api/notes # Create note
PUT /api/notes/{id} # Update note
DELETE /api/notes/{id} # Delete note
# Micropub API (token auth)
POST /api/micropub # Create via Micropub
GET /api/micropub?q=config # Query config
# Auth API
GET /admin/login # Login form
POST /admin/login # Initiate IndieLogin
GET /auth/callback # IndieLogin callback
POST /admin/logout # Logout
Data Flow: File + Database Sync
Creating a Note
User submits note
↓
Generate slug
↓
Create file: data/notes/YYYY/MM/{slug}.md
↓
Calculate content hash
↓
Insert database record (slug, file_path, hash, timestamps)
↓
If database insert fails: delete file, return error
↓
Return success
Reading a Note
Request note by slug
↓
Query database for file_path
↓
Read markdown from file
↓
Render to HTML (if needed)
↓
Return content + metadata
Updating a Note
User submits changes
↓
Atomic write: new content to temp file
↓
Calculate new hash
↓
Update database (timestamp, hash)
↓
If database update succeeds: atomic rename temp → actual
↓
If database update fails: delete temp, return error
↓
Return success
Benefits:
- Files provide portability
- Database provides fast queries
- Content hash detects external changes
- Atomic operations prevent corruption
Reference: ADR-004
IndieLogin Authentication Flow
┌─────────┐ ┌──────────┐ ┌─────────────┐
│ User │ │ StarPunk │ │ IndieLogin │
└────┬────┘ └────┬─────┘ └──────┬──────┘
│ │ │
│ 1. Click "Login" │ │
├─────────────────────────>│ │
│ │ │
│ 2. Enter website URL │ │
├─────────────────────────>│ │
│ │ │
│ 3. Generate state token │
│ │ │
│ 4. Redirect to IndieLogin with: │
│ - me=user_website │
│ - client_id=starpunk_url │
│ - redirect_uri=starpunk/callback │
│ - state=random_token │
│ ├──────────────────────────>│
│ │ │
│ │ 5. Verify user's │
│ │ identity │
│ <────────────────────────────────────────────────── │
│ (User authenticates via │
│ chosen method) │
│ ──────────────────────────────────────────────────> │
│ │ │
│ 6. Redirect back with code + state │
│ <──────────────────────────────────────────────────│
├─────────────────────────>│ │
│ │ │
│ 7. Verify state │
│ │ │
│ 8. POST to IndieLogin: │
│ - code │
│ - client_id │
│ - redirect_uri │
│ ├──────────────────────────>│
│ │ │
│ │ 9. Return verified "me" │
│ │<──────────────────────────│
│ │ │
│ 10. Verify me == ADMIN_ME │
│ │ │
│ 11. Create session │
│ │ │
│ 12. Set session cookie │ │
│ <───────────────────────│ │
│ │ │
│ 13. Redirect to admin │ │
│ <───────────────────────│ │
│ │ │
Security Features:
- State token prevents CSRF
- Session tokens are cryptographically random
- HttpOnly cookies prevent XSS
- Only ADMIN_ME URL can authenticate
- Sessions expire after 30 days
Reference: ADR-005
Progressive Enhancement Pattern
Principle: Core functionality works without JavaScript
Public Interface
- Without JS: Full functionality (view notes, RSS feed)
- With JS: No difference (no JS used on public pages)
Admin Interface
- Without JS:
- Create/edit notes via HTML forms
- Submit to server, server renders markdown
- Full page refresh on submit
- With JS:
- Real-time markdown preview
- No page refresh for preview
- Still submits via form (progressive enhancement)
Implementation:
<!-- Note editor works without JS -->
<form method="POST" action="/api/notes">
<textarea name="content" required></textarea>
<button type="submit">Publish</button>
</form>
<!-- JS enhances with preview -->
<script src="/static/js/preview.js"></script>
Reference: ADR-003
Standards Compliance
IndieWeb Standards
Microformats2
Required Classes:
h-entry: Mark up notesh-card: Mark up author informatione-content: Note contentdt-published: Publication timestampu-url: Permalink URL
Example:
<article class="h-entry">
<div class="e-content">
<p>Note content here</p>
</div>
<footer>
<a class="u-url" href="/note/abc123">
<time class="dt-published" datetime="2024-11-18T12:00:00Z">
November 18, 2024
</time>
</a>
</footer>
</article>
Validation: https://indiewebify.me/
IndieAuth
Compliance: OAuth 2.0 authorization code flow Endpoints: Delegated to indielogin.com Token Format: Bearer tokens Validation: Token introspection
Reference: https://indieauth.spec.indieweb.org/
Micropub
Compliance: Full Micropub spec support Content Types: JSON and form-encoded Required Responses: 201 Created with Location header Query Support: q=config, q=source
Validation: https://micropub.rocks/
Reference: https://micropub.spec.indieweb.org/
Web Standards
RSS 2.0
Compliance: Valid RSS 2.0 XML Required Elements: title, link, description, pubDate, guid Date Format: RFC-822 HTML Content: CDATA-wrapped
Validation: https://validator.w3.org/feed/
HTTP
Status Codes: Proper use of 200, 201, 400, 401, 404, 500 Headers: Content-Type, Cache-Control, Location Methods: GET, POST, PUT, DELETE CORS: Allow cross-origin for API endpoints
HTML5
Compliance: Valid semantic HTML Accessibility: ARIA labels, alt text, proper heading hierarchy Responsive: Viewport meta tag, mobile-first CSS Forms: Proper labels, validation attributes
Validation: https://validator.w3.org/
Security Architecture
Authentication Security
- State tokens for CSRF protection (5-minute expiry)
- Session tokens are cryptographically random (32 bytes)
- HttpOnly cookies prevent XSS theft
- Secure flag requires HTTPS
- SameSite=Lax prevents CSRF
- Single admin user (ADMIN_ME verification)
Input Validation
- Validate all user input
- Sanitize markdown (prevent XSS in rendered HTML)
- Validate Micropub payloads against spec
- URL validation for IndieAuth
- File path validation (prevent directory traversal)
Database Security
- Parameterized queries (prevent SQL injection)
- Input sanitization before storage
- Hash session tokens before storage
- Content hashing for integrity
Network Security
- HTTPS required in production
- SSL certificate verification on httpx requests
- Secure headers (CSP, X-Frame-Options, etc.)
- Rate limiting via reverse proxy (nginx/Caddy)
File System Security
- Atomic file operations
- Restricted permissions on data/ directory
- Prevent directory traversal attacks
- Validate file paths before operations
Performance Targets
Response Times
- API responses: < 100ms
- Page loads: < 200ms
- RSS feed: < 300ms
Optimization Strategy
- SQLite indexes on frequently queried columns
- Cache RSS feed (5 minutes)
- Minimal dependencies = fast startup
- Server-side rendering = fast first paint
- Single CSS file = one request
- Optional JS = doesn't block rendering
Resource Usage
- Memory: < 100MB for typical workload
- Disk: Minimal (SQLite + markdown files)
- CPU: Minimal (no heavy processing)
Scaling: Designed for single-user, typical load is <10 requests/minute. Over-engineering for scale would violate simplicity principle.
Testing Strategy
Unit Tests (pytest)
- Database operations (CRUD, queries)
- Slug generation and validation
- Markdown rendering
- File operations (atomic writes)
- Session management
- Token validation
- Content hash calculation
Integration Tests
- IndieLogin authentication flow (mocked API)
- Micropub note creation (full flow)
- RSS feed generation (validation)
- API endpoints (request/response)
- File + database sync
- Error handling
Manual Tests
- Real IndieLogin authentication
- Micropub client integration (e.g., Quill)
- RSS feed in actual reader
- Browser compatibility
- Mobile responsiveness
- Accessibility (screen readers)
Validation Tests
- HTML validation (W3C validator)
- RSS validation (W3C feed validator)
- Microformats validation (indiewebify.me)
- Micropub compliance (micropub.rocks)
Risk Assessment
Technical Risks
Risk: IndieLogin.com Outage
Impact: Cannot authenticate new sessions Likelihood: Low (stable service) Mitigation:
- Sessions last 30 days (brief outages don't lock out user)
- Document manual session creation in database
- V2: Add fallback authentication method
Risk: File/Database Sync Failure
Impact: Data inconsistency Likelihood: Low (atomic operations, error handling) Mitigation:
- Write files first, database second
- Transaction rollback on failure
- Integrity check on startup (optional)
- Regular backups
Risk: File System Corruption
Impact: Lost notes Likelihood: Very low (standard filesystem operations) Mitigation:
- Atomic file writes
- Regular backups (user responsibility)
- Markdown files are recoverable
Risk: Dependency Vulnerabilities
Impact: Security breach Likelihood: Medium (all software has bugs) Mitigation:
- Minimal dependencies (6 direct)
- All dependencies are mature, maintained
- Regular updates
- Security scanning (optional)
Operational Risks
Risk: User Misconfiguration
Impact: Application doesn't work Likelihood: Medium (manual setup required) Mitigation:
- Clear documentation
- .env.example with all settings
- Validation on startup
- Helpful error messages
Risk: Backup Neglect
Impact: Data loss Likelihood: Medium (user responsibility) Mitigation:
- Document backup procedures
- Make backup easy (copy data/ folder)
- Consider automated backup scripts (V2)
Migration and Future Considerations
V1 to V2 Migration Path
- Add features without breaking existing data
- Markdown files remain compatible
- Database schema migrations (ALTER TABLE)
- Backward compatible API changes
Potential V2 Enhancements
- Webmentions support
- Media uploads (photos)
- Additional post types (articles, replies)
- Full-text search (SQLite FTS)
- Automated backups
- Self-hosted IndieAuth option
- Multiple IndieAuth providers
- Draft/scheduled posts
- Tags and categories
- Import/export tools
Data Portability Strategy
Export Formats:
- Markdown files (already portable)
- JSON export (notes + metadata)
- RSS feed (existing notes)
- HTML archive (static site generator)
Import Strategy (V2):
- From other blogging platforms
- From JSON backup
- From markdown directories
Success Criteria
The technology stack is successful if:
-
User can publish notes from any Micropub client ✓
- Protocol: Micropub over HTTP
- Auth: IndieAuth tokens
- Format: Stored as markdown files
-
Notes appear in RSS readers immediately ✓
- Format: Valid RSS 2.0
- Generator: feedgen library
- Caching: 5 minutes
-
System runs on minimal resources ✓
- Stack: Flask + SQLite (single process)
- Memory: < 100MB
- Dependencies: 6 direct
-
Code is readable and maintainable ✓
- Language: Python (user's preference)
- Framework: Flask (minimal, clear)
- Style: black formatting, type hints
-
All IndieWeb validators pass ✓
- Microformats: Server-side templating makes this easy
- IndieAuth: Delegated to indielogin.com
- Micropub: Spec-compliant implementation
-
Setup takes less than 5 minutes ✓
- Steps: Clone, venv, pip install, configure .env, run
- No build process
- No complex dependencies
-
System runs for months without intervention ✓
- Architecture: Stateless application
- Persistence: SQLite (reliable)
- Auth: Long-lived sessions (30 days)
Quick Start Guide
Development Setup
# Clone repository
git clone <repo> && cd starpunk
# Create virtual environment
python -m venv venv
source venv/bin/activate # or `venv\Scripts\activate` on Windows
# Install dependencies
pip install -r requirements.txt
# Configure
cp .env.example .env
# Edit .env: set SITE_URL, ADMIN_ME, SESSION_SECRET
# Initialize database
flask db init
# Run development server
flask run
# Visit http://localhost:5000
Production Deployment
# Setup (same as development)
# ...
# Install production server
pip install gunicorn
# Run with Gunicorn
gunicorn -w 4 -b 127.0.0.1:8000 app:app
# Configure nginx/Caddy for HTTPS
# Configure systemd for process management
# Set up regular backups of data/ directory
Configuration Reference
# .env file
SITE_URL=https://starpunk.example.com # Your domain
ADMIN_ME=https://your-website.com # Your IndieWeb identity
SESSION_SECRET=random-secret-key # Generate with: python -c "import secrets; print(secrets.token_hex(32))"
DATA_PATH=./data # Where to store notes and database
Summary
StarPunk's technology stack achieves radical simplicity through careful technology selection:
- Backend: Flask (micro-framework) + SQLite (embedded DB) + Python stdlib
- Storage: Markdown files (portability) + SQLite metadata (performance)
- Frontend: Jinja2 (SSR) + custom CSS (200 lines) + optional vanilla JS
- Auth: IndieLogin.com (external, zero maintenance)
- Build: None (zero build tools, zero npm)
- Deploy: Single package (Gunicorn + systemd + nginx)
Total Direct Dependencies: 6 (Flask, markdown, feedgen, httpx, python-dotenv, pytest)
Lines of Code Estimate: ~1500 LOC for complete V1 implementation
Setup Time: < 5 minutes from clone to running
This stack embodies the project philosophy: "Every line of code must justify its existence." Each technology choice prioritizes simplicity, standards compliance, and user data ownership over features and complexity.
References
Architecture Decision Records
- ADR-001: Python Web Framework Selection
- ADR-002: Flask Extensions and Dependencies
- ADR-003: Front-end Technology Stack
- ADR-004: File-Based Note Storage Architecture
- ADR-005: IndieLogin Authentication Integration
Standards and Specifications
- IndieWeb: https://indieweb.org/
- IndieAuth Spec: https://indieauth.spec.indieweb.org/
- Micropub Spec: https://micropub.spec.indieweb.org/
- Microformats2: http://microformats.org/wiki/h-entry
- RSS 2.0: https://www.rssboard.org/rss-specification
- CommonMark: https://spec.commonmark.org/
Tools and Libraries
- Flask: https://flask.palletsprojects.com/
- Jinja2: https://jinja.palletsprojects.com/
- IndieLogin.com: https://indielogin.com/
- Python Markdown: https://python-markdown.github.io/
- feedgen: https://feedgen.kiesow.be/
- httpx: https://www.python-httpx.org/
Validation and Testing
- IndieWebify.me: https://indiewebify.me/
- Micropub Rocks: https://micropub.rocks/
- W3C Feed Validator: https://validator.w3.org/feed/
- W3C HTML Validator: https://validator.w3.org/