{"id":48867056,"url":"https://github.com/jamesccupps/noteforge","last_synced_at":"2026-04-20T20:01:36.463Z","repository":{"id":351379554,"uuid":"1210649141","full_name":"jamesccupps/NoteForge","owner":"jamesccupps","description":"Encrypted, offline note-taking app with notebook/section/page organization and AES-256-GCM encryption. Your data stays local — no cloud, no account.","archived":false,"fork":false,"pushed_at":"2026-04-15T13:41:46.000Z","size":286,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T19:36:51.299Z","etag":null,"topics":["aes-256","copy-paste","desktop-app","electron","encrypted","encrypted-notes","javascript","local-storage","note-taking","notebook","notepad","notes-app","offline-first","open-source","privacy-focused","secure","text-editor"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesccupps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-14T16:11:20.000Z","updated_at":"2026-04-15T18:42:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jamesccupps/NoteForge","commit_stats":null,"previous_names":["jamesccupps/noteforge"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/jamesccupps/NoteForge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesccupps%2FNoteForge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesccupps%2FNoteForge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesccupps%2FNoteForge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesccupps%2FNoteForge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesccupps","download_url":"https://codeload.github.com/jamesccupps/NoteForge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesccupps%2FNoteForge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32063458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes-256","copy-paste","desktop-app","electron","encrypted","encrypted-notes","javascript","local-storage","note-taking","notebook","notepad","notes-app","offline-first","open-source","privacy-focused","secure","text-editor"],"created_at":"2026-04-15T19:03:57.365Z","updated_at":"2026-04-20T20:01:36.457Z","avatar_url":"https://github.com/jamesccupps.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NoteForge\n\n**Encrypted, offline note-taking.** A OneNote-style app that keeps your data local and protected with AES-256-GCM encryption.\n\n## Features\n\n- **3-panel layout** — Notebooks → Sections → Pages, just like OneNote\n- **AES-256-GCM encryption** — Master password encrypts all data at rest with scrypt key derivation (N=65536)\n- **Per-notebook locks** — Individual password for sensitive notebooks, with per-notebook brute-force rate limiting\n- **Fully offline** — All fonts, scripts, and dependencies bundled locally. The only network request is an optional update check against GitHub Releases on launch\n- **Rich text editor** — Bold, italic, headings, lists, tables, code blocks, links, images, checklists\n- **Image auto-downscale** — Pasted photos are automatically resized to 1600px and JPEG-compressed\n- **Find \u0026 Replace** — Safe text-node walking that won't break HTML\n- **Auto-lock** — Configurable idle timeout (5/15/30/60 min) + Ctrl+L manual lock + per-notebook \"Re-lock Now\"\n- **Encrypted backup** — Export/restore `.enc` backup files with password-verified-before-clobber restore\n- **Auto-update** — Checks GitHub Releases on launch, downloads and installs updates seamlessly\n- **Dark \u0026 light themes** — Persisted across sessions, applied to all dialogs\n- **Export** — HTML and plain text with unencrypted file warnings\n- **Keyboard shortcuts help** — Press F1 for a list of every shortcut\n- **Sandboxed renderer** — Chromium OS-level process sandbox enabled by default\n- **Content Security Policy** — `connect-src 'none'`, `script-src 'self'` — no eval, no outbound connections\n- **DOMPurify + hardening hook** — All note content sanitized on load and paste; `\u003cinput\u003e` types restricted to `checkbox` only to block in-note phishing\n\n## What's new in 2.7.1\n\n- **Fix blank window on launch** — 2.7.0 added Subresource Integrity (SRI) hashes to the bundled React/DOMPurify scripts. SRI requires CORS, which Electron's `file://` loader does not support, so all three scripts were blocked silently and the app loaded to a blank window. SRI has been removed; integrity of bundled files is guaranteed by the signed installer instead.\n\n## What's new in 2.7.0\n\n- **Sandbox** — Renderer now runs in Chromium's OS-level process sandbox by default. Can be disabled per-user in `noteforge-config.json` if it causes issues on a specific system.\n- **Phishing-input hardening** — DOMPurify hook strips `\u003cinput type=\"password\"\u003e` (and every other non-checkbox input type) so malicious content can't impersonate a password prompt inside a note.\n- **Safe backup restore** — Restore now test-decrypts the backup with your password *before* touching current data. Failed restores leave everything intact. A rollback copy is kept on successful restores.\n- **Per-notebook rate limiting** — Wrong-password counter now tracks each notebook independently (keyed by the blob's salt+iv hash), so one mistyped password doesn't burn the lockout quota for every other notebook.\n- **Custom modal dialogs** — Native `alert()`/`confirm()`/`prompt()` replaced with themed modals. Password dialogs now honor light/dark theme.\n- **Paste any photo** — Images up to 5 MB auto-downscale to 1600px JPEG instead of being rejected.\n- **Idle timer improvements** — Now resets on `mousemove`/`wheel` too (throttled to 1 Hz). No more auto-locking mid-read.\n- **Re-lock Now** — Right-click a notebook you've unlocked this session to relock it without closing the app.\n- **Empty Trash** — Bulk-purge all soft-deleted pages from the trash view.\n- **Cursor-aware toolbar** — Heading and font-size selectors now reflect the format under your cursor.\n- **F1 shortcuts overlay** — Built-in keyboard shortcut cheat sheet.\n- **Schema version stamp** — Every saved file now carries a `version` field for safe future migrations.\n- **Graceful auto-updater failures** — If `electron-updater` fails to load (corrupt install, symlink weirdness), the app still starts. Update errors now log instead of silently vanishing.\n- **Automated test suite** — `npm test` runs 89+ tests covering crypto round-trips, KDF-downgrade protection, XSS vector blocking, IPC channel coverage, and the input-type hook. CI-ready.\n\n## Install\n\n### Download (Windows)\n\nDownload the latest installer from [Releases](../../releases):\n\n- **`NoteForge Setup x.x.x.exe`** — Standard Windows installer (recommended)\n- **`NoteForge-x.x.x-portable.exe`** — Portable version, no install needed\n\nReleases are built automatically by GitHub Actions — no manual build steps required.\n\n\u003e **Note:** Windows may show a SmartScreen warning because the app isn't code-signed yet. Click **\"More info\"** → **\"Run anyway\"** to proceed. The source code is fully open for inspection.\n\n### Build from Source\n\nRequires [Node.js](https://nodejs.org/) 18+.\n\n```bash\ngit clone https://github.com/jamesccupps/NoteForge.git\ncd NoteForge\nnpm install\nnpm run build:jsx\nnpm test      # runs the full test suite\nnpm start\n```\n\nTo build the installer locally (or use `Build.bat` on Windows):\n\n```bash\nnpm run dist\n```\n\nOutput goes to `dist/`. The `predist` hook runs tests before building — if any test fails, the build aborts.\n\n## Security\n\n### Encryption\n\n| Layer | Algorithm | Key Derivation |\n|---|---|---|\n| Master (file-level) | AES-256-GCM | scrypt N=65536, r=8, p=1 |\n| Notebook locks | AES-256-GCM | scrypt N=65536, r=8, p=1 |\n\n- Master password is **never stored** — only the derived key (Buffer) lives in memory during the session\n- Session key is zeroed (`Buffer.fill(0)`) on lock, close, and idle timeout\n- Locked notebook sections are **stripped from disk on every write** via `sanitizeForDiskSync()` — plaintext never reaches the data file\n- **Per-notebook** rate limiting with exponential backoff on failed password attempts (persisted across restarts)\n- KDF-downgrade protection: rejects any blob with weakened N/r/p parameters\n- Password strength enforcement: 10+ chars, 3/4 character classes, dictionary check against 160+ common passwords, low-entropy rejection\n\n### Content Security Policy (Renderer)\n\n```\ndefault-src 'none';\nscript-src 'self';\nstyle-src 'self' 'unsafe-inline';\nfont-src 'self';\nimg-src 'self' data:;\nconnect-src 'none';\n```\n\nAll scripts and fonts loaded from local `lib/` directory. Zero CDN dependencies at runtime. `connect-src 'none'` blocks any outbound fetch/XHR from the renderer process, even if code is injected.\n\n**Note:** The auto-updater runs in the main process (not governed by the renderer's CSP) and makes a single HTTPS request to GitHub Releases on launch to check for new versions. This can be disabled in File → Settings.\n\n### Additional Hardening\n\n- **Sandboxed renderer** (`sandbox: true`) — Chromium OS-level sandbox, on by default\n- Navigation guards block all non-`file://` navigation\n- `contextIsolation: true`, `nodeIntegration: false`\n- All permissions denied (`setPermissionRequestHandler`)\n- DevTools disabled in production builds\n- **DOMPurify input-type hook** — blocks in-note phishing by forcing all non-checkbox `\u003cinput\u003e` elements to lose their type attribute\n- Links inserted into notes get `target=\"_blank\" rel=\"noopener noreferrer\"` automatically\n- Export dialogs warn about unencrypted output\n- Print dialogs warn for password-protected notebooks\n- Config keys allowlisted — renderer can only write known settings\n- CI actions pinned to commit SHAs to prevent supply-chain attacks\n\n### Disabling sandbox (fallback)\n\nIf `sandbox: true` causes issues on a specific system (rare), close NoteForge and edit `noteforge-config.json` in the data folder:\n\n```json\n{ \"autoUpdate\": true, \"sandbox\": false }\n```\n\nThen restart. No rebuild required.\n\n## Development\n\n### File Structure\n\n```\nNoteForge/\n├── .github/workflows/build.yml  # CI: auto-build on tag push\n├── app.jsx           # React source (edit this)\n├── app.js            # Compiled output (generated)\n├── main.js           # Electron main process + crypto\n├── preload.js        # IPC bridge (contextBridge)\n├── index.html        # Shell with CSP\n├── styles.css        # All styling + @font-face\n├── package.json      # Scripts + electron-builder config\n├── lib/              # Bundled dependencies (React, DOMPurify, fonts)\n├── assets/           # Icons\n├── test/             # Test harness — crypto, XSS, install, input hook\n├── Build.bat         # Windows build helper\n├── NoteForge.bat     # Windows dev launcher\n├── LICENSE\n└── README.md\n```\n\n### Workflow\n\n1. Edit `app.jsx` (React/JSX source)\n2. Compile: `npm run build:jsx`\n3. Test: `npm test`\n4. Run: `npm start`\n5. Build installer: `npm run dist`\n\n### Data Location\n\n| OS | Path |\n|---|---|\n| Windows | `%APPDATA%\\noteforge\\` |\n| macOS | `~/Library/Application Support/noteforge/` |\n| Linux | `~/.config/noteforge/` |\n\nFiles: `noteforge-data.json` (unencrypted) or `noteforge-data.enc` (encrypted), `window-state.json`, `ratelimit.json`, `noteforge-hint.txt`, `noteforge-config.json`, `noteforge-data.enc.pre-restore.bak` (after a restore, for rollback)\n\n## Keyboard Shortcuts\n\nPress **F1** in the app for an interactive cheat sheet.\n\n| Shortcut | Action |\n|---|---|\n| Ctrl+N | New Page |\n| Ctrl+Shift+N | New Notebook |\n| Ctrl+B / I / U | Bold / Italic / Underline |\n| Ctrl+D | Duplicate Page |\n| Ctrl+F | Find \u0026 Replace |\n| Ctrl+L | Lock App |\n| Ctrl+Z / Ctrl+Y | Undo / Redo |\n| Ctrl+= / Ctrl+- | Zoom In / Out |\n| Ctrl+\\\\ | Toggle Sidebar |\n| Ctrl+Shift+D | Toggle Theme |\n| Ctrl+P | Print |\n| Ctrl+Shift+E | Export HTML |\n| F1 | Keyboard Shortcuts |\n\n## License\n\n[MIT](LICENSE) — James Cupps\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesccupps%2Fnoteforge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesccupps%2Fnoteforge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesccupps%2Fnoteforge/lists"}