- Extract shared esc() utility (public/utils/html.js) replacing 8
duplicate escHtml() functions across all page modules
- Apply HTML escaping to all user-controlled data in innerHTML
templates: titles, names, locations, descriptions, colors, notes
content, weather data, autocomplete suggestions
- Remove user-scalable=no and maximum-scale=1 from viewport meta
tag, restoring pinch-to-zoom for WCAG 1.4.4 compliance
- Bump version to 0.7.1
App refuses to start without SESSION_SECRET regardless of NODE_ENV.
Removes risk of accidental insecure deployment when NODE_ENV is not
explicitly set to production.
- Translate German error/warn messages in auth.js to English
- Add CODE_OF_CONDUCT.md (Contributor Covenant v2.1)
- Remove docs/claude-md-migration.md (internal migration artifact)
- Clarify README first-login instruction with credential hint
Convert all server/, test, and setup files from require()/module.exports
to import/export syntax. Activate ESM globally via "type": "module" in
package.json and load dotenv via --import dotenv/config in npm scripts.
Add server/logger.js - zero-dependency, level-based logger that outputs
JSON in production and human-readable format in development. Controlled
via LOG_LEVEL env var (debug/info/warn/error, default: info).
Replaces all 100 console.log/warn/error calls in 14 server files.
Instead of a static 'dev-only-secret-not-for-production' fallback,
generate a random one-time secret in development. Warns that sessions
won't survive restarts. Production guard unchanged.
Resolves all 5 npm audit vulnerabilities (4 high in tar via
node-pre-gyp, 1 high path-to-regexp ReDoS). bcrypt 6 replaces
node-pre-gyp with prebuildify, removing 46 transitive packages.
The `date` import from validate.js shadowed the `date` field from
req.body, so SQLite received a function reference instead of a string
when updating a budget entry - causing a TypeError.
Fix by aliasing the import to `validateDate` and adding `date` to
the req.body destructuring.
Closes#8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix stored XSS in tasks (titles/subtasks) and settings (member list)
by applying escHtml(). Harden trust proxy to loopback default, add
OAuth state parameter for Google Calendar CSRF protection, sanitize
CSV export against formula injection, invalidate sessions on user
deletion, restrict usernames to alphanumeric chars, and require admin
role for calendar sync triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all — with - in all source files (JS, CSS, HTML, JSON,
Markdown) for consistency and readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Translate BACKLOG.md and docs/SPEC.md from German to English for
international contributor accessibility. Fix outdated CONTRIBUTING.md
statement that claimed UI text is German-only — app now supports
multiple locales via i18n.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add complete Italian translation (497 keys) based on PR #7 by
@albanobattistella. Fixed filename from "it. json" to "it.json" and
registered Italian in SUPPORTED_LOCALES and the locale picker component.
Co-Authored-By: albanobattistella <34811668+albanobattistella@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root causes:
1. parseRRule did not strip the "RRULE:" prefix stored by the ICS parser,
causing all recurrence rules from CalDAV sync to silently fail parsing
2. YEARLY frequency (used by birthday events) was not supported
3. expandRecurringEvents filtered instances only by start date, missing
multi-day events that start before the view window but span into it
4. All-day recurring instances got datetime end values instead of date-only
Fixes#5 (follow-up from @tschig)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All-day events showed on the correct day plus the next day because ICS
DTEND for VALUE=DATE is exclusive (RFC 5545) but was treated as inclusive.
Multi-day events using DURATION instead of DTEND were missing entirely.
Birthday calendars were explicitly filtered out during Apple Calendar sync.
Closes#5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Apple Calendar sync hardcoded created_by=1 which fails when no user
with ID 1 exists, causing every single event import to fail silently.
Now dynamically resolves the first available user. Also syncs all
calendars instead of only the first one, adds the missing cfgDel helper,
and gracefully skips unreachable calendars.
Fixes#4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The hex-encoded encryption key (x'...') is not valid as a bare PRAGMA
value in better-sqlite3. Wrapping it in double quotes produces valid
SQLCipher PRAGMA syntax.
Fixes#3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix SQLCipher PRAGMA key interpolation (hex-encode key to prevent crash on single quotes)
- Enforce min password length (8 chars) on admin user creation
- Add length bounds on username/display_name and login inputs
- Invalidate other sessions on password change
- Multi-stage Docker build (exclude build tools from runtime)
- Exclude docs/ from Docker image
- Consolidate dotenv.config() to single entry point
- Document flat family authorization model in SECURITY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Builds and pushes to ghcr.io/ulsklyc/oikos on every push to main
and on version tags. Tags: branch name, semver, short SHA.
Uses Docker layer caching via GitHub Actions cache.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prepared oikos.yml and issue template for awesome-selfhosted-data.
Submission blocked until first release (v0.1.0, 2026-03-30) is 4 months old (earliest: 2026-07-30).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Static bilingual (EN/DE) landing page with dark/light theme support,
responsive design, scroll animations, theme-aware screenshot gallery,
and Open Graph images for social sharing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node 24 is not yet LTS and native dependencies (bcrypt, better-sqlite3,
sharp) fail to compile on it, causing CI failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New docs/installation.md covers the full setup journey for Docker
beginners: prerequisites, step-by-step install, .env reference,
Nginx/HTTPS, updates, backup/restore, and troubleshooting.
README Quick Start updated to include clone + .env steps and
links to the detailed guide.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Separate docker compose and setup steps for clarity, remove redundant
horizontal rules, split License into its own section per style guide.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace verbose README with a streamlined structure inspired by
Immich/Mealie/LobeChat. Focus on scanability, mobile-first screenshots,
and clear communication of architectural decisions (zero-dependency
frontend, privacy-first, PWA).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audit identifies redundancies with CONTRIBUTING.md, missing paths
(utils/ux.js, locales/, offline.html), incorrect info (node:20 vs 22,
non-existent public/assets/), and informational sections that don't
steer behavior. Proposed version reduces to 82 lines with clear hard
constraints block and reference document table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix [Unreleased] compare link (v0.5.1→v0.5.2), add missing [0.5.2]
compare link, remove phantom social-preview.html from .dockerignore.
Add full repo audit document.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .nvmrc (22) for nvm/fnm users
- README: add API section pointing to SPEC.md and server/routes/
- README: add Roadmap section linking to BACKLOG.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete root social-preview.png (identical copy of docs/social-preview.png,
only the docs/ version is referenced in README)
- Consolidate BACKLOG.md: all 10 entries were completed, compress into a
reference table and clear the active section for future use
- Delete prompt-i18n.md (untracked, i18n fully shipped in v0.5.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dockerfile used node:20-slim but the project requires Node >=22
(--experimental-sqlite in tests, CI matrix). package.json had a
duplicate engines block where the second (>=20.0.0) silently
overwrote the correct first one (>=22.0.0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cookies were sent without Secure flag outside of production (NODE_ENV check).
New logic: secure=true by default; set SESSION_SECURE=false in .env to
allow HTTP explicitly (local dev without reverse proxy). Affects session
cookie, CSRF cookie in login handler, and CSRF middleware.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rate-limit SPA fallback route (missing rate limiting on fs access)
- Add csrfMiddleware to all state-changing auth routes (logout, create
user, change password, delete user) — previously bypassed global CSRF
middleware due to router registration order
- Fix incomplete vCard escaping: escape backslashes before other special
characters to prevent injection via contact fields
- Restrict CI GITHUB_TOKEN to contents: read (least privilege)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Internal Claude Code working documents (plans, specs) are not relevant
for contributors. Remove tracked files and add to .gitignore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add social-preview.png to version control (referenced in README but untracked)
- Update README: test count 146+ → 162 across 9 suites
- Add engines.node >=22.0.0 to package.json (required for --experimental-sqlite)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files that kept their original names after content replacement were served
from GitHub's CDN cache. Rename with -2 suffix to force fresh delivery.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>