I've spent the last month implementing and testing IMAP support for my app [1].
It has... not been very fun.
Before we were limited to providers with a REST API, so implementing IMAP should in theory allow us to support a much wider range of email servers with a much lower support burden.
Although the protocol spec is tight, it seems like some providers randomly bug out or hang [2], respond with custom error messages, offer bespoke functionality [3], or generally don't comply with the spec in breaking ways [4].
As a result I'm more worried about this going forward than I had hoped.
I'll now also happily support anything that can help modernize email, so I'll apply to the developer program and will be watching JMAP closely.
Having worked with IMAP before myself, the spec is very vague a lot. It's a bad spec.
We had our own IMAP server implementation and had all sorts of problems even between different versions of outlook.
Each project had an inbox which you could cc in, resulting in huge inboxes (for the 2000s at least). You could also drag and drop an email you'd received into the project folder to share it with colleagues. And we had mysterious disappearing emails, but only at some clients.
One (of the many) problems I remember is that messages could have an ID, but the spec declared that it could change between sessions, so one version of outlook stored that in a short uint (max 65535) because why would you need more for a temporary id. So have fun if you sent anything more than that. And of course the client had different versions of outlook on different sites so it took ages to even figure out what was happening.
Another was that you had to make several back and forths on every request, messages about the messages, to say the payload and number of messages, but the spec was gleefully vague about the order this should happen in. So back then what worked for one email client resulted in an empty inbox for another.
I don't think we ever got it working properly with Firebird.
Your app looks really cool! I've been looking for something like this for a while now to deal with my crazy inbox. You've made a lot of right choices like opt-in cookies, credit-based purchases, and audit logs, so keep on keeping on! Thanks and best of luck!
Its still far better than POP. Its really nice how you can let the server decode the structure of a message and then selectively download only the parts you actually want to display.
This is ironic as imap was designed specifically for mobile and remote machines.
The definition of those terms is quite different of course today, so my statement isn’t meant to contradict yours. I doubt Mark ever saw a laptop, at least when working with imap. Imap was a real step forward at the time.
Are you talking about the IMAP RFC? It's the worst spec I have ever seen and so many parts are open to interpretation. If you think it's tight it must be because you didn't realized all the ambiguities.
Though I have no complaints about the way they went about implementing it (open standards hooray!), as a mail client author and someone with lots of experience with mail in general - I'm NACK to JMAP. I think that it comes with some questionable design decisions (HTTP? JSON?) which on their face I imagine look fine to the typical HNer, but in many ways is a regression from IMAP. I would prefer to see the limitations of IMAP addressed with a few carefully written extensions to the spec - which is already extensible and has a thriving set of useful extensions in the wild.
By no means is IMAP a particularly good protocol, but it is extremely broadly supported and designed more with email in mind than JMAP appears to be, which seems to focus more on bringing shiny web tech to email regardless of its utility in such a context. It's also one of many closely interlinked mail-related specifications, and JMAP fails to integrate well with many related specs imo.
You might not like it, but every programming language in existence has easy to use HTTP and JSON parsing libraries. It's basically a solved problem.
As a software developer I dread working with protocols that are not via HTTP because most often then not the clients are badly designed, leaky and I can't fix it by changing the client.
And I love JSON in spite of its inefficiencies, because it is easy to debug and there are really good JSON parsing libraries for the statically typed languages I'm using (Scala, Haskell). You basically declare your high level types and the library will derive (de)serialization and validation logic for you.
Also consider that this isn't necessarily about building full fledged email clients. Maybe you want an automated way to read an Inbox and import certain messages in your database. Maybe you build a browser extension that counts the number of unread messages. There are many, many micro tasks that developers would be interested in doing, if they had an easy way to do it.
And do you know what protocol does NOT have good libraries for every language in existence? IMAP.
It's basically not a competition from my perspective, HTTP/JSON wins by default.
"We have libraries for these technologies" is a poor substitute for "These technologies don't really solve the problems that need to be solved."
The nice thing about the old internet protocols is that they tried to distinguish between abstraction and implementation layers. (Not always successfully or effectively, but they did at least try.)
Modern protocol designs seem to do the opposite. They start from a default implementation layer - usually REST, HTTP, and JSON - and then try to build the abstraction layer out of that.
This isn't necessarily a good approach. But it is a reflection of the difference in culture - the change from hackers who were comfortable with open-ended bottom-up development, and modern developers who seem to prefer snap-together development using standard libraries.
> every programming language in existence has easy to use HTTP and JSON parsing libraries
That argument doesn't hold much water. Every programming language in existence also has easy to use socket libraries. Are you implying that HTTP and JSON are less error-prone than whatever-over-TCP? There's nothing magical about either HTTP or JSON that makes them the right tool for every job. But I guess when all you have is a hammer ...
You're very welcome to join the EXTRA working group at IETF and propose the extensions that you think would make IMAP better. Apart from working on JMAP, I have also written an extension to IMAP and may write another one next year bringing some of the push stuff from JMAP back to IMAP as well. But JMAP is based on many years of working on servers, middleware and (at least on the web) clients.
We did actually extent IMAP with some experimental stuff first (cross folder threading and thread collapsing for example) but it never solved the batching and proxying difficulties and the cost of instantiating message numbers per-session that are un-avoidable parts of the IMAP model. By the way, engineers who work on other servers have said similar things to us - IMAP is costly to implement in terms of server resources when you're doing true concurrency, high availability, etc.
> IMAP is costly to implement in terms of server resources when you're doing true concurrency, high availability
Yes, IMAP requires expensive coordination among multi-master servers to generate UIDs in such a way that won't mess with client synchronization. In other words, IMAP is not a fully distributed design, it assumes a centralized control plane (think Raft or Paxos), i.e. a single central server with many distributed clients, which is a shame for new systems building on CRDTs.
How does JMAP address this achilles heel of IMAP? Merkle tree sync?
JMAP is literally the only positive thing that has happened to email in decades, please don't dismiss it (or do you know of anything else in the works that has any chance of success?).
HTTP and JSON are such an insignificant price to pay for the sliver of hope that email can be saved and not being in complete control of google et al.
The real positive thing that needs to happen to email, is putting an end to near-monopoly of gmail. Email is great. It's decentralized. You can simply roll your own. You can use whatever client you like. Apparently even using HTTP/JSON.
But you can't send mail to anyone using gmail, which is nearly everyone, unless you play by Google's rules. And even then they'll still put you in their spam folder, or just randomly blackhole you without any explanation or recourse.
JMAP vs IMAP vs POP is moot as far as I'm concerned. As long as my mail client continues to work, I'm good. If this gains any traction, I'm sure dovecot and the likes will add support for it (if they haven't already). But no amount of protocol fiddling can fix the fundamental issues of email centralization.
> HTTP and JSON are such an insignificant price to pay for the sliver of hope that email can be saved and not being in complete control of google et al.
How would JMAP accomplish that, in a way that other protocols don't?
Some interesting work was done on adding prescence and chat to email that im disappointed not to see in jmap. Be great if we could chat to anyone with a name and dns domain instead of being stuck in a silo like facebook.
HTTP is not designed for persistent connections (WebSockets were hacked on later, but JMAP does not use them anyway). This turns a push protocol into a poll protocol, which is a stark regression. It's also got all sorts of other crap built-in which is really not necessary for this use-case but which an intrepid implementer will have to deal with regardless.
JSON does not cope well with binary data, which is common in emails. It fails to elegantly deal with the various email encodings which exist in the wild. Consider as well that all JSON numbers are floating point numbers, despite the fact that floating point numbers provide absolutely nothing of value to this spec and in fact are more likely to introduce bugs than not. And embedded devices can't deal with floats quickly or elegantly, but still need to implement them if they want to use JSON-based protocols. And for what gain!
In short, JMAP reinvents the wheel but worse for the sake of making it easier for web developers to build web shit around email. This kind of stupid change for change's sake is an epidemic in the webshit scene. IMAP is warty but it's fine. Let it be. When your only hammer is JavaScript, everyone else's thumb looks like a nail.
Pretty cool protocol. I've been following this for a couple of years. I had already written a library for an earlier version of JMAP (back then references worked differently / didn’t exist); and it was interesting to see how that improved in the IETF process.
I wrote a library [1] and a POC email client for Android [2] earlier this year. It takes a moment to fully understand a now fairly complex protocol but when you get the hang of it it becomes very powerful.
Sadly the server support isn’t really there yet. The support for Cyrus hasn’t been released yet (you need git) and some vital functionality like push [3] is still missing. Also no word from Dovecot yet.
But it's rudimentary - it will need sql_db and dblookup components to make it fast enough to integrate with an mboxevent reading component to do the actual pushes. At least - that's how we'll probably implement it at Fastmail, and there's a plan to extract the key parts of our non-blocking push daemon and lift them up into the Cyrus project.
Do you use Lttrs daily? I'm wondering if it works in practice (even when using non-standard setup) or if it's just a proof-of-concept (that'd mean it may take some time to mature).
No I don’t use it daily. It can’t even send emails yet and you have to set your login credentials at compile time. I got frustrated having to deal with HTML emails and now I’m waiting for the servers to mature a bit. And honestly also for dovecot to announce support. I mean I love what the fastmail+cyrus team is doing; but if Dovecot is not going to support it, it is probably not worth it to build a JMAP-only MUA.
I'm a believer in open protocols, but in recent years, I'm also loving Exchange and ActiveSync. I know that's not going to be a popular opinion here. To be honest, I don't know much about how the magic works, but the configuration is simple, the push is reliable, and everything just works. It doesn't drain my mobile and it doesn't lose its indexing and download for hours like IMAP always seems to. And besides, it's mature -- Exchange and EAS have been around for many years.
So here's to JMAP becoming the open standard that replaces that.
Sure you can, I use z-push [0] integrated with Kopano which works great. You can also use z-push with IMAP, CalDAV and CardDAV. The only issue I know of using IMAP is that the z-push server polls your mailbox, so it's not true push, but vastly better than the battery drain of IMAP IDLE.
While I appreciate the value it brings, I can see the 5 years of struggle in the sheer size of the multiple specs needed to define some pretty rudimentary mail client/server functionality (retrieve, send, push, pagination, etc).
At some points they use JSON (a data format) to describe procedures... like a programming language written in JSON. It feels a bit like using a screwdriver handle as a hammer.
How did we end up with JMAP instead of something simpler or modular, especially with GraphQL and other similar innovations on the radar?
> At some points they use JSON (a data format) to describe procedures... like a programming language written in JSON. It feels a bit like using a screwdriver handle as a hammer.
And folks laugh at me for suggesting S-expressions until I'm blue in the face!
From what I gather it should be possible to use 2FA with JMAP. I‘m looking forward to no longer having to decide between using third-party email clients and properly securing the account that’s probably most worth securing.
What would be nice is if the servers supported using client side certificates as an authentication factor. It's possible to configure postfix and dovecot to support this [1], but I haven't looked into whether it's possible to require both the certificate and the username and password to authenticate.
For IMAP already, there are three main approaches to logging in that get used: use your main password for the service to access IMAP, use a separate token for each client (“app passwords” is the most common term of art, and what we call them at Fastmail), and OAuth, which can then have whatever restrictions you like, 2FA or whatever. IMAP providers each support one or more of these techniques. (At this time, we support the second. Gmail supports the third and, if you have “less secure apps” turned on, the first.)
The JMAP core deliberately doesn’t express opinions about authentication technique, because that’s a divisive topic where there is no clearly right answer. We shall see what happens there; conventions will rise.
For Fastmail’s internal JMAP usage, we have our own authentication flow, which will entail 2FA if you have that set up on your account. I cannot remark at this time about what our plans are around authentication for public JMAP access.
But this is my conclusion here: look, I love JMAP, but I don’t believe it actually changes anything on this front over existing email protocols.
One high level problem that jumps out at me is: When do we authenticate?
For web sites WebAuthn drops nicely into the existing login flow. The user knows what they're trying to do is log in, and so when they're prompted to touch the button or whatever it makes sense in that context.
But today most email apps just silently update all the time. So are we authenticating when the app starts and then re-using some credentials obtained to re-connect as necessary? Is that safe? Even if that's all we do (which might be safe) it means the user gets prompted for WebAuthn (to press the button) when the mail app starts, even if they aren't currently interested in email. If they dismiss it, when do we prompt them again? Because meanwhile they of course don't get any new email.
The fastest route to MUA support is compatibility with existing MDAs like procmail, maildrop, and the like. At the very least a JMAP client that populates a local Maildir would get you Thunderbird, Evolution, mutt, and more.
Could a fuse filesystem do the trick for maildir emulation? Assuming that'd be beneficial to a full maildir, but I guess it'd only be so at large sizes.
The strong suit of JMAP (among other things) is getting access to specific properties of a server side parsed email. It will do the MIME parsing for you; separate attachments and text bodies from each other and for example allows a client to fetch only Subject and senders instead of the entire email. By brainlessly dumping all that into a Maildir you'd be giving up a lot of the benefits of JMAP. I'm a big fan of JMAP but I don’t see it replacing IMAP for (desktop) clients that will keep a fully synced Maildir locally.
They are providing an open source server implementation, which is a great way to increase adoption. Adding support to widely used open source mail clients is also a great option, but I doubt usage of mutt is anywhere near high enough to provide a good return on investment.
Implementing in the default mail client on Ubuntu might be worth it, or maybe the most popular open source web mail client that exists could be worth it. Ideally though they need adoption in something like Outlook, Gmail, Apple Mail/iCloud, etc, but these are all closed source so a) impossible to do without support from the vendor, and b) of limited use to increasing the community of JMAP implementors.
That's what the ircv3 guys did. I think it's a cancerous attitude: you get your protocol implemented regardless of whether it's good or not, since most managers of open source software can't say no to free code
I don’t think anyone in the IRCv3 team has provided free code to any other project just for implementing IRCv3 functionality. In fact, the opposite is true, most projects have to spend significant effort to support it — but believe that it is worth it.
If they want it to be taken seriously by the open source community, might as well start with mail clients used by the top contributors to things like the Linux kernel and core open source libraries used in most Linux distros. This is how to help ensure adoption and making it a replacement for IMAP.
One thing I find weird (probably because I’m new to JMAP and still ignorant) is having position AND anchor+anchorOffset. Why have both? Surely the latter is sufficient and safer (as any position is only guaranteed to be valid when the last response was written)? From a glance of the spec I didn’t see any explanation of why. If anyone could shed more light it’d be much appreciated.
Both are necessary because you may not have the necessary IDs.
anchor + anchorOffset is useful when you use pagination and new entries might appear at the head of the list but you don’t want them to mess with your pagination once you’re inside.
position is useful when you have a total count and wish to jump in at the middle; as a concrete example, when you use lazy loading instead of pagination. For example, if I have a mailbox of 100,000 emails, and jump to the bottom: I don’t want to need to fetch all the IDs (probably well over 1MB to fetch), just so that I can specify an anchor. I want instead to ask “give me the thirty messages from position 99,970”.
(Note that when I speak of lazy loading I’m not talking of infinite scrolling, but rather of when you know that you have N messages, and that each message is allocated M pixels of vertical space, so you can allocate an exact space, and provide a proper scrollbar. This is how Fastmail does it and how desktop email clients have historically done it.)
I've spent the last month implementing and testing IMAP support for my app [1].
It has... not been very fun.
Before we were limited to providers with a REST API, so implementing IMAP should in theory allow us to support a much wider range of email servers with a much lower support burden.
Although the protocol spec is tight, it seems like some providers randomly bug out or hang [2], respond with custom error messages, offer bespoke functionality [3], or generally don't comply with the spec in breaking ways [4].
As a result I'm more worried about this going forward than I had hoped.
I'll now also happily support anything that can help modernize email, so I'll apply to the developer program and will be watching JMAP closely.
[1]: https://leavemealone.app/
[2]: Yahoo Mail always down (https://downdetector.com/status/yahoo-mail)
[3]: https://developers.google.com/gmail/imap/imap-extensions
[4]: https://github.com/mscdex/node-imap/issues/775
We had our own IMAP server implementation and had all sorts of problems even between different versions of outlook.
Each project had an inbox which you could cc in, resulting in huge inboxes (for the 2000s at least). You could also drag and drop an email you'd received into the project folder to share it with colleagues. And we had mysterious disappearing emails, but only at some clients.
One (of the many) problems I remember is that messages could have an ID, but the spec declared that it could change between sessions, so one version of outlook stored that in a short uint (max 65535) because why would you need more for a temporary id. So have fun if you sent anything more than that. And of course the client had different versions of outlook on different sites so it took ages to even figure out what was happening.
Another was that you had to make several back and forths on every request, messages about the messages, to say the payload and number of messages, but the spec was gleefully vague about the order this should happen in. So back then what worked for one email client resulted in an empty inbox for another.
I don't think we ever got it working properly with Firebird.
[1]: https://sockettools.com
(Yes, I once wrote a mobile IMAP client.)
The definition of those terms is quite different of course today, so my statement isn’t meant to contradict yours. I doubt Mark ever saw a laptop, at least when working with imap. Imap was a real step forward at the time.
Are you talking about the IMAP RFC? It's the worst spec I have ever seen and so many parts are open to interpretation. If you think it's tight it must be because you didn't realized all the ambiguities.
I guess I was just lucky and can remain happily ignorant.
By no means is IMAP a particularly good protocol, but it is extremely broadly supported and designed more with email in mind than JMAP appears to be, which seems to focus more on bringing shiny web tech to email regardless of its utility in such a context. It's also one of many closely interlinked mail-related specifications, and JMAP fails to integrate well with many related specs imo.
You might not like it, but every programming language in existence has easy to use HTTP and JSON parsing libraries. It's basically a solved problem.
As a software developer I dread working with protocols that are not via HTTP because most often then not the clients are badly designed, leaky and I can't fix it by changing the client.
And I love JSON in spite of its inefficiencies, because it is easy to debug and there are really good JSON parsing libraries for the statically typed languages I'm using (Scala, Haskell). You basically declare your high level types and the library will derive (de)serialization and validation logic for you.
Also consider that this isn't necessarily about building full fledged email clients. Maybe you want an automated way to read an Inbox and import certain messages in your database. Maybe you build a browser extension that counts the number of unread messages. There are many, many micro tasks that developers would be interested in doing, if they had an easy way to do it.
And do you know what protocol does NOT have good libraries for every language in existence? IMAP.
It's basically not a competition from my perspective, HTTP/JSON wins by default.
The nice thing about the old internet protocols is that they tried to distinguish between abstraction and implementation layers. (Not always successfully or effectively, but they did at least try.)
Modern protocol designs seem to do the opposite. They start from a default implementation layer - usually REST, HTTP, and JSON - and then try to build the abstraction layer out of that.
This isn't necessarily a good approach. But it is a reflection of the difference in culture - the change from hackers who were comfortable with open-ended bottom-up development, and modern developers who seem to prefer snap-together development using standard libraries.
That argument doesn't hold much water. Every programming language in existence also has easy to use socket libraries. Are you implying that HTTP and JSON are less error-prone than whatever-over-TCP? There's nothing magical about either HTTP or JSON that makes them the right tool for every job. But I guess when all you have is a hammer ...
We did actually extent IMAP with some experimental stuff first (cross folder threading and thread collapsing for example) but it never solved the batching and proxying difficulties and the cost of instantiating message numbers per-session that are un-avoidable parts of the IMAP model. By the way, engineers who work on other servers have said similar things to us - IMAP is costly to implement in terms of server resources when you're doing true concurrency, high availability, etc.
Yes, IMAP requires expensive coordination among multi-master servers to generate UIDs in such a way that won't mess with client synchronization. In other words, IMAP is not a fully distributed design, it assumes a centralized control plane (think Raft or Paxos), i.e. a single central server with many distributed clients, which is a shame for new systems building on CRDTs.
How does JMAP address this achilles heel of IMAP? Merkle tree sync?
More detail on the problem: https://news.ycombinator.com/item?id=20479011
HTTP and JSON are such an insignificant price to pay for the sliver of hope that email can be saved and not being in complete control of google et al.
But you can't send mail to anyone using gmail, which is nearly everyone, unless you play by Google's rules. And even then they'll still put you in their spam folder, or just randomly blackhole you without any explanation or recourse.
JMAP vs IMAP vs POP is moot as far as I'm concerned. As long as my mail client continues to work, I'm good. If this gains any traction, I'm sure dovecot and the likes will add support for it (if they haven't already). But no amount of protocol fiddling can fix the fundamental issues of email centralization.
How would JMAP accomplish that, in a way that other protocols don't?
Those are the premises for JMAP, from which the rest follows.
JSON does not cope well with binary data, which is common in emails. It fails to elegantly deal with the various email encodings which exist in the wild. Consider as well that all JSON numbers are floating point numbers, despite the fact that floating point numbers provide absolutely nothing of value to this spec and in fact are more likely to introduce bugs than not. And embedded devices can't deal with floats quickly or elegantly, but still need to implement them if they want to use JSON-based protocols. And for what gain!
In short, JMAP reinvents the wheel but worse for the sake of making it easier for web developers to build web shit around email. This kind of stupid change for change's sake is an epidemic in the webshit scene. IMAP is warty but it's fine. Let it be. When your only hammer is JavaScript, everyone else's thumb looks like a nail.
Sadly the server support isn’t really there yet. The support for Cyrus hasn’t been released yet (you need git) and some vital functionality like push [3] is still missing. Also no word from Dovecot yet.
[1]: https://github.com/iNPUTmice/jmap
[2]: https://github.com/iNPUTmice/lttrs-android
[3]: https://github.com/cyrusimap/cyrus-imapd/issues/2714
https://github.com/cyrusimap/cyrus-imapd/commit/7144a747267c...
But it's rudimentary - it will need sql_db and dblookup components to make it fast enough to integrate with an mboxevent reading component to do the actual pushes. At least - that's how we'll probably implement it at Fastmail, and there's a plan to extract the key parts of our non-blocking push daemon and lift them up into the Cyrus project.
So here's to JMAP becoming the open standard that replaces that.
[0] http://z-push.org
While I appreciate the value it brings, I can see the 5 years of struggle in the sheer size of the multiple specs needed to define some pretty rudimentary mail client/server functionality (retrieve, send, push, pagination, etc).
At some points they use JSON (a data format) to describe procedures... like a programming language written in JSON. It feels a bit like using a screwdriver handle as a hammer.
How did we end up with JMAP instead of something simpler or modular, especially with GraphQL and other similar innovations on the radar?
And folks laugh at me for suggesting S-expressions until I'm blue in the face!
Seriously, this is one of the areas in which they excel (ref.: literally every line of Lisp ever written). They're a pretty nifty data format too (cf. https://sites.google.com/site/steveyegge2/the-emacs-problem).
[1] https://blog.mortis.eu/blog/2017/06/dovecot-and-postfix-with...
The JMAP core deliberately doesn’t express opinions about authentication technique, because that’s a divisive topic where there is no clearly right answer. We shall see what happens there; conventions will rise.
For Fastmail’s internal JMAP usage, we have our own authentication flow, which will entail 2FA if you have that set up on your account. I cannot remark at this time about what our plans are around authentication for public JMAP access.
But this is my conclusion here: look, I love JMAP, but I don’t believe it actually changes anything on this front over existing email protocols.
For web sites WebAuthn drops nicely into the existing login flow. The user knows what they're trying to do is log in, and so when they're prompted to touch the button or whatever it makes sense in that context.
But today most email apps just silently update all the time. So are we authenticating when the app starts and then re-using some credentials obtained to re-connect as necessary? Is that safe? Even if that's all we do (which might be safe) it means the user gets prompted for WebAuthn (to press the button) when the mail app starts, even if they aren't currently interested in email. If they dismiss it, when do we prompt them again? Because meanwhile they of course don't get any new email.
They are providing an open source server implementation, which is a great way to increase adoption. Adding support to widely used open source mail clients is also a great option, but I doubt usage of mutt is anywhere near high enough to provide a good return on investment.
Implementing in the default mail client on Ubuntu might be worth it, or maybe the most popular open source web mail client that exists could be worth it. Ideally though they need adoption in something like Outlook, Gmail, Apple Mail/iCloud, etc, but these are all closed source so a) impossible to do without support from the vendor, and b) of limited use to increasing the community of JMAP implementors.
anchor + anchorOffset is useful when you use pagination and new entries might appear at the head of the list but you don’t want them to mess with your pagination once you’re inside.
position is useful when you have a total count and wish to jump in at the middle; as a concrete example, when you use lazy loading instead of pagination. For example, if I have a mailbox of 100,000 emails, and jump to the bottom: I don’t want to need to fetch all the IDs (probably well over 1MB to fetch), just so that I can specify an anchor. I want instead to ask “give me the thirty messages from position 99,970”.
(Note that when I speak of lazy loading I’m not talking of infinite scrolling, but rather of when you know that you have N messages, and that each message is allocated M pixels of vertical space, so you can allocate an exact space, and provide a proper scrollbar. This is how Fastmail does it and how desktop email clients have historically done it.)