A folder someone shared was reachable only by switching the whole app to their account from the profile menu — which nobody would look in for files, and which pointed mail, calendar and contacts at them too. The server refused all three, so nothing leaked; it was the app claiming to be somewhere it could not go.
Files now lists shared accounts itself, under Shared with me, and opens them in place. Only Files moves: accountId in its store is the account being browsed, ownAccountId is the reader's, and nothing else in the app notices.
Why routing alone could never have fixed it
Stalwart advertises the whole capability set on a shared account — mail, calendars, contacts, sieve, the lot, identical to a personal one, whatever was actually shared. Checked live on 0.16.19:
personal [email protected] : mail submission calendars contacts sieve filenode … (17)
shared [email protected] : mail submission calendars contacts sieve filenode … (17)
So "route only the shared capabilities" is not implementable — the server says everything is shared. The list offers every account that is not the reader's own and lets its folders answer for themselves.
The mock's shared account now advertises the same full set, deliberately. A mock that quietly advertised only what it shared would have agreed with a fix that cannot work.
Shares went unseen until the next sign-in
They arrive in the JMAP session, fetched once and refreshed only when a session-state change is pushed to that tab. A share granted while the tab was open stayed invisible; one removed stayed on offer. That is two browsers disagreeing about whether an account still existed.
Opening Files now re-reads the session, throttled to once every 30s, and the section header carries a refresh button for when someone is waiting on a share they've just been promised.
Also
The sidebar's primary button on Files was Compose, which wrote mail from the file manager. It uploads now.
Verified
Against the mock, which grew a second account to make any of this testable: Shared with me lists it, opening it shows its folders and not the reader's, the header names whose they are, Back to my files returns, and the profile menu is not involved at any point.
328 web and 77 server tests pass. npm run typecheck clean.
Next
Attaching a file from Files to an email — a FileNode already has a blobId an email can reference, so it shouldn't need a re-upload. Separate PR.
A folder someone shared was reachable only by switching the whole app to their account from the profile menu — which nobody would look in for files, and which pointed mail, calendar and contacts at them too. The server refused all three, so nothing leaked; it was the app claiming to be somewhere it could not go.
Files now lists shared accounts itself, under **Shared with me**, and opens them in place. Only Files moves: `accountId` in its store is the account being browsed, `ownAccountId` is the reader's, and nothing else in the app notices.
### Why routing alone could never have fixed it
Stalwart advertises the **whole capability set** on a shared account — mail, calendars, contacts, sieve, the lot, identical to a personal one, whatever was actually shared. Checked live on 0.16.19:
```
personal [email protected] : mail submission calendars contacts sieve filenode … (17)
shared [email protected] : mail submission calendars contacts sieve filenode … (17)
```
So "route only the shared capabilities" is not implementable — the server says everything is shared. The list offers every account that is not the reader's own and lets its folders answer for themselves.
The mock's shared account now advertises the same full set, deliberately. A mock that quietly advertised only what it shared would have agreed with a fix that cannot work.
### Shares went unseen until the next sign-in
They arrive in the JMAP session, fetched once and refreshed only when a session-state change is pushed to that tab. A share granted while the tab was open stayed invisible; one removed stayed on offer. That is two browsers disagreeing about whether an account still existed.
Opening Files now re-reads the session, throttled to once every 30s, and the section header carries a refresh button for when someone is waiting on a share they've just been promised.
### Also
The sidebar's primary button on Files was **Compose**, which wrote mail from the file manager. It uploads now.
### Verified
Against the mock, which grew a second account to make any of this testable: Shared with me lists it, opening it shows its folders and not the reader's, the header names whose they are, Back to my files returns, and the profile menu is not involved at any point.
328 web and 77 server tests pass. `npm run typecheck` clean.
### Next
Attaching a file from Files to an email — a `FileNode` already has a `blobId` an email can reference, so it shouldn't need a re-upload. Separate PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
**Merged** 2026-08-27 as coffey-labs/ihasmail@88f9474b2402
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A folder someone shared was reachable only by switching the whole app to their account from the profile menu — which nobody would look in for files, and which pointed mail, calendar and contacts at them too. The server refused all three, so nothing leaked; it was the app claiming to be somewhere it could not go.
Files now lists shared accounts itself, under Shared with me, and opens them in place. Only Files moves:
accountIdin its store is the account being browsed,ownAccountIdis the reader's, and nothing else in the app notices.Why routing alone could never have fixed it
Stalwart advertises the whole capability set on a shared account — mail, calendars, contacts, sieve, the lot, identical to a personal one, whatever was actually shared. Checked live on 0.16.19:
So "route only the shared capabilities" is not implementable — the server says everything is shared. The list offers every account that is not the reader's own and lets its folders answer for themselves.
The mock's shared account now advertises the same full set, deliberately. A mock that quietly advertised only what it shared would have agreed with a fix that cannot work.
Shares went unseen until the next sign-in
They arrive in the JMAP session, fetched once and refreshed only when a session-state change is pushed to that tab. A share granted while the tab was open stayed invisible; one removed stayed on offer. That is two browsers disagreeing about whether an account still existed.
Opening Files now re-reads the session, throttled to once every 30s, and the section header carries a refresh button for when someone is waiting on a share they've just been promised.
Also
The sidebar's primary button on Files was Compose, which wrote mail from the file manager. It uploads now.
Verified
Against the mock, which grew a second account to make any of this testable: Shared with me lists it, opening it shows its folders and not the reader's, the header names whose they are, Back to my files returns, and the profile menu is not involved at any point.
328 web and 77 server tests pass.
npm run typecheckclean.Next
Attaching a file from Files to an email — a
FileNodealready has ablobIdan email can reference, so it shouldn't need a re-upload. Separate PR.🤖 Generated with Claude Code
Merged 2026-08-27 as coffey-labs/ihasmail@88f9474b24
Rebuilt from: git history, session transcript.