Complete implementation of v1.2.0 "IndieWeb Features" release. ## Phase 1: Custom Slugs - Optional custom slug field in note creation form - Auto-sanitization (lowercase, hyphens only) - Uniqueness validation with auto-numbering - Read-only after creation to preserve permalinks - Matches Micropub mp-slug behavior ## Phase 2: Author Discovery + Microformats2 - Automatic h-card discovery from IndieAuth identity URL - 24-hour caching with graceful fallback - Never blocks login (per ADR-061) - Complete h-entry, h-card, h-feed markup - All required Microformats2 properties - rel-me links for identity verification - Passes IndieWeb validation ## Phase 3: Media Upload - Upload up to 4 images per note (JPEG, PNG, GIF, WebP) - Automatic optimization with Pillow - Auto-resize to 2048px - EXIF orientation correction - 95% quality compression - Social media-style layout (media top, text below) - Optional captions for accessibility - Integration with all feed formats (RSS, ATOM, JSON Feed) - Date-organized storage with UUID filenames - Immutable caching (1 year) ## Database Changes - migrations/006_add_author_profile.sql - Author discovery cache - migrations/007_add_media_support.sql - Media storage ## New Modules - starpunk/author_discovery.py - h-card discovery and caching - starpunk/media.py - Image upload, validation, optimization ## Documentation - 4 new ADRs (056, 057, 058, 061) - Complete design specifications - Developer Q&A with 40+ questions answered - 3 implementation reports - 3 architect reviews (all approved) ## Testing - 56 new tests for v1.2.0 features - 842 total tests in suite - All v1.2.0 feature tests passing ## Dependencies - Added: mf2py (Microformats2 parser) - Added: Pillow (image processing) Version: 1.2.0-rc.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
3.9 KiB
ADR-057: Media Attachment Model
Status
Accepted
Context
The v1.2.0 media upload feature needed a clear model for how media relates to notes. Initial design assumed inline markdown image insertion (like a blog editor), but user feedback clarified that notes are more like social media posts (tweets, Mastodon toots) where media is attached rather than inline.
Key insights from user:
- "Notes are more like tweets, thread posts, mastodon posts etc. where the media is inserted is kind of irrelevant"
- Media should appear at the TOP of notes when displayed
- Text content should appear BELOW media
- Multiple images per note should be supported
Decision
We will implement a social media-style attachment model for media:
-
Database Design: Use a junction table (
note_media) to associate media files with notes, allowing:- Multiple media per note (max 4)
- Explicit ordering via
display_ordercolumn - Per-attachment metadata (captions)
- Future reuse of media across notes
-
Display Model: Media attachments appear at the TOP of notes:
- 1 image: Full width display
- 2 images: Side-by-side layout
- 3-4 images: Grid layout
- Text content always appears below media
-
Syndication Strategy:
- RSS: Embed media as HTML in description (universal support)
- ATOM: Use both
<link rel="enclosure">and HTML content - JSON Feed: Use native
attachmentsarray (cleanest)
-
Microformats2: Multiple
u-photoproperties for multi-photo posts
Rationale
Why attachment model over inline markdown?
- Matches user mental model (social media posts)
- Simplifies UI/UX (no cursor tracking needed)
- Better syndication support (especially JSON Feed)
- Cleaner Microformats2 markup
- Consistent display across all contexts
Why junction table over array column?
- Better query performance for feeds
- Supports future media reuse
- Per-attachment metadata
- Explicit ordering control
- Standard relational design
Why limit to 4 images?
- Twitter limit is 4 images
- Mastodon limit is 4 images
- Prevents performance issues
- Maintains clean grid layouts
- Sufficient for microblogging use case
Consequences
Positive
- Clean separation of media and text content
- Familiar social media UX pattern
- Excellent syndication feed support
- Future-proof for media galleries
- Supports accessibility via captions
- Efficient database queries
Negative
- No inline images in markdown content
- All media must appear at top
- Cannot mix text and images
- More complex database schema
- Additional JOIN queries needed
Neutral
- Different from traditional blog CMSs
- Requires grid layout CSS
- Media upload is separate from text editing
Alternatives Considered
Alternative 1: Inline Markdown Images
Store media URLs in markdown content as .
- Pros: Traditional blog approach, flexible positioning
- Cons: Poor syndication, complex editing UX, inconsistent display
Alternative 2: JSON Array in Notes Table
Store media IDs as JSON array column in notes table.
- Pros: Simpler schema, fewer tables
- Cons: Poor query performance, no per-media metadata, violates 1NF
Alternative 3: Single Media Per Note
Restrict to one image per note.
- Pros: Simplest implementation
- Cons: Too limiting, doesn't match social media patterns
Implementation Notes
- Migration will create both
mediaandnote_mediatables - Feed generators must query media via JOIN
- Template must render media before content
- Upload UI shows thumbnails, not markdown insertion
- Consider lazy loading for performance