A lightweight, offline-first, multi-platform note-taking application with server synchronization.
| Component | Technology | Why |
|---|---|---|
| Backend | FastAPI + SQLite (aiosqlite) | Fast, async, auto-docs, Python, easy to host anywhere |
| Desktop (Linux/macOS) | Python Tkinter | Built-in, no dependencies, works everywhere |
| Android | React Native + Expo | Cross-platform mobile, hot reload, easy dev |
| Chrome Extension | Plain JS/HTML/CSS (MV3) | No framework needed, lightweight |
| Web Client | Plain HTML/JS | Debugging and quick access |
┌─────────────────────────────────────────────────────┐
│ Clients │
│ ┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────────┐ │
│ │ Desktop │ │ Android │ │Chrome│ │ Web │ │
│ │ (Tkinter) │ │ (Expo) │ │Ext. │ │ (HTML) │ │
│ └─────┬────┘ └─────┬────┘ └──┬───┘ └─────┬────┘ │
│ │ │ │ │ │
│ └────────────┴─────────┴────────────┘ │
│ │ HTTP/REST │
│ ▼ │
│ ┌───────────────┐ │
│ │ FastAPI │ │
│ │ + SQLite │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────┘
The backend is hosted at https://sahabnote.onrender.com
- Health check: https://sahabnote.onrender.com/api/health
- API docs: https://sahabnote.onrender.com/docs
- Free tier spins down after 15 min of inactivity; first request after idle may take 30-60s to wake up.
cd backend
pip install -r requirements.txt
python run.pyServer runs on http://localhost:8000
API docs available at http://localhost:8000/docs
cd desktop
python app.pycd sahabnote # or current root
npx expo start # Standard (uses port 8081)
npm run start:worktree # Worktree-safe (uses port 8082, auto-clears cache)Dev Utilities:
npm run versions # Print all key package versions (expo, RN, react, etc.)
npm run doctor # Run Expo config validation (expo-doctor)
npm run validate # Full validation: versions + config + doctor- Open Chrome and go to
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select the
chrome-extensionfolder
Open web/index.html in any browser.
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Health check |
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
Login with username/password |
| POST | /api/auth/sync-key-login |
Login with sync key |
| POST | /api/auth/verify |
Verify token |
| GET | /api/v1/notes |
List notes |
| POST | /api/v1/notes |
Create note |
| GET | /api/v1/notes/:id |
Get note |
| PUT | /api/v1/notes/:id |
Update note |
| DELETE | /api/v1/notes/:id |
Soft-delete note |
| POST | /api/v1/sync/push |
Push local changes |
| GET | /api/v1/sync/pull |
Pull server changes |
| POST | /api/v1/sync/resolve-conflict |
Resolve sync conflict |
- Each note has a
versionnumber that increments on every update. - Clients push pending changes to
/api/v1/sync/push. - Clients pull server changes from
/api/v1/sync/pull. - If a conflict is detected (server version > client version), the client is notified.
- Conflict resolution: last-write-wins by default. The server version is kept if the client pushes an older version.
synced- Note is in sync with serverlocal_only- Note only exists locally (not yet pushed)pending_sync- Note has local changes not yet pushedsync_conflict- Conflict detected between local and server versionsdeleted_pending_sync- Note deleted locally, pending server deletion
All clients store notes locally:
- Desktop:
~/.sahabnote/notes.json - Android: AsyncStorage
- Chrome Extension:
chrome.storage.local
Notes are always editable offline. Changes are queued with pending_sync or local_only status. When the connection is restored, the "Sync Now" button pushes all pending changes to the server.
cd backend
python -m pytest tests/ -vEach client allows configuration of:
- Server URL: The backend API URL (e.g.,
http://localhost:8000for local dev, orhttps://sahabnote.onrender.comfor the production backend) - Auth Token: JWT token or sync key for authentication — use the same sync key across all clients to share notes under the same account
- Device ID: Auto-generated unique device identifier
⚠️ Important for cross-client sync: The auth token/sync key is shared per user account. If you register from the Chrome Extension, you'll get a sync key. Use that same sync key in the Android app and Desktop app to sync with the same account. Do not register a separate user per client.
sn_f9480efa28b70b6bb18885ea09f82c93ed09a6c7d7e6a3c7
Two scripts are available to monitor the backend health:
scripts/health_tray.py— Linux system tray icon (requirespystrayandPillow). Shows green/red dot.scripts/health-widget.html— Browser widget. Open in any browser, shows green/red indicator.
Both ping https://sahabnote.onrender.com/api/health every 30 seconds.
- Start backend:
cd backend && python run.py - Register a user: Open any client, go to Settings, enter username/password, click Register
- Create a note: Click "+ New Note" and type content. Note is saved locally.
- Sync to server: Click "Sync Now" - note appears on server.
- Open another client: Configure same server URL, login with same credentials, click Sync Now.
- Edit offline: Disconnect internet, edit a note. Changes saved locally with "pending_sync".
- Reconnect and sync: Reconnect, click Sync Now. Changes pushed to server.
- Delete: Delete a note. It's soft-deleted locally, then synced as deleted.
- Copy/Clear: Copy copies content to clipboard. Clear empties the current note.
- No real-time sync: Uses manual "Sync Now" button or periodic sync (Chrome extension: every 5 min).
- Last-write-wins: Conflict resolution is basic. No merge UI yet.
- Single user focus: Authentication exists but UX is basic.
- No encryption: Data is not encrypted at rest. Use HTTPS in production.
- Token expiration: JWT tokens expire after 30 days. No refresh token flow.
- WebSocket real-time sync
- Rich text / Markdown editor
- Note categories and tags
- Attachments and file uploads
- End-to-end encryption
- Multiple user collaboration
- iOS support (via Expo)
- PWA support
- CI/CD pipelines
- Docker deployment
sahabnote/
├── backend/ # FastAPI server
│ ├── main.py # App entry point
│ ├── database.py # DB init and connection
│ ├── models.py # Pydantic models
│ ├── auth.py # Auth utilities
│ ├── routes/
│ │ ├── auth_routes.py # Authentication endpoints
│ │ ├── note_routes.py # CRUD endpoints
│ │ └── sync_routes.py # Sync endpoints
│ ├── tests/
│ │ └── test_api.py # Integration tests (17 tests)
│ ├── requirements.txt
│ └── run.py
├── desktop/ # Python Tkinter app
│ ├── app.py # Main Tkinter application
│ ├── local_store.py # Local JSON storage
│ └── sync_client.py # HTTP sync client
├── android/ # React Native / Expo
│ └── src/
│ ├── App.js # Main React Native app
│ └── utils/
│ ├── storage.js # AsyncStorage wrapper
│ └── sync.js # Sync client
├── chrome-extension/ # Manifest V3
│ ├── manifest.json
│ ├── background.js
│ ├── popup/
│ │ ├── popup.html
│ │ ├── popup.css
│ │ └── popup.js
│ └── options/
│ ├── options.html
│ └── options.js
├── web/ # Simple web client
│ └── index.html
├── App.js # Expo root (delegates to android/)
├── app.json # Expo config
└── package.json # Expo packages
- MCP servers used:
filesystemfor file operations,run_commandsfor running tests/setup. - Missing MCP servers: A
databaseMCP to directly query the SQLite backend during testing. Achrome-extensionMCP to validate extension loading. - Repetitive tasks: Setting up test databases before each test run could be automated with a
make testscript. - Useful CLI commands:
sahabnote-clitool to create/read/update/delete notes from terminal. - Custom MCP idea: A
multi-platform-deployMCP that can install/test all clients from one command. - Future agent docs: Add
.ai-guide.mdwith architecture decisions and design patterns for AI agents. - Technical debt: The async fixture issue with pytest-asyncio required switching to
TestClient. Need to upgrade test infrastructure. - Next tasks (priority order):
- Add WebSocket real-time sync
- Add Markdown rendering in editor
- Add Docker Compose for easy backend deployment
- Add CI pipeline with GitHub Actions
- Add notification badges for Chrome extension
- Add iOS support via Expo