v1.15.4 — A self-hosted, anonymous workload-logging PWA for NHS and healthcare teams. Built with TypeScript, Express 5, SQLite, and vanilla JS.
- Anonymous by design — usernames are auto-generated memorable word pairs. No real names, emails, or patient data stored.
- PWA — installable on mobile, works offline for cached assets.
- Task tracking — duty vs personal tasks, categories, subcategories, outcomes, interruption handling.
- Faster Log Task start flow — up to 9 frequency-ranked quick-pick buttons for Task From/Task Type with the most recent option pinned first using a green border, plus dark-blue highlighting only after selection, and 3 one-tap date-start actions (Previous Task date, Yesterday, Selected Date).
- Task flags — admin-managed list of structured task annotations (e.g. "Sent to wrong user", "Priority too high"). Users select any that apply; free-text notes removed for data protection. Users can suggest new flags via email.
- Analytics — session and 30-day history with Chart.js charts, filtering, flag distribution chart, linear regression trendlines, and XLSX analytics report download.
- Excel export — users can download their raw task data as
.xlsx(includes Flags column), or download a full analytics report as.xlsxwith one data sheet per chart. - User groups — administrators create groups that define which dropdown options users see.
- Personal option customisation — users can tick/untick individual options to build their own personalised dropdown lists.
- SMTP email suggestions — dropdown and flag suggestions are emailed to the administrator instead of being stored on the server, improving data security. Configure via admin panel or environment variables.
- Notices — administrators can post notices that appear on every user's home screen.
- User messages — administrators can send messages to individual users or broadcast to all users; messages appear on the user's home screen and are dismissable.
- Integrated combobox dropdowns — all task dropdowns are searchable comboboxes.
- Admin panel — user management, DB backup/restore, dropdown configuration, SMTP settings, notices management, task flag options, registration settings, group management, pending proposals. Desktop-optimised layout.
- Configurable registration — administrator controls three levels for self-registration and user invitations.
- 30-day data retention — task data is automatically deleted after 30 days.
- Health-check endpoint —
GET /readyzreturns a JSON status response for uptime/heartbeat monitoring. - Landing, SEO & crawler-ready homepage — homepage now includes semantic marketing copy, structured data, Open Graph/Twitter cards,
robots.txt,sitemap.xml, andllms.txt. - Asset version endpoint —
GET /api/versionreturns{"version":"1.15.4"}for client-side cache-busting. - Cloudflare Turnstile CAPTCHA — optional bot-protection for login and self-registration. When
TURNSTILE_SITE_KEYandTURNSTILE_SECRET_KEYare set, Turnstile widgets are rendered on the login and registration forms; tokens are verified server-side before credentials are checked. The feature is fully disabled (and invisible) when the environment variables are not set.
| Document | Location |
|---|---|
| Installation guide | docs/installation.md |
| Maintenance & troubleshooting | docs/maintenance.md |
| Search engine submission guide | docs/search-engine-submission.md |
| User guide (in-app) | /help route |
| Data & Use Policy (in-app) | /policy route |
# 1. Install dependencies
npm install
# 2. Build TypeScript
npm run build
# 3. Copy and configure environment
cp .env.example .env
# 4. Start the server
node dist/server.jsServer runs on port 3020 by default (set PORT in .env to override).
Then create the admin account — see Installation guide.
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3020 |
SESSION_SECRET |
Session signing secret (set this in production!) | Random (changes on restart) |
NODE_ENV |
Set to production to enable secure cookies (requires HTTPS) |
— |
APP_URL |
Full public base URL. Used for review links plus canonical, robots.txt, sitemap.xml, and llms.txt output. |
Request origin |
SSL_CERT_DIR |
Directory containing Let's Encrypt certificate files | /etc/letsencrypt/live/yourdomain |
SSL_CERT |
Path to the certificate chain (auto-detects HTTPS if this file exists) | $SSL_CERT_DIR/fullchain.pem |
SSL_KEY |
Path to the private key | $SSL_CERT_DIR/privkey.pem |
TURNSTILE_SITE_KEY |
Cloudflare Turnstile site key — enables CAPTCHA on login and registration when set | — |
TURNSTILE_SECRET_KEY |
Cloudflare Turnstile secret key — required alongside TURNSTILE_SITE_KEY |
— |
HTTPS is detected automatically. If both
SSL_CERTandSSL_KEYexist on disk the server starts in HTTPS mode. Otherwise it starts in plain HTTP mode.
⚠️ In production, always setSESSION_SECRETto a long random string and serve over HTTPS.
npm run dev # ts-node src/server.ts (no build step required)
npm run build # compile TypeScript → dist/src/
server.ts Express 5 app, middleware wiring, SSL detection, dynamic SEO shell rendering, /readyz health check, /api/version, robots/sitemap/llms, 30-day retention job
db.ts SQLite schema + migrations + seed data, getDb(), getSetting(), setSetting(), TASKER_DB_PATH override for tests
words.ts Memorable two-word username generator
turnstile.ts Cloudflare Turnstile CAPTCHA helpers — isTurnstileEnabled(), verifyTurnstileToken()
middleware/index.ts requireAuth, requireAdmin, CSRF, logEvent
routes/
auth.ts /api/auth/* — register, login, logout, change-password, me, account delete, invite, user-groups, set-group, propose-group, my-options, turnstile-config
tasks.ts /api/tasks/* — start, active, PATCH, GET; validates task start/end, assigned date, and interruption times are not future-dated
analytics.ts /api/analytics/* — session, history, export (xlsx)
dropdowns.ts /api/dropdowns/* — list, propose, admin CRUD
admin.ts /api/admin/* — stats, users, pending-users, approve, settings, backup, restore, user-groups, pending-groups
__tests__/
security.test.ts 241-test negative security suite (CSRF, IDOR, SQLi, XSS, input validation, resource exhaustion, error handling, session fixation, SMTP sanitisation, option allowlist)
helpers/testApp.ts Isolated Express app + test-user helpers for jest/supertest
public/
index.html SEO-aware SPA shell + static landing content template
favicon.svg SVG favicon (browser tab icon)
manifest.json PWA manifest
social-preview.svg Social preview asset for Open Graph / Twitter cards
sw.js Service worker (cache-first static, network-first API)
policy.html Data & Use Policy (served at /policy)
help.html User guide (served at /help)
css/app.css Mobile-first styles
js/app.js Complete SPA — views, Chart.js charts, regression trendlines, browser history navigation, asset version checking, integrated combobox dropdowns
icons/
icon-192.png PWA home-screen icon (192×192)
icon-512.png PWA splash / store icon (512×512)
docs/
installation.md Full installation guide (server, SSL, systemd, Nginx/Caddy)
maintenance.md Routine maintenance, backup/restore, troubleshooting
- CSRF tokens on all mutating requests (
X-CSRF-Tokenheader) - Rate limiting: auth 20/15 min, API 200/min (including
/readyzhealth-check) - bcrypt cost factor 12 for passwords
- Account lockout after repeated failed login attempts
- Friendly error messages on failed login (username/password invalid)
- HTTP-only, SameSite=strict session cookies
- 30-minute idle session timeout with a 5-minute inactivity warning + midnight session expiry
- Session ID regeneration on successful login and after 2FA verification (prevents session fixation)
- Helmet.js security headers
- Parameterised SQL queries throughout; explicit allowlist for any dynamic column names
- Path validation on DB restore endpoint
- Automatic HTTPS when certificates are present
/readyzhealth-check endpoint is unauthenticated but rate-limited and returns no sensitive datasafeId()helper sanitises combobox container IDs before insertion into inline event handlers- SMTP error details logged server-side only; clients receive generic safe messages
- Constant-time comparison for MFA code verification (prevents timing attacks)
- Unapproved dropdown options are rejected when users update their personal option list
- Global error handler prevents accidental stack-trace leakage on unexpected errors
- 241-test negative security suite covering CSRF, IDOR, SQL injection, XSS, input validation, resource exhaustion, error handling, session fixation, SMTP sanitisation, and option validation (
npm test) - Cloudflare Turnstile CAPTCHA — optional bot-protection on login and self-registration; server-side token verification via
src/turnstile.ts; disabled and invisible when env vars are not set
See /policy for the full Data and Use Policy.
- Pending-task graph refresh — logging a new pending-task count from the user's homepage now redraws the pending-task graph with the newest snapshot instead of leaving the previous chart instance visible.
- Homepage metric label — renamed "Tasks logged" to "Recent tasks logged" on the main site homepage and user-facing homepage stat labels for clearer wording.
- Version bump — incremented the bugfix release to 1.15.4 in package metadata and runtime version surfaces.
- Documentation version sync — updated user-facing pages and manuals so current-version labels consistently show 1.15.4.
- Version bump — incremented the minor release to 1.15.3 in package metadata and runtime version surfaces.
- SEO-first homepage rendering — kept the public homepage content in static HTML and limited JavaScript to progressively enhancing the app launch controls.
- Landing page cleanup — simplified homepage calls to action and launch controls while preserving login, signup, and Turnstile flows.
- Task submission validation — task start, end, assigned date, and interruption date/time fields now reject future values in the browser and at the API layer.
- Route helper documentation — repository documentation updated to reflect the new task temporal validation functions.
- Version bump — incremented the bugfix release to 1.14.3 in package metadata and runtime version surfaces.
- Version bump — incremented the bugfix release to 1.14.2 in package metadata and runtime version surfaces.
- Documentation version sync — updated user-facing pages and manuals so current-version labels consistently show 1.14.2.
- Feature documentation correction — updated help/guide review-screen guidance to reflect the live product: structured task flags are supported and free-text notes are not.
- Homepage repositioning — rewrote the public landing experience to emphasise Tasker’s real differentiators: self-hosting, anonymous usernames, privacy-first workload evidence, interruption tracking, analytics, and export-ready reporting for healthcare teams.
- Search, AI crawler, and social readiness — added semantic homepage content, JSON-LD structured data, Open Graph/Twitter metadata,
robots.txt,sitemap.xml,llms.txt, and a dedicated social preview asset. - Documentation update — expanded repository documentation with a search-engine submission guide and refreshed versioning to 1.14.0 across the current docs set.
- Clicked-only blue highlights — on the Log Task screen, Task From and Task Type quick-pick buttons now turn dark blue only after the user actively selects them.
- Recent option border retained — the most recently used quick-pick still stays pinned first with the existing green border cue.
- Version bump — Version number incremented to 1.13.8; documentation updated to reflect the revised Log Task button behaviour.
- Date assigned quick-start actions — the Log Task screen now provides three horizontally aligned start buttons: Prev (dd/mm) (green) to use the most recent task's assigned date, Yesterday (yellow), and Selected Date (blue) for the date currently shown in the picker.
- Task From / Task Type quick picks expanded — quick-pick rows now show up to 9 options instead of 6, ordered by frequency with the most recently used option pinned first.
- Persistent quick-pick highlighting — when a user taps a quick-pick button (or selects the same value in the combobox), the corresponding quick-pick stays highlighted to confirm the active selection.
- Version bump — Version number incremented to 1.13.7; documentation updated to reflect the new Log Task behavior.
- Bug fix: Event listeners not properly removed —
stopActivityTracking()was removing event listeners without the{ passive: true }option, whilestartActivityTracking()added them with this option. According to browser specs,removeEventListenermust be called with the exact same parameters asaddEventListener, including the options object. This mismatch caused the listeners to never be properly removed, potentially causing listener accumulation and interfering with proper inactivity tracking. UpdatedstopActivityTracking()to include{ passive: true }when removing event listeners. - Version bump — Version number incremented to 1.13.2; all page footers and documentation updated accordingly.
- Version bump — Version number incremented to 1.13.1; all page footers and documentation updated accordingly. Timeout functions (
isTurnstileEnabled,verifyTurnstileToken) are already documented in technical manual §10a.
- Cloudflare Turnstile CAPTCHA — optional bot-protection added to the login and self-registration forms. When
TURNSTILE_SITE_KEYandTURNSTILE_SECRET_KEYenvironment variables are set, a Turnstile challenge widget is rendered on both forms. The token submitted by the browser is verified server-side (viasrc/turnstile.ts) before credentials are checked; an invalid or missing token returns403 CAPTCHA verification failed. When the environment variables are not set, Turnstile is completely disabled — no widget is displayed and no network call is made. The Cloudflare Turnstile script (https://challenges.cloudflare.com) is now permitted in the Content-Security-Policy (scriptSrc,frameSrc,connectSrc). A newGET /api/auth/turnstile-configendpoint returns{ enabled, siteKey }for the client to decide whether to render the widget. - Version bump — Version number incremented to 1.13.0; all page footers and documentation updated accordingly.
- Technical manual (§12.6) — Section 12.6 "Session Inactivity Tracking" rewritten to accurately describe the inactivity system as overhauled in v1.12.3: documents that the clock advances only on real user interaction (no passive background
activityInterval), the single consolidatedvisibilitychangeasync handler (hide path no longer stamps the clock; show path callscheckClientInactivity()immediately before proceeding to interruption checks), and thewindow.focusbelt-and-suspenders listener. - Version bump — Version number incremented to 1.12.4; all page footers and documentation updated accordingly.
- Bug fix:
activityIntervalsilently reset the inactivity clock every 60 s —startActivityTracking()was callingsetInterval(updateLastActive, 60000).updateLastActive()writesDate.now()to localStorage, so the "last active" timestamp was unconditionally refreshed every minute regardless of user activity. This meant the 5-minute warning and 30-minute logout thresholds could almost never accumulate; whether they fired at all was a timing race between the interval andinactivityCheckInterval, explaining the intermittent/non-deterministic behaviour. Removed theactivityIntervalentirely. The clock is now only advanced by real user interaction (theACTIVITY_EVENTSlisteners) andinactivityCheckInterval(every 60 s) reliably reads the true elapsed idle time. - Bug fix: tab-hide path reset the inactivity clock — The async
visibilitychangehandler calledupdateLastActive()whendocument.hiddenbecametrue(i.e. the user switched away from the tab). This stamped "the moment you left" as the last-active time. Consequence: idle 20 min, switch tab — clock reset; return 5 min later — system sees 5 min idle, not 25 min; warning/logout never fires. Removed theupdateLastActive()call from the hidden path entirely; the clock only moves on user interaction. - Bug fix: two competing
visibilitychangehandlers — A simple synchronous handler (line 443) and a complex async handler (line 4169) both responded to the same event. The simple handler correctly calledcheckClientInactivity()to show the banner; the async handler then calledupdateLastActive()which immediately dismissed it. Merged into a single canonical async handler: the duplicate simple handler has been removed andcheckClientInactivity()(plus a_sessionExpiryInProgressguard) is now called at the top of the async handler. - Bug fix:
updateLastActive()dismissed the banner it had just shown — Inside the interruption-check block for non-admin active-task users,updateLastActive()was called aftercheckInactivityInterruption().updateLastActive()callsdismissInactivityWarning(), so any banner shown bycheckClientInactivity()a few lines earlier was immediately removed. RemovedupdateLastActive()from this return-to-app path; the clock is reset naturally when the user next interacts viaACTIVITY_EVENTS. - Net behaviour — 30 min of uninterrupted idleness now reliably auto-redirects to the login screen; 5 min of idleness reliably shows the "App last used" ticker; any interaction dismisses the ticker and resets the clock; returning to the tab without interacting leaves the ticker visible until the user touches something.
- Version bump — Version number incremented to 1.12.3; all page footers and documentation updated accordingly.
- Bug fix: banner persists on refocus —
checkClientInactivity()had noelsebranch, so when the user refocused the app (viawindow.focusorvisibilitychange) and the elapsed time had fallen back below the 5-minute warning threshold, the "App last used" strip was never removed. Added anelse { dismissInactivityWarning(); }branch so that refocusing always dismisses the banner when the session is still within the warning window. - Version bump — Version number incremented to 1.12.2; all page footers and documentation updated accordingly.
- Refocus expiry check — Added a
window.focusevent listener as a belt-and-suspenders complement to the existingvisibilitychangelistener. When the browser window regains focus from another application (or after a screen-lock/wake cycle on platforms wherevisibilitychangedoes not fire), the session-expiry check now runs immediately, redirecting users to the login screen if the 30-minute inactivity timeout has elapsed. - Version bump — Version number incremented to 1.12.1; all page footers and documentation updated accordingly.
- Inactivity warning overlay — After 5 minutes of no interaction a slim amber strip appears at the bottom of the screen showing "App last used: HH:MM". Any interaction (tap, key press, scroll) immediately dismisses it and resets the inactivity clock. The overlay has no interactive controls of its own (
pointer-events: none) so the underlying app remains fully usable without a separate dismiss step. Correct iOS safe-area padding is applied so the strip never covers the home-indicator gesture bar. - Interaction-triggered expiry — If a user interacts with the app after the full 30-minute idle timeout has already elapsed (e.g. returning from a long background tab that missed the periodic check), the interaction itself immediately triggers the session-expiry flow instead of silently extending the clock.
- Stale-token fix — "Log in again" after an inactivity logout now performs a full
location.reload()rather than an in-place re-render. This guarantees a fresh CSRF token and clean client state, eliminating the second attempt needed bug on return-to-login. renderLogin()CSRF refresh —renderLogin()now unconditionally fetches a fresh CSRF token before rendering, covering the 401-redirect code path that bypassesreturnToLogin().- Bug fix: immediate re-expiry on fresh login —
startActivityTracking()previously calledupdateLastActive()at startup, which (with the new expiry-trigger guard) would immediately callforceSessionExpiry()if a staletasker_last_activetimestamp from a prior session was still in localStorage. Fixed: the session baseline is now stamped directly without running the expiry check. - Bug fix: double forceSessionExpiry invocation — Added a
_sessionExpiryInProgressguard flag to preventforceSessionExpiry()from being entered a second time from avisibilitychangeevent that fires betweenstopActivityTracking()andstate.user = null. - Version bump — Version number incremented to 1.12.0; all page footers and documentation updated accordingly.
- Full UI and functional check — all routes, middleware, SPA views, and user flows reviewed end-to-end. No functional regressions found. All 241 tests pass; TypeScript compiles with zero errors.
- Version bump — Version number incremented to 1.11.1; all page footers and documentation updated accordingly.
- Session fixation prevention — The session ID is now regenerated (
req.session.regenerate()) immediately after successful credential validation on both the standard login path and after 2FA code verification. This eliminates the session fixation attack vector. - SMTP error sanitisation — Raw
nodemailererror messages (which can contain SMTP host names, port numbers, and authentication failure details) are no longer forwarded to clients. All email-delivery failures now return a generic "SMTP error" message. The full error is logged server-side only. - Global error handler — An Express error-handling middleware has been added as the last registered middleware in
server.ts. Any unexpected synchronous or asynchronous error is caught, logged, and returned as a generic 500 JSON response, preventing accidental stack-trace or path leakage. - MFA constant-time comparison — The 6-digit 2FA code is now compared using
crypto.timingSafeEqual()instead of a plain string equality check, eliminating a theoretical timing side-channel. - User option allowlist enforcement —
PUT /api/auth/my-optionsnow validates that every submitted option ID corresponds to anapproved=1dropdown option. Unapproved IDs are silently discarded. Previously a user could pin an unapproved (pending) option to their account. /readyzrate-limited — The health-check endpoint now sits behind the sameapiLimiter(200 req/min per IP) as all other public endpoints, preventing it from being used as an open amplification target.- Column-name allowlist in
common-fields— ThetopNhelper inGET /api/tasks/common-fieldsnow includes an explicitALLOWED_FIELDSSet guard before interpolating column names into SQL, providing defence-in-depth against any future refactoring that might pass user input to the function. - Security test suite expanded — 7 additional tests added (session fixation, SMTP sanitisation ×3, option allowlist ×2) bringing the total to 241 negative security tests.
- Version bump — Version number incremented to 1.10.0.
- Analytics XLSX report — Replaced the "Print / Save as PDF" button in the analytics section with a "Download Analytics (.xlsx)" button. Clicking it downloads
Tasker-Analytics-YYYYMMDDHHmm.xlsx— a multi-sheet workbook with one data table sheet per chart, mirroring all graphical output: Summary, Time by Category, Duty vs Personal, Outcome Distribution, Outcome by Category, Avg Duration (Category), Tasks by Type, Avg Duration (Task Type), Task Types by Source Group, Flag Distribution, Flags by Source Group, Activity by Hour, Activity by Day of Week, Task Types by Day Assigned, Personal by Day (Origin), Personal by Day (Type), Tasks Over Time (with optional regression trend column), Interruptions Over Time, and Assignment Lag. Sheets are only included when the corresponding chart would be visible. Served by a newGET /api/analytics/reportendpoint that accepts the same filter parameters as the history view. - Version bump — Version number incremented to 1.9.1; all page footers and documentation updated accordingly.
- Version bump — Version number incremented to 1.9.0; all page footers and documentation updated accordingly.
- Suggestion safety notice — The "Send suggestion to developers" input now displays a prominent warning instructing users not to submit any patient, location, or staff-identifiable information. The notice also clarifies that submitted freetext is sent to an NHS.net email address and invites users to include their own email address if they wish to receive a reply.
- Documentation updates — help.html, guide.html, dpia.html, technical-manual.html, installation.md, and maintenance.md updated to document the suggestion feature data flow and acceptable-use requirements.
- SMTP email configuration — Added SMTP settings section in the admin panel. Dropdown and flag suggestions from users are now emailed to the administrator instead of being stored on the server. This removes free-text personal data from the database in line with data protection principles. Supports STARTTLS (port 587) and SSL/TLS (port 465). SMTP password is encrypted at rest using AES-256-GCM.
- Task flags replace free-text notes — The free-text "Notes" field has been removed from task review. Instead, users select from an admin-managed list of structured flag options (e.g. "Sent to wrong user", "Priority too high"). Multiple flags can be applied per task. Flags are stored per-task in a dedicated
task_flagstable. - User-suggestable flags — Users can suggest new flag options from the task review screen. Suggestions are sent by email to the administrator and never stored on the server.
- Notices — Administrators can create, edit, activate/deactivate, and delete notices that appear prominently on every user's home screen.
- User messages — Administrators can send messages to individual users or broadcast to all active users. Messages appear on the user's home screen with individual dismiss controls.
- Auto-notification on dropdown approval — When an admin adds a new dropdown option for a field, users who had pending email proposals for that field automatically receive a user message confirming the update.
- Analytics updates — New "Flagged tasks" stat card; new "Task Flag Distribution" bar chart; flag labels shown on individual task cards; export includes
Flagscolumn instead ofNotes. - Default flag options — Five default task flag options are seeded on first run: "Sent to wrong user", "Priority too high", "Priority too low", "Should be sent to group", "Should be sent to specific user".
- Dependency — Added
nodemailer@8.0.5.
- Policy update — Removed restriction on use over NHS networks or NHS Wi-Fi. The application may now be accessed from any network. Updated Data and Use Policy, DPIA, and all supporting documentation accordingly.
- User groups — administrators create named groups that control which dropdown options users see. Each group has an independent option set configurable from the admin panel.
- Personal option customisation — after selecting a group, users are presented with a ⚙️ Customise My Options screen showing all group defaults as tick-boxes. Users can untick options they never use; their choices are stored per-account and override group defaults in all task forms. The screen is accessible at any time from ⚙️ Settings.
- Group proposals — users can suggest new group names from the group-selection screen. Proposals appear in a new Pending Group Proposals section of the Admin Panel for administrator approval or rejection.
- Option proposals — users can suggest new dropdown values (Task From, Task Type, Outcome) inline from the Customise My Options screen. Proposals appear in the existing Pending User Proposals section.
- Integrated combobox dropdowns — all task-form dropdowns (Task From, Task Type, Outcome) are now fully searchable comboboxes. Clicking opens a panel; typing filters options instantly; arrow keys navigate; Enter selects; Escape closes. No separate search boxes.
- Admin desktop layout — the admin panel is now wider (max-width 900 px) on desktop with Users/User Groups in a two-column grid and Dropdown Options shown three-across.
- Modal positioning fix — option and group modals are now centred in the viewport on screens ≥640 px with action buttons pinned to the bottom of the dialog.
- Security hardening —
POST /api/auth/set-groupnow enforcesis_approved=1, preventing users from joining a pending/unapproved group. AsafeId()helper strips non-identifier characters from combobox IDs before insertion into inline HTML event handlers. - Security test suite — 52 negative tests (
src/__tests__/security.test.ts, run withnpm test) covering CSRF, authentication, IDOR, SQL injection, XSS, input validation, resource exhaustion, error handling, temporal consistency, path traversal, and group access control. TASKER_DB_PATHenv var — allows test isolation by pointing the DB singleton at an in-process temp file.
- SPA back/forward navigation — browser Back and Forward buttons now work correctly throughout the app. Every view transition records itself in the browser history stack (
history.pushState); apopstatelistener dispatches navigation events back to the correct render function with an auth guard. - Asset version-gate reload — on startup the app fetches
/api/version(network-first, bypassing the service worker cache) and compares it to the version stored inlocalStorage. On a mismatch all service worker caches are cleared, the service worker is unregistered, and the page reloads to guarantee freshapp.js,app.css, andindex.htmlare loaded. GET /api/version— new lightweight endpoint returning{ "version": "1.4.0" }, rate-limited.
- Health-check endpoint —
GET /readyzreturns{"ok":true,"service":"Tasker","version":"1.2.0","timestamp":"..."}for uptime/heartbeat polling servers. No authentication required. - Login error messages — failed login attempts (wrong username or password) now display the server's friendly error message in the login form instead of silently resetting the form.
- Initial public release with task logging, analytics, admin panel, Excel export, PWA support, and configurable registration.