Files

87 lines
4.1 KiB
Markdown

# Changelog
All notable changes to librenotes are recorded here. The format
loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and the project follows [Semantic Versioning](https://semver.org/).
## [Unreleased]
## [0.1.0] — 2026-04-29
The initial public release: a fork of Notesium turned into a
multi-tenant SaaS, plus the surrounding infrastructure.
### Added
- **Fork.** Forked [alonswartz/notesium](https://github.com/alonswartz/notesium)
at `aff9f460c2d864112db7f0935b4168b107289d91`, restructured into
the standard Go layout (`cmd/`, `internal/`), renamed the
module to `git.librete.ch/public/librenotes`, preserved the MIT
license alongside librenotes copyright, documented the upstream
remote and cherry-pick workflow.
- **User model + SQLite storage** (`internal/storage`). UUIDv4
IDs, email uniqueness, WAL mode, embedded migrations.
- **Magic-link authentication** (`internal/auth`). 32-byte
cryptographically random tokens, SHA-256-hashed at rest,
single-use, 15-minute expiry. HS256 JWT sessions with 24-hour
lifetime, `jwt.WithValidMethods` to reject `alg=none`. Pluggable
`Mailer` interface with `SMTPMailer` and dev-friendly
`LogMailer`. DB-backed per-email rate limiting.
- **Per-tenant filesystem isolation** (`internal/tenant`). Each
user gets a sandboxed directory; all reads and writes go
through `os.Root` so path traversal and symlink escapes are
rejected at the syscall layer.
- **Tenant-aware HTTP API** (`internal/httpapi`). JWT middleware,
tenant context, `/api/whoami`, `/api/notes` CRUD with
optimistic-locking conflict detection (`?base=<unix>`),
`/healthz`.
- **`librenotes serve`** command wires storage, auth, tenants,
notes, and a static frontend into one binary. Configuration
via `LIBRENOTES_*` env vars or flags. Background goroutine
purges expired magic tokens.
- **Frontend** (`cmd/librenotes/web/public/`): mobile-first
landing page, login + verify pages, app shell with sync
badge and conflict dialog. Vanilla JS, no build step.
- **JWT session client** with sessionStorage + automatic
`Authorization: Bearer` injection and 401-redirects-to-login.
- **Tenant-scoped localStorage** wrapper so two users on the
same browser have isolated UI state; cleared on logout.
- **Offline cache** in IndexedDB (`notes-cache.js`) with dirty
tracking and tombstones for deletes.
- **Background sync** (`sync.js`) — pushes dirty rows on
reconnect, pulls remote changes, surfaces conflicts to the UI
via custom events.
- **PWA support**: manifest, service worker (cache-first shell,
network-first API), 192/512/maskable icons, install prompt.
- **Responsive CSS** with explicit breakpoints from 320px to
2560px, including a 3-column ultrawide layout primitive.
- **Pointer events** on the resize handle so touch and pen work
identically to mouse, including pointer capture for drag.
- **Containerised deployment**: multi-stage Dockerfile producing
a distroless `nonroot` image, dev + prod Compose stacks,
in-binary `librenotes healthcheck` for distroless HEALTHCHECK.
- **CI**: Gitea Actions workflow runs `make lint`, `make build`,
`make test` on every push and PR.
- **Deploy workflow** (gated on `DEPLOY_ENABLED=true`) builds
and pushes images, SSHes to the host, runs `compose pull && up
-d`, polls `/healthz`.
- **Backup tooling** (`scripts/`): SQLite online snapshot + tar
of notes, optional rclone off-site copy, retention pruning,
weekly automated restore-test, systemd timer units.
- **Documentation**: user guide, self-hosting, API reference,
operations, contributing.
- **Community infrastructure**: bug-report and feature-request
Gitea issue templates, PR template, Code of Conduct.
- **First-run onboarding**: welcome dialog + seeded sample note,
dismissal persisted per tenant.
### Notes
- The bundled notes engine (the original Notesium UI under
`internal/notesium/web/app/`) is not yet wired into the
multi-tenant frontend. Phase 7 will replace or wrap it.
- SMTP delivery has been exercised against the LogMailer only;
real-provider integration is part of self-hosting validation.
[0.1.0]: https://git.librete.ch/public/librenotes/releases/tag/v0.1.0