Security headers, cross-site write refusal, atomic JSON writes, admin backup download, /healthz and Docker health check, OnlyOffice requires a JWT secret, npm test, CI audit, arm64 hub image. New Links tab for personal bookmarks, saved with per-person settings.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Home Now Playing block is now the same live card as the Music tab.
The player sends Plex timeline updates (playing/paused/stopped, position every 10 s).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- full-screen lyrics view that follows the song: highlighted current line with a colour sweep (word by word when the source has word timing), tap a line to jump, text size, timing nudge, 'Wrong lyrics?' search
- Karaoke mode: the current line large and centered
- lyrics from Plex first, then the bundled Lyrica service (LRCLIB, Musixmatch via Lrcmux, NetEase, Megalobiz); YouTube captions never used; wrong-length recordings refused; answers kept in /data/lyrics
- lyrica/: Lyrica pinned to one commit, built by the workflow as hub-lyrica; compose service sharing the hub's network
- tools/lyrics-test.js
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- read every JSON value in a frame (lines, arrays), binary frames too
- a person seen by number and by private id is one conversation (merged)
- delivery/read receipts match on message time, not on who sent them
- reconnect every 1-5 s; the chat list says when the link was down
- /api/signal/debug: kinds of messages seen lately (no text)
- edit a contact (kept in the app and sent to Signal), clear messages, remove a chat; right-click menu in the list; name for a new number
- tests: 54 checks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Music: a card with art, seek bar and controls above the albums, artists and playlists
- Signal: chime, optional desktop notification when the app is in the background, (n) in the tab title, app badge; Settings -> Signal alerts (per device)
- README, player test stand-in updated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- signal.js: talks to signal-cli-rest-api (a linked device of your Signal account); keeps conversations in /data/signal; live updates; send/receive text, files, replies, reactions, groups, receipts
- Signal tab and a floating chat panel on every screen, shown only on desktop-class windows; new Signal permission per person
- compose: signal bridge container sharing the hub's network
- tools/signal-test.js (fake bridge), README
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- cypht/: official Cypht 2.13.0 on a SQLite file, served under /webmail/; workflow builds git.necloud.us/nate/hub-cypht
- the hub passes /webmail/ through behind the passkey sign-in and signs in to Cypht for you (CYPHT_PASSWORD)
- provider sign-in pages (Google, Microsoft) open in the whole window and return to the Mail tab; other links open in a new tab
- Gmail / Outlook.com / Office365 OAuth settings (Microsoft permissions patched to the current ones)
- Roundcube removed; README, compose and webmail tests updated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- roundcube/: official Roundcube plus hub.php settings
- workflow builds and pushes git.necloud.us/nate/hub-roundcube, and runs the player and webmail tests
- compose uses the image instead of an inline config
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- docker-compose: roundcube service with inline config (Gmail/iCloud/Yahoo presets, framing allowed, persistent key); only reachable through the hub
- hub passes /webmail/ through to Roundcube behind the passkey sign-in; Microsoft's sign-in page opens in the whole window and returns to the Mail tab
- M365_* settings add 'Login with Microsoft 365' (README has the Entra app registration steps)
- Side menu collapses to icons or hides completely (remembered per device, Ctrl/Cmd+B)
- tools/webmail-test.js
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Mail page redesigned as one card per account (inbox + compose)
- Settings -> Mail apps: tick the apps you use; saved on the server per person
- WEBMAIL_URL shows your own webmail (e.g. Roundcube) inside the Mail tab; provider shortcuts one tap away
- README: Roundcube setup and framing headers
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Files: multi-select, batch move/copy/delete, rename checks, sort, filter, shortcuts, drag in (files and folders) to upload with progress and retries, drag to move, drag a file out to download
- Mini player: previous, seek bar with buffered range, time, shuffle/repeat, buffering spinner, swipe for next/previous, close
- Back/Forward bar that knows what history is possible
- Audio tab is now Audiobooks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Seafile token is saved on the server (encrypted) so Settings shows the real signed-in state everywhere
- Player: whole-track cache for current and next two, stall recovery, instant track changes, shuffle/repeat, Plex play counts, transcode fallback, Plex request timeout
- Audio tab: toolbar, full-height kept-alive frame; /api/audiobooks/enter adds the secret login link on the server
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Accounts: the setup code (or an existing single-user install, migrated automatically) creates the admin;
the admin adds people in Settings -> People and gives each a one-time invite code to enrol their own passkey
- Each person's tasks, NE Hub events and subscribe link, settings, calendar links, iCloud connection and Plex
sign-in are private, stored in their own folder (/data/users/<id>/); Plex sign-in moved from browser cookies
into that folder (encrypted), so it also follows them across devices
- Tab access is enforced by the server (403) and reflected in the app (nav, Home, Settings, search, /api/config
hides addresses of tabs a person cannot use); browser storage is namespaced per person
- Admin tools: change tabs, invite a device, sign out everywhere (session epoch), turn off, make/remove admin,
delete a person and their data; the last admin cannot be removed
- Sessions carry the person (uid.epoch.expiry.signature); old single-user cookies still work as the admin
- tools/users-test.js (83 checks: isolation, access limits, invites, sign-out/disable/delete, Plex and iCloud
per person, migration) now runs in the Gitea workflow before the image is built
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Preferences lived only in the browser, so clearing it, using another device or the separate iOS
home-screen storage lost them. They are now saved in /data/settings.json (the audiobooks sign-in
encrypted), loaded at start-up, and existing browser-only values are uploaded once. A failed save
is reported instead of silent.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- iOS ignores SVG touch icons: add 120/152/167/180 apple-touch-icons, 192/512 and maskable manifest icons,
and 25 splash screens (every iPhone size, iPad portrait+landscape incl. 12.9")
- Icons and splash screens are public so iOS can fetch them without a session
- Manifest: id, scope, categories, shortcuts; iOS standalone polish (no callout, no overscroll)
- Sessions last SESSION_DAYS (default 180) and renew daily while in use
- tools/make-icons.py regenerates the artwork
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Lists, quick add, due dates, notes, complete/reopen, delete; Home card for overdue/due today
- iCloud VTODO lists read and written over CalDAV (If-Match, edits patch the latest copy so
priority/alarms survive); NE Hub lists work regardless
- Fix: text inside a nested VALARM was read as the task's / event's own description and then
written back on edit; nested components are now skipped (regression tests added)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Add, edit and delete events; month/agenda views open them on tap
- NE Hub calendar stored on the server and published as a private ICS subscribe link
- iCloud CalDAV read/write with an app-specific password (encrypted at rest,
only *.icloud.com contacted, event addresses checked against the calendar)
- Edits patch the existing entry so alarms/attendees survive; writes use If-Match / If-None-Match
- Time zone definitions (VTIMEZONE) generated so other apps read times correctly
- Podcast tab (PODCAST_URL) alongside Podcast Admin
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Calendar agenda from private ICS feeds (recurrence, EXDATE, overrides, time zones, SSRF guard)
- Search across libraries, files (recursive fallback) and Plex
- Home dashboard: now playing, today's agenda, recent files
- Files: rename, move, share link, preview, create new documents
- Music: recently added, play next / add to queue, queue sheet, resume on launch
- Phone tab bar shows four tabs plus a More menu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Mobile-first PWA (phone tab bar with More menu, iPad/desktop rail)
- Passkey (WebAuthn) sign-in gates the whole app; first passkey via setup code
- Files: Seafile browse/upload/new doc/rename/move/share link/preview/delete
- In-app OnlyOffice editing with JWT-signed configs and save-back to Seafile
- Plex music player via plex.tv sign-in and a server-side proxy
- Calendar agenda from private ICS links (recurrence + time zones)
- Search, Home dashboard, webmail deep links, audiobooks frame
- Dockerfile, compose, and a Gitea Actions workflow that builds the image
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>