Files
kite/.planning/v1.0-MILESTONE-AUDIT.md
T
curo1305 33e5efe846 docs(v1.0): add milestone audit — 48/54 requirements satisfied, 3 blockers
Audit findings: share recipient doc-metadata 404 (SHARE-02/DOC-01),
cloud document delete corrupts MinIO quota (STORE-06/SEC-09), admin
CSV export returns 403 (ADMIN-06). 6 tech debt warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:44:16 +02:00

20 KiB
Raw Blame History

milestone, audited, status, scores, gaps, tech_debt, nyquist
milestone audited status scores gaps tech_debt nyquist
v1.0 2026-05-30 gaps_found
requirements phases_verified integration_blockers integration_warnings flows_complete
48/54 2/5 3 6 2/4
requirements integration flows
id status phase claimed_by_plans completed_by_plans verification_status evidence
SHARE-02 partial 4
04-01-PLAN.md
04-04-PLAN.md
04-04-SUMMARY.md (Sharing API SHARE-01..05 in commit log)
missing Grant/revocation/list work. But GET /api/documents/{id} enforces doc.user_id == current_user.id — share recipients get 404 on metadata, blocking the document detail view.
id status phase claimed_by_plans completed_by_plans verification_status evidence
DOC-01 partial 4
04-01-PLAN.md
04-09-SUMMARY.md (implicit)
missing Owners can view document metadata and extracted text. Share recipients who navigate to /document/{id} get 404 because documents.py:542 checks ownership only, not share grants.
id status phase claimed_by_plans completed_by_plans verification_status evidence
STORE-06 partial 3
03-02-PLAN.md
03-02-SUMMARY.md (STORE-06)
missing services/storage.delete_document() always calls MinIOBackend.delete_object() regardless of doc.storage_backend, then decrements MinIO quota. Cloud-stored documents never incremented MinIO quota (D-11), so deletion incorrectly decrements it. Actual cloud provider files are not deleted on user-initiated document delete — they become orphaned.
id status phase claimed_by_plans completed_by_plans verification_status evidence
SEC-09 partial 4
04-07-PLAN.md
05-05-PLAN.md
04-07-SUMMARY.md (SEC-09 MinIO cleanup)
05-05-SUMMARY.md (SEC-09 cloud cleanup)
partial — Phase 5 VERIFICATION.md confirms admin delete path; user delete path unverified Admin-initiated delete (admin.py lines 522546) correctly purges CloudConnection rows before MinIO cleanup. User-initiated document delete (services/storage.delete_document) does not call get_storage_backend_for_document — cloud provider files are orphaned when a user deletes a cloud-stored document.
id status phase claimed_by_plans completed_by_plans verification_status evidence
ADMIN-06 partial 4
04-06-PLAN.md
04-06-SUMMARY.md (audit.py)
04-02-SUMMARY.md (GIN index, audit-logs bucket)
missing GET /api/admin/audit-log JSON viewer works end-to-end. GET /api/admin/audit-log/export: AuditLogTab.vue uses window.location.href for the export button, which does not send the Authorization: Bearer header. get_current_admin requires HTTPBearer — export always returns 403.
id status phase claimed_by_plans completed_by_plans verification_status evidence
CLOUD-03 partial 5
05-05-PLAN.md
05-09-PLAN.md
05-05-SUMMARY.md (CLOUD-03)
05-06-SUMMARY.md (CLOUD-03)
Phase 5 VERIFICATION.md: SATISFIED PATCH /api/users/me/default-storage endpoint exists and is registered in main.py. updateDefaultStorage() is defined in client.js. However, no Vue component imports or calls updateDefaultStorage(). No UI selector for default backend in SettingsCloudTab.vue or any other component. Default storage (minio) can only be changed via direct API call, not through the UI.
blocker description
SHARE-02 / DOC-01 — Share recipient document detail blocked documents.py line 542: `if doc is None or doc.user_id != current_user.id: raise HTTPException(404)`. Recipients with valid share records cannot access GET /api/documents/{id} — they see 404 despite being able to stream /content.
blocker description
STORE-06 / SEC-09 — Cloud document delete corrupts MinIO quota and orphans cloud files services/storage.delete_document() calls self._backend().delete_object(doc.object_key) where _backend() always returns MinIOBackend. Cloud-stored documents: (1) MinIO delete_object silently fails (NoSuchKey); (2) MinIO quota decremented even though no quota was charged at upload; (3) actual file in Google Drive / OneDrive / Nextcloud / WebDAV is never deleted.
blocker description
ADMIN-06 — Audit log CSV export always returns 403 AuditLogTab.vue line 191: `window.location.href = '/api/admin/audit-log/export?${params}'`. Browser navigation strips Authorization: Bearer header. Backend requires get_current_admin (HTTPBearer). All admin CSV export clicks result in 403.
name breaks_at affected_requirements
Recipient views shared document GET /api/documents/{id} — ownership check excludes recipients
SHARE-02
DOC-01
name breaks_at affected_requirements
User deletes cloud document services/storage.delete_document() — MinIO backend hardcoded
STORE-06
SEC-09
name breaks_at affected_requirements
Admin exports audit log AuditLogTab.vue — window.location.href drops Bearer token
ADMIN-06
name breaks_at affected_requirements
User selects default cloud storage backend No UI component calls updateDefaultStorage()
CLOUD-03
phase items
02-users-authentication
Phase 2 VERIFICATION.md status=gaps_found (gap: admin JWT → 403 on documents). Gap was closed by Phase 3 (get_regular_user dep added to all /api/documents/* handlers) but no re-verification was run for Phase 2.
phase items
01-infrastructure-foundation
No VERIFICATION.md — phase marked complete but never formally verified.
phase items
03-document-migration-multi-user-isolation
No VERIFICATION.md — phase marked complete but never formally verified.
phase items
04-folders-sharing-quotas-document-ux
No VERIFICATION.md — phase marked complete but never formally verified.
phase items
04-folders-sharing-quotas-document-ux
SharedView.vue renders share.document?.created_at and size_bytes, but /api/shares/received returns a flat object (no nested document key) — date/size lines never render for recipients.
phase items
03-document-migration-multi-user-isolation
Document.user_id ORM column has nullable=True (models.py) but DB has NOT NULL constraint (migration 0003). ORM divergence from actual schema.
phase items
05-cloud-storage-backends
cloud.py module docstring claims all endpoints use get_regular_user but OAuth callback intentionally omits it (state-token auth). Misleading, though behavior is correct.
CLOUD-05 REQUIRES_REAUTH transitions work for OAuth providers (Google Drive, OneDrive). Nextcloud/WebDAV credential failures produce generic 502 — no REQUIRES_REAUTH state transition. Requirement is OAuth-specific so this is spec-compliant, but a user experience gap.
_doc_to_dict() omits storage_backend and folder_id — document list response cannot distinguish cloud vs local docs.
phase items
all
REQUIREMENTS.md checkboxes are stale: many implemented requirements still show [ ]. Last updated 2026-05-21, before any execution.
CLAUDE.md specifies ES256 (ECDSA P-256) JWT algorithm, email_hmac deterministic index, and fgp token fingerprint binding. None are implemented — HS256 used, email stored in plaintext, no fingerprint claim. These are v2 hardening items outside the 54 formal v1 REQ-IDs.
compliant_phases partial_phases missing_phases overall
05-cloud-storage-backends
01-infrastructure-foundation
03-document-migration-multi-user-isolation
04-folders-sharing-quotas-document-ux
02-users-authentication
partial

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 (draft) false /gsd:validate-phase 1
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