feat(tags): Add database schema and tags module (v1.3.0 Phase 1)
Implements tag/category system backend following microformats2 p-category specification. Database changes: - Migration 008: Add tags and note_tags tables - Normalized tag storage (case-insensitive lookup, display name preserved) - Indexes for performance New module: - starpunk/tags.py: Tag management functions - normalize_tag: Normalize tag strings - get_or_create_tag: Get or create tag records - add_tags_to_note: Associate tags with notes (replaces existing) - get_note_tags: Retrieve note tags (alphabetically ordered) - get_tag_by_name: Lookup tag by normalized name - get_notes_by_tag: Get all notes with specific tag - parse_tag_input: Parse comma-separated tag input Model updates: - Note.tags property (lazy-loaded, prefer pre-loading in routes) - Note.to_dict() add include_tags parameter CRUD updates: - create_note() accepts tags parameter - update_note() accepts tags parameter (None = no change, [] = remove all) Micropub integration: - Pass tags to create_note() (tags already extracted by extract_tags()) - Return tags in q=source response Per design doc: docs/design/v1.3.0/microformats-tags-design.md Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
593
docs/design/v1.0.0/indieauth-removal-phases.md
Normal file
593
docs/design/v1.0.0/indieauth-removal-phases.md
Normal file
@@ -0,0 +1,593 @@
|
||||
# IndieAuth Removal: Phased Implementation Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This document breaks down the IndieAuth server removal into testable phases, each with clear acceptance criteria and verification steps.
|
||||
|
||||
## Phase 1: Remove Authorization Server (4 hours)
|
||||
|
||||
### Objective
|
||||
Remove the authorization endpoint and consent UI while keeping the system functional.
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 1.1 Remove Authorization UI (30 min)
|
||||
```bash
|
||||
# Delete consent template
|
||||
rm /home/phil/Projects/starpunk/templates/auth/authorize.html
|
||||
|
||||
# Verify
|
||||
ls /home/phil/Projects/starpunk/templates/auth/
|
||||
# Should be empty or not exist
|
||||
```
|
||||
|
||||
#### 1.2 Remove Authorization Endpoint (1 hour)
|
||||
In `/home/phil/Projects/starpunk/starpunk/routes/auth.py`:
|
||||
- Delete `authorization_endpoint()` function
|
||||
- Delete related imports from `starpunk.tokens`
|
||||
- Keep admin auth routes intact
|
||||
|
||||
#### 1.3 Remove Authorization Tests (30 min)
|
||||
```bash
|
||||
# Delete test files
|
||||
rm /home/phil/Projects/starpunk/tests/test_routes_authorization.py
|
||||
rm /home/phil/Projects/starpunk/tests/test_auth_pkce.py
|
||||
```
|
||||
|
||||
#### 1.4 Remove PKCE Implementation (1 hour)
|
||||
From `/home/phil/Projects/starpunk/starpunk/auth.py`:
|
||||
- Remove `generate_code_verifier()`
|
||||
- Remove `calculate_code_challenge()`
|
||||
- Remove PKCE validation logic
|
||||
- Keep session management functions
|
||||
|
||||
#### 1.5 Update Route Registration (30 min)
|
||||
Ensure no references to `/auth/authorization` in:
|
||||
- URL route definitions
|
||||
- Template URL generation
|
||||
- Documentation
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
✅ **Server Starts Successfully**
|
||||
```bash
|
||||
uv run python -m starpunk
|
||||
# No import errors or missing route errors
|
||||
```
|
||||
|
||||
✅ **Admin Login Works**
|
||||
```bash
|
||||
# Navigate to /admin/login
|
||||
# Can still authenticate via IndieLogin.com
|
||||
# Session created successfully
|
||||
```
|
||||
|
||||
✅ **No Authorization Endpoint**
|
||||
```bash
|
||||
curl -I http://localhost:5000/auth/authorization
|
||||
# Should return 404 Not Found
|
||||
```
|
||||
|
||||
✅ **Tests Pass (Remaining)**
|
||||
```bash
|
||||
uv run pytest tests/ -k "not authorization and not pkce"
|
||||
# All remaining tests pass
|
||||
```
|
||||
|
||||
### Verification Commands
|
||||
```bash
|
||||
# Check for orphaned imports
|
||||
grep -r "authorization_endpoint" /home/phil/Projects/starpunk/
|
||||
# Should return nothing
|
||||
|
||||
# Check for PKCE references
|
||||
grep -r "code_challenge\|code_verifier" /home/phil/Projects/starpunk/
|
||||
# Should only appear in migration files or comments
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Remove Token Issuance (3 hours)
|
||||
|
||||
### Objective
|
||||
Remove token generation and issuance while keeping token verification temporarily.
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 2.1 Remove Token Endpoint (1 hour)
|
||||
In `/home/phil/Projects/starpunk/starpunk/routes/auth.py`:
|
||||
- Delete `token_endpoint()` function
|
||||
- Remove token-related imports
|
||||
|
||||
#### 2.2 Remove Token Generation (1 hour)
|
||||
In `/home/phil/Projects/starpunk/starpunk/tokens.py`:
|
||||
- Remove `create_access_token()`
|
||||
- Remove `create_authorization_code()`
|
||||
- Remove `exchange_authorization_code()`
|
||||
- Keep `verify_token()` temporarily (will modify in Phase 4)
|
||||
|
||||
#### 2.3 Remove Token Tests (30 min)
|
||||
```bash
|
||||
rm /home/phil/Projects/starpunk/tests/test_routes_token.py
|
||||
rm /home/phil/Projects/starpunk/tests/test_tokens.py
|
||||
```
|
||||
|
||||
#### 2.4 Clean Up Exceptions (30 min)
|
||||
Remove custom exceptions:
|
||||
- `InvalidAuthorizationCodeError`
|
||||
- `ExpiredAuthorizationCodeError`
|
||||
- Update error handling to use generic exceptions
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
✅ **No Token Endpoint**
|
||||
```bash
|
||||
curl -I http://localhost:5000/auth/token
|
||||
# Should return 404 Not Found
|
||||
```
|
||||
|
||||
✅ **No Token Generation Code**
|
||||
```bash
|
||||
grep -r "create_access_token\|create_authorization_code" /home/phil/Projects/starpunk/starpunk/
|
||||
# Should return nothing (except in comments)
|
||||
```
|
||||
|
||||
✅ **Server Still Runs**
|
||||
```bash
|
||||
uv run python -m starpunk
|
||||
# No import errors
|
||||
```
|
||||
|
||||
✅ **Micropub Temporarily Broken (Expected)**
|
||||
```bash
|
||||
# This is expected and will be fixed in Phase 4
|
||||
# Document that Micropub is non-functional during migration
|
||||
```
|
||||
|
||||
### Verification Commands
|
||||
```bash
|
||||
# Check for token generation references
|
||||
grep -r "generate_token\|issue_token" /home/phil/Projects/starpunk/
|
||||
# Should be empty
|
||||
|
||||
# Verify exception cleanup
|
||||
grep -r "InvalidAuthorizationCodeError" /home/phil/Projects/starpunk/
|
||||
# Should be empty
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Database Schema Simplification (2 hours)
|
||||
|
||||
### Objective
|
||||
Remove authorization and token tables from the database.
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 3.1 Create Removal Migration (30 min)
|
||||
Create `/home/phil/Projects/starpunk/migrations/003_remove_indieauth_tables.sql`:
|
||||
```sql
|
||||
-- Remove IndieAuth server tables
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
-- Drop dependent objects first
|
||||
DROP INDEX IF EXISTS idx_tokens_hash;
|
||||
DROP INDEX IF EXISTS idx_tokens_user_id;
|
||||
DROP INDEX IF EXISTS idx_tokens_client_id;
|
||||
DROP INDEX IF EXISTS idx_auth_codes_code;
|
||||
DROP INDEX IF EXISTS idx_auth_codes_user_id;
|
||||
|
||||
-- Drop tables
|
||||
DROP TABLE IF EXISTS tokens CASCADE;
|
||||
DROP TABLE IF EXISTS authorization_codes CASCADE;
|
||||
|
||||
-- Clean up any orphaned sequences
|
||||
DROP SEQUENCE IF EXISTS tokens_id_seq;
|
||||
DROP SEQUENCE IF EXISTS authorization_codes_id_seq;
|
||||
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
#### 3.2 Run Migration (30 min)
|
||||
```bash
|
||||
# Backup database first
|
||||
pg_dump $DATABASE_URL > backup_before_removal.sql
|
||||
|
||||
# Run migration
|
||||
uv run python -m starpunk.migrate
|
||||
```
|
||||
|
||||
#### 3.3 Update Schema Documentation (30 min)
|
||||
Update `/home/phil/Projects/starpunk/docs/design/database-schema.md`:
|
||||
- Remove token table documentation
|
||||
- Remove authorization_codes table documentation
|
||||
- Update ER diagram
|
||||
|
||||
#### 3.4 Remove Old Migration (30 min)
|
||||
```bash
|
||||
# Archive old migration
|
||||
mv /home/phil/Projects/starpunk/migrations/002_secure_tokens_and_authorization_codes.sql \
|
||||
/home/phil/Projects/starpunk/migrations/archive/
|
||||
```
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
✅ **Tables Removed**
|
||||
```sql
|
||||
-- Connect to database and verify
|
||||
\dt
|
||||
-- Should NOT list 'tokens' or 'authorization_codes'
|
||||
```
|
||||
|
||||
✅ **No Foreign Key Errors**
|
||||
```sql
|
||||
-- Check for orphaned constraints
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE conname LIKE '%token%' OR conname LIKE '%auth%';
|
||||
-- Should return minimal results (only auth_state related)
|
||||
```
|
||||
|
||||
✅ **Application Starts**
|
||||
```bash
|
||||
uv run python -m starpunk
|
||||
# No database connection errors
|
||||
```
|
||||
|
||||
✅ **Admin Functions Work**
|
||||
- Can log in
|
||||
- Can create posts
|
||||
- Sessions persist
|
||||
|
||||
### Rollback Plan
|
||||
```bash
|
||||
# If issues arise
|
||||
psql $DATABASE_URL < backup_before_removal.sql
|
||||
# Re-run old migration
|
||||
psql $DATABASE_URL < /home/phil/Projects/starpunk/migrations/archive/002_secure_tokens_and_authorization_codes.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: External Token Verification (4 hours)
|
||||
|
||||
### Objective
|
||||
Replace internal token verification with external provider verification.
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 4.1 Implement External Verification (2 hours)
|
||||
Create new verification in `/home/phil/Projects/starpunk/starpunk/micropub.py`:
|
||||
```python
|
||||
import hashlib
|
||||
import httpx
|
||||
from typing import Optional, Dict, Any
|
||||
from flask import current_app
|
||||
|
||||
# Simple in-memory cache
|
||||
_token_cache = {}
|
||||
|
||||
def verify_token(bearer_token: str) -> Optional[Dict[str, Any]]:
|
||||
"""Verify token with external endpoint"""
|
||||
# Check cache
|
||||
token_hash = hashlib.sha256(bearer_token.encode()).hexdigest()
|
||||
if token_hash in _token_cache:
|
||||
data, expiry = _token_cache[token_hash]
|
||||
if time.time() < expiry:
|
||||
return data
|
||||
del _token_cache[token_hash]
|
||||
|
||||
# Verify with external endpoint
|
||||
endpoint = current_app.config.get('TOKEN_ENDPOINT')
|
||||
if not endpoint:
|
||||
return None
|
||||
|
||||
try:
|
||||
response = httpx.get(
|
||||
endpoint,
|
||||
headers={'Authorization': f'Bearer {bearer_token}'},
|
||||
timeout=5.0
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
|
||||
# Validate response
|
||||
if data.get('me') != current_app.config.get('ADMIN_ME'):
|
||||
return None
|
||||
|
||||
if 'create' not in data.get('scope', '').split():
|
||||
return None
|
||||
|
||||
# Cache for 5 minutes
|
||||
_token_cache[token_hash] = (data, time.time() + 300)
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Token verification failed: {e}")
|
||||
return None
|
||||
```
|
||||
|
||||
#### 4.2 Update Configuration (30 min)
|
||||
In `/home/phil/Projects/starpunk/starpunk/config.py`:
|
||||
```python
|
||||
# External IndieAuth settings
|
||||
TOKEN_ENDPOINT = os.getenv('TOKEN_ENDPOINT', 'https://tokens.indieauth.com/token')
|
||||
ADMIN_ME = os.getenv('ADMIN_ME') # Required
|
||||
|
||||
# Validate configuration
|
||||
if not ADMIN_ME:
|
||||
raise ValueError("ADMIN_ME must be configured")
|
||||
```
|
||||
|
||||
#### 4.3 Remove Old Token Module (30 min)
|
||||
```bash
|
||||
rm /home/phil/Projects/starpunk/starpunk/tokens.py
|
||||
```
|
||||
|
||||
#### 4.4 Update Tests (1 hour)
|
||||
Update `/home/phil/Projects/starpunk/tests/test_micropub.py`:
|
||||
```python
|
||||
@patch('starpunk.micropub.httpx.get')
|
||||
def test_external_token_verification(mock_get):
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
'me': 'https://example.com',
|
||||
'scope': 'create update'
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Test verification
|
||||
result = verify_token('test-token')
|
||||
assert result is not None
|
||||
assert result['me'] == 'https://example.com'
|
||||
```
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
✅ **External Verification Works**
|
||||
```bash
|
||||
# With a valid token from tokens.indieauth.com
|
||||
curl -X POST http://localhost:5000/micropub \
|
||||
-H "Authorization: Bearer VALID_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type": ["h-entry"], "properties": {"content": ["Test"]}}'
|
||||
# Should return 201 Created
|
||||
```
|
||||
|
||||
✅ **Invalid Tokens Rejected**
|
||||
```bash
|
||||
curl -X POST http://localhost:5000/micropub \
|
||||
-H "Authorization: Bearer INVALID_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type": ["h-entry"], "properties": {"content": ["Test"]}}'
|
||||
# Should return 403 Forbidden
|
||||
```
|
||||
|
||||
✅ **Token Caching Works**
|
||||
```python
|
||||
# In test environment
|
||||
token = "test-token"
|
||||
result1 = verify_token(token) # External call
|
||||
result2 = verify_token(token) # Should use cache
|
||||
# Verify only one external call made
|
||||
```
|
||||
|
||||
✅ **Configuration Validated**
|
||||
```bash
|
||||
# Without ADMIN_ME set
|
||||
unset ADMIN_ME
|
||||
uv run python -m starpunk
|
||||
# Should fail with clear error message
|
||||
```
|
||||
|
||||
### Performance Verification
|
||||
```bash
|
||||
# Measure token verification time
|
||||
time curl -X GET http://localhost:5000/micropub \
|
||||
-H "Authorization: Bearer VALID_TOKEN" \
|
||||
-w "\nTime: %{time_total}s\n"
|
||||
# First call: <500ms
|
||||
# Cached calls: <50ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Documentation and Discovery (2 hours)
|
||||
|
||||
### Objective
|
||||
Update all documentation and add proper IndieAuth discovery headers.
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 5.1 Add Discovery Links (30 min)
|
||||
In `/home/phil/Projects/starpunk/templates/base.html`:
|
||||
```html
|
||||
<head>
|
||||
<!-- Existing head content -->
|
||||
|
||||
<!-- IndieAuth Discovery -->
|
||||
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
|
||||
<link rel="token_endpoint" href="{{ config.TOKEN_ENDPOINT }}">
|
||||
<link rel="micropub" href="{{ url_for('micropub.micropub_endpoint', _external=True) }}">
|
||||
</head>
|
||||
```
|
||||
|
||||
#### 5.2 Update User Documentation (45 min)
|
||||
Create `/home/phil/Projects/starpunk/docs/user-guide/indieauth-setup.md`:
|
||||
```markdown
|
||||
# Setting Up IndieAuth for StarPunk
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Add these links to your personal website's HTML:
|
||||
```html
|
||||
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
|
||||
<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
|
||||
<link rel="micropub" href="https://your-starpunk.com/micropub">
|
||||
```
|
||||
|
||||
2. Configure StarPunk:
|
||||
```ini
|
||||
ADMIN_ME=https://your-website.com
|
||||
TOKEN_ENDPOINT=https://tokens.indieauth.com/token
|
||||
```
|
||||
|
||||
3. Use any Micropub client!
|
||||
```
|
||||
|
||||
#### 5.3 Update README (15 min)
|
||||
- Remove references to built-in authorization
|
||||
- Add "Prerequisites" section about external IndieAuth
|
||||
- Update configuration examples
|
||||
|
||||
#### 5.4 Update CHANGELOG (15 min)
|
||||
```markdown
|
||||
## [0.5.0] - 2025-11-24
|
||||
|
||||
### BREAKING CHANGES
|
||||
- Removed built-in IndieAuth authorization server
|
||||
- Removed token issuance functionality
|
||||
- All existing tokens are invalidated
|
||||
|
||||
### Changed
|
||||
- Token verification now uses external IndieAuth providers
|
||||
- Simplified database schema (removed token tables)
|
||||
- Reduced codebase by ~500 lines
|
||||
|
||||
### Added
|
||||
- Support for external token endpoints
|
||||
- Token verification caching for performance
|
||||
- IndieAuth discovery links in HTML
|
||||
|
||||
### Migration Guide
|
||||
Users must now:
|
||||
1. Configure external IndieAuth provider
|
||||
2. Re-authenticate with Micropub clients
|
||||
3. Update ADMIN_ME configuration
|
||||
```
|
||||
|
||||
#### 5.5 Version Bump (15 min)
|
||||
Update `/home/phil/Projects/starpunk/starpunk/__init__.py`:
|
||||
```python
|
||||
__version__ = "0.5.0" # Breaking change per versioning strategy
|
||||
```
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
✅ **Discovery Links Present**
|
||||
```bash
|
||||
curl http://localhost:5000/ | grep -E "authorization_endpoint|token_endpoint|micropub"
|
||||
# Should show all three link tags
|
||||
```
|
||||
|
||||
✅ **Documentation Complete**
|
||||
- [ ] User guide explains external provider setup
|
||||
- [ ] README reflects new architecture
|
||||
- [ ] CHANGELOG documents breaking changes
|
||||
- [ ] Migration guide provided
|
||||
|
||||
✅ **Version Updated**
|
||||
```bash
|
||||
uv run python -c "import starpunk; print(starpunk.__version__)"
|
||||
# Should output: 0.5.0
|
||||
```
|
||||
|
||||
✅ **Examples Work**
|
||||
- [ ] Example configuration in docs is valid
|
||||
- [ ] HTML snippet in docs is correct
|
||||
- [ ] Micropub client setup instructions tested
|
||||
|
||||
---
|
||||
|
||||
## Final Validation Checklist
|
||||
|
||||
### System Health
|
||||
- [ ] Server starts without errors
|
||||
- [ ] Admin can log in
|
||||
- [ ] Admin can create posts
|
||||
- [ ] Micropub endpoint responds
|
||||
- [ ] Valid tokens accepted
|
||||
- [ ] Invalid tokens rejected
|
||||
- [ ] HTML has discovery links
|
||||
|
||||
### Code Quality
|
||||
- [ ] No orphaned imports
|
||||
- [ ] No references to removed code
|
||||
- [ ] Tests pass with >90% coverage
|
||||
- [ ] No security warnings
|
||||
|
||||
### Performance
|
||||
- [ ] Token verification <500ms
|
||||
- [ ] Cached verification <50ms
|
||||
- [ ] Memory usage stable
|
||||
- [ ] No database deadlocks
|
||||
|
||||
### Documentation
|
||||
- [ ] Architecture docs updated
|
||||
- [ ] User guide complete
|
||||
- [ ] API docs accurate
|
||||
- [ ] CHANGELOG updated
|
||||
- [ ] Version bumped
|
||||
|
||||
### Database
|
||||
- [ ] Old tables removed
|
||||
- [ ] No orphaned constraints
|
||||
- [ ] Migration successful
|
||||
- [ ] Backup available
|
||||
|
||||
## Rollback Decision Tree
|
||||
|
||||
```
|
||||
Issue Detected?
|
||||
├─ During Phase 1-2?
|
||||
│ └─ Git revert commits
|
||||
│ └─ Restart server
|
||||
├─ During Phase 3?
|
||||
│ └─ Restore database backup
|
||||
│ └─ Git revert commits
|
||||
│ └─ Restart server
|
||||
└─ During Phase 4-5?
|
||||
└─ Critical issue?
|
||||
├─ Yes: Full rollback
|
||||
│ └─ Restore DB + revert code
|
||||
└─ No: Fix forward
|
||||
└─ Patch issue
|
||||
└─ Continue deployment
|
||||
```
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Quantitative
|
||||
- **Lines removed**: >500
|
||||
- **Test coverage**: >90%
|
||||
- **Token verification**: <500ms
|
||||
- **Cache hit rate**: >90%
|
||||
- **Memory stable**: <100MB
|
||||
|
||||
### Qualitative
|
||||
- **Simpler architecture**: Clear separation of concerns
|
||||
- **Better security**: Specialized providers handle auth
|
||||
- **Less maintenance**: No auth code to maintain
|
||||
- **User flexibility**: Choice of providers
|
||||
- **Standards compliant**: Pure Micropub server
|
||||
|
||||
## Risk Matrix
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|---------|------------|
|
||||
| Breaking existing tokens | Certain | Medium | Clear communication, migration guide |
|
||||
| External service down | Low | High | Token caching, timeout handling |
|
||||
| User confusion | Medium | Low | Comprehensive documentation |
|
||||
| Performance degradation | Low | Medium | Caching layer, monitoring |
|
||||
| Security vulnerability | Low | High | Use established providers |
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Created**: 2025-11-24
|
||||
**Author**: StarPunk Architecture Team
|
||||
**Status**: Ready for Implementation
|
||||
Reference in New Issue
Block a user