@bumail/imap

中文文档
0.6.0发布于 2026/11/05MITTypeScriptES Module零依赖近 30 天下载 2117

Bun.listen(RFC 9051)上的IMAP4rev2服务器:STARTLS,LOGIN和AUTHENTICATE PLAIN只加密一次,IDLE,MOVE,UIDPLUS,CONDSTORE,QRESYNC,BINARY,Special-USE,服务于任何@bumail/store

此软件包已被标记为弃用

moved to @softistx/bumail-imap (private, GitHub Packages)

中文使用文档
文档目录9 节
  1. 143街和993街的店铺
  2. 有雷鸟试一试
  3. 限制
  4. 不重启而更新证书
  5. TCP 代理服务器后面
  6. 陷阱
  7. API 密码
  8. 文档
  9. 许可证

@ bumail/ imap (帮助)

Bun 的 IMAP 服务器, 打开 Bun.listen: IMAP4rev2(RFC 9051),与 IMAP4rev1客户端也服务;STATLS或隐含的TLS;LOGIN和 AUTHENTICATE PLAIN,只加密一次;IDLE,MOVE,UIDPLUS,CONDSTORE,QRESYNC,BINARY,Special-USE, 名单 -- -- 电子研究和文学+。 它服务于任何邮件 @bumail/store MailStore,并读取信件 @bumail/mime。命令线客户端( curl, Python ) imaplib, (中文(简体) ), imapclient以可重复的文字与它相对应; 雷鸟和苹果邮报通过人工检查. 运行,没有,是 编辑 界面。 。 。 .

bun add @bumail/imap @bumail/store @bumail/mime

@bumail/store 财务报告和审定财务报表 @bumail/mime 是同等端: 安装您的应用程序版本 用户. typescript 是一个可选的对等点,用于类型.

143街和993街的店铺

这个 authenticate 钩子是用户登录的方式:它获得证书和 回答要服务的存储账户的id,或 null 拒绝。 没错 只通过 TLS 询问 .

import { createImapServer } from '@bumail/imap';
import type { ImapServerOptions } from '@bumail/imap';
import { MemoryMailStore } from '@bumail/store';

const store = new MemoryMailStore();
const alice = await store.createAccount('[email protected]');
await store.createMailbox(alice.id, { name: 'INBOX', role: 'inbox' });
await store.createMailbox(alice.id, { name: 'Sent', role: 'sent' });
await store.createMailbox(alice.id, { name: 'Archive', role: 'archive' });

const passwords = new Map([['alice', await Bun.password.hash('correct horse')]]);
const accounts = new Map([['alice', alice.id]]);

const options: ImapServerOptions = {
    hostname: 'imap.example.com',
    store,
    tls: {
        key: await Bun.file('/etc/ssl/imap.example.com.key').text(),
        cert: await Bun.file('/etc/ssl/imap.example.com.crt').text(),
    },
    async authenticate({ username, password }) {
        const hash = passwords.get(username);
        if (!hash || !(await Bun.password.verify(password, hash))) return null;
        return accounts.get(username) ?? null;
    },
};

// 143: clear at first, STARTTLS before logging in.
await createImapServer(options).listen({ port: 143 });
// 993: TLS from the first byte (RFC 8314), what most clients pick.
await createImapServer({ ...options, implicitTls: true }).listen({ port: 993 });

