experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add comprehensive profiles documentation

Created:
- docs/profiles.md - Complete profiles guide covering architecture,
usage, Chromium profile relationship, API, and limitations
- PROFILES-IMPLEMENTATION-SUMMARY.md - Implementation summary with
design decisions, migration strategy, and testing status

Updated:
- DEVELOPMENT.md - Added profile management section explaining
production vs dev isolation and profile structure
- docs/sync.md - Updated to reflect per-profile sync configuration
and profile parameter on API endpoints

Key topics covered:
- Profile architecture (desktop + server)
- Profile vs Chromium profile relationship (nested, not same)
- Production/dev isolation (dev NEVER touches production)
- Per-profile sync configuration
- Migration strategy (automatic, zero data loss)
- Security considerations and limitations

+735 -14
+12 -3
DEVELOPMENT.md
··· 167 167 - Tables: `addresses`, `visits`, `content`, `tags`, `blobs`, `scripts_data`, `feeds` 168 168 169 169 **Profile Management**: 170 - - Packaged app uses `default` profile 171 - - Running from source (`yarn start`) uses `dev` profile 172 - - `PROFILE` env var overrides automatic detection 170 + 171 + Peek supports user profiles for isolated data workspaces. See `docs/profiles.md` for full details. 172 + 173 + - **Production builds** (installed in /Applications) use `default` profile 174 + - **Development builds** (`yarn start`) ALWAYS use `dev` profile (isolated from production) 175 + - **Custom profiles** can be created via Settings UI 176 + - Profile switching requires app restart 177 + - Each profile has its own: 178 + - SQLite database (`datastore.sqlite`) 179 + - Chromium session data (cookies, cache, localStorage) 180 + - Sync configuration (optional API key + server profile mapping) 181 + - `PROFILE` env var overrides automatic detection (for testing) 173 182 174 183 ## Critical Rules 175 184
+350
PROFILES-IMPLEMENTATION-SUMMARY.md
··· 1 + # User Profiles Implementation - Summary 2 + 3 + ## Overview 4 + 5 + Successfully implemented user profiles and profile switching across desktop and server. Each profile provides isolated data storage with optional per-profile sync configuration. 6 + 7 + ## What Was Implemented 8 + 9 + ### Server (Multi-User System) 10 + 11 + 1. **Profiles Table** (`backend/server/users.js`) 12 + - Added `profiles` table to `system.db` 13 + - Schema: `id`, `user_id`, `slug`, `name`, `created_at`, `last_used_at` 14 + - CRUD functions: `createProfile`, `listProfiles`, `getProfile`, `deleteProfile` 15 + 16 + 2. **Connection Pooling** (`backend/server/db.js`) 17 + - Changed from `userId` to `userId:profileSlug` composite keys 18 + - Database path: `data/{userId}/profiles/{profileSlug}/datastore.sqlite` 19 + - All DB functions accept optional `profileSlug` parameter (defaults to "default") 20 + 21 + 3. **API Endpoints** (`backend/server/index.js`) 22 + - All endpoints accept `?profile={slug}` query parameter 23 + - New endpoints: GET/POST/DELETE `/profiles` 24 + - Migration function: `migrateUserDataToProfiles()` moves `peek.db` → `profiles/default/` 25 + - Backward compatible: defaults to "default" profile if not specified 26 + 27 + ### Desktop Client 28 + 29 + 4. **Profile Database Module** (`backend/electron/profiles.ts`) - NEW FILE 30 + - `profiles.db` stores profile metadata and sync config 31 + - Schema includes: id, name, slug, syncEnabled, apiKey, serverProfileSlug, lastSyncAt 32 + - Functions: initProfilesDb, listProfiles, createProfile, getProfile, deleteProfile 33 + - Active profile tracking: getActiveProfile, setActiveProfile 34 + - Sync config: enableSync, disableSync, getSyncConfig, updateLastSyncTime 35 + - Migration: migrateExistingProfiles() detects existing profile directories 36 + 37 + 5. **IPC Handlers** (`backend/electron/ipc.ts`) 38 + - Added `registerProfileHandlers()` function 39 + - Handlers for: list, create, get, delete, getCurrent, switch 40 + - Sync configuration handlers: enableSync, disableSync, getSyncConfig 41 + - Profile switching triggers app relaunch 42 + 43 + 6. **Profile Initialization** (`backend/electron/entry.ts`) 44 + - Initialize profiles.db on startup 45 + - Profile selection logic: 46 + 1. Explicit `PROFILE` env var (testing override) 47 + 2. **Development builds → ALWAYS use 'dev'** (production isolation) 48 + 3. Production builds → use active profile from profiles.db 49 + 4. Fallback → 'default' 50 + - **Critical fix**: Dev builds never touch production profiles 51 + 52 + 7. **Sync Integration** (`backend/electron/sync.ts`) 53 + - Modified to use per-profile sync configuration 54 + - `getSyncConfig()` reads from active profile 55 + - Pull/push operations include `?profile={serverProfileSlug}` parameter 56 + - `syncAll()` updates per-profile lastSyncTime 57 + 58 + 8. **Settings UI** (`app/settings/settings.js`) 59 + - New "Profiles" section between Sync and Themes 60 + - Displays current active profile 61 + - Profile list with radio buttons for switching 62 + - "Add Profile" button with name input dialog 63 + - Delete profile button (disabled for default/active) 64 + - Per-profile sync configuration UI: 65 + - Enable/disable sync buttons 66 + - API key and server profile slug inputs 67 + - Shows sync status and server profile mapping 68 + 69 + 9. **Frontend API** (`preload.js`) 70 + - Exposed `api.profiles` object 71 + - Functions: list, create, get, delete, getCurrent, switch 72 + - Sync: enableSync, disableSync, getSyncConfig 73 + 74 + ## Key Design Decisions 75 + 76 + ### Production vs Development Isolation 77 + 78 + **Critical Rule**: Development builds NEVER touch production data. 79 + 80 + - **Production** (packaged in /Applications) → uses "default" or user-selected profile 81 + - **Development** (`yarn start` from source) → ALWAYS uses "dev" profile 82 + - Single-instance lock skipped for dev profiles → allows both to run simultaneously 83 + 84 + This prevents accidental corruption of production data during development. 85 + 86 + ### Profile vs Chromium Profile 87 + 88 + **Nested relationship, NOT the same:** 89 + 90 + ``` 91 + Peek Profile # Application-level 92 + └── Chromium Profile # Browser session data 93 + 94 + ~/.config/Peek/ 95 + ├── profiles.db # Peek profile metadata 96 + ├── default/ # Peek profile 97 + │ ├── datastore.sqlite # Peek data (items, tags) 98 + │ └── chromium/ # Chromium session (cookies, cache) 99 + └── work/ # Another Peek profile 100 + ├── datastore.sqlite 101 + └── chromium/ # Separate Chromium session 102 + ``` 103 + 104 + **Electron configuration:** 105 + ```typescript 106 + const profileDataPath = path.join(userDataPath, PROFILE); 107 + const sessionDataPath = path.join(profileDataPath, 'chromium'); 108 + 109 + app.setPath('userData', profileDataPath); 110 + app.setPath('sessionData', sessionDataPath); 111 + ``` 112 + 113 + Each Peek profile gets its own isolated Chromium session, providing: 114 + - Separate cookies (login sessions) 115 + - Separate localStorage 116 + - Separate cache 117 + - Separate browser extensions 118 + 119 + ### Per-Profile Sync Configuration 120 + 121 + Each profile can independently sync to different server profiles: 122 + 123 + ``` 124 + Desktop Profile Server User Server Profile 125 + ───────────────── ────────── ────────────── 126 + Work alice work 127 + Personal alice personal 128 + ``` 129 + 130 + - One API key (authenticates user) 131 + - Multiple server profile targets 132 + - Stored in profiles.db per desktop profile 133 + 134 + ## Data Storage Structure 135 + 136 + ### Desktop 137 + 138 + ``` 139 + {userData}/ 140 + ├── profiles.db # Profile metadata + sync config 141 + ├── default/ # Production profile 142 + │ ├── datastore.sqlite 143 + │ └── chromium/ 144 + ├── dev/ # Development profile 145 + │ ├── datastore.sqlite 146 + │ └── chromium/ 147 + └── work/ # Custom user profile 148 + ├── datastore.sqlite 149 + └── chromium/ 150 + ``` 151 + 152 + ### Server 153 + 154 + ``` 155 + data/ 156 + ├── system.db # Users and profiles 157 + └── {userId}/ 158 + └── profiles/ 159 + ├── default/ 160 + │ └── datastore.sqlite 161 + ├── work/ 162 + │ └── datastore.sqlite 163 + └── personal/ 164 + └── datastore.sqlite 165 + ``` 166 + 167 + ## Migration Strategy 168 + 169 + ### Automatic Migration 170 + 171 + On first launch with new code: 172 + 173 + 1. `initProfilesDb()` creates `profiles.db` if missing 174 + 2. `migrateExistingProfiles()` detects existing directories: 175 + - `default/` → creates "Default" profile record 176 + - `dev/` → creates "Development" profile record 177 + 3. `ensureDefaultProfile()` ensures default profile exists 178 + 4. Existing data preserved, no data loss 179 + 180 + ### Server Migration 181 + 182 + `migrateUserDataToProfiles()` runs automatically: 183 + - Detects `data/{userId}/peek.db` (old path) 184 + - Moves to `data/{userId}/profiles/default/datastore.sqlite` 185 + - Creates "default" profile record in system.db 186 + - One-time operation, idempotent 187 + 188 + ## Backward Compatibility 189 + 190 + - `PROFILE` env var still works (overrides all logic) 191 + - API endpoints default to `profile=default` if not specified 192 + - Existing profile directories detected and migrated 193 + - Zero breaking changes for existing deployments 194 + 195 + ## Testing Status 196 + 197 + ### Manual Testing Performed 198 + 199 + ✅ App starts with profile migration (saw "Migrated existing dev profile directory") 200 + ✅ Dev build uses "dev" profile (verified in logs) 201 + ✅ Dev and production can run simultaneously (single-instance skip works) 202 + 203 + ### Pending Manual Tests 204 + 205 + - [ ] Create new profile via Settings UI 206 + - [ ] Switch profiles (app restart) 207 + - [ ] Delete profile 208 + - [ ] Enable sync for a profile 209 + - [ ] Sync to different server profiles 210 + - [ ] Verify data isolation between profiles 211 + 212 + ### Automated Tests 213 + 214 + Existing tests already use profile-based isolation: 215 + - `getTestProfile()` generates unique test profiles 216 + - `PROFILE` env var passed to test instances 217 + - Tests should work without modification 218 + 219 + ## Files Modified/Created 220 + 221 + ### Server 222 + - `backend/server/users.js` - Added profiles table and CRUD 223 + - `backend/server/db.js` - Profile-aware connection pooling 224 + - `backend/server/index.js` - Profile API endpoints + migration 225 + 226 + ### Desktop 227 + - **NEW**: `backend/electron/profiles.ts` - Profile management module 228 + - `backend/electron/entry.ts` - Profile initialization and selection 229 + - `backend/electron/ipc.ts` - Profile IPC handlers 230 + - `backend/electron/sync.ts` - Per-profile sync config 231 + - `app/settings/settings.js` - Profiles UI section 232 + - `preload.js` - Profiles API exposure 233 + 234 + ### Documentation 235 + - **NEW**: `docs/profiles.md` - Comprehensive profiles documentation 236 + - `DEVELOPMENT.md` - Updated profile management section 237 + - `docs/sync.md` - Updated with per-profile sync 238 + - **NEW**: `PROFILES-IMPLEMENTATION-SUMMARY.md` - This file 239 + 240 + ## Known Limitations 241 + 242 + 1. **Profile switching requires app restart** - Electron limitation 243 + 2. **No profile encryption** - Data stored in plaintext 244 + 3. **No profile export/import** - Manual directory copy required 245 + 4. **Mobile support pending** - Desktop-only for now 246 + 5. **API keys stored in plaintext** - In local SQLite file 247 + 248 + ## Security Considerations 249 + 250 + - Profile isolation is filesystem-based 251 + - No encryption at rest 252 + - API keys visible in profiles.db 253 + - Single-instance lock skipped for dev profiles (intentional for development) 254 + - Chromium sessions isolated per profile (cookies, cache separated) 255 + 256 + ## Scripts and Build Configs 257 + 258 + ### No Changes Required 259 + 260 + Existing scripts work correctly: 261 + - `yarn start` → Uses dev profile automatically 262 + - `yarn package:install` → Packaged build uses default profile 263 + - Tests use unique profiles per test case 264 + - `PROFILE` env var still works for testing 265 + 266 + ### Future Script Considerations 267 + 268 + Potential additions: 269 + - `yarn profile:list` - List all profiles 270 + - `yarn profile:create <name>` - Create profile from CLI 271 + - `yarn profile:export <slug>` - Export profile data 272 + - `yarn profile:import <slug> <path>` - Import profile data 273 + 274 + ## Deployment Impact 275 + 276 + ### Server Deployment 277 + 278 + **Zero downtime, backward compatible:** 279 + 1. Migrations run automatically on first request 280 + 2. Old API calls (without profile param) work (defaults to "default") 281 + 3. Old clients continue working 282 + 4. New clients can use profiles 283 + 284 + ### Desktop Deployment 285 + 286 + **Zero disruption:** 287 + 1. Migration runs on first launch 288 + 2. Existing data preserved in "default" profile 289 + 3. Users can continue using default profile 290 + 4. Profile switching is opt-in 291 + 292 + ## Future Enhancements 293 + 294 + 1. **Profile import/export** - Backup and restore functionality 295 + 2. **Profile templates** - Pre-configured profiles for different use cases 296 + 3. **Profile encryption** - At-rest encryption for sensitive data 297 + 4. **Profile-level theme settings** - Different themes per profile 298 + 5. **Mobile support** - Extend to Tauri mobile 299 + 6. **Profile backup automation** - Scheduled backups per profile 300 + 7. **Profile-specific extension configs** - Different extensions per profile 301 + 302 + ## Questions Answered 303 + 304 + ### Q: How do Peek profiles relate to Chromium profiles? 305 + 306 + **A: They are nested, not the same.** 307 + 308 + - **Peek Profile** = High-level data workspace (items, tags, sync config) 309 + - **Chromium Profile** = Low-level browser session data (cookies, cache) 310 + 311 + Each Peek profile automatically gets its own Chromium session directory. This provides complete isolation including browser state. Users manage Peek profiles; Chromium profiles are an implementation detail. 312 + 313 + ### Q: Can dev and production run simultaneously? 314 + 315 + **A: Yes, by design.** 316 + 317 + Development builds: 318 + - Always use "dev" profile (isolated from production) 319 + - Skip single-instance lock 320 + - Can run alongside production instance safely 321 + 322 + ### Q: What happens to existing data? 323 + 324 + **A: Automatically migrated, zero data loss.** 325 + 326 + - Existing `default/` → "Default" profile 327 + - Existing `dev/` → "Development" profile 328 + - Data stays in same location, just tracked in profiles.db 329 + 330 + ## Commits 331 + 332 + 1. `feat(server): add profiles support to users.js` 333 + 2. `feat(server): add profile-aware connection pooling to db.js` 334 + 3. `feat(server): add profile endpoints and migration to index.js` 335 + 4. `feat(client): create profiles.ts module for profile management` 336 + 5. `feat(client): add profile IPC handlers to ipc.ts` 337 + 6. `feat(client): update entry.ts to initialize profiles` 338 + 7. `feat(sync): update sync.ts for per-profile configuration` 339 + 8. `feat(settings): add profiles section to settings UI` 340 + 9. `feat(frontend): add profiles API to preload` 341 + 10. `fix(profiles): enforce dev profile isolation from production` 342 + 11. `docs: add comprehensive profiles documentation` 343 + 344 + ## Status 345 + 346 + ✅ **Implementation Complete** 347 + ✅ **Documentation Complete** 348 + ⏳ **Manual Testing In Progress** 349 + 350 + All code is committed and ready for testing.
+341
docs/profiles.md
··· 1 + # Peek User Profiles 2 + 3 + Multi-profile support for managing separate data workspaces on desktop and server. 4 + 5 + ## Overview 6 + 7 + Peek supports **user profiles** - isolated data environments that allow you to maintain separate collections of items, settings, and browser sessions. Each profile has its own: 8 + 9 + - **Data storage** - Separate SQLite database (`datastore.sqlite`) 10 + - **Sync configuration** - Independent API key and server profile mapping 11 + - **Browser session** - Isolated Chromium session data (cookies, cache, localStorage) 12 + - **Settings** - Profile-specific configuration 13 + 14 + ## Architecture 15 + 16 + ### Desktop Client 17 + 18 + ``` 19 + {userData}/ 20 + ├── profiles.db # Profile metadata and sync config 21 + ├── default/ # Production profile 22 + │ ├── datastore.sqlite # Items, tags, etc. 23 + │ ├── chromium/ # Electron session data 24 + │ └── extension_settings # Legacy settings 25 + ├── dev/ # Development profile 26 + │ ├── datastore.sqlite 27 + │ └── chromium/ 28 + └── work/ # Custom user profile 29 + ├── datastore.sqlite 30 + └── chromium/ 31 + ``` 32 + 33 + ### Server 34 + 35 + ``` 36 + data/ 37 + ├── system.db # Users and profile metadata 38 + │ ├── users (id, api_key_hash) 39 + │ └── profiles (id, user_id, slug, name) 40 + └── {userId}/ 41 + └── profiles/ 42 + ├── default/ 43 + │ └── datastore.sqlite 44 + ├── work/ 45 + │ └── datastore.sqlite 46 + └── personal/ 47 + └── datastore.sqlite 48 + ``` 49 + 50 + ## Profile Metadata 51 + 52 + **Desktop `profiles.db` schema:** 53 + 54 + ```sql 55 + CREATE TABLE profiles ( 56 + id TEXT PRIMARY KEY, -- UUID 57 + name TEXT NOT NULL UNIQUE, -- User-visible (e.g., "Work") 58 + slug TEXT NOT NULL UNIQUE, -- Filesystem-safe (e.g., "work") 59 + 60 + -- Sync configuration (optional) 61 + sync_enabled INTEGER DEFAULT 0, -- Boolean 62 + api_key TEXT, -- Server user API key 63 + server_profile_slug TEXT, -- Which server profile to sync to 64 + last_sync_at INTEGER, -- Unix ms 65 + 66 + created_at INTEGER NOT NULL, -- Unix ms 67 + last_used_at INTEGER NOT NULL, -- Unix ms 68 + is_default INTEGER DEFAULT 0 -- Boolean 69 + ); 70 + 71 + CREATE TABLE active_profile ( 72 + id INTEGER PRIMARY KEY CHECK (id = 1), 73 + profile_slug TEXT NOT NULL 74 + ); 75 + ``` 76 + 77 + ## Profile Types 78 + 79 + ### Production Profile (default) 80 + 81 + - Used by packaged builds installed in `/Applications` 82 + - Created automatically on first launch 83 + - Cannot be deleted 84 + - Isolated from development work 85 + 86 + ### Development Profile (dev) 87 + 88 + - Used by builds running from source (`yarn start`) 89 + - Completely isolated from production 90 + - Skips single-instance lock (allows dev + production to run simultaneously) 91 + - Never touched by production builds 92 + 93 + ### Custom Profiles 94 + 95 + - User-created via Settings UI 96 + - Can be deleted (except active profile) 97 + - Independent sync configuration 98 + - Switchable via Settings (requires app restart) 99 + 100 + ## Profile Selection Logic 101 + 102 + **Desktop (entry.ts):** 103 + 104 + 1. **Explicit `PROFILE` env var** - Takes precedence (for testing) 105 + 2. **Development builds** - ALWAYS use `dev` (source or dev-packaged builds) 106 + 3. **Production builds** - Use active profile from `profiles.db` 107 + 4. **Fallback** - Use `default` 108 + 109 + This ensures: 110 + - Development never touches production data 111 + - Production can use profile switching 112 + - Dev and production can run side-by-side 113 + 114 + ## Sync Configuration 115 + 116 + Each desktop profile can optionally sync to a server profile: 117 + 118 + - **API Key** - Authenticates with server user account 119 + - **Server Profile Slug** - Which server profile to sync to 120 + 121 + One server user can have multiple server profiles. A desktop user can map different local profiles to different server profiles under the same server account. 122 + 123 + **Example:** 124 + 125 + ``` 126 + Desktop Profile Server User Server Profile 127 + ───────────────── ────────── ────────────── 128 + Work alice work 129 + Personal alice personal 130 + ``` 131 + 132 + Both desktop profiles use the same API key (alice's account) but sync to different server profiles. 133 + 134 + ## Chromium Profiles vs Peek Profiles 135 + 136 + **They are nested, NOT the same:** 137 + 138 + ``` 139 + Peek Profile # Application-level profile 140 + └── Chromium Profile # Browser session data 141 + 142 + ~/.config/Peek/ 143 + ├── profiles.db # Peek profile metadata 144 + ├── default/ # Peek profile "default" 145 + │ ├── datastore.sqlite # Peek data 146 + │ └── chromium/ # Chromium session for this profile 147 + │ ├── Cookies 148 + │ ├── Local Storage 149 + │ ├── Cache 150 + │ └── ... 151 + └── work/ # Peek profile "work" 152 + ├── datastore.sqlite 153 + └── chromium/ # Separate Chromium session 154 + ``` 155 + 156 + **Electron configuration:** 157 + 158 + ```typescript 159 + const profileDataPath = path.join(userDataPath, PROFILE); 160 + const sessionDataPath = path.join(profileDataPath, 'chromium'); 161 + 162 + app.setPath('userData', profileDataPath); 163 + app.setPath('sessionData', sessionDataPath); 164 + ``` 165 + 166 + Each Peek profile gets its own Chromium session directory. This provides: 167 + 168 + - **Isolated cookies** - Separate login sessions per profile 169 + - **Isolated localStorage** - Settings don't bleed between profiles 170 + - **Isolated cache** - Browser cache separation 171 + - **Isolated extensions** - Browser extensions per profile 172 + 173 + **Key Difference:** 174 + 175 + - **Peek Profile** = High-level data workspace (items, tags, sync config) 176 + - **Chromium Profile** = Low-level browser session data (cookies, cache, etc.) 177 + 178 + Chromium profiles are automatically created per Peek profile and are not directly user-visible or manageable. 179 + 180 + ## Usage 181 + 182 + ### Settings UI 183 + 184 + Open Settings → Profiles section to: 185 + 186 + 1. **View current profile** - Shows active profile name 187 + 2. **Create profile** - Click "Add Profile", enter name 188 + 3. **Switch profiles** - Select radio button (app restarts) 189 + 4. **Delete profile** - Delete button (disabled for default/active) 190 + 5. **Configure sync** - Enable/disable sync per profile 191 + 192 + ### IPC API 193 + 194 + ```javascript 195 + // List all profiles 196 + const result = await window.app.profiles.list(); 197 + // result.data = [{ id, name, slug, syncEnabled, ... }] 198 + 199 + // Get current profile 200 + const current = await window.app.profiles.getCurrent(); 201 + 202 + // Create new profile 203 + await window.app.profiles.create('Work'); 204 + 205 + // Switch profile (app restarts) 206 + await window.app.profiles.switch('work'); 207 + 208 + // Delete profile 209 + await window.app.profiles.delete(profileId); 210 + 211 + // Sync configuration 212 + await window.app.profiles.enableSync(profileId, apiKey, serverProfileSlug); 213 + await window.app.profiles.disableSync(profileId); 214 + const syncConfig = await window.app.profiles.getSyncConfig(profileId); 215 + ``` 216 + 217 + ### Backend API (profiles.ts) 218 + 219 + ```typescript 220 + import { 221 + initProfilesDb, 222 + listProfiles, 223 + createProfile, 224 + getProfile, 225 + deleteProfile, 226 + getActiveProfile, 227 + setActiveProfile, 228 + enableSync, 229 + disableSync, 230 + getSyncConfig, 231 + } from './profiles.js'; 232 + ``` 233 + 234 + ## Migration 235 + 236 + ### First Launch 237 + 238 + When profiles support first launches: 239 + 240 + 1. `initProfilesDb()` creates `profiles.db` if missing 241 + 2. `migrateExistingProfiles()` detects existing profile directories 242 + 3. Existing `default/` and `dev/` directories become profile records 243 + 4. `ensureDefaultProfile()` creates default profile if none exist 244 + 245 + ### Data Preservation 246 + 247 + - Profile directories are NOT deleted when profile is removed from `profiles.db` 248 + - Data remains on disk in `{userData}/{slug}/` 249 + - User can manually delete directory if desired 250 + 251 + ### Backward Compatibility 252 + 253 + - `PROFILE` env var still works (overrides profiles.db) 254 + - Existing profile directories detected and migrated 255 + - Development builds always use `dev` regardless of profiles.db state 256 + 257 + ## Server API 258 + 259 + ### Profile Endpoints 260 + 261 + ``` 262 + GET /profiles # List user's profiles 263 + POST /profiles # Create profile 264 + { "slug": "work", "name": "Work" } 265 + DELETE /profiles/:id # Delete profile 266 + ``` 267 + 268 + ### Data Endpoints with Profile Parameter 269 + 270 + All data endpoints accept optional `?profile={slug}` parameter: 271 + 272 + ``` 273 + GET /items?profile=work # Get items from work profile 274 + POST /items?profile=work # Create item in work profile 275 + ``` 276 + 277 + Default: `profile=default` if not specified (backward compatible) 278 + 279 + ## Security Considerations 280 + 281 + - API keys stored in plaintext in `profiles.db` (local SQLite file) 282 + - Profile data isolation is filesystem-based 283 + - No encryption on profile data at rest 284 + - Single-instance lock skipped for dev profiles (allows dev+prod simultaneously) 285 + 286 + ## Limitations 287 + 288 + - **Profile switching requires app restart** - Electron limitation 289 + - **No profile encryption** - Data stored in plaintext SQLite 290 + - **No profile export/import** - Manual directory copy required 291 + - **Mobile support pending** - Currently desktop-only feature 292 + 293 + ## Files 294 + 295 + **Implementation:** 296 + 297 + - `backend/electron/profiles.ts` - Profile management module 298 + - `backend/electron/entry.ts` - Profile initialization and selection 299 + - `backend/electron/ipc.ts` - IPC handlers for profiles 300 + - `backend/electron/sync.ts` - Per-profile sync configuration 301 + - `app/settings/settings.js` - Profiles UI section 302 + - `preload.js` - Profiles API exposure 303 + 304 + **Server:** 305 + 306 + - `backend/server/users.js` - Profile CRUD functions 307 + - `backend/server/db.js` - Profile-aware connection pooling 308 + - `backend/server/index.js` - Profile API endpoints 309 + 310 + **Documentation:** 311 + 312 + - `docs/profiles.md` - This file 313 + - `DEVELOPMENT.md` - Updated with profile architecture 314 + - `docs/sync.md` - Updated with per-profile sync 315 + 316 + ## Testing 317 + 318 + ```bash 319 + # Test profile isolation 320 + yarn start # Should use dev profile 321 + # Open Settings → Profiles 322 + # Create "Test" profile 323 + # Switch to "Test" (app restarts) 324 + # Verify data is separate 325 + 326 + # Test sync configuration 327 + # Enable sync for "Test" profile 328 + # Add items 329 + # Run sync 330 + # Verify items go to correct server profile 331 + ``` 332 + 333 + ## Future Enhancements 334 + 335 + - Profile import/export functionality 336 + - Profile templates (pre-configured profiles) 337 + - Profile-level theme settings 338 + - Profile backup/restore 339 + - Mobile profile support (Tauri) 340 + - Profile encryption at rest 341 + - Profile-specific extension configurations
+32 -11
docs/sync.md
··· 52 52 53 53 ### Server 54 54 55 + All endpoints accept an optional `?profile={slug}` parameter (defaults to `default`): 56 + 55 57 ``` 56 - GET /items # All items 57 - GET /items/since/:timestamp # Items modified after timestamp 58 - GET /items/:id # Single item 59 - POST /items # Create item 60 - PATCH /items/:id/tags # Update tags 61 - DELETE /items/:id # Delete item 58 + GET /items?profile=work # All items in work profile 59 + GET /items/since/:timestamp?profile=work # Items modified after timestamp 60 + GET /items/:id?profile=work # Single item 61 + POST /items?profile=work # Create item in profile 62 + PATCH /items/:id/tags?profile=work # Update tags 63 + DELETE /items/:id?profile=work # Delete item 64 + ``` 65 + 66 + **Profile Management:** 67 + ``` 68 + GET /profiles # List user's profiles 69 + POST /profiles # Create profile 70 + DELETE /profiles/:id # Delete profile 62 71 ``` 63 72 64 73 ### Desktop IPC ··· 75 84 76 85 ### Desktop 77 86 78 - Sync settings stored in `extension_settings` table: 79 - - `sync.serverUrl` - Server URL 80 - - `sync.apiKey` - API key 81 - - `sync.lastSyncTime` - Last sync timestamp 82 - - `sync.autoSync` - Enable auto-sync 87 + Sync configuration is **per-profile** and stored in `profiles.db`: 88 + - `api_key` - API key (authenticates with server user) 89 + - `server_profile_slug` - Which server profile to sync to 90 + - `last_sync_at` - Last sync timestamp 91 + - `sync_enabled` - Enable sync for this profile 92 + 93 + **Server URL** is environment-based (`SYNC_SERVER_URL` env var or default). 94 + 95 + Each desktop profile can independently sync to different server profiles under the same server user account. 96 + 97 + **Example:** 98 + ``` 99 + Desktop Profile API Key Server Profile 100 + ────────────── ────────── ────────────── 101 + Work alice's key work 102 + Personal alice's key personal 103 + ``` 83 104 84 105 ### Mobile 85 106