fdc32d431d
5 plans across 5 sequential waves covering: Alembic migration 0003 (null-user cleanup, NOT NULL constraint, quota reconciliation), presigned MinIO PUT upload flow with atomic quota enforcement, auth guards on all document/topic endpoints, flat-file settings retirement + per-user AI classification, and frontend quota bar with 3-step XHR upload progress. Verification passed across all 12 dimensions. All 8 phase requirements covered (STORE-03/04/05/06, SEC-04, DOC-03/04/05). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.1 KiB
6.1 KiB
gsd_state_version, milestone, milestone_name, current_phase, status, last_updated, progress
| gsd_state_version | milestone | milestone_name | current_phase | status | last_updated | progress | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1.0 | v1.0 | milestone | 3 | planned | 2026-05-23T00:00:00Z |
|
Project State
Project: DocuVault Status: Phase 3 Planned — Ready to Execute Current Phase: 3 Last Updated: 2026-05-23
Phase Status
| Phase | Name | Status |
|---|---|---|
| 1 | Infrastructure Foundation | ✓ Complete |
| 2 | Users & Authentication | ✓ Complete (5/5 plans) |
| 3 | Document Migration & Multi-User Isolation | Planned (5 plans, ready to execute) |
| 4 | Folders, Sharing, Quotas & Document UX | Not Started |
| 5 | Cloud Storage Backends | Not Started |
Current Position
Phase: 02-users-authentication — Complete Plan: 5/5 complete (Plan 05: Admin panel frontend) Progress: ████░░░░░░ 40% (2/5 phases complete)
Performance Metrics
| Metric | Value |
|---|---|
| Phases complete | 1 / 5 |
| Requirements mapped | 54 / 54 |
| Plans written | 5 (Phase 1) |
| Plans complete | 10 (5 Phase 1 + 5 Phase 2) |
Accumulated Context
Key Decisions
| Decision | Rationale |
|---|---|
| PostgreSQL + MinIO | Multi-user quotas and horizontal scaling require shared, consistent state |
| HKDF per-user key derivation | Single Fernet key would be catastrophic on leak — must be derived before first credential is stored |
| Presigned MinIO URL flow | FastAPI handles metadata only; bytes never pass through the API layer |
| Atomic PostgreSQL quota UPDATE | Never perform quota arithmetic in Python between two DB statements |
| JWT in httpOnly cookie | Refresh token in httpOnly cookie; access token in Pinia memory only — never localStorage |
| Refresh token family revocation | RFC 9700 — reuse of a rotated token revokes entire family and alerts user |
| BackgroundTasks replacement | FastAPI BackgroundTasks is per-instance; replace with Celery+Redis or pgqueuer before horizontal scale |
| AuditLog metadata_ ORM attribute | metadata is reserved on DeclarativeBase; ORM attribute is metadata_ with name="metadata" kwarg to avoid silent collision |
| documents.user_id nullable Phase 1 | D-03 — no auth in Phase 1; Phase 2 migration adds NOT NULL after auth lands |
| groups stub table Phase 1 | D-02 — groups is a v2 feature; table created now for schema completeness, no rows until Phase 2+ |
| SEQUENCES grants in migration | GRANT USAGE/SELECT on sequences required for audit_log.id autoincrement nextval() by docuvault_app |
| Admin impersonation excluded | Explicit architectural exclusion — no endpoint or UI pathway; violates privacy-first core value |
| user_id as refresh token family proxy | No separate family_id column; user_id serves as family per RFC 9700 — simpler schema |
| pwdlib over passlib | pwdlib actively maintained with clean Argon2Hasher API; passlib unmaintained |
| TOTP replay TTL=90s | valid_window=1 covers ±30s (90s total) — TTL matches window |
| HIBP fail-open | Network errors return False + log warning; auth never blocked by external service |
| Two-DSN PostgreSQL strategy | DATABASE_URL (docuvault_app, DML only) + DATABASE_MIGRATE_URL (docuvault_migrate, DDL only); celery-worker gets only DATABASE_URL |
| MinIO healthcheck via mc ready local | curl removed from MinIO Docker image since Oct 2023; mc is the correct in-container healthcheck tool |
| pydantic-settings v2 SettingsConfigDict | SettingsConfigDict API used (not deprecated class Config form) for env var config |
| async_client fixture name | Distinct from legacy sync client fixture to avoid collision; both coexist until Plan 05 |
| xfail(strict=False) for Wave 0 | All pre-implementation scaffolds use strict=False so unexpected passes don't break CI |
| StorageBackend ABC + factory mirrors ai/ pattern | 5 abstract methods; get_storage_backend() factory; MinIOBackend wraps all sync Minio SDK calls in asyncio.to_thread() |
| STORE-02 key enforced in code | MinIOBackend.put_object constructs {user_id}/{document_id}/{uuid4()}{ext}; no filename parameter — only extension passes through |
| null-user D-03 sentinel | services/storage.save_upload uses user_id="null-user" in Phase 1 (no auth); Phase 2 replaces with str(current_user.id) |
| load_settings flat-file Phase 1 | users.ai_provider/ai_model columns cannot be populated until Phase 2; settings remain flat-file JSON for Phase 1 |
| Deferred Celery import in /password-reset | send_reset_email.delay called via from tasks.email_tasks import send_reset_email inside handler body — same circular-import fix as document_tasks |
| TOTP QR code as otpauth:// link | No QR library installed; plan permits manual secret display for MVP; functional flow complete without rendered QR image |
| ConfirmBlock no acknowledgment checkbox | ConfirmBlock handles message + button pair; BackupCodesDisplay owns its separate acknowledgment checkbox — no overlap |
| ADMIN-07 enforced by omission | No impersonation endpoint exists; AST check + test_admin_impersonation_not_found verify absence; violates privacy-first core value |
| _user_to_dict() whitelist for admin responses | Explicit field whitelist prevents accidental password_hash/credentials_enc leakage from admin endpoints |
| Quota warning is 200 not 4xx | Below-usage limit change is applied; warning=True advisory field returned — not a rejection |
| AdminQuotasTab fetches quotas per-user via Promise.allSettled | adminListUsers() does not include quota fields; per-user endpoint parallelized; failed quotas filtered silently |
| Temp password via crypto.getRandomValues | Browser-native CSPRNG; no external library; always satisfies AUTH-01 strength rules |
Open Questions
- Verify cloud SDK minor versions on PyPI before Phase 5 pinning
Blockers
None.
Session Continuity
Updated at each phase transition.
| Field | Value |
|---|---|
| Last session | 2026-05-23 — Planned Phase 3 (5 plans, 5 waves; verification passed) |
| Next action | Run /gsd:execute-phase 3 to begin execution |
| Pending decisions | None — all Phase 3 decisions locked in 03-CONTEXT.md |
| Resume file | .planning/phases/03-document-migration-multi-user-isolation/03-01-PLAN.md |