寄入商店的邮件(截至 @bumail/smtp是因为 onData达到 iDLE中的客户端 idleInterval 秒(默认为10个)。 打电话 server.notify(accountId) 送货后立即到达.

有雷鸟试一试

  1. 用 Thunderbird 信任的证书运行上方的服务器 本地尝试,一个自签名的 localhost,接受雷鸟的 安全例外 当它问.
  2. 账户设置 – 账户动作 – 添加邮件账户,则 手动配置。 。 。 .
  3. 来电: IMAP, 主机 localhost端口 993 与 SSL/TLS 系统,或 143 与 削减; 认证 普通密码; 用户名 alice。 。 。 .
  4. 请检查date=中的日期值 (帮助) INBOX, Sent and Archive with their special use 角色,读取消息,标记被读取,并移动到存档.

Apple Mail 相同:IMAP,端口993, 使用 TLS/ SSL, (中文(简体) ), 密码 认证。 也没有为0.5.0运行;见 界面 要检查什么.

限制

选项默认
maxConnections1 000个再来一次 * BYE [UNAVAILABLE] Too many connections, try later
maxMessageSize25个小米B最大附录,宣布为 APPENDLIMIT;流入商店,从未持有
maxLiteralSize64 基贝任何其他命令的最大字节
timeout1800 s (英语)服务器挂起前秒闲置; 至少 1800 (RFC 9051 §5.4)
loginTimeout60岁s从问候到登录 不论客户寄出多少
handshakeTimeout10 s (简体中文)从TCP连接到TLS握手结束 proxyProtocol,对于一个信任的代理机头;通过它,套接字关闭
idleInterval10 s (简体中文)两人在IDLE期间看店
hookTimeout60岁s页:1 authenticate 解决
maxSearchIndex10 000人当商店在搜索存在之前持有一些存储时, 其字节中有一个 SEARCH 索引

每个计时器最多2 147 483秒,什么 setTimeout 等会儿 A 类 命令行最多为64 KiB,最多为32升,列表或 搜索密钥嵌入最多 32 个深; 在登录前, 最多2升 每人一基贝. 一个邮箱的名字最多有32级和1024个字符 (每等255个),最多16个图案的列表,最多32个TEXT的SEARCH或 车钥匙 A 类 序列设置, 例如 1:4294967295 决不会 扩大。 一个停止阅读的客户端仍然挂断在 loginTimeout 或者说 timeout,并还原其位置 maxConnections 一次; 没有排队, 它仍然读到 BYE,然后一个干净的结束,每当它再次读.

在隐含的 TLS 上,套接字占据其位置 maxConnections 从 TCP连接上,在握手前, 关闭,没有字, handshakeTimeout 连接未完成的秒后; 问候,或 BYE 一个完整的服务器,等待握手。 这么说吧 连接且从未发送客户端 Hello 的套接字无法填充服务器 :

await createImapServer({ ...options, implicitTls: true, handshakeTimeout: 10 }).listen({ port: 993 });

不重启而更新证书

setTls 需要更新的密钥和证书:下一次STARTLS升级或 隐含的 TLS 连接使用它们, 而已打开的会话保留 他们有TLS。 双对先读后检查; 无法使用 拒绝方式 INVALID_OPTION 和旧对子继续使用.

// After certbot, Traefik or an ACME client wrote new files:
await server.setTls({
    key: Bun.file('/etc/ssl/imap.example.com.key'),
    cert: Bun.file('/etc/ssl/imap.example.com.crt'),
});

在每个持有一对的服务器上调用(143和993为2个). 这个 指南 说它如何在每一种港口工作.

TCP 代理服务器后面

TCP代理,如Traefik TCP路由器或HAProxy,隐藏客户端: 服务器查看代理地址。 与 proxyProtocol,代词 列入清单 trusted 首先发送 PROXY 协议头( 第 1 或 2 版) , 财务报告和审定财务报表 session.remoteAddress,用于 authenticate 财务报告和审定财务报表 onError,这是 又是客户地址 默认关闭.

// The proxy at 10.0.0.5 sends a PROXY header; anyone else is served as before.
const proxyProtocol = { trusted: ['10.0.0.5'] };

await createImapServer({ ...options, proxyProtocol }).listen({ port: 143 });
await createImapServer({ ...options, implicitTls: true, proxyProtocol }).listen({ port: 993 });

信任的代理服务器必须在内部发送有效的信头 handshakeTimeout,或 它的连接在没有单词的情况下被重置 。 代理通过 TLS : 993上,服务器仍然持有证书。 这个 指南 拥有Traefik配置和每个案件.

陷阱

  • LOGIN仅在TLS之后. 在明确的连接上,服务器广告 LOGINDISABLED 和答案 NO [PRIVACYREQUIRED] 改为LOGIN 和 AUTHENTICATE 不读密码. tls 需要这样做 理由; 无法清晰登录 .
  • 通讯和通讯 (RFC 7162): (法语) ENABLE CONDSTORE, (中文(简体) ), HIGHESTMODSEQ, (中文(简体) ), FETCH 与 MODSEQ 财务报告和审定财务报表 CHANGEDSINCE, (中文(简体) ), STORE 与 UNCHANGEDSINCE (单位:千美元)OK [MODIFIED …] 命名它留下的消息) 财务报告和审定财务报表 SEARCH MODSEQ。使用 modeseq 命令使它能够,并且 MODSEQ 财务报告和审定财务报表 UID 然后进入每一个 FETCH 告诉旗帜. ENABLE QRESYNC 制造 VANISHED 替换 EXPUNGE 用于连接,以及 SELECT INBOX (QRESYNC (<uidvalidity> <modseq> [<known UIDs> [(<seq> <uids>)]])) 财务报告和审定财务报表 UID FETCH … (CHANGEDSINCE n VANISHED) 回复 VANISHED (EARLIER){\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}从商店的记忆中 清除,或者,当它不再记得 至今,每个已知的UID 邮箱缺乏。 没有任何人被告知没有首先要求这样做.
  • 比利时 (RFC 3516): (法语) FETCH BINARY[1.2]<0.1024>, (中文(简体) ), BINARY.PEEK[…] 财务报告和审定财务报表 BINARY.SIZE[…] 以树叶为基部64或可引用打印 由服务器解码, 部分计数解码字节; 有 NUL 的数据 当作字面上的8, ~{n},附录为 ~{n} 或者说 ~{n+} 8-位内容( NUL 为 NO [UNKNOWN-CTE]) (中文(简体) ). BINARY 套装 \Seen, (中文(简体) ), BINARY.PEEK 没有。 一个 编码服务器无法解码 NO [UNKNOWN-CTE]; 多块或多块 message/rfc822 实值 NO [CANNOT]解码是流的,从未被持有.
  • Append 线程其消息: 标题(最多64 KiB)为 字符串中, 因此一个回覆会加入其父线程; 一个较长的页眉 启动它自己的线程.
  • 新的 UIDPLUS 名称 (RFC 4315):附录答复 [APPENDUID <uidvalidity> <uid>],COPY和UID COPY回答 [COPYUID <uidvalidity> <source UIDs> <new UIDs>]移动说 相同的无标记 OK 目 录(续) EXPUNGE 答复(RFC 6851) (第4.3节)。 同时删除的另一客户端的信息被排除于两者之外 设定, 保持步态 .
  • SEARCH是商店的,在解码文本上. BODY 匹配已解码 电文的文本部分(base64,可引用打印和 字符集被撤销, HTML 读作文本, 附件被忽略)和 TEXT 那个 和信件的每个解码头值, 两者都无大小写. 天数 (单位:千美元)BEFORE, (中文(简体) ), ON, (中文(简体) ), SINCE, (中文(简体) ), SENT*)是协调世界时的天数;没有 Date 日期为抵达 SENT*。搜索前存储的信息 存在是由第一个需要它的SEARCH索引的, maxSearchIndex 时间 时间; 过去的命令是 NO [UNAVAILABLE]数字 : 重试, 或运行 bumail store index 一次 一次吗 附录索引其信息.
  • 最后一个消息的序列号是 BAD, (中文(简体) ), 作为 RFC 9051 §6.4.4说;一个UID设置,没有消息的名称不是.
  • 登录前,一个字数最多为 1 KiB, 随便啦 maxLiteralSize 说 密码比那还要长 通过AUTHENTICATE PLAIN.
  • 移动到选中的邮箱中可以, 且不会改变 : 没有 EXPUNGE同样的UID.
  • trusted 列出代理,没有其他. 列出的对等点可以 要求任何客户地址。 从不列出一个范围客户端连接, 也不要 0.0.0.0/0 在公共端口上: trusted: ['10.0.0.5']。 。 。 .

