A quiet, local-first journal that thinks with you.
Your words live on your machine as plain Markdown. The AI features run through your own API key from the provider you choose β Anthropic, OpenAI, or a local model β nothing in between, no accounts, no servers of ours.
πΏ nopy-site.vercel.app
Nopy is a journal you keep on your own machine. Entries are plain Markdown files in a folder you choose β readable in any editor, grep-able, backup-able, yours. When you want to go deeper, a gentle AI companion β grounded in CBT and ACT β can sit with the page alongside you, notice patterns over time, and ask better questions than a blank screen ever will.
There is no nopy server. There is no account. There is no telemetry. The only thing that ever leaves your laptop is the text you explicitly send to the AI β and only then, straight to your chosen provider's API through your own key. Point it at a local model instead and nothing leaves your machine at all.
| Tool | Version | Install |
|---|---|---|
| Node.js | 20 LTS (v20.x) | nodejs.org or via nvm |
| npm | 10.x (bundled with Node 20) | Comes with Node.js |
| Rust | Latest stable | rustup.rs (required for the Tauri desktop app) |
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Install and use the correct Node version (reads .nvmrc)
nvm install
nvm use# Clone the repo
git clone https://github.com/JoshuaHarris391/nopy
cd nopy
# Switch to the correct Node version (if using nvm)
nvm use
# Install dependencies
npm installnpm run tauri devOpens a native window with full file system access. Set your journal location in Settings > Data & Privacy to save entries as Markdown files on disk.
The macOS binary isn't code-signed yet, so Gatekeeper may quarantine it and show "nopy" is damaged and can't be opened. You should move it to the Bin. The app is fine β this is just the unsigned-binary warning. Clear the quarantine attribute, then reopen it:
xattr -dr com.apple.quarantine /Applications/nopy.appAdjust the path if you keep the app somewhere other than /Applications.
npm run devOpen http://localhost:5173. Entries are stored in browser IndexedDB only.
Write a daily entry, with mood and word-count at a glance:
Then sit down with the AI companion to make sense of what you wrote:
Nopy separates cleanly into two layers, and we'd rather be upfront about both than hand-wave at "privacy-first."
What stays fully local, always
- Your journal entries β stored as
.mdfiles in the folder you pick (desktop app) or in browser IndexedDB (web). - Your psychological profile and entry index β stored as JSON on disk, never synced anywhere.
- Your AI provider API key (Anthropic or OpenAI) or local server URL β saved locally, used only to make calls directly from your machine to the provider you picked.
- App state, preferences, session history β all on-device.
What gets sent to your AI provider, and only then
When you use the AI chat, generate a profile, or index an entry, nopy sends the relevant text (the entries or message in scope) to your chosen provider's API using your key. Nothing is sent otherwise β not when you type, not when you save, not in the background. You can use nopy as a plain Markdown journal and never send a single byte anywhere.
You pick the provider: Anthropic, OpenAI, or a local model via LM Studio β and a local model makes no network calls at all, so your text never leaves the machine. For the cloud providers, each one's own data policies apply to that slice of traffic. The notes below cover Anthropic, the default; if you use OpenAI, see OpenAI's policies instead.
- Not used for training. Under Anthropic's Commercial Terms, API inputs and outputs are not used to train their models. (Commercial Terms)
- Short retention by default. Anthropic retains API inputs and outputs for up to 30 days, after which they are deleted, unless flagged by their automated trust & safety systems (in which case retention may extend up to 2 years for safety review). (Privacy Policy)
- Zero Data Retention available. Qualifying organisations can enable Zero Data Retention (ZDR) on their Anthropic account, which means API inputs and outputs are not retained past the response. If you have ZDR enabled on your Anthropic account, nopy's AI features automatically inherit it β nopy doesn't override anything. (ZDR details)
If any of this ever changes upstream, the source of truth is Anthropic's policy pages linked above β not this README.
Want zero AI involvement? Leave the API key blank. Nopy still works as a markdown journal with mood tracking and the writing surface, with no network calls at all.
- React 19 + TypeScript β UI framework
- Vite β build tool and dev server
- Tailwind CSS β utility-first styling
- Tauri β lightweight desktop shell (Rust) for native file system access
- Zustand β state management with IndexedDB + filesystem persistence
- AI providers β bring your own: Anthropic (defaults to Claude Sonnet 4.5 for chat, Claude Haiku 4.5 for indexing), OpenAI, or a local model via LM Studio. Uses the
@anthropic-ai/sdkandopenaiSDKs.
- Structured journaling β simple markdown editor with manual save
- AI psychologist chat β conversational agent with streaming responses and session continuity, in selectable therapy modes (CBT, ACT, or breakup support)
- Multiple AI providers β use Anthropic, OpenAI, or a local model via LM Studio β or none at all
- Context Workspace β curate exactly what gets injected into the AI prompt (profile, index, and custom notes) with a live context-window budget
- Journal Launcher β open a recent journal or create a new one on every app start
- Psychological profile β auto-generated insights from your journal entries
- Entry indexing β mood tracking, theme extraction, and a searchable index
- Local-first storage β entries saved as
.mdfiles to a directory you choose; profile and index as JSON - Privacy by design β no cloud, no telemetry, no accounts
| Command | Description |
|---|---|
npm run dev |
Start the browser dev server with hot reload |
npm run build |
Type-check and build for production (output in dist/) |
npm run preview |
Preview the production build locally |
npm run lint |
Run ESLint to check code style |
npm run tauri dev |
Run the desktop app with hot reload |
npm run tauri build |
Build the desktop app for distribution |
The test suite uses Vitest and covers schemas, utilities, service helpers, stores, hooks, and a few end-to-end integration flows. Tests are focused on behaviour (inputs β expected outputs) rather than coverage metrics.
# Run all tests once
npm test
# Run in watch mode (re-runs on file change)
npm run test:watchTests live in src/__tests__/, mirroring the source tree:
| File | What it covers |
|---|---|
schemas/journal.test.ts |
Zod validation + AI output coercion for mood, tags, summary |
schemas/profile.test.ts |
Profile schema validation and framework catch defaults |
schemas/frontmatter.test.ts |
Frontmatter parsing with optional field defaults |
services/fs.test.ts |
slugify, entryToMarkdown, parseMarkdown, extractDateFromFilename |
services/entryProcessor.test.ts |
computeLocalStats β mood averaging, streak, reflection depth |
services/contextAssembler.test.ts |
Context assembly β token budgeting, message truncation, profile injection |
services/llm.test.ts |
Provider-agnostic LLM request dispatch |
services/localServer.test.ts |
LM Studio local server client |
services/chatPersistence.test.ts |
Chat history persistence (NDJSON) |
services/therapyRegistry.test.ts |
Therapy agent registry (CBT / ACT / breakup) |
stores/settingsStore.test.ts |
Settings store and per-provider model selection |
hooks/useLocalModels.test.ts |
Fetching the available local model list |
integration/journalToChat.test.tsx |
End-to-end journal entry β chat flow |
integration/chatStreamRender.test.tsx |
Streaming chat rendering |
(The table above is representative, not exhaustive β see src/__tests__/ for the full set.) Live calls to external AI providers are mocked rather than hit directly. The suite favours functional flow tests over exhaustive edge-case matrices.
On first launch, go to Settings and configure:
- AI provider β choose Anthropic, OpenAI, or Local (LM Studio), then enter the API key (Anthropic/OpenAI) or local server URL (LM Studio needs only a URL β no key). Required for AI chat and entry indexing; your key stays local.
- Journal location β choose where to save your entries as Markdown files (desktop app only).
See the docs/ folder for detailed project documentation, including the noobStack guides β a set of docs aimed at contributors who are new to this tech stack (e.g. data engineers coming from Python).
See LICENSE for details.


