-
Notifications
You must be signed in to change notification settings - Fork 72
feat(039): PanaversityFS SSO Authentication with Better Auth #325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ands, and documentation references.
…ersityFS hardening (Phase 1-2) Phase 1 (Setup): - Add sqlalchemy, asyncpg, aiosqlite, alembic, prometheus-client, hypothesis - Initialize Alembic migrations - Add database_url to config Phase 2 (Foundational): - Database: FileJournal and AuditLog SQLAlchemy models with CHECK constraints - Connection: Async session factory with SQLite/PostgreSQL support - Migration: Initial schema with indexes and constraints - Path validation: CONTENT_PATH_PATTERN, ASSET_PATH_PATTERN, overlay validation - Metrics: Prometheus counters, histograms, gauges with @Instrument decorators - Errors: SchemaViolationError, HashRequiredError Tests: 74 passing (42 path_utils, 20 metrics, 12 journal) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Phase 3 - User Story 1: Streaming Archives (T018-T023) - Refactor bulk.py with StreamingArchiveBuffer for 64MB memory cap - Add @instrument_archive decorator for metrics - Add archive_memory gauge tracking - Create integration tests for streaming ZIP generation - Create performance tests for SC-001/R4 validation Phase 4 - User Story 2: Conflict Detection (T024-T033) - Rename file_hash to expected_hash in WriteContentInput (FR-003) - Integrate FileJournal queries into write_content() (FR-002) - Add HashRequiredError for updates without expected_hash (FR-004) - Add atomic journal+storage transactions with rollback (FR-002) - Add mode: "created"|"updated" to write response (FR-005) - Apply @instrument_write decorator - Create integration tests for conflict detection - Create property tests for journal-storage consistency (R2) Test suite: 214 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…ing (Phase 5-6) Delta Build System (FR-025/026/027): - Add plan_build tool with manifest-hash-based delta detection - Add ManifestSnapshot table for historical state tracking - Return only changed files (added/modified/deleted) for CI/CD efficiency - Add delta_build tool for timestamp-based change detection Validation Tools: - Add validate_book tool for structural integrity checks - Add schema enforcement for content (FR-007) and assets (FR-008) - Integrate validation into read/write/delete operations Archive Improvements: - Rewrite get_book_archive to use disk-based streaming - Produce single ZIP file for Docusaurus compatibility - Maintain <64MB memory constraint via tempfile approach Presigned URL Support: - Add presign_read/presign_write to storage layer - Update upload_asset to return presigned URLs for large files Documentation: - Add comprehensive developer guide (7 chapters) - Update MCP-TOOLS.md with new tool documentation Testing: - Add 20 delta build tests covering all scenarios - Add property tests for R1 (schema), R5 (overlay), R6 (audit), R7 (agent) - Add unit tests for audit chain, overlay content, validation - All 313 tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Following MCP SDK best practices: Database Lifecycle (app.py): - Add app_lifespan context manager with AppContext dataclass - Initialize database on startup via init_db() - Dispose database engine on shutdown via reset_engine() - Pass lifespan to FastMCP constructor Session Manager Lifecycle (server.py): - Add starlette_lifespan for MCP session manager - Wrap streamable_http_app in Starlette with lifespan - Ensures proper session cleanup on server shutdown This fixes potential connection leaks when the server shuts down, as the database engine was never being disposed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Problem:
- Previous design used singleton engine with connection pool
- Connections stayed open for server lifetime
- Not suitable for serverless (Cloud Run) or cloud DBs (Neon, Supabase)
Solution:
- Use NullPool - no local connection pooling
- Each request creates fresh connection, disposed immediately after
- Cloud database providers (Neon PgBouncer, Supabase) handle pooling
- Removed unnecessary lifespan management from app.py
Pattern for cloud databases:
```python
engine = create_async_engine(url, poolclass=NullPool)
async with session:
# ... use session
# Connection released back to cloud pooler
await engine.dispose()
```
Benefits:
- Works with serverless (no warm connections needed)
- Compatible with Neon, Supabase, PlanetScale connection poolers
- No connection leaks on server restart
- Simpler lifecycle - no startup/shutdown hooks needed
Test fixtures updated to use StaticPool for in-memory SQLite.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
Implements dual authentication for PanaversityFS MCP server: - JWT/JWKS verification for user sessions (RS256) - API key verification for M2M (GitHub Actions, agents) - CombinedTokenVerifier routes by prefix (sk_live_, pana_) - FR-021 audit logging via MCP SDK auth contextvar - CORS middleware for browser-based MCP clients All 317 tests pass with updated mock_context fixtures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR integrates PanaversityFS MCP server with Panaversity SSO (Better Auth) and introduces FR-021 compliant audit logging by extracting real agent identifiers from authenticated contexts.
Key Changes:
- Replaces JWT-only authentication with dual-mode system supporting both RS256 JWT (JWKS) and M2M API keys
- Implements
CombinedTokenVerifierthat routes authentication by token prefix - Updates audit logging to extract
client_idfrom MCP SDK's auth contextvar instead of hardcoded "system" - Adds CORS middleware for browser-based MCP clients (Inspector)
Reviewed changes
Copilot reviewed 85 out of 86 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
specs/039-panaversity-fs-hardening/* |
New specification documents for production hardening with authentication integration |
panaversity-fs/tests/unit/test_auth.py |
Refactored authentication tests for new dual-auth architecture |
panaversity-fs/tests/unit/test_search_tools.py |
Updated search tool tests to accept mock_context parameter |
panaversity-fs/tests/unit/test_* |
Multiple test files updated to pass mock_context parameter to tool functions |
panaversity-fs/src/panaversity_fs/tools/search.py |
Updated search tools to accept Context parameter, removed hardcoded "system" agent_id |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Auto-convert postgresql:// and postgres:// to postgresql+asyncpg:// - Convert sslmode=require to ssl=require for asyncpg compatibility - Use TIMESTAMP(timezone=True) for all datetime columns - Add migration for timezone-aware timestamps - Fix .env.example: PANAVERSITY_DATABASE_URL prefix, Bearer auth docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add apiKey config option to MCPHttpClient constructor - Pass Authorization: Bearer header in all HTTP requests - Add PANAVERSITY_API_KEY environment variable support - Update .env.example with API key configuration - Log auth status on plugin startup Enables Docusaurus to fetch content from authenticated PanaversityFS MCP server using API keys issued by Panaversity SSO. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add Authorization: Bearer header for API key authentication - Wrap tool arguments in 'params' object (MCP SDK Pydantic requirement) - Required secrets: PANAVERSITY_SERVER_URL, PANAVERSITY_API_KEY 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
OpenDAL adds bucket name automatically. Including it in endpoint causes double bucket path (bucket/bucket/path) and 404 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add AbortController-based timeout to callTool method - Default 2 minute timeout (book fetch can be slow) - Configurable via timeoutMs option - Better error messages for timeouts Fixes MCP timeout errors when fetching large book content. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Summary
Key Changes
Authentication (
auth.py)JWKSTokenVerifier: RS256 JWT validation via JWKS endpointAPIKeyVerifier: M2M API key verification via SSO APICombinedTokenVerifier: Routes by prefix (sk_live_,pana_→ API key, else → JWT)Audit Logging (
audit.py)get_access_token()from MCP SDK auth contextvarclient_idfrom verified tokenConfiguration (
config.py)PANAVERSITY_AUTH_SERVER_URL: Enables auth when setServer (
server.py)Test plan
Auth Flow
🤖 Generated with Claude Code