Phase 1 moved from partial_phases to compliant_phases after gsd-validate-phase 1 closed all 3 gaps. Audit status remains gaps_found (3 blockers unchanged: SHARE-02/DOC-01, STORE-06/SEC-09, ADMIN-06). Nyquist overall: partial (phases 3, 4 still draft; phase 2 missing). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
milestone, audited, status, scores, gaps, tech_debt, nyquist
| milestone | audited | status | scores | gaps | tech_debt | nyquist | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| v1.0 | 2026-05-30 | gaps_found |
|
|
|
|
DocuVault v1.0 — Milestone Audit Report
Audited: 2026-05-30 Status: ⚠ gaps_found Score: 48/54 requirements satisfied (89%)
Executive Summary
All 5 phases executed and marked complete. Phase 5 was formally verified (7/7 truths confirmed). Phase 2 has a VERIFICATION.md with one gap that was closed by Phase 3. Phases 1, 3, and 4 have no VERIFICATION.md.
The integration checker found 3 blockers and 6 warnings through cross-phase wiring analysis. Six requirements are partially unsatisfied.
Requirements Coverage (3-Source Cross-Reference)
Sources: VERIFICATION.md (where present) + SUMMARY.md requirements-completed frontmatter + REQUIREMENTS.md traceability + codebase verification
Phase 1 — Infrastructure Foundation (3/3 satisfied)
| REQ-ID | Description | REQUIREMENTS.md | SUMMARY.md | VERIFICATION.md | Status |
|---|---|---|---|---|---|
| STORE-01 | PostgreSQL + MinIO migration | [ ] (stale) | 01-03 ✓ | MISSING | ✅ satisfied |
| STORE-02 | MinIO key schema {user_id}/{doc_id}/{uuid4()}{ext} | [ ] (stale) | not claimed (implicit in 01-04) | MISSING | ✅ satisfied (confirmed in minio_backend.py:75) |
| STORE-07 | Stateless backend | [ ] (stale) | 01-03 ✓ | MISSING | ✅ satisfied (no BackgroundTasks, Celery used) |
Phase 2 — Users & Authentication (20/20 satisfied)
Note: Phase 2 VERIFICATION.md status=gaps_found (4/5). The gap (admin JWT → 403 on /api/documents/) was closed in Phase 3 by adding get_regular_user dep. Effectively all Phase 2 requirements are satisfied.*
| REQ-ID | Description | Status |
|---|---|---|
| AUTH-01 | Register (Argon2 + HIBP) | ✅ satisfied |
| AUTH-02 | JWT session + httpOnly cookie | ✅ satisfied |
| AUTH-03 | TOTP enrollment + backup codes | ✅ satisfied |
| AUTH-04 | Login via TOTP or backup code | ✅ satisfied |
| AUTH-05 | Password reset email | ✅ satisfied |
| AUTH-06 | Sign out all devices | ✅ satisfied |
| AUTH-07 | Refresh token family revocation | ✅ satisfied |
| AUTH-08 | TOTP single-use within window | ✅ satisfied |
| SEC-01 | CSRF (SameSite=Strict + origin validation) | ✅ satisfied |
| SEC-02 | Rate limiting on auth endpoints | ✅ satisfied |
| SEC-03 | Parameterized queries / ORM only | ✅ satisfied |
| SEC-05 | Security headers (CSP, X-Frame-Options, X-Content-Type-Options) | ✅ satisfied |
| SEC-06 | Constant-time comparison for token verification | ✅ satisfied |
| SEC-07 | Admin role dep + admin blocked from doc content | ✅ satisfied (gap closed Phase 3) |
| ADMIN-01 | Admin creates user | ✅ satisfied |
| ADMIN-02 | Admin deactivates user | ✅ satisfied |
| ADMIN-03 | Admin initiates password reset | ✅ satisfied |
| ADMIN-04 | Admin adjusts user quotas | ✅ satisfied |
| ADMIN-05 | Admin assigns AI provider/model | ✅ satisfied |
| ADMIN-07 | No admin impersonation | ✅ satisfied |
Phase 3 — Document Migration & Multi-User Isolation (8/9 satisfied)
| REQ-ID | Description | Status |
|---|---|---|
| STORE-03 | Atomic quota enforcement at upload | ✅ satisfied |
| STORE-04 | Quota bar (80%/95% warnings) | ✅ satisfied |
| STORE-05 | Upload rejected at quota limit | ✅ satisfied |
| STORE-06 | Quota decremented on document delete | ⚠️ partial — cloud docs decrement MinIO quota they never incremented; cloud provider file not deleted |
| STORE-08 | BackgroundTasks replaced with Celery | ✅ satisfied |
| SEC-04 | File access via DB lookup only | ✅ satisfied |
| DOC-03 | AI provider/model from admin-assigned DB field | ✅ satisfied |
| DOC-04 | System + per-user topic overrides | ✅ satisfied |
| DOC-05 | Classification uses user's assigned AI config | ✅ satisfied |
Phase 4 — Folders, Sharing, Quotas & Document UX (11/15 satisfied)
| REQ-ID | Description | Status |
|---|---|---|
| FOLD-01 | Folder CRUD with count confirmation | ✅ satisfied |
| FOLD-02 | Move documents between folders | ✅ satisfied |
| FOLD-03 | Breadcrumb navigation | ✅ satisfied |
| FOLD-04 | Document list sort | ✅ satisfied |
| FOLD-05 | Full-text search via tsvector | ✅ satisfied |
| SHARE-01 | Share by user handle | ✅ satisfied |
| SHARE-02 | "Shared with me" folder; no quota for recipient | ⚠️ partial — recipient can stream /content but GET /api/documents/{id} returns 404 (ownership-only check) |
| SHARE-03 | View-only default sharing | ✅ satisfied |
| SHARE-04 | Share revocation | ✅ satisfied |
| SHARE-05 | Shared indicator in owner's list | ✅ satisfied |
| SEC-08 | credentials_enc excluded from all serializers | ✅ satisfied |
| SEC-09 | Account deletion purges cloud files | ⚠️ partial — admin delete path correct; user-initiated document delete does not purge cloud provider files |
| ADMIN-06 | Admin audit log viewer | ⚠️ partial — JSON viewer works; CSV export returns 403 (Bearer header dropped by window.location.href) |
| DOC-01 | View document metadata and extracted text | ⚠️ partial — owners: ✅; share recipients: 404 at GET /api/documents/{id} |
| DOC-02 | In-browser PDF preview (bytes proxied, no presigned URLs) | ✅ satisfied |
Phase 5 — Cloud Storage Backends (6/7 satisfied)
| REQ-ID | Description | Status |
|---|---|---|
| CLOUD-01 | Connect OneDrive, Google Drive, Nextcloud, WebDAV | ✅ satisfied |
| CLOUD-02 | HKDF per-user credential encryption | ✅ satisfied |
| CLOUD-03 | Local and cloud storage coexist; user selects default | ⚠️ partial — coexist: ✅; select default: API exists but no UI component calls it |
| CLOUD-04 | Connection status display (ACTIVE/REQUIRES_REAUTH/ERROR) | ✅ satisfied |
| CLOUD-05 | invalid_grant transitions to REQUIRES_REAUTH | ✅ satisfied (OAuth providers; WebDAV/Nextcloud don't use OAuth) |
| CLOUD-06 | Disconnect; credentials permanently deleted from DB | ✅ satisfied |
| CLOUD-07 | StorageBackend ABC + factory | ✅ satisfied |
Phase Verification Status
| Phase | VERIFICATION.md | Status | Score | Notes |
|---|---|---|---|---|
| 01 — Infrastructure Foundation | ❌ MISSING | Unverified | — | No formal verification run |
| 02 — Users & Authentication | ✅ Present | gaps_found (4/5) | 4/5 | Gap closed by Phase 3 (get_regular_user on /api/documents/*) |
| 03 — Document Migration | ❌ MISSING | Unverified | — | No formal verification run |
| 04 — Folders, Sharing, Quotas | ❌ MISSING | Unverified | — | No formal verification run |
| 05 — Cloud Storage Backends | ✅ Present | human_needed | 7/7 | 6 human UAT items (cloud credentials required) |
Nyquist Compliance (Validation Coverage)
| Phase | VALIDATION.md | nyquist_compliant | Action |
|---|---|---|---|
| 01 — Infrastructure Foundation | ✅ exists (compliant) | ✅ true | — (validated 2026-05-30) |
| 02 — Users & Authentication | ❌ missing | — | /gsd:validate-phase 2 |
| 03 — Document Migration | ✅ exists (draft) | ❌ false | /gsd:validate-phase 3 |
| 04 — Folders, Sharing, Quotas | ✅ exists (draft) | ❌ false | /gsd:validate-phase 4 |
| 05 — Cloud Storage Backends | ✅ exists (complete) | ✅ true | — |
Critical Blockers (3)
BLOCKER-1 — Share Recipient Cannot View Document Metadata
Affected Requirements: SHARE-02, DOC-01
File: backend/api/documents.py line 542
Root cause: if doc is None or doc.user_id != current_user.id: raise HTTPException(404) — no share-grant check.
Broken flow: SharedView.vue → click shared item → DocumentView.vue → api.getDocument(id) → 404 for recipient.
Fix: Add share-grant check to get_document(): if doc.user_id != current_user.id, query Share table for (document_id=doc_id, recipient_id=current_user.id) and allow if found.
BLOCKER-2 — Cloud Document Delete Corrupts Quota and Orphans Files
Affected Requirements: STORE-06, SEC-09
File: backend/services/storage.py (delete_document function)
Root cause: self._backend().delete_object(doc.object_key) always uses MinIOBackend regardless of doc.storage_backend. Then decrements MinIO quota unconditionally.
Broken flow: User uploads to Google Drive (quota=0) → deletes document → delete_object() gets NoSuchKey on MinIO (silently swallowed) → quota decremented below actual MinIO usage → actual Google Drive file never deleted.
Fix: Use get_storage_backend_for_document(doc, session) in delete_document() (same pattern as admin delete). Gate quota decrement on doc.storage_backend == "minio".
BLOCKER-3 — Admin Audit Log CSV Export Always Returns 403
Affected Requirements: ADMIN-06
File: frontend/src/components/admin/AuditLogTab.vue line 191
Root cause: window.location.href = '/api/admin/audit-log/export?${params}' — browser navigation strips Authorization: Bearer header. get_current_admin requires HTTPBearer.
Broken flow: Admin clicks "Export CSV" → 403 Forbidden.
Fix: Use fetch() with Authorization: Bearer ${accessToken} header and download the blob via URL.createObjectURL(), or pass the access token as a query param (less secure but simple).
Warnings (6)
| # | Severity | Description | Requirement |
|---|---|---|---|
| W-1 | Medium | SharedView.vue uses share.document?.created_at but /api/shares/received returns flat objects — date/size lines never render |
SHARE-02 |
| W-2 | Medium | updateDefaultStorage() defined in client.js but never called; no default-backend UI selector exists |
CLOUD-03 |
| W-3 | Low | _doc_to_dict() omits storage_backend and folder_id — list response cannot distinguish cloud vs local docs |
CLOUD-03 |
| W-4 | Low | Document.user_id ORM column has nullable=True but DB has NOT NULL constraint (migration 0003) — ORM/schema drift |
STORE-03 |
| W-5 | Low | cloud.py module docstring says all endpoints use get_regular_user but OAuth callback intentionally omits it |
— |
| W-6 | Info | CLAUDE.md specifies ES256, email_hmac, fgp fingerprint claim — none implemented (HS256, plaintext email, no fingerprint). Outside 54 v1 REQ-IDs. | v2 scope |
Tech Debt by Phase
Phase 01: No VERIFICATION.md written.
Phase 02: VERIFICATION.md status=gaps_found; Phase 3 closed the gap but no re-verification was run.
Phase 03: No VERIFICATION.md written. Document.user_id ORM nullable divergence.
Phase 04: No VERIFICATION.md written. VALIDATION.md in draft state.
Phase 05: CLOUD-05 REQUIRES_REAUTH transition not implemented for WebDAV/Nextcloud (spec-compliant; quality gap).
All: REQUIREMENTS.md checkboxes not maintained during execution — many satisfied requirements still show [ ].
Integration Wiring Summary
| Connection | Status |
|---|---|
| Auth deps (get_regular_user / get_current_admin) on all protected endpoints | ✅ All wired |
| Phase 2 admin gap (admin JWT → 403 on /api/documents/*) | ✅ Closed in Phase 3 |
| Atomic quota at upload (MinIO path) | ✅ Wired |
| Atomic quota decrement at delete (MinIO path only) | ⚠️ Cloud path broken |
| Cloud document content proxy (authenticated fetch) | ✅ Wired |
| Admin delete: cloud cleanup before MinIO before DB | ✅ Wired (SEC-09) |
| User-initiated doc delete: cloud provider cleanup | ❌ Not wired (STORE-06, SEC-09) |
| Share recipient access to /content | ✅ Wired |
| Share recipient access to GET /documents/{id} | ❌ Ownership check blocks recipients |
| Admin audit log JSON viewer | ✅ Wired end-to-end |
| Admin audit log CSV export | ❌ Bearer header dropped |
| Default storage backend selection UI | ❌ Client function orphaned, no UI |
| HKDF credential encryption throughout cloud flows | ✅ Wired |
| All routers registered in main.py | ✅ Confirmed |
Remediation Plan
3 blockers require closure phases (or targeted inline fixes). In priority order:
Gap 1 — Share recipient metadata access (BLOCKER-1)
Affects: SHARE-02, DOC-01
Effort: Small — add share-grant check to get_document() in documents.py (~15 lines)
Gap 2 — Cloud document delete (BLOCKER-2)
Affects: STORE-06, SEC-09
Effort: Medium — refactor delete_document() in services/storage.py to use get_storage_backend_for_document() and conditionally decrement quota (~30 lines)
Gap 3 — Admin audit log CSV export (BLOCKER-3)
Affects: ADMIN-06
Effort: Small — change window.location.href to fetch() with Bearer header and blob download in AuditLogTab.vue (~20 lines)
These three fixes are small enough to close as a single gap-closure phase or inline as part of /gsd:complete-milestone v1.0 pre-work.
Audited: 2026-05-30 Auditor: Claude (gsd-audit-milestone) Integration checker: gsd-integration-checker