OpenTodo 0.17.0 adds calm limits to a busy day, each off by default: an optional Today limit with Done today and Log a win, Morning, Afternoon and This Evening in Today, sequential projects and areas, and a review interval per project with a Maintenance status for projects on low burn. Focus is one tap away from every task, new tasks can get a default reminder and can be added at the top, a pasted list becomes one task per line, the sidebar shows counts on every list in your own order, and a right-click on a task opens its menu. Sub-projects inherit their parent's custom fields, and a round of fixes covers Gemini and compatible assistant providers, CalDAV events from iOS Calendar, the selection bar, short calendar items and copying over plain http.
Upgrade notes
- Replace the image or the binary and restart. No migrations and no new variables; the new settings are additive fields with defaults that keep today's behaviour. Upgrade the server before the devices, as for every release.
- The old image name is no longer updated. From 0.17.0 releases are published only as
ghcr.io/opentodoapp/opentodo(and the chart asoci://ghcr.io/opentodoapp/charts/opentodo);ghcr.io/pixari/opentodostays at 0.16.0. If your compose file,docker runcommand or template still names it, switch now. (Docker)
Added
- An optional Today limit, Done today, and Log a win. Settings → General → Start view and briefing → Today limit (or Limit Today… in Today's menu in the method modes) caps how many tasks Today lists: 3, 5, 7, 10 or any number from 1 to 20. The slots fill first come, first served (today's focus, the tasks you let in, Earlier oldest first, then the rest by when they were created), so a task captured on a full day waits instead of pushing out a planned one; the others sit in a folded Waiting for a slot group, and the line under the title reads 3 of 5. Take a slot (on the row, in the task's menu or the palette) lets a waiting task in for the day. Blocked tasks, later steps of a sequential project and habits never take a slot, and nothing is ever moved or refused. A folded Done today group lists what you completed today (shown by default in the method modes), and Log a win adds work that was never a task, already done.
today_summaryreportstoday_limitandwaiting_for_slot,ot todayprints a "Waiting for a slot" section (--allfor one list) andot add --donelogs a win from the terminal. Off by default; Simple lists users see no change (Today limit and Done today). - Morning, Afternoon and This Evening in Today, for tasks and habits. A task or a habit can now belong to a part of the day: Morning or Afternoon, beside the existing This Evening. Turn on Group by day part (Settings → General → Start view and briefing, or Today's menu in the method modes) and Today lists the day under Today, Morning, Afternoon and This Evening; a task with a time but no part goes to the part of its time (before 12:00, until 17:59, from 18:00), and a part you chose always wins. Set a part in the When editor (Any time, Morning, Afternoon, Evening), by dragging a task onto a part's heading, from the Actions menu's Day part or the command palette, each with Undo; Shift+E still toggles This Evening. Quick add reads
this morning,this afternoonand a part after a date (tomorrow morning,fri evening) in all six languages while day parts are on, andMorning run tomorrowkeeps its title. Filters gainmorningandafternoon; the part reaches CalDAV (X-OPENTODO-DAY-PART), the Markdown vault ([daypart:: morning]),ot add/edit --part, the REST API and the assistant tools. Off by default: with day parts off Today is unchanged, and a part set elsewhere shows as a chip. (Day parts) - A review interval per project, and Maintenance for projects on low burn. The weekly review's project walk now shows only the projects that are due: each project comes up every week unless you choose Review every… in its menu (or press e on its card) for every 2 weeks, every month, every quarter or paused, counted in your own days (reviewed on 31 January, monthly, due on 28 February). The interval is yours: in a shared project each member keeps their own rhythm. After the last card the walk says how many projects are not due yet and how many are paused, and Review them too adds them. Put in maintenance (project menu, the row menu on All projects, or m in the walk) marks a project as deliberately on low burn for everyone who shares it: its health reads Maintenance instead of Overdue, Quiet or On track (a passed deadline still shows Late), it is never stalled or without a next action in the review, on the Loop home or in the weekly email, and its tasks stay where they were. Under Simple lists both items appear only once something other than the default is set. REST (
statuson projects,activeormaintenance), the agent tools and MCP (update_project status,statusin project output), the STATUS column ofot project listandopentodo-statusin vault files follow. Additive fieldsproject.statusandproject_prefs.review_every; no migration. (Review intervals and maintenance,project-review-cadence, closes #575) - Sequential projects and sections, and areas that never finish. Flow… in a project's menu makes it Sequential: only the first open step is available, and the steps after it stay in place, dimmed, with After and the step they wait for, until it is completed (offline too, with nothing extra written). Sections are steps of their own and can be Parallel or Sequential from their heading or column menu; a Sequential section in an ordinary project offers its tasks one at a time. A step that starts later, is blocked or is assigned to someone else holds the sequence; recurring tasks, habits and Someday never do. Area marks an ongoing list such as Home or Finances: no progress on All projects (it reads Area), never Quiet and never "without a next action" in the review, the Loop home or the weekly email. Today folds due steps that wait into its Blocked group, Next shows one step per sequential project, and the new filter keyword
availablelists what can be done now. Under Simple lists the item only appears where a flow is set; the palette has Set project flow…. REST (flow_modeon projects and sections), the agent tools and MCP (flow_mode,list_tasks available),ot list --available, a FLOW column inot project listandopentodo-flowin vault files follow, all computed by one shared rule (internal/flowandweb/src/lib/flow.ts, pinned by shared vectors). Additive fieldsproject.flow_modeandsection.flow_mode; no migration. (Sequential projects and areas,project-flow-modes, closes #574) - Focus is easy to find, and its timer got three comforts. Focus is now in the sidebar's More group after Statistics (and can be shown beside Inbox and Today from Settings → Appearance), a clock button at the top of the task panel starts it in one tap, and every task menu (., the row's ⋯, a right-click, or on a phone Actions with one task ticked after a long press) has Start focus: it opens the Focus screen with a session already running (Countdown asks for the length first), and switching from another task's session names it in a notice with Undo. When a Pomodoro or Countdown session ends, +5 min and +15 min carry on with the same task for up to 10 minutes after the end. The Countdown dialog shows your presets (10, 20 and 40 minutes unless you change them) as one-tap buttons, and Background noise (white, pink or brown, generated on the device, off by default) plays only while you work, with the volume set per device. A long press on a phone now vibrates only while haptic feedback is on. Two additive
user_prefsfields,focus_presetsandfocus_noise; no migration. (Focus, Focus breaks, presets and noise,focus-everywhere, closes #543, #553 and #535) - A calmer sidebar you can tune: counts on every list, your own order, icons for views, and a New view draft that waits. Settings → Appearance → Sidebar → Sidebar counts adds a choice, Every list, that shows the number of open tasks on every project, label and saved view (and their favourites) at rest; with a mouse, pointing at a row puts its ⋯ button where the number was. The default stays Inbox and Today. Sidebar destinations lists Inbox, Today, Upcoming, Calendar and the More destinations in one list you reorder by dragging, from each row's menu or with Alt+↑ / ↓; the order syncs, the phone's tab bar keeps Today, Inbox and Upcoming, and Open Loops first keeps its six places. A saved view's menu has Icon…, the projects' icon picker; the icon shows in the sidebar, Favourites, the views list, the search box and the view's header, and
/api/v1/viewsacceptsicon(a catalogue name or null). The New view page keeps its query and display options on the device, per workspace, across navigation and reloads, until you save the view or choose Clear (with Undo). Additive fieldsuser_prefs.sidebar_counts,user_prefs.sidebar_orderandview.icon; no migration. (Sidebar counts and order, Saved views,sidebar-polish, closes #545, #562, #560 and #559) - Paste a list, get one task per line. Pasting two or more lines into an empty quick add (a list's, a section's, the command bar's, the phone's add sheet, the share screen or a calendar day's) no longer squashes them into one long title: OpenTodo asks Add 7 tasks, one per line? with a preview, and Add 7 tasks (or Enter) creates them in the pasted order, while Paste as one line keeps the old behaviour and Esc pastes nothing. List markers (
-,*,•,1.,a),[ ],- [x],☐…) and blank lines are dropped, and with parsing on each line is read like a typed one, with the view's defaults for the rest. All tasks of a paste are one batch, offline too, and one Undo removes them all; a line too long to encrypt refuses the whole paste. The Add subtask line does the same with Add N subtasks, without parsing. Up to 100 lines at a time; longer lists point to Import. A paste into a box that already holds text is untouched. No server change. (Quick add syntax,paste-to-tasks, closes #578) - Default reminders for tasks with a date or a time. Settings → Notifications → Default reminders sets one reminder for tasks with a time (at the due time, 10 or 30 minutes, 1 hour or 1 day before, or a custom offset) and one for tasks with a date only (on the day at 09:00, 1, 2 or 3 days or a week before, or custom). When a task without a date gets one, by quick add, the date picker, the calendar, bulk Set date, an AI assistant,
ot add/ot editor the browser extension, it gets that reminder: an ordinary reminder you can change or delete, delivered by push, the phone apps and CalDAV like any other. It is added once per task (a deleted one stays deleted, Undo takes it back with the date), your own reminder from quick add wins, it follows a time being added or removed, and habits, teammates' scheduling, CalDAV, calendar sync, imports, rules and email never add one. Both start at None. REST callers opt in with?default_reminder=trueonPOST /api/v1/tasksandPATCH /api/v1/tasks/{id};othas--no-default-reminder. Two additiveuser_prefsfields,reminder_default_timedandreminder_default_dated; no migration. (Default reminders,default-reminders, closes #546) - Right-click a task row to get its menu, where you clicked. Until now a right-click only offered the browser's menu, which knows nothing about a task: pointer users had to tick the row to raise the Selection bar or reach for
.. Right-clicking a row in a list, board, filter, saved view or method list now opens the same Actions menu the keyboard opens, at the pointer, for the whole ticked selection when that row is ticked and for the row alone when it is not — and a touch hold opens it at the finger on a screen you can point at. Links, fields and selected text inside a row keep the browser's own menu, and on a phone a long press keeps ticking rows as it always did. (Work from the list, Keyboard,task-row-context-menu) - Today's schedule at the bottom of the sidebar. Open Schedule above your name to keep the time and the rest of today in sight while you work in a list with a task open beside it: today's timed tasks and calendar events that haven't ended, in order, with the current one highlighted. Click a task to open it in the side panel, or an event to see its details; N more today opens the Day calendar. Closed until you open it, and remembered on each device. (Schedule in the sidebar,
sidebar-schedule, closes #536) - Add new tasks at the top, and the Add row stays in reach. Settings → Capture → Quick add → Add new tasks puts a task added from a project's or the Inbox's quick add, a section's add row, a board column, the command bar or the phone's add sheet At the top of the list instead of the bottom (the default stays the bottom). It is one choice for your account, on all your devices; lists sorted by date or priority keep their order, and subtasks, o, duplicates, templates, imports and tasks created through the API, the CLI or MCP still append. From 768 px wide the quick add now stays under the page title while a long list scrolls under it, and a task that lands out of sight says where it went (Added to the bottom of Inbox) with Show. One additive
user_prefsfield,add_position; no migration. (Quick add syntax,add-position, closes #540 and #541) - Sub-projects inherit their parent's custom fields. A field defined on a project now applies to every sub-project below it: its tasks show, edit, filter by and export the field like their own, and the sub-project's Fields… dialog lists inherited fields first, read-only. Names stay unique across the tree, the 30-field limit counts inherited fields, and the API and assistant tools follow the same rules. No migration; values are stored as before. (Custom fields,
inherited-custom-fields, closes #405)
Fixed
- Gemini and compatible endpoints say what is wrong when a test or answer fails. Google Gemini answers a wrong key with HTTP 400 and its errors as a list, so a wrong key read as "the provider refused the request", and a wrong model or base URL as a bare 404 that looked like OpenTodo's own. A rejected key now reads as a rejected key (
provider_auth) for every provider, and the provider's own 404 asprovider_not_found: "no such model or endpoint, check the model id" (and the base URL for a compatible endpoint). Test connection no longer sends a second request in those cases, the same rules apply on the device for encrypted accounts, and a compatible base URL pasted with/chat/completionsat the end is shortened to its base. The provider's own error text is still never shown. (Set up the assistant,assistant-providers-fix, fixes #501) - The task menu fits on the screen. Right-clicking a task (or pressing .) opened a menu of about sixteen rows, taller than a laptop window, because every priority and every date choice had its own row. Priority… and Date… are now one item each and open their choices inside the menu; the first item or Esc goes back. The keys 1 to 4, the row's own buttons and the command palette still set them in one step. Keyboard focus also no longer jumps out of the menu while it loads.
- Dragging a project in the sidebar shows one clear drop position, and the project stays in its workspace. The row under the pointer showed a line and an outline at the same time, so a reorder looked like making a sub-project. Now a line with a dot marks a reorder, starting at the level the project lands on, and an outline marks a drop inside. A sub-project dragged or moved to the top level in any workspace but the first also no longer jumps to the first workspace and disappears from the sidebar; one that already did can be brought back with ⋯ → Move to workspace…. (Workspaces,
sidebar-project-drag, fixes #544) - Icon-only menu buttons show their name on hover. The Filter button of a list, the page header's More actions and the other icon buttons that open a menu (the "…" on tasks, comments and settings rows, Attach, Add reaction, More formatting) showed nothing on hover, unlike the icons beside them. They now show their name like every other icon button. (
menu-icon-tooltips, fixes #528) - Copy buttons work on a server reached over plain http. Browsers only offer the clipboard to pages served over https or from localhost, so on a server opened at a LAN address every Copy button (the MCP URL, API tokens, invite and public links, recovery codes) silently did nothing. They now fall back to copying through a hidden text field, which works over http, and the recovery key is only marked as copied when the copy succeeded. (
copy-without-https, fixes #525) - Short calendar items no longer overlap. In the week and day calendars, short tasks close together (13:30, 13:45, 13:50) were drawn on top of each other, because a block is drawn taller than its length to fit its title but was placed as if it were not. Blocks are now laid out by the room they take on screen: short ones that would cover each other sit side by side, longer ones that follow each other keep the full width, and the overlap marker only shows on times that really overlap. (
calendar-short-blocks, fixes #526) - The selection bar stays in sight in a long list. Ticking tasks far down a long list used to leave "Actions" and "Clear" at the top, out of sight. The bar now stays pinned just below the page header while the list scrolls, and a row moved to with the keyboard stays below it. (
sticky-selection-bar, fixes #518) - A JSON file without ids is no longer reported as already imported. After one custom JSON file (a converted Google Keep list, say) was imported, every later JSON file without ids showed all its rows as "already imported" and could not be imported, because rows without an id were recognised by their position. They are now recognised by their content: the same file imported again is still skipped, a different file never is. (Import,
import-content-ids, fixes #520) - The task you select with the keyboard stays in sight. Moving up a long list with the arrow keys or
kused to hide the selected task behind the page header, and the first task never brought the list back to the top. The selected row now stays just below the header, and the first task scrolls the list to the top, in project lists, the standard views, saved views and the New view page. (keep-cursor-row-visible, fixes #514) - A data folder the server cannot write is explained. On a fresh Unraid install Docker creates the appdata folder owned by root, and the container stopped with only "open lock file: permission denied". The message now names the user the server runs as (65532 in the image) and the
chowncommand that fixes it, and the Unraid guide and template explain it, including how to bring back a template setting you removed. (Unraid,unwritable-data-dir, fixes #511) - A task moved from a project to the Inbox stays in its workspace. With several workspaces, dragging a task out of a project onto the Inbox (or moving it there with Move, the API,
otor an AI agent) used to put it in the first workspace's Inbox. It now lands in the Inbox of the workspace it was in. Tasks already moved to the wrong Inbox stay there; move them into a project of the right workspace to bring them back. (Workspaces,inbox-move-keeps-workspace, fixes #508) - An event saved to OpenTodo from iOS Calendar is no longer lost. Calendar apps send events, and the CalDAV endpoint used to refuse them; iOS then dropped the item silently, and an event moved there from iCloud disappeared altogether. An event now becomes a task: due at its start, its length as the estimate, its alarms as reminders, its repeat rule where OpenTodo can express it. It then shows in Reminders and OpenTodo rather than in Calendar. (Calendar apps,
caldav-events-as-tasks, fixes #500)
Changed
- A request your reverse proxy changed now says so. An API path asked with the wrong method answers 405 with an
Allowheader ("GET is not allowed on /api/v1/settings/assistant/test; use POST") instead of "unknown API route", and a 404 names the method and path the server received. A 301 or 302 redirect in front of OpenTodo turns aPOSTinto aGET; the assistant's Test connection now explains that instead of showing the raw error. (API, Behind a reverse proxy,api-method-not-allowed, refs #501) - The top of the sidebar shows your server's name and your workspace on two lines. With one workspace the header used to squeeze both into one line ("OpenTodo · Personal"), so a longer server name cut off the workspace. Now the name sits on the first line and the workspace below it, each with its own room. (#492, Workspaces)
Install
- Docker:
docker run -d -p 8080:8080 -v opentodo:/data ghcr.io/opentodoapp/opentodo:0.17.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.