Repository navigation
Releases: docfide/duct
Release list
Duct 1.0.0-alpha.3
Downloads are below, a few minutes after publishing: macOS for Apple silicon (arm64) and Intel, Windows (.exe) and Linux (.AppImage, .deb).
They aren't signed yet. On macOS, open Duct once, then allow it in System Settings › Privacy & Security › Open Anyway. On Windows, choose More info › Run anyway.
Accessibility statement and WCAG 2.2 conformance report: https://duct.tensflare.com/legal/accessibility/
Added
- Accessibility: Duct aims to meet WCAG 2.2 AA, and publishes a conformance report (VPAT®) at duct.tensflare.com/legal/accessibility. Text and field borders meet the contrast minimums; focus is always visible; there's a Skip to content link; the documents list is a table for screen readers; the number of results is announced. In a document, ↑ and ↓ select a paragraph and Enter adds it to a notebook, and ⌘⇧N / Ctrl+Shift+N adds any selection. Everything reflows at 400% zoom. The / shortcut can be turned off (Settings › Features › Keyboard), and the mascot stops moving within 5 seconds.
npm run a11ychecks every main screen with axe-core, and CI runs it. - Search results are grouped by document. A PDF that matches on several pages is one result with its best passage, links to the other pages ("Also on p. 2, p. 7") and its other passages a click away; the heading counts documents and passages. Quick search shows one row per document.
- Open shows the document inside Duct, on its own, with your notebooks beside it; Open in app hands it to the program it belongs to. Closing one of two documents in the workspace leaves the other full width, and One / Side by side / Stacked switches between them.
- Selected text can go to any notebook: the pop-up says Add to "", and its menu lists every notebook you can add to, or starts a new one named after the document.
- Find in every document: the words you searched for are highlighted when it opens, "2 of 5" with previous and next (Shift+Enter, Enter) steps through every match on every page, and the find box takes other words too.
- Documents look like themselves: Word files with their headings, lists, tables and pictures; spreadsheets as tables with a tab per sheet; Markdown formatted; emails with their headers and body. Where LibreOffice is installed, Word, OpenDocument, RTF, PowerPoint and Keynote files are shown as their real pages instead (converted on this computer, once per version). Text still shows what Duct read. HTML from documents is cleaned to a short list of safe tags, and an email's remote images are never loaded.
GET /api/document-view,GET /api/document-pdf. - Search inside audio: voice notes (including WhatsApp's), recorded calls, meetings and interviews. What's said is transcribed on this computer by Whisper, never uploaded; the 77 MB model downloads once, the first time, and the privacy ledger lists it under On-device models. Results say "at 2:00" and play from there; the workspace shows the recording with its transcript, a time on every line. Off until turned on in Settings › Features; songs (tagged with an artist and an album) are skipped. Not available on Intel Macs.
- Open at login (Settings › This computer) starts Duct when you sign in, so quick search and watched folders are always ready.
Fixed
- Long file names in All documents ran over the folder, status and buttons; they now end in "…", with the full name and path on hover.
- Highlights lit up a searched word inside another word ("tax" in "taxation").
Duct 1.0.0-alpha.2
Fixes the desktop app not opening in 1.0.0-alpha.1. If you installed alpha.1, install this over it.
Downloads are attached below as they finish building (a few minutes after publishing): macOS for Apple silicon (arm64) and Intel, Windows and Linux.
They aren't signed yet. On macOS, open Duct once, then allow it in System Settings › Privacy & Security › Open Anyway. On Windows, choose More info › Run anyway.
Fixed
- The desktop app didn't open: it started without a window, because a package Duct's server needs (express-rate-limit) wasn't included in the installers. The same would have broken
duct servefrom npm. CI now fails any build whose code uses a package that wouldn't be shipped. - If something goes wrong while the desktop app starts, it now says so and closes, instead of staying open with no window.
- The Linux installers build again, and the Intel Mac installer is built on a supported machine.
Everything new in Duct 1.0 is in the 1.0.0-alpha.1 notes.
Duct 1.0.0-alpha.1
The desktop installers in this release don’t open. Use 1.0.0-alpha.2 instead; it fixes that and nothing else changes.
Downloads are attached below as they finish building (a few minutes after publishing): macOS for Apple silicon (arm64) and Intel, Windows and Linux.
They aren't signed yet. On macOS, open Duct once, then allow it in System Settings › Privacy & Security › Open Anyway. On Windows, choose More info › Run anyway.
Duct 1.0 turns the search library into a product family: the desktop app, a team server you can run in your own cloud, the developer API with its clients, and Tensflare accounts for paid plans (hosted AI, sync, connectors, team sign-in). This alpha is for testers; 1.0.0 follows once payments, identity providers, Docker images and Windows and Linux builds are verified.
Upgrading from 0.2
- Requires Node.js 22.13 or later.
- Indexes from 0.2 are migrated on first start, and their documents are re-read in the background. The CLI keeps one index in
$DUCT_HOMEor~/.duct. duct servelistens on127.0.0.1by default. Any other--hostneeds--auth-tokenor sign-in (--oidc-issuer).POST /api/watchonly accepts folders under--watch-root, and the API only deletes files Duct stored in its Library.- Docker: the image keeps its data in
/data(DUCT_HOME) and runs as the unprivilegednodeuser (uid 1000). Move a volume that was mounted elsewhere to/data, and make it writable by uid 1000 (chown -R 1000:1000).
Security
- Spreadsheets are read with SheetJS 0.20.3 (fixes the prototype-pollution and ReDoS advisories in 0.18.5); pdf.js 6 and sharp 0.35.5 also clear their advisories.
duct servelistens on127.0.0.1by default and refuses other interfaces without--auth-token. It accepts only expectedHostheaders, rejects cross-origin writes, and sends a Content-Security-Policy.- The API no longer indexes local paths sent as URLs, never indexes files without a supported extension, and only deletes files that Duct stored in its Library.
POST /api/watchonly accepts folders under--watch-root; the desktop app picks folders with the native dialog instead.- URLs that resolve to private, loopback or link-local addresses are refused by
duct serve, including after redirects. - LLM answers are escaped before display, and API keys are never written to disk.
- Browsers log in to a token-protected server once (
POST /api/login, HttpOnly cookie).
Added
- Quick search from anywhere (⌘⇧Space, Ctrl+Shift+Space) got better. With nothing typed it shows your recent searches and the documents you opened from it, so the shortcut takes you back to what you were doing. Results show documents by the names you know them by (an upload's original name, "Contract.pdf · from Ada"). A search that finds nothing says why and offers "Did you mean", as the main window does. ⌘↵ (Ctrl+↵) shows the file in its folder and ⇧↵ opens every result in Duct. Results arrive faster: the pause before searching is 60 ms instead of 140.
- Choose the quick-search keys in Settings › Features. If another app already uses them, Duct says so there instead of the shortcut silently doing nothing.
- The workspace: open two documents side by side (Side by side on a result or in the document list). PDFs are drawn as they are, with search words highlighted; Word, Excel, PowerPoint, OpenDocument, EPUB, email, Markdown, code, text and every other format show the text Duct read, a page, slide, sheet or chapter at a time, and images can switch to the text read from them (OCR). Pick or change either document from the pane's title. Switch between side by side and stacked, drag the divider to resize (arrow keys work too; double-click resets to half and half), and Duct remembers your choice.
- Shared notebooks. On a server where people sign in, a notebook belongs to whoever made it and is private until they share it, from Share in the workspace: with people, a whole domain or everyone on the server, to read or to edit. Notes show who added them, open notebooks pick up others' changes within seconds, and Notebooks in the sidebar lists yours and the ones shared with you. Sharing a notebook never shares the documents it quotes: people only see notes from documents they can open. Notebooks made before this stay everyone's.
PUT /api/notebooks/:id/sharing. - Links to notebooks from the desktop app: Share › Link anyone can open uploads the notebook's quotes, document names, pages and comments (never documents, paths or who wrote what) to Tensflare as a link that works for 7, 30 or 90 days, then is deleted. Make a new one or take it down from the notebook or the account website. Needs a free Tensflare account; the privacy ledger lists these uploads under Tensflare.
- Public notebook links on team servers: a notebook's owner can make a link anyone can open without signing in, served by the team's own server. The page shows the notes its owner can see, leaves out who added them, isn't indexed by search engines, and counts how often it was opened (nothing about visitors is kept). A new link replaces the old one, and the owner can turn it off. Admins can switch public links off for the whole server (Settings › Features › Public notebook links).
POST/DELETE /api/notebooks/:id/public-link,GET /n/:token. - Send a notebook as a page: one .html file anyone can open in a browser, on a phone too, with no Duct and no account. It holds the quotes, document names, pages and comments, never file paths, and runs no scripts. Someone with Duct can add it to their notebooks with Import a shared notebook (
POST /api/notebooks/import), which also reads Duct's JSON exports. - Notebooks: select text in either document and choose Add to notes. The quote goes into a named notebook with its document and page, where you can comment on it, reorder and delete notes, and export the notebook to Word, Markdown, CSV or JSON, with each quote's source and your comment.
/api/notebooks,/api/notes,GET /api/document-text; see docs/api.md. - No dead ends: a search that finds nothing says what Duct searched, what it couldn't look inside (scans with no text yet, password-protected and unreadable files, files still being read), how many results the filters hid, and "Did you mean" using the spelling in your documents.
GET /api/searchreturns this ashelp. - Permission-aware team search: on a server with sign-in, results from Google Drive and Microsoft 365 follow each file's sharing at the source, so people only find what they could open there. Sharing links ("anyone with the link") don't count, and files whose sharing can't be read stay with the person who connected the source. It applies to search, Ask, the documents list, opening, exporting, the deadlines radar and suggestions. Each source can instead be visible to everyone, or to chosen people and domains (Settings › Library,
PUT /api/connectors/:id/visibility). Member tokens only see documents open to everyone.duct.setDocumentAccess()andSearchScope.viewerfor the library. - The deadlines radar: the home screen's "Coming up" lists dates documents say something expires, is due or renews on (a lease that expires in 12 days, an invoice whose due date passed, a licence that renews on Friday), read on this computer from the words next to each date, so signing and issue dates don't count. Recently passed, the next 30 days, and up to a year ahead; each opens the document at the date. Duct says "due date passed", not "overdue", since it can't know what was paid. A switch in Settings › Features;
GET /api/deadlines,duct.deadlines(). - WhatsApp as a source: + Add › Add a WhatsApp chat takes the .zip from WhatsApp's Export chat (with media). The conversation is indexed day by day ("8 March 2026" in results) and every attachment Duct can read is indexed with who sent it, in which chat and when: results say "WhatsApp · from Chidi Okafor in Okafor Holdings · 8 Mar 2026", the sender's name is searchable with the file ("chidi contract"), and the file's date is when it was sent, so the Modified filters work. Exporting the chat again updates it. iPhone and Android exports, either date order, 12- and 24-hour times. Exported chat files in watched folders are read by day too.
POST /api/whatsapp. - The first minute: while Duct reads a new library it shows what it's finding ("Found so far: 60 invoices, 25 contracts, 12 meeting minutes and 6 CVs", worked out on the device from names and opening text). When it's done, instead of dropping you into the app, it asks you to search for a document you couldn't find last week, with searches to try drawn from your own files (kinds of document and names that recur across them), each checked to find something. The suggestions stay on the home screen.
GET /api/discover,duct.discover(). - The privacy ledger (Settings › Privacy): every connection Duct makes to another computer, recorded on the device by Duct itself: where to, what for (Tensflare, the AI provider you chose, cloud sources, sign-in, web pages you added), how many requests and how much was sent. When nothing left, it says "Nothing." It watches
fetch, Node'shttp/https(used by provider SDKs) and, in the desktop app, the windows' own requests; connections to this computer (a local Ollama) aren't counted. Kept 30 days;GET/DELETE /api/ledger. - Offline: Duct says "You're offline. Everything in Duct is still searchable."
- "Why this result?": each result can show the words that matched as written in the passage (including other forms, such as "termination" for "terminate"), a file-name match, or a match by meaning (
whyin the API). - Duct in your own cloud:
deploy/has Docker Compose with automatic HTTPS (Caddy) and Kubernetes manifests; see docs/deploy.md. Shared servers can sign people in with the organisation's identity provider (OpenID Connect: Google Workspace, Entra ID,...
v0.2.0 — Multi-Provider Embeddings
What's new
8 embedding providers (up from 2): OpenAI, Gemini, Cohere, Voyage AI, Mistral, Jina AI, Ollama, and any OpenAI-compatible API.
- Factory pattern with auto-detection from environment variables
- All SDKs loaded lazily — no startup overhead
- Settings UI dropdown with all providers
- New optional SDKs as optionalDependencies
- Full docs updated with provider comparison tables
Breaking changes
None. The existing OpenAI and Gemini embedders work exactly as before.