notes — Personal Sticky-Notes System
Personal kanban / sticky-notes system that lives on Eric's Mac as floating frameless windows, syncs to a server, and shows on his iPhone Home Screen via a Scriptable widget. Replaces ad-hoc use of Apple Stickies and Apple Notes for short-lived task and idea capture.
Scope is firm: personal-only, forever. Not a product, never to be shared, no multi-user features.
Status
- Backend: live at
https://notes.workflows.heyroyal.co(containerized on the AI Clients droplet) - macOS app:
/Applications/notes.app(built from~/Projects/notes/), pointing at production - iOS widget: Scriptable script written, requires manual install on the phone
- Code:
~/Projects/notes/ - Dashboard entry: projects-dashboard at
localhost:9880shows "notes" with an "Open App" button
Where everything lives — canonical map
| What | Path |
|---|---|
| Source code | ~/Projects/notes/ |
| Repo entry point for Claude | ~/Projects/notes/CLAUDE.md |
| Stack source of truth | ~/Projects/notes/docs/TECHNOLOGY-STACK.md |
| In-app cheatsheet | ~/Projects/notes/desktop/help.html (also reachable via tray menu → Help) |
| Packaged app | /Applications/notes.app |
| Build output | ~/Projects/notes/dist/notes-darwin-arm64/ |
| User config (server URL + token) | ~/Library/Application Support/notes/config.json |
| Window positions | ~/Library/Application Support/notes/positions.json |
| Local SQLite (dev only) | ~/Projects/notes/data/notes.db |
| Production SQLite | droplet: /opt/ai-clients/data/notes/notes.db |
Production env (AUTH_TOKEN) |
droplet: /opt/ai-clients/clients/notes/config.env |
| Production nginx site | droplet: /opt/ai-clients/nginx/conf.d/notes.conf |
| Production compose service | droplet: /opt/ai-clients/docker-compose.yml (service: notes) |
| Project hub (this file) | ~/.claude-royal/project-notes/notes/README.md |
| Original build plan | ~/.claude-royal/plans/does-apple-still-have-iterative-fog.md |
| Infrastructure memory | ~/.claude-royal/projects/-Users-edowns-Desktop/memory/infrastructure_servers.md |
| Dashboard registration | ~/.claude/skills/projects-dashboard/projects.json (id: "notes") |
Components
| Piece | Path / URL | Notes |
|---|---|---|
| Server (Hono + better-sqlite3) | ~/Projects/notes/server/ → container on droplet |
Listens on :5681 inside the docker network |
| Mac desktop app (Electron) | ~/Projects/notes/desktop/ |
Tray app, one frameless BrowserWindow per note. Run with npm run desktop from the project. |
| iOS widget (Scriptable) | ~/Projects/notes/widget/notes-widget.js |
See widget/README.md for install steps |
| Backend image | docker-compose service notes in /opt/ai-clients/ |
Built from ~/Projects/notes/Dockerfile |
| Data (SQLite) | droplet: /opt/ai-clients/data/notes/notes.db |
Volume-mounted into the container |
| Auth secret | droplet: /opt/ai-clients/clients/notes/config.env |
AUTH_TOKEN — also stored in 1Password |
Integrations
| Integration | Status | Where it lives |
|---|---|---|
| Production URL | https://notes.workflows.heyroyal.co |
Reuses *.workflows.heyroyal.co wildcard cert + DNS |
| Hosting | AI Clients droplet 159.89.182.182 |
See infrastructure_servers.md memory |
| DNS | Cloudflare (heyroyal.co) | Wildcard A record for *.workflows.heyroyal.co |
| TLS | Let's Encrypt (certbot) | /etc/letsencrypt/live/workflows.heyroyal.co/ |
| Reverse proxy | nginx-in-docker | Conf at /opt/ai-clients/nginx/conf.d/notes.conf |
| Auth secret | 1Password | TBD — see "Open items" |
| Sites registry | ~/.claude/skills/sites-dashboard/sites.json |
TBD — entry not added yet |
| Backups | Volume /opt/ai-clients/data/notes/ |
TBD — no rotation cron yet |
Daily use
/Applications/notes.app — packaged Electron app. Launch via Spotlight ("notes") or Finder. No dock icon (LSUIElement=1); lives in the menubar.
Hotkeys (registered globally — work from any app)
- ⌘⇧N — new note
- ⌘⇧T — Tidy: snap all visible notes into a clean grid in the upper-left
In-window UX
- Drag note → magnetic snap to screen edges and to other notes within ~14px
⋯(top-right of any note) → move to a different column×(top-right) → delete- Edit body → autosaves on blur or after 500ms idle
Tray menu (click menubar icon)
New note · New in column · Refresh now · Tidy notes · Show all · Hide all · All on top · Launch at login · Settings… · Quit notes
Rebuild the .app
cd ~/Projects/notes
npm run package # → dist/notes-darwin-arm64/notes.app
rm -rf /Applications/notes.app
cp -R dist/notes-darwin-arm64/notes.app /Applications/
Local dev (against localhost server)
cd ~/Projects/notes
node --env-file=.env server/index.js # backend on :5681 against ./data/notes.db
npm run desktop # Electron app pointed at the URL in ~/Library/Application Support/notes/config.json
⚠️ The dev launch (npm run desktop) and the packaged /Applications/notes.app share the same userData directory, so don't run both at once or they'll fight over the same note windows.
For the desktop app, switch between local and production by editing ~/Library/Application Support/notes/config.json:
{ "baseUrl": "https://notes.workflows.heyroyal.co", "tokenPlain": "<token>" }
(tokenPlain is the dev-friendly fallback. Production will use Electron safeStorage once the user goes through the in-app Settings flow.)
Deploy
From the local repo:
rsync -az --delete -e "ssh -p 22" \
--include="package.json" --include="package-lock.json" --include="Dockerfile" \
--include="server/" --include="server/**" --exclude="*" \
~/Projects/notes/ root@159.89.182.182:/opt/ai-clients/notes/
ssh -p 22 root@159.89.182.182 \
"cd /opt/ai-clients && docker compose up -d --build notes && \
docker compose exec nginx nginx -s reload"
API surface
| Method | Path | Notes |
|---|---|---|
GET |
/healthz |
No auth |
GET |
/api/board |
All columns + all notes |
GET |
/api/snapshot |
Compact projection used by the iOS widget |
POST |
/api/columns / PATCH /api/columns/:id / DELETE /api/columns/:id |
|
POST |
/api/notes / PATCH /api/notes/:id / DELETE /api/notes/:id |
|
POST |
/api/reorder |
Bulk position update used by the (retired) browser drag handler |
All /api/* requires Authorization: Bearer <token>.
Open items
- [ ] Stash the production
AUTH_TOKENin 1Password (Ventures vault, "notes server") - [ ] Add an entry in
~/.claude/skills/sites-dashboard/sites.json - [ ] Configure nightly SQLite backup on the droplet (
cp+find -mtime +14 -delete) - [ ] Decide whether to delete the now-unused PWA frontend in
server/public/or leave it as a fallback - [ ] Remove the temporary
tokenPlainpath fromdesktop/main.cjsonce safeStorage flow is verified end-to-end