- ADR-033: Database migration redesign - ADR-034: Full-text search with FTS5 - ADR-035: Custom slugs in Micropub - ADR-036: IndieAuth token verification method - ADR-039: Micropub URL construction fix - Implementation plan and decisions - Architecture specifications - Validation reports for implementation and search UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.2 KiB
6.2 KiB
ADR-035: Custom Slugs in Micropub
Status
Proposed
Context
Currently, StarPunk auto-generates slugs from note content (first 5 words). While this works well for most cases, users may want to specify custom slugs for:
- SEO-friendly URLs
- Memorable short links
- Maintaining URL structure from migrated content
- Creating hierarchical paths (e.g.,
2024/11/my-note) - Personal preference and control
The Micropub specification supports custom slugs via the mp-slug property, which we should honor.
Decision
Implement custom slug support through the Micropub endpoint:
- Accept mp-slug: Process the
mp-slugproperty in Micropub requests - Validation: Ensure slugs are URL-safe and unique
- Fallback: Auto-generate if no slug provided or if invalid
- Conflict Resolution: Handle duplicate slugs gracefully
- Character Restrictions: Allow only URL-safe characters
Implementation approach:
def process_micropub_request(request_data):
# Extract custom slug if provided
custom_slug = request_data.get('properties', {}).get('mp-slug', [None])[0]
if custom_slug:
# Validate and sanitize
slug = sanitize_slug(custom_slug)
# Ensure uniqueness
if slug_exists(slug):
# Add suffix or reject based on configuration
slug = make_unique(slug)
else:
# Fall back to auto-generation
slug = generate_slug(content)
return create_note(content, slug=slug)
Rationale
Supporting custom slugs provides:
- User Control: Authors can define meaningful URLs
- Standards Compliance: Follows Micropub specification
- Migration Support: Easier to preserve URLs when migrating
- SEO Benefits: Human-readable URLs improve discoverability
- Flexibility: Accommodates different URL strategies
- Backward Compatible: Existing auto-generation continues working
Validation rules:
- Maximum length: 200 characters
- Allowed characters:
a-z0-9-_/ - No consecutive slashes or dashes
- No leading/trailing special characters
- Case-insensitive uniqueness check
Consequences
Positive
- Full Micropub compliance for slug handling
- Better user experience and control
- SEO-friendly URLs when desired
- Easier content migration from other platforms
- Maintains backward compatibility
Negative
- Additional validation complexity
- Potential for user confusion with conflicts
- Must handle edge cases (empty, invalid, duplicate)
- Slightly more complex note creation logic
Security Considerations
- Path Traversal: Reject slugs containing
..or absolute paths - Reserved Names: Block system routes (
api,admin,feed, etc.) - Length Limits: Enforce maximum slug length
- Character Filtering: Strip or reject dangerous characters
- Case Sensitivity: Normalize to lowercase for consistency
Alternatives Considered
Alternative 1: No Custom Slugs
- Pros: Simpler, no validation needed
- Cons: Poor user experience, non-compliant with Micropub
- Rejected because: Users expect URL control in modern CMS
Alternative 2: Separate Slug Field in UI
- Pros: More discoverable for web users
- Cons: Doesn't help API users, not Micropub standard
- Rejected because: Should follow established standards
Alternative 3: Slugs Only via Direct API
- Pros: Advanced feature for power users only
- Cons: Inconsistent experience, limits adoption
- Rejected because: Micropub clients expect this feature
Alternative 4: Hierarchical Slugs (/2024/11/25/my-note)
- Pros: Organized structure, date-based archives
- Cons: Complex routing, harder to implement
- Rejected because: Can add later if needed, start simple
Implementation Plan
Phase 1: Core Logic (2 hours)
- Modify note creation to accept optional slug parameter
- Implement slug validation and sanitization
- Add uniqueness checking with conflict resolution
- Update database schema if needed (no changes expected)
Phase 2: Micropub Integration (1 hour)
- Extract
mp-slugfrom Micropub requests - Pass to note creation function
- Handle validation errors appropriately
- Return proper Micropub responses
Phase 3: Testing (1 hour)
- Test valid custom slugs
- Test invalid characters and patterns
- Test duplicate slug handling
- Test with Micropub clients
- Test auto-generation fallback
Validation Specification
Allowed Slug Format
^[a-z0-9]+(?:-[a-z0-9]+)*(?:/[a-z0-9]+(?:-[a-z0-9]+)*)*$
Examples:
- ✅
my-awesome-post - ✅
2024/11/25/daily-note - ✅
projects/starpunk/update-1 - ❌
My-Post(uppercase) - ❌
my--post(consecutive dashes) - ❌
-my-post(leading dash) - ❌
my_post(underscore not allowed) - ❌
../../../etc/passwd(path traversal)
Reserved Slugs
The following slugs are reserved and cannot be used:
- System routes:
api,admin,auth,feed,static - Special pages:
login,logout,settings - File extensions: Slugs ending in
.xml,.json,.html
Conflict Resolution Strategy
When a duplicate slug is detected:
- Append
-2,-3, etc. to make unique - Check up to
-99before failing - Return error if no unique slug found in 99 attempts
Example:
- Request:
mp-slug=my-note - Exists:
my-note - Created:
my-note-2
API Examples
Micropub Request with Custom Slug
POST /micropub
Content-Type: application/json
Authorization: Bearer {token}
{
"type": ["h-entry"],
"properties": {
"content": ["My awesome post content"],
"mp-slug": ["my-awesome-post"]
}
}
Response
HTTP/1.1 201 Created
Location: https://example.com/note/my-awesome-post
Invalid Slug Handling
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "invalid_request",
"error_description": "Invalid slug format: 'my/../../etc/passwd'"
}
Migration Notes
- Existing notes keep their auto-generated slugs
- No database migration required (slug field exists)
- No breaking changes to API
- Existing clients continue working without modification
References
- Micropub Specification: https://www.w3.org/TR/micropub/#mp-slug
- URL Slug Best Practices: https://stackoverflow.com/questions/695438/safe-characters-for-friendly-url
- IndieWeb Slug Examples: https://indieweb.org/slug