# 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:

1. **Accept mp-slug**: Process the `mp-slug` property in Micropub requests
2. **Validation**: Ensure slugs are URL-safe and unique
3. **Fallback**: Auto-generate if no slug provided or if invalid
4. **Conflict Resolution**: Handle duplicate slugs gracefully
5. **Character Restrictions**: Allow only URL-safe characters

Implementation approach:
```python
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:

1. **User Control**: Authors can define meaningful URLs
2. **Standards Compliance**: Follows Micropub specification
3. **Migration Support**: Easier to preserve URLs when migrating
4. **SEO Benefits**: Human-readable URLs improve discoverability
5. **Flexibility**: Accommodates different URL strategies
6. **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
1. **Path Traversal**: Reject slugs containing `..` or absolute paths
2. **Reserved Names**: Block system routes (`api`, `admin`, `feed`, etc.)
3. **Length Limits**: Enforce maximum slug length
4. **Character Filtering**: Strip or reject dangerous characters
5. **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)
1. Modify note creation to accept optional slug parameter
2. Implement slug validation and sanitization
3. Add uniqueness checking with conflict resolution
4. Update database schema if needed (no changes expected)

### Phase 2: Micropub Integration (1 hour)
1. Extract `mp-slug` from Micropub requests
2. Pass to note creation function
3. Handle validation errors appropriately
4. Return proper Micropub responses

### Phase 3: Testing (1 hour)
1. Test valid custom slugs
2. Test invalid characters and patterns
3. Test duplicate slug handling
4. Test with Micropub clients
5. Test auto-generation fallback

## Validation Specification

### Allowed Slug Format
```regex
^[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:
1. Append `-2`, `-3`, etc. to make unique
2. Check up to `-99` before failing
3. 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
```http
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
HTTP/1.1 201 Created
Location: https://example.com/note/my-awesome-post
```

### Invalid Slug Handling
```http
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "error": "invalid_request",
    "error_description": "Invalid slug format: 'my/../../etc/passwd'"
}
```

## Migration Notes
1. Existing notes keep their auto-generated slugs
2. No database migration required (slug field exists)
3. No breaking changes to API
4. 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