3.4 KiB
3.4 KiB
VaultDrop
Project Status
Project initialized — mobile/ (React Native / Expo) and backend/ (Go) have scaffolding in place. The SQLite layer (schema v4, migrations, repositories) is implemented and covered by tests.
Architecture
- Backend: Go, Gin HTTP framework, PostgreSQL, Tesseract OCR (system call)
- Frontend: React Native (Expo SDK 57), expo-router, expo-sqlite, expo-file-system (SAF)
Key Commands
# Backend
cd backend && go run cmd/server/main.go
# PostgreSQL (via docker-compose)
docker compose up postgres -d
# Frontend
cd mobile && npx expo start
# Typecheck frontend
cd mobile && npx tsc --noEmit
# SQLite layer tests (migrations + repositories)
cd mobile && npm run test:db
Backend Structure
- Entry point:
backend/cmd/server/main.go - Internal packages:
handlers/,models/,repository/,service/,ocr/ - Response helpers:
pkg/api/response.go - File uploads stored in
backend/uploads/ - Standard JSON response envelope:
{ "data": ..., "meta": { "page": ..., "total": ... } } - Error format:
{ "error": { "code": "...", "message": "..." } } - OCR language:
fra+eng - Handlers are stub implementations (return not-implemented errors)
Frontend Structure
- Entry point:
mobile/App.tsx(expo-router layout + Auth context) - Data layer —
mobile/services/:safDirectory.ts+safDirectory.types.ts: physical access viaexpo-file-system(pick/list/create, Documents/SAF uris)db/— SQLite persistence, seemobile/AGENTS.mdfor the full contract (schema, migrations, repositories, tests)localStorage.ts— thin re-export ofservices/db(legacy alias)
features/syncDevice.ts— device sync orchestration (two-pass SAF walk, single transaction per root,exists = 0reconciliation)context/AuthContext.tsx— session context: exposesdeviceUserId(bootstrapped fromgetDeviceUserId()) and starts the backgroundsyncDeviceloop- No business logic on the client — heavy processing stays server-side
- API base URL configured via
EXPO_PUBLIC_API_BASE_URLenv var (client + hooks not yet built)
Data Conventions
- Canonical identity for folders/files/shares/share_links is
resource_id: opaquelower(hex(randomblob(16))), generated locally, never reused. The physicaluriis nullable (NULL = cloud-only) and is the reconciliation key for the SAF walk. owner_idis NOT NULL on every folder/file row, seeded from the device'sdevice_user_id.- Folder/file
sync_statusis a placement state:local|cloud|local-cloud(transitions viatransitionSyncStatus). It is not a push progress marker. - Shares/share_links carry no
sync_status; theirpushStatus(pending/synced/failed) is derived from thepending_operationsoutbox. - Decisions are made offline from a cached
resource_permissionssnapshot pushed by the server; the server remains the source of truth.canAccessenforces ranking (viewer < commenter < editor < owner),inherit,expires_at, and a 24h stale-cache read-only downgrade. password_hashand download counters are server-side only; the client only stores thehas_passwordboolean and a counter mirror.
Non-Goals (V1)
- Plugin system
- On-device OCR
- Full multi-tenant federation / public discovery
- Multi-writer sync conflicts (single-owner device identity; device-local
device_user_id)
References
README.md— full spec, API endpoints, data flow, folder structure, iteration roadmap