OpenTodo 0.21.0 is about planning horizons and the home. Plan a task for this week, next week or this month without a made-up date (time sectors), and pin a few objectives for the quarter and the week above Plan and in the weekly review. A project can be a reusable list for groceries or packing, where adding an item that is already there brings it back instead of duplicating it. A repeating chore in a shared project can take turns between people. On Android, location reminders fire when you arrive at or leave a place (GitHub and F-Droid builds; Google Play follows). Also: buttons that only close a panel now say Close, the phone Day calendar leaves room for the hours, and a fix for encrypted accounts (see Security).
Security
- Turning on end-to-end encryption now encrypts all your existing content, and finishes the job for accounts that already turned it on. When encryption is turned on, the app re-writes the text you already have as ciphertext. That job missed several kinds of content, so their text written before encryption was turned on stayed in plaintext on the server: notes (title, text and appended lines, since 0.19.0), time entry notes and the line for tomorrow from closing the day (since 0.9.0), saved locations (name, address and coordinates) and routine names (since 0.10.0), and calendar frame and slot titles (since 0.18.0). Text written after encryption was on was always encrypted. The job now covers every kind of content, and a test fails if a new kind is ever left out. If your account already encrypts, there is nothing to do: after the update, each unlocked device checks once, at the end of its next complete sync, and re-writes as ciphertext whatever of that text the server still holds in plaintext. Values already encrypted are left alone, so the check writes nothing when another device got there first. Run Purge plaintext history in Settings → Account → End-to-end encryption afterwards to delete the superseded plaintext operations; backups made before then may still contain that text. Turning encryption off now writes all of these kinds back as plaintext too.
Upgrade notes
- Replace the image or the binary and restart; upgrade the server before the devices. No migration and no new variable. New synced entity
objectiveand new synced fieldsproject.reusable,project.aisles,section.aisle,task.sector,task.rotation,reminder.radius_m(with the reminder kindsarriveandleave),user_prefs.time_sectorsanduser_prefs.objectives, all additive: older devices keep working and ignore them, but only an upgraded server stores and serves them. Back up first; there are no down-migrations. - Encrypted accounts: after updating, open the app once on an unlocked device; it finishes encrypting older content by itself (see Security).
Added
- Objectives for the quarter and the week. A few short lines that say what the quarter and the week are for, pinned where you plan (#580). Turn on Objectives in Settings → Account → How you work: the Plan shows this quarter's and this week's objectives above its columns, and the weekly review's Look ahead step lets you write next week's and carry, finish or drop last week's open ones (and last quarter's, in the first review of a new quarter). At most five open per period, one line each, edited in place; titles are end-to-end encrypted. Also in
GET/POST /api/v1/objectives,ot objectivesand the MCP toollist_objectives. See Objectives. - Take turns on a household chore. A repeating task in a shared project can now pass from person to person each time it is completed: the bins on Monday, the bathroom on Saturday (#582). Open the task's assignee and choose Take turns…, then pick the people, or Add everyone in a workspace group, and drag them into order (or Alt+↑/↓). Completing the task assigns the next occurrence to the next person, who gets an It's your turn notification; skipping or reassigning doesn't pass the turn. The panel shows Takes turns: Alice → Bob → Carol and the row marks the assignee with a rotate sign. When someone leaves the project, their turn goes to the next person. Every device that completes offline picks the same next person, and the REST API (
rotation,400 invalid_rotation,409 not_a_member), CalDAV completions,ot edit --rotate/--no-rotateand the MCPupdate_taskstool all work the same way (Take turns). - Time sectors: plan for this week, next week or this month without a fake date. A task can now be planned for a week, a month or a quarter instead of a day (#579). Turn on Time sectors in Settings → Account → How you work, then choose This week, Next week, This month, Next month, This quarter or Next quarter (or Later…) in the Sector tab of a task's When editor, run Set time sector… from the command palette on a selection, or type
this weekorsometime next weekin quick add (in all six languages). A sectored task leaves the Inbox and Anytime, is never overdue, and keeps its deadline. Rows show the sector where the date would be, named from your date and first day of the week, and a sector whose period has ended reads From week 44 without anything being rewritten. In the Calendar, Plan from a list › Time sectors groups the tasks you can work on by sector, ready to drag onto a day, and the weekly review's Look ahead lists what was carried over and next week's tasks. Filters gainsector: this week,carriedandno sector, and the API,ot --sector, the assistant tools, CalDAV (X-OPENTODO-SECTOR) and the Markdown vault ([sector:: 2026-W45]) carry the stored period. See Time sectors. - Reusable lists. A project can now be a reusable list for groceries, the pharmacy or packing (#581): Project menu → List… → Reusable list. Adding an item that is already there, such as "milk" when Milk was ticked off, reopens it in its section with its notes instead of adding a second Milk, and says "Milk is back on the list" with Undo and Add anyway; an item that is still open flashes instead. Names match ignoring case, accents and extra spaces, on your device, so it works offline. Ticked items stay at the end in a Got it group on every member's devices, and when two phones add the same item offline, the list settles on one (the other goes to the Trash, shown as a merged duplicate). Sort into aisles offers sections such as Produce and Dairy and eggs in your language and puts new items in the right one from a word list built into the app.
POST /api/v1/taskstakes"reuse": true,ot addand the MCPcreate_taskstool reuse items too, andot project edit --reusable --aislesturns the mode on (Reusable lists, Sort a list into aisles). - Location reminders: when you arrive at a place or leave it. A task with a location can now remind you on your phone when you get there or go (#583): Add reminder → At a place → When I arrive or When I leave, with a radius from 100 m to 5 km (150 m by default). They fire in the Android app, while it is closed too, once you turn on Settings → Notifications → Location reminders → On this phone; the app first explains the use, then asks for location all the time. The phone watches the places with Android's own proximity alerts (no Google Play services), at most the 19 nearest, and your position never leaves it. The notification has Done and Dismiss, and a crossing fires at most once an hour. In a browser you can add and change them, and they say Fires on your phone. The iPhone app follows; the Play Store version follows Google's review of background location, so F-Droid and GitHub builds have it first. The API takes
kind: "arrive" | "leave"withradius_m,ot task showlists them, and calendar apps never see them (Apple's location alarms stay client data). See Location reminders.
Changed
- Attachment sizes in an email-to-task description use binary units. The list of attachments read
300.0 KB; it now reads300.0 KiB(andMiB,GiB…), the unit that matches the 1024 divisor, and keeps scaling past MB. - Close, not Done, on buttons that only close. The editors of a task's details (When, Assignee, Take turns, Labels) ended with Done, which on a phone sits right under the task's checkbox and reads as "complete the task" (#801). They now say Close, as do the When editor's own button and the dialog after publishing a task page. On a touch screen, Select in a list now switches to Close, and the selection bar's button that hides the checkboxes says Close. On the Focus screen, a Flowtime session's button that ends the count, beside Complete, now says Stop the timer. Nothing else changes: the buttons do what they did. See Task panel.
- Location reminders stay out of the Google Play version for now. The Android app from Google Play no longer declares location access (fine, coarse or all the time) and has no location reminders until Google has approved the background-location use; its Settings → Notifications → Location reminders says they come to the Play version later and that the app from GitHub or F-Droid has them. The APK on GitHub and F-Droid is unchanged and still reproducible. CI now builds the Play bundle with
-Popentodo.play=trueand fails when it declares a location permission or the APK lost one (scripts/android-store-check.sh). See The Android app. (play-location-later)
Fixed
- Notes and time entries in a shared project stay readable for its members. On an account with encryption on, a note in a shared project and a time entry on a shared project's task were encrypted like personal content, so the project's other members saw Cannot decrypt. Shared projects are never encrypted: these now stay plaintext there like the project's tasks, and the same one-time check writes any of them already encrypted back as plaintext.
- A long panel title shows its start again after editing. A two-line title scrolled inside its field while it was edited kept that position after the edit, so the beginning stayed hidden. Leaving the field now scrolls the title back to its start.
- The installed app's splash is the dark canvas. The manifest's splash background was a slate
#0f172athat matches no part of the app; it is now the dark canvas#0d0e10. A manifest has one splash colour, so a light device sees a dark splash before the light page, which is the lesser flash compared with a bright splash on a phone in dark mode. - The installed app's status bar keeps its colour from the first frame. The static theme-colour metas, the boot script's light fallback and the manifest still said
#0f172a, while the default emerald appearance uses#047857in light and#0d0e10in dark, so Android changed the tint after the app loaded. They now use the default bars, and a test keeps the three files in step. - The floating add button no longer covers Settings rows on a phone. Settings has no quick-add action, and the button sat over the chevrons at the end of its rows. On a phone the tab bar stays, and the button returns on the next view that offers quick add.
- The unscheduled lane leaves room for the hours on a phone. In the Day calendar on a phone, every task without a time was a full-height card in the lane above the hours. Seven of them took almost the whole screen, so dragging one onto a time was hard (#775). On a touch screen or a narrow window, a lane with more than three tasks now folds behind an Unscheduled header with the count. A tap opens it, and the device remembers your choice. Open, the lane scrolls inside a short area of at most three and a half rows, so the hour grid keeps most of the screen. The folded header still accepts a dropped block. The Week grid on a tablet does the same. The red current-time line, and the ghosts of a drag, no longer paint over the lane when the hours scroll under it, at any width. With a mouse on a wide window the lane looks as before.
- An answer cut off on the way no longer counts as an empty success. When the connection, or the page, went away while a server answer was still arriving, Safari and the iOS app read the cut-off answer as an empty one, so the app went on as if the request had worked but without its content. Opening a project could then end in an error in the browser console while the project header counted the project's public links. A cut-off answer is now treated like no answer at all: the app reports a network problem, or tries again later where it would after any other failed request.
- Subtasks go to Later with their parent. Deferring a task with subtasks (giving it a start date in the future, or parking it in Someday) moved the task to its project's Later group but left its subtasks behind in the old group, at the top level with only the parent's name, so they looked cut off (#755). A project's list now keeps each subtree together: the subtasks of a deferred or parked task are listed under it in Later, each with its own dates, and a deferred subtask of an active task stays under its parent with its Starts chip. Display mode's project page does the same. Nothing is written; it is how the list is drawn, offline included.
- The theme format's JSON Schema is on the docs website. Themes linked
/docs/themes/opentodo-theme.schema.json, but the docs build copied only images, so the link was broken on the website. A file underdocs/that a page links is now served beside the pages at that path. - Release assets are only the files a release ships. The 0.20.0 GitHub release also carried two of the web app's build chunks,
opentodo-dark-*.jsandopentodo-light-*.js, picked up because the release job copied every file whose name started withopentodo-. It now copies only the server andotbinaries, the extension and skill zips, the Android APK and AAB and the site bundle, by exact name and from the top of their artifact (scripts/release/collect-assets.sh, tested bymake repo-check), and fails when a binary is missing. - A failed move of a repeating task's occurrence says so. Dragging a projected occurrence to another day in the Plan, Month, Week or Day dropped any failure silently; it now shows the usual "could not be saved" message, like every other move.
- Waiting for a slot: a time that has passed says so. In the folded Waiting for a slot group, a task whose time today had already passed looked like one still to come, and the heading could name it as next (follow-up to #766). It now shows its time in red with Time passed, keeps its place and Take a slot, and the heading names the next one still ahead; when every time has passed it says so (time passed: Call the school, 9:00 AM, or 2 past their time). The list follows the clock.
ot todaymarks those lines(time passed)and its--jsonandtoday_summaryaddtime_passed. - Escape closes a failure toast and returns focus. The focus failure message had no keyboard dismissal: Escape with focus on its close button left the toast open with its timer paused. It now closes on Escape and returns focus to the element that had it before (OpenSpec
trash-undo-toasts). - A failure toast keeps clear of the focused field. The focus failure message could sit over the field the person was typing in, and when its close button had focus, removing it left the next message held open. It now moves to the opposite corner like the Undo offer, and its hover or focus hold resets when the message changes or leaves (OpenSpec
trash-undo-toasts). - A failure toast never covers the Undo offer. A focus time-entry failure could render a second toast region over the offer's Undo control. It now waits while an offer shows, takes the region when the offer leaves, starts its five-second window then, and pauses on hover or focus like the offer itself (OpenSpec
trash-undo-toasts). - A failed Focus time entry is no longer silent. When a session could not start or stop its time entry, the minutes were lost with only a console warning. The session now keeps running and a toast says whether it is not being tracked or its minutes may not be logged, with the underlying error (OpenSpec
trash-undo-toasts). - Deleting a task always offers Undo. The command palette, Vim's
dd, the Filter view and saved views wrote a bare tombstone and skipped the Undo toast the main list shows. They now use the same undoable trash, so every delete restores the task and its subtasks the same way (OpenSpectrash-undo-toasts). - Undo a big delete. A bulk Trash offer held Undo for only ten seconds, and Delete on a large ticked set trashed it without asking. The offer now holds Undo for fifteen seconds and offers Show Trash, and Delete on more than twenty ticked tasks asks first with the count; smaller sets stay immediate (OpenSpec
trash-undo-toasts).
Install
- Docker:
docker run -d -p 8080:8080 -v opentodo:/data ghcr.io/opentodoapp/opentodo:0.21.0 - Binary: download
opentodo-<os>-<arch>below, then run it (data goes to./data). - Command-line client:
ot-<os>-<arch>. Browser extension:opentodo-browser-extension-chrome.zipandopentodo-browser-extension-firefox.zip.
Verify downloads with sha256sum -c SHA256SUMS. Self-hosting guides: docs/self-hosting.