Peek Datastore#
The Peek Personal Datastore stores URLs, navigation history, tags, notes, and other user data. It uses SQLite with a unified schema across desktop backends (Electron, Tauri). The browser extension uses IndexedDB with the same logical schema.
Architecture#
All data operations are handled in the main/backend process, with renderer processes accessing via IPC through window.app.datastore.
Renderer Process Main/Backend Process
┌─────────────────┐ ┌─────────────────────┐
│ window.app. │ IPC invoke() │ IPC Handlers │
│ datastore.* │───────────────▶│ ├─ add-item │
│ │ │ ├─ query-items │
│ │ │ ├─ add-visit │
│ │ │ └─ ... │
└─────────────────┘ │ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ SQLite │ │
│ │ Database │ │
│ └─────────────┘ │
└─────────────────────┘
Why IPC-Based?#
- Backend Portability - Same renderer code works with Electron, Tauri, or mobile
- Storage Flexibility - Can swap SQLite for other backends without renderer changes
- Security - Datastore logic in trusted main process
- Sync Readiness - Same API can route to local or remote storage
Database Schema#
Location: {app_data}/{profile}/datastore.sqlite
Core Tables#
items - Unified Item Storage#
Stores all user content types: URLs, text notes, tagsets, images.
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key (UUID) |
| type | TEXT | url, text, tagset, image |
| content | TEXT | The actual content (URL, note text, etc.) |
| mimeType | TEXT | MIME type (e.g., text/html) |
| metadata | TEXT | JSON for flexible extra data |
| syncId | TEXT | Server-assigned ID for sync |
| syncSource | TEXT | Origin of sync (server, history, etc.) |
| syncedAt | INTEGER | Last sync timestamp (ms) |
| createdAt | INTEGER | Creation timestamp (ms) |
| updatedAt | INTEGER | Last update timestamp (ms) |
| deletedAt | INTEGER | Soft-delete timestamp (0 if active) |
| starred | INTEGER | 1 if starred |
| archived | INTEGER | 1 if archived |
| visitCount | INTEGER | Total visit count |
| lastVisitAt | INTEGER | Most recent visit timestamp |
| frecencyScore | INTEGER | Calculated frecency for ranking |
| title | TEXT | Display title (denormalized for URLs) |
| domain | TEXT | Domain (denormalized for URLs) |
| favicon | TEXT | Favicon URL (denormalized for URLs) |
item_visits - Navigation History#
Tracks page visits with timing, context, and navigation chaining. Local-only (not synced).
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key |
| itemId | TEXT | FK to items |
| timestamp | INTEGER | Unix timestamp (ms) |
| duration | INTEGER | Time spent (ms) |
| source | TEXT | direct, link, bookmark, reload, etc. |
| sourceId | TEXT | ID of referring visit/item |
| windowType | TEXT | main, modal, panel |
| metadata | TEXT | JSON for extra context |
| scrollDepth | INTEGER | How far user scrolled (0-100) |
| interacted | INTEGER | 1 if user interacted (clicked, typed) |
| prevId | TEXT | Previous visit in chain |
| nextId | TEXT | Next visit in chain |
Frecency Scoring: Each device calculates frecency from its local visits using time-decay weighting. Recent visits and interactions score higher.
tags - Tag Definitions#
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key |
| name | TEXT | Tag name (unique) |
| frecency | REAL | Usage frequency score |
item_tags - Tag Associations#
| Column | Type | Description |
|---|---|---|
| item_id | TEXT | FK to items |
| tag_id | TEXT | FK to tags |
extension_settings - Extension Config#
Key-value storage for extension and core settings.
| Column | Type | Description |
|---|---|---|
| key | TEXT | Setting key (e.g., sync.serverUrl) |
| value | TEXT | JSON-encoded value |
API Reference#
Access via window.app.datastore in any peek:// page.
Items#
// Add item
const result = await api.datastore.addItem({
type: 'url',
content: 'https://example.com',
metadata: { title: 'Example' }
});
// Query items
const urls = await api.datastore.queryItems({ type: 'url' });
const recent = await api.datastore.queryItems({ type: 'url', sortBy: 'lastVisit', limit: 20 });
const popular = await api.datastore.queryItems({ type: 'url', sortBy: 'frecency', limit: 20 });
const searched = await api.datastore.queryItems({ type: 'url', search: 'github' });
// Update item
await api.datastore.updateItem(id, { metadata: { title: 'New Title' } });
// Delete item (soft delete)
await api.datastore.deleteItem(id);
Query filters: type, starred, archived, domain, search, limit, includeDeleted
Sort options: created, updated, frecency, lastVisit, visitCount
Tags#
// Get or create tag
const tag = await api.datastore.getOrCreateTag('bookmark');
// Tag an item
await api.datastore.tagItem(itemId, tagId);
// Untag
await api.datastore.untagItem(itemId, tagId);
// Get item's tags
const tags = await api.datastore.getItemTags(itemId);
// Get items by tag
const items = await api.datastore.getItemsByTag(tagId);
Visits & Navigation#
// Track a navigation (finds/creates item, records visit, updates frecency)
const result = await api.datastore.trackNavigation('https://example.com', {
source: 'link',
title: 'Example Page',
favicon: 'https://example.com/favicon.ico'
});
// Returns: { visitId, itemId, created: true/false }
// Record visit to existing item
await api.datastore.recordItemVisit(itemId, {
source: 'direct',
interacted: 1
});
// Get visits for an item
const visits = await api.datastore.getItemVisits(itemId, { limit: 50 });
// Query by frecency (optimized for omnibox/history)
const topUrls = await api.datastore.queryItemsByFrecency({
search: 'github',
limit: 10
});
Visit sources: direct, link, bookmark, reload, form, generated, frame, other
Settings#
// Get setting
const value = await api.datastore.getSetting('sync.serverUrl');
// Set setting
await api.datastore.setSetting('sync.serverUrl', 'https://...');
Stats#
const stats = await api.datastore.getStats();
// Returns: { items: 150, visits: 1200, tags: 25 }
Sync#
The datastore syncs between backends via the server API:
- Desktop ↔ Server: Bidirectional sync in
backend/electron/sync.ts - Mobile → Server: Push sync from mobile app
- Conflict Resolution: Last-write-wins based on
updatedAt
See docs/sync.md for sync architecture details.
Files#
backend/electron/datastore.ts- Electron SQLite implementationbackend/tauri/src-tauri/src/datastore.rs- Tauri SQLite implementationbackend/server/db.js- Server SQLite implementationapp/datastore/- Shared schema and helpers