mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 12:06:57 +02:00
51dd844f962813da3bb746967c7c1da75b056818
Groundwork for issue #77 (admin cannot reset other users' passwords). Two pure, colocated-tested modules shared by the API route and the settings UI that follow. password.ts — generatePassword() over a charset pinned at exactly 64 characters. 64 is a power of two, so nanoid's customAlphabet takes its exactly-uniform masking path (256 % 64 === 0) rather than the rejection branch; 120 bits of entropy at the default length of 20. Visually ambiguous characters (0 O o 1 l I) are excluded so a password survives being read aloud or retyped. Class coverage is guaranteed by discarding and regenerating a whole password that is missing a group, which stays uniform over exactly the set of valid passwords — rather than forcing characters into positions and shuffling, which would need its own unbiased index source. auth-methods.ts — canResetPassword() and passwordResetBlockedReason(), the single source of truth for eligibility so the endpoint and the component cannot drift. Password login is gated on authProvider (verifyPassword matches only rows where it equals 'password'), so writing a hash for any other provider stores a credential that can never be used to sign in. The reason lookup is a Map, not an object literal: an object inherits Object.prototype, so a lookup for 'toString' or '__proto__' resolves to an inherited member and the nullish fallback never fires — returning a function where the signature promises string | null. A Map has no such keys, so totality holds by construction instead of by a guard a later refactor could drop. DoD items verified: - [x] 1. generatePassword() returns 20 chars; explicit length honoured - [x] 2. always >=1 lowercase, uppercase, digit and symbol - [x] 3. never contains 0 O o 1 l I - [x] 4. randomness from nanoid customAlphabet; no Math.random - [x] 5. throws for length < 8 (the server minimum) - [x] 6. retry bounded at 100 with an unconditional throw (inspection) - [x] 7. canResetPassword true only for 'password' - [x] 8. totality: every blocked input yields a non-empty string - [x] 9. exact reason copy per provider; null when allowed - [x] 10. colocated tests pass Verified independently by the orchestrator: pnpm check 0 errors / 14 pre-existing warnings; pnpm test:unit 306 passed / 26 files (from 288 / 24); coverage 45.52 lines, 45.52 statements, 54.01 functions, 84.87 branches — every metric above both its .coverage-thresholds.json floor and the previous baseline, new files at 100%. Reviewed-by: adversarial-review (PASS, second reviewer; first found a prototype-chain totality violation, since fixed and re-reviewed fresh) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RbqJcFofZSbhbGwUwZP3wU
Crumbs by Bretzel
A self-hostable, offline-first note-taking app that respects your privacy. Part of the Bretzel app universe.
Install
Docker (Recommended)
docker compose up -d
Open http://localhost:3000 and create your admin account on first visit.
With SSO (optional)
# docker-compose.yml
services:
crumbs:
image: ghcr.io/bretzel-app/crumbs:latest
ports:
- "3000:3000"
volumes:
- crumbs-data:/data
environment:
- ORIGIN=https://notes.example.com
- AUTH_OIDC_ISSUER=https://authentik.example.com/application/o/crumbs/
- AUTH_OIDC_CLIENT_ID=your-client-id
- AUTH_OIDC_CLIENT_SECRET=your-client-secret
- AUTH_OIDC_DISPLAY_NAME=Authentik
restart: unless-stopped
volumes:
crumbs-data:
Google and GitHub OAuth are also supported. See docs/AUTH.md for all providers and setup guides.
With Email Notifications (optional)
Add SMTP settings to enable email notifications (share alerts, welcome emails, security alerts):
environment:
- SMTP_HOST=smtp.example.com
- SMTP_PORT=587
- SMTP_USER=your-username
- SMTP_PASS=your-password
- SMTP_FROM=Crumbs <noreply@example.com>
Auto-enabled when SMTP_HOST is set. Users can opt out in Settings > Preferences. See docs/DEPLOYMENT.md for details.
Manual (Node.js)
git clone <repo-url> crumbs && cd crumbs
pnpm install
pnpm build
DATABASE_URL=./data/crumbs.db ORIGIN=http://localhost:3000 node build
Development
pnpm install
pnpm dev
Run make help to see all available commands, or use pnpm directly:
pnpm test # Unit + E2E tests
pnpm check # Type checking
pnpm build # Production build
Features
- Rich notes with Markdown, checklists, image attachments, and 12 color themes
- Organize with #tags, pinning, archive, and trash
- Full-text search across titles, content, and tags
- Note version history — browse and restore previous versions
- Share notes with other users on the same instance
- Email notifications for shares, security alerts, and account events (optional SMTP)
- PWA — installable, works offline via IndexedDB + LWW CRDT sync
- MCP server — let AI assistants (Claude Code, etc.) manage your notes
- Multi-user auth (Argon2) with optional OAuth/SSO (Google, GitHub, OIDC)
- Docker deployment with a single command
See docs/FEATURES.md for detailed feature documentation.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | SvelteKit 2 (Svelte 5 runes) |
| Language | TypeScript (strict) |
| UI | Tailwind CSS 4 |
| Database | SQLite (better-sqlite3) + Drizzle ORM |
| Client DB | IndexedDB (idb) |
| Sync | LWW CRDTs |
| Auth | Argon2 + session cookies |
| Testing | Vitest + Playwright |
| Container | Docker (multi-stage) |
| CI/CD | GitHub Actions |
Documentation
| Document | Description |
|---|---|
| Features | Detailed feature list and behavior |
| Architecture | Local-first sync design, tech rationale, DB schema |
| Auth | Password auth, OAuth/SSO setup (Authentik, Keycloak, etc.) |
| Deployment | Docker, Node.js, reverse proxy, backups, env vars |
| API | REST API reference (auto-generated) |
| Contributing | How to contribute |
| Security | Vulnerability reporting policy |
| Changelog | Release history |
CI/CD
- CI — lint, type check, unit tests, E2E tests, Docker build on every push/PR
- Release — builds and pushes Docker image on
v*tags
Configure registry via GitHub Secrets: REGISTRY_URL, REGISTRY_USER, REGISTRY_TOKEN.
License
Languages
TypeScript
68.5%
Svelte
25.1%
HTML
2.7%
CSS
2.7%
Shell
0.5%
Other
0.5%
