Read any mailbox: Microsoft 365, and IMAP with CalDAV, behind one reader - #6
Merged
Merged
Conversation
GoogleAccount becomes LinkedAccount, with a provider and room for more than one per person, because a work Outlook and a personal Gmail are both where recruiters write. Google, Microsoft Graph, and IMAP plus CalDAV each implement one MailReader and CalendarReader interface; every screen and tool merges across accounts and names which one each thread and event came from. Read-only is the interface, not a convention: there is no send. Microsoft 365 connects through an Entra app registration an admin sets under Admin → Configuration → Accounts. Anything else — Fastmail, iCloud, Yahoo, a self-hosted server — connects from a form with presets and an app password, both servers logged in to before anything is saved. Slack and Discord sit on the picker as coming soon. Tools: list_linked_accounts, connect_imap_account, test_linked_account, disconnect_account, and the correspondence tools made provider-neutral; admin_get_microsoft_config, admin_set_microsoft_config. No SMTP: the app never sends, and that is what makes handing over an inbox safe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JBeuHate5rfgqAkLcHzJww
…hosts The IMAP library is Node-only, and pipeline.ts — imported by client components for its labels — had started reaching it through the accounts layer. The schedule merge lives in src/lib/data/schedule.ts now, and the mail libraries are server externals. A hostname typed into the account form is a request the server makes on the person's behalf. Loopback, link-local and multicast targets are refused, after resolving the name; a server on a home LAN still works. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JBeuHate5rfgqAkLcHzJww
…with every mailbox Main redrew Connections as rows with a library sheet and added the archive while this branch generalised accounts. The accounts now sit in main's layout — one row per connected account, the library's Accounts tab offers Google, Microsoft 365, any IMAP and CalDAV provider, and Slack and Discord as coming soon — and the archive's filters are ported into the matching code that replaced data/google.ts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JBeuHate5rfgqAkLcHzJww
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Mail and calendar were Google-only. Now a person can connect Google, Microsoft 365 / Outlook.com, or any provider that speaks IMAP and CalDAV — and more than one, since a work Outlook and a personal Gmail are both where recruiters write. Slack and Discord sit on the account picker as coming soon.
What changed
GoogleAccount→LinkedAccount. Aprovidercolumn (GOOGLE, MICROSOFT, IMAP), provider-neutralfeatures("mail", "calendar") replacing Google's scope URLs, IMAP and CalDAV fields on the same row, and unique on (userId, provider, email) so several accounts fit. The migration renames rather than recreates, so a Google connection from the previous release survives with its scopes rewritten.src/lib/accounts/types.tsdefinesMailReaderandCalendarReader.google.ts(moved fromgoogle-api.ts),microsoft.ts(Graph),imap.ts(imapflow + mailparser) andcaldav.ts(tsdav + ical.js) implement them; nothing else in the app knows which answered. There is no write in the interface, so no provider can grow one.src/lib/data/accounts.tsreplacesdata/google.ts: every thread and event says whichaccountit came from, thread ids are scoped to their account, one account failing is a line inwarningsrather than an empty result, and Microsoft's rotated refresh tokens are kept.list_linked_accounts,connect_imap_account,test_linked_account,disconnect_account, pluslist_correspondence/search_email/get_email_thread/search_calendarwith an optionalaccountId. Admin:admin_get_microsoft_config,admin_set_microsoft_config.get_google_connectionanddisconnect_googleare gone; the briefing and every description say "Settings → Connections".src/lib/data/schedule.tssopipeline.ts, which client components import for constants, no longer reaches the Node-only mail libraries; those are server externals innext.config.ts.Deliberately not
No SMTP. The app never sends on anyone's behalf; read-only is what makes handing over an inbox safe, and sending is a product decision to make on purpose rather than a side effect of "support IMAP".
Verification
npm run typecheckandnpm run buildpass;node tools/gen-tool-docs.mjs --checkis current (106 tools for a member, 138 for an admin). The migration matchesprisma migrate diffoutput for the schema. A tenant-isolation audit of the data layer, providers, tools, actions and OAuth routes found no violations. The ical.js calls in the CalDAV reader were exercised against a sample calendar with a weekly recurrence. Not exercised end to end here: no database or provider credentials in the build container, so the first real connect of each kind on an instance is the test.Docs
docs/tools/accounts.mdxreplacesgoogle.mdx; newdocs/self-hosting/microsoft.mdx; the inbox-and-calendar guide, the Google self-hosting page, README and the decision log are updated.🤖 Generated with Claude Code
https://claude.ai/code/session_01JBeuHate5rfgqAkLcHzJww
Generated by Claude Code