From ca7c956860062bc6b1dfd805e3b7893a3172b253 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 02:42:11 +0000 Subject: [PATCH] docs: simplify README for users and contributors https://claude.ai/code/session_01PXpbQhbs19zKfh8M84By4y --- README.md | 366 ++++++++++++++++++++++-------------------------------- 1 file changed, 151 insertions(+), 215 deletions(-) diff --git a/README.md b/README.md index 7f00719..32b3926 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,142 @@ # NexaNote -Open-source, privacy-friendly, self-hostable note-taking app with stylus support. +**Self-hosted handwritten notes for your own NAS.** -An alternative to Samsung Notes, OneNote and GoodNotes that respects your data. +NexaNote is a privacy-focused note-taking app for people who want to keep their +notes on hardware they control. It pairs a **Flutter** client (Linux desktop and +Android) with a small, self-hosted **Python (FastAPI)** backend that stores +everything as plain files on your own server or NAS. + +Your notes never touch a third-party cloud. You own the storage, the sync +server, and the data. --- -## About development -This project is actively developed by me, with the help of AI tools for productivity and experimentation. -All design decisions and direction are fully human-driven. +## Features + +- **Markdown note storage** — typed notes are saved as plain `.md` files you can + open in Obsidian or any text editor. +- **Stylus & drawing support** — handwritten notes with pen, highlighter, + eraser, and pressure sensitivity (mouse works too). +- **Self-hosted backend** — runs on your NAS or any always-on machine. +- **WebDAV sync** — sync notes between devices through your own WebDAV server. +- **Docker deployment** — a single multi-arch image for `amd64` and `arm64`. +- **Android APK** — installable builds published via GitHub Releases. +- **Offline-first direction** — the app is built to keep working without a + network connection and sync when one is available. --- -## What works today -- Typed notes stored as plain Markdown files (Obsidian-style) -- Handwritten notes with stylus or mouse (pen, highlighter, eraser, pressure sensitivity) -- Notebooks to organize your notes -- WebDAV sync with your NAS, Nextcloud, or any WebDAV server -- Offline-first — works without internet -- Conflict resolution when editing the same note on multiple devices -- Search by title -- Linux desktop app (Flutter) -- Python backend with REST API and WebDAV server +## Current status -## What's coming +NexaNote is under **active development**. It's already usable for everyday +testing, but please set expectations accordingly: -- Android app -- PDF export -- Handwriting OCR -- Page templates (lined, grid, dotted) -- End-to-end encryption +- Core features (notes, drawings, notebooks, WebDAV sync) work today. +- Sync reliability is improving but is not yet bulletproof. +- **Not yet recommended for critical notes without your own backups.** Keep a + copy of anything you can't afford to lose. -See [docs/android-roadmap.md](docs/android-roadmap.md) for the full Android & S26 Ultra plan. +If you hit problems, the in-app diagnostics and sync logs (see below) make it +much easier to report what went wrong. --- -## Architecture +## Quick start: backend with Docker -NexaNote uses two components that work together: +The easiest way to run the backend is the prebuilt Docker image +`thezupzup/nexanote:latest`. Create a `docker-compose.yml`: + +```yaml +services: + nexanote-backend: + image: thezupzup/nexanote:latest + container_name: nexanote-backend + ports: + - "8766:8766" # REST API (used by the app) + - "8765:8765" # WebDAV (used for sync) + volumes: + - ./data:/data + restart: unless-stopped +``` -- **Python backend** — handles storage (file-based, Markdown), REST API, and WebDAV sync server -- **Flutter app** — the interface, runs on Linux desktop and Android (coming soon) +Then start it: +```bash +docker compose up -d ``` -NexaNote/ -├── main.py # Start the backend -├── requirements.txt -├── nexanote/ # Python backend -│ ├── models/ # Data models -│ ├── storage/ # File-based store (Markdown + JSON) + SQLite migration -│ ├── sync/ # WebDAV server + sync engine + conflict resolution -│ └── api/ # REST API (FastAPI) -├── app/ # Flutter app -│ └── lib/ -│ ├── screens/ # UI screens -│ ├── widgets/ # Reusable widgets (ink canvas, notes list...) -│ └── services/ # API client, app state -└── tests/ # 100+ tests + +The image is published as a multi-arch manifest (`linux/amd64` + +`linux/arm64`), so x86 servers and ARM NAS units pull the right variant +automatically. + +> Want a single hostname / one TLS certificate instead of two ports? See +> [docs/docker.md](docs/docker.md) for reverse-proxy examples (Nginx, Caddy, +> Cloudflare Tunnel). + +--- + +## Using a NAS + +On a NAS, point the volume at a path on your storage pool instead of `./data`. +Common examples: + +```yaml +# Synology +volumes: + - /volume1/docker/nexanote/data:/data + +# Ugreen (or a second pool) +volumes: + - /volume2/docker/nexanote/data:/data ``` -### Storage layout (v1.0.0+) +Tested with Ugreen NAS (UGOS Pro); it should work on any system that can run +Docker. The mounted `data` directory holds all your notes, so back it up like +any other important folder. -Notes are plain files on disk, Obsidian-style: +--- + +## Connecting the app + +Open **Settings → Backend** in the app and enter your backend URL. + +**Backend (REST API) URL:** + +``` +http://NAS_IP:8766 +https://nexanote.example.com +``` + +**WebDAV URL:** ``` -/ -├── notebooks/.yaml # Notebook metadata (YAML) -├── notes/.md # Markdown body + YAML frontmatter -└── drawings/.json # Stylus strokes (one file per note) +http://NAS_IP:8765 +https://webdav-nexanote.example.com ``` -Each note's frontmatter carries `title`, `tags`, `created_at`, `updated_at`, -plus the metadata the app needs (id, notebook_id, page list, sync_status). -Single-page notes have a clean body with no NexaNote-specific markers, so -they can be opened/edited directly in Obsidian or any Markdown editor. +If you run behind a reverse proxy that serves both on one hostname, the app can +derive the WebDAV URL automatically — see [docs/docker.md](docs/docker.md). +Otherwise, set both URLs explicitly. + +--- + +## Android APK -A pre-v1.0.0 SQLite database (`nexanote.db`) is detected on first startup -and migrated automatically — the original DB is renamed to -`nexanote.db.legacy_backup` and kept in place. See -[`CHANGELOG.md`](CHANGELOG.md) for details. +There is no Play Store listing yet. To install on Android: + +1. Go to the [GitHub Releases](https://github.com/TheZupZup/NexaNote/releases) + page. +2. Download the latest **`NexaNote-Android-*.apk`** asset. +3. Open the downloaded file to install it manually. +4. If prompted, allow **install from unknown sources** for your browser or file + manager. + +The Android build is functional but still being polished, so expect rough edges. --- -## Getting started +## Local development ### Requirements @@ -92,208 +146,90 @@ and migrated automatically — the original DB is renamed to ### Backend ```bash -cd NexaNote -python -m venv venv -source venv/bin/activate pip install -r requirements.txt python main.py ``` -The backend starts two servers: - -| Service | URL | Purpose | -|---------|-----|---------| -| REST API | http://127.0.0.1:8766 | Used by the Flutter app | -| WebDAV | http://127.0.0.1:8765 | Connect your NAS or Nextcloud | -| API docs | http://127.0.0.1:8766/docs | Interactive Swagger UI | +This starts two servers: -The two ports work directly out of the box. If you'd rather expose the -backend behind a single hostname (one URL to type into the app, one -certificate to manage, one firewall rule), see -[Single backend URL](#single-backend-url) below. +| Service | URL | Purpose | +|----------|------------------------------|------------------------------| +| REST API | http://127.0.0.1:8766 | Used by the Flutter app | +| WebDAV | http://127.0.0.1:8765 | Sync | +| API docs | http://127.0.0.1:8766/docs | Interactive Swagger UI | ### Flutter app ```bash -cd NexaNote/app +cd app flutter pub get flutter run -d linux ``` -### Launch everything at once +### Tests ```bash -bash ~/NexaNote/nexanote.sh -``` - -This script starts the backend and the app automatically. - -### Run the backend with Docker - -Prefer to run the backend persistently on a NAS or always-on machine? - -From source: - -```bash -docker compose up -d --build -``` - -Or pull the prebuilt image from Docker Hub (no clone needed): - -```bash -docker run -d \ - --name nexanote-backend \ - -p 8766:8766 -p 8765:8765 \ - -v /path/on/host/nexanote-data:/data \ - --restart unless-stopped \ - thezupzup/nexanote-backend:latest +python -m pytest tests/ -v ``` -This starts the backend on ports `8766` (API) and `8765` (WebDAV), with data -persisted in the mounted volume. The image is published as a multi-arch -manifest (`linux/amd64` + `linux/arm64`), so x86 servers and ARM NAS units -(Ugreen DXP, Raspberry Pi, etc.) both pull the right variant automatically. - -See [docs/docker.md](docs/docker.md) for the full guide, including a NAS -(Synology / Ugreen) compose example and multi-arch buildx instructions. - --- -## Single backend URL - -By default the backend exposes the REST API on `:8766` and the WebDAV server -on `:8765` — both still work directly and unchanged. To make life easier on -mobile (one URL to type, one TLS cert, one firewall rule), put a reverse -proxy in front and route: - -| Path on the public host | Backend target | Purpose | -|-------------------------|----------------|---------| -| `/` | `127.0.0.1:8766` | REST API | -| `/webdav` | `127.0.0.1:8765` | WebDAV | - -The Flutter app then accepts a single base URL (e.g. -`https://nexanote.example.com`) and derives the WebDAV URL by appending -`/webdav`. Advanced users who run the legacy two-port deployment can still -set the WebDAV URL explicitly in Settings → Backend; this overrides the -derivation and is preserved across upgrades. - -### Nginx - -```nginx -server { - listen 443 ssl http2; - server_name nexanote.example.com; - - # TLS certificates (e.g. Let's Encrypt) - ssl_certificate /etc/letsencrypt/live/nexanote.example.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/nexanote.example.com/privkey.pem; - - # WebDAV — strip the /webdav prefix before forwarding so wsgidav - # serves its root at "/" as it expects. - location /webdav/ { - proxy_pass http://127.0.0.1:8765/; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_redirect off; - client_max_body_size 0; # allow large uploads - proxy_request_buffering off; # stream PUTs straight through - } - - # REST API — everything else. - location / { - proxy_pass http://127.0.0.1:8766; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -### Caddy +## Storage layout -``` -nexanote.example.com { - handle_path /webdav/* { - reverse_proxy 127.0.0.1:8765 - } - reverse_proxy 127.0.0.1:8766 -} -``` +Everything lives under your `data` directory as plain files: -### Cloudflare Tunnel - -Cloudflared forwards the request path to the origin unchanged, so the -cleanest setup is to run a local reverse proxy (Nginx or Caddy from above) -on `127.0.0.1:80` and point the tunnel at it: - -```yaml -# ~/.cloudflared/config.yml -tunnel: -credentials-file: /etc/cloudflared/.json - -ingress: - - hostname: nexanote.example.com - service: http://127.0.0.1:80 - - service: http_status:404 ``` - -If you'd rather skip the local proxy, use two subdomains and configure the -WebDAV URL explicitly in the app's advanced settings: - -```yaml -ingress: - - hostname: nexanote.example.com - service: http://127.0.0.1:8766 - - hostname: webdav.nexanote.example.com - service: http://127.0.0.1:8765 - - service: http_status:404 +data/ +├── notes/ # Markdown note bodies (+ YAML frontmatter) +├── notebooks/ # Notebook metadata (YAML) +├── drawings/ # Stylus strokes (JSON, one file per note) +└── sync_logs/ # Latest sync report (for diagnostics) ``` -After configuring the proxy, point the Flutter app at -`https://nexanote.example.com` and you're done — no second URL required -(unless you took the two-subdomain route, in which case set the WebDAV URL -override in Settings → Backend). +Because notes are plain Markdown on disk, you can read or edit them with any +Markdown editor, and back them up with ordinary file tools. --- -## Sync with your NAS +## Sync diagnostics -Once your NAS has WebDAV enabled, open Settings in the app and enter your NAS URL and credentials. NexaNote will sync your notes automatically. +When sync misbehaves, NexaNote gives you a few ways to see what happened: -Tested with Ugreen NAS (UGOS Pro). Should work with any WebDAV-compatible server including Nextcloud. +- **Sync logs** — the most recent sync writes a report to + `data/sync_logs/latest.json` (note ids/titles and outcomes), also available + from the backend API. +- **Dry-run** — trigger a sync with `?dry_run=true` to preview what *would* + change without writing anything. +- **Copy diagnostics** — the app's connection screen has a button that builds a + copy/paste-able diagnostic summary (with credentials redacted), handy for bug + reports. --- -## Running tests - -```bash -python -m pytest tests/ -v -``` +## Roadmap -100+ tests covering models, file-based storage, SQLite → file migration, WebDAV provider, conflict resolution, and REST API. +- Stronger sync reliability +- Better Android polish +- A clearer conflict-resolution UI +- F-Droid readiness +- Optional desktop packaging --- ## Contributing -The project is in early development. Contributions are welcome. +Contributions are welcome. -| Task | Difficulty | -|------|-----------| -| Android app (Flutter) | Hard | -| PDF export | Medium | -| Page templates | Easy | -| Handwriting OCR | Hard | -| End-to-end encryption | Hard | +- Open an issue to report bugs or discuss ideas before large changes. +- Keep pull requests small and focused — one change per PR. +- Don't modify unrelated files. +- Tests are appreciated. -1. Fork the repo -2. Create a branch (`git checkout -b feature/my-feature`) -3. Commit your changes -4. Open a pull request +See [CONTRIBUTING.md](CONTRIBUTING.md) for branch and PR conventions. --- ## License -[MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) — modifications must remain open-source, commercial use is allowed. +[MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/) — modifications must remain +open-source, and commercial use is allowed. See [LICENSE](LICENSE).