API 密码

导出
createImapServer(options)服务器; 丢出一个 ImapError (单位:千美元)INVALID_OPTION) 在一个糟糕的选择
ImapServerlisten({ port, hostname? }),解决了束缚 { port, hostname } (一次;再次,甚至在第一次解决之前,就抛出) ALREADY_LISTENING; 在默认 TLS 上, 键或证书不能读取或使用抛出 INVALID_OPTION, (中文(简体) ), setTls({ key, cert }) (用于新STARTLS升级的更新配对和新的隐含的TLS连接;开放会话保留它们的配对;无法使用的配对 INVALID_OPTION 旧的将永居其中, stop(closeConnections?) (单位:千美元)true 挂在每一个客户端, 在STARTLS之后, notify(accountId) 为了唤醒账户的IDLE会话, connections,打开的连接数
ImapServerOptionshostname, (中文(简体) ), store, (中文(简体) ), tls, (中文(简体) ), implicitTls, (中文(简体) ), authenticate,上述限制, proxyProtocol, (中文(简体) ), onError
ImapSession什麽 authenticate 财务报告和审定财务报表 onError 接收: id, (中文(简体) ), remoteAddress, (中文(简体) ), secure, (中文(简体) ), user, (中文(简体) ), accountId,以及 data 为了你自己的国家
AuthResult什麽 authenticate 答案:账户编号,或 null 编号 : undefined 拒绝
Credentials什麽 authenticate 接收 : mechanism (单位:千美元)LOGIN 或者说 PLAIN, (中文(简体) ), username, (中文(简体) ), password, (中文(简体) ), authorizationId?
TlsOptionskey 财务报告和审定财务报表 cert,作为 Bun.listen 拿着
ProxyProtocolOptionstrusted: 读取 PROXY 标题的代理地址 IPv4 和 IPv6 地址和 CIDR
ImapError, (中文(简体) ), ImapErrorCodecode数字 : INVALID_OPTION, (中文(简体) ), ALREADY_LISTENING, (中文(简体) ), STOPPED 联合国 stop() 来过之前 listen (已解决) HOOK_TIMEOUT,哪个 onError 获得
encodeUtf7(name), (中文(简体) ), decodeUtf7(name)邮箱名来自修改过的UTF-7(RFC 3501 § 5.1.3),因为IMAP4rev1客户端会写

文档

  • 指数:各页,何时读出.
  • 指南: 会话,每个命令,存储映射到IMAP,IDLE和 notify,运行在 TCP 代理机后, RFCs 跟踪和没有.
  • 解决问题:每一个错误,以及回复的客户端按其准确的文字报告,分为配置,登录和连接(包含一个代理),语法,邮箱和消息.
  • 间接: 哪些客户端运行于服务器( curl, imaplib, (中文(简体) ), imapclient他们查过了 雷鸟和苹果邮报没有运行.
  • 路线图:什么是未来,什么是没有计划的.

许可证

麻省理工学院

版本与发布记录

@bumail/imap 共发布 14 个版本,首次发布于 2026/10/04;过去 12 个月发布了 14 个版本。

版本发布日期距今
0.6.0latest2026/11/053 天前
0.5.62026/11/054 天前
0.5.52026/11/044 天前
0.5.42026/11/045 天前
0.5.32026/11/045 天前
0.5.22026/11/036 天前

关于 @bumail/imap 的常见问题

@bumail/imap 是什么?

Bun.listen(RFC 9051)上的IMAP4rev2服务器:STARTLS,LOGIN和AUTHENTICATE PLAIN只加密一次,IDLE,MOVE,UIDPLUS,CONDSTORE,QRESYNC,BINARY,Special-USE,服务于任何@bumail/store注意:该包已被作者标记为弃用——moved to @softistx/bumail-imap (private, GitHub Packages)

如何安装 @bumail/imap?

在项目目录运行 npm install @bumail/imap,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@bumail/mime(^0.4.0)、@bumail/store(^0.9.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@bumail/imap 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bumail/imap 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bumail/imap 可以商用吗?

@bumail/imap 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bumail/imap 的下载量有多少?

根据 npm 官方下载统计,@bumail/imap 在 2026/10/09 – 2026/11/07 这 30 天内被下载 2,117 次,日均约 71 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bumail/imap 还在维护吗?

最新版本 0.6.0 发布于 2026/11/05(3 天前);至今共发布 14 个版本,其中过去 12 个月发布了 14 个;首次发布于 2026/10/04。作者已将其标记为弃用,新项目建议寻找替代方案。