emailengine-app 2.68.0 → 2.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.github/codeql/codeql-config.yml +16 -0
  2. package/.github/workflows/codeql.yml +102 -0
  3. package/.github/workflows/deploy.yml +8 -0
  4. package/.github/workflows/release.yaml +4 -0
  5. package/.github/workflows/test.yml +3 -0
  6. package/CHANGELOG.md +49 -0
  7. package/SECURITY.md +80 -0
  8. package/SECURITY.txt +27 -0
  9. package/config/default.toml +2 -0
  10. package/data/google-crawlers.json +13 -1
  11. package/lib/account.js +62 -25
  12. package/lib/api-routes/account-routes.js +493 -75
  13. package/lib/api-routes/blocklist-routes.js +337 -0
  14. package/lib/api-routes/delivery-test-routes.js +321 -0
  15. package/lib/api-routes/export-routes.js +1 -12
  16. package/lib/api-routes/gateway-routes.js +376 -0
  17. package/lib/api-routes/license-routes.js +142 -0
  18. package/lib/api-routes/mailbox-routes.js +318 -0
  19. package/lib/api-routes/message-routes.js +21 -129
  20. package/lib/api-routes/oauth2-app-routes.js +631 -0
  21. package/lib/api-routes/outbox-routes.js +173 -0
  22. package/lib/api-routes/pubsub-routes.js +98 -0
  23. package/lib/api-routes/route-helpers.js +45 -0
  24. package/lib/api-routes/settings-routes.js +331 -0
  25. package/lib/api-routes/stats-routes.js +77 -0
  26. package/lib/api-routes/submit-routes.js +472 -0
  27. package/lib/api-routes/template-routes.js +7 -55
  28. package/lib/api-routes/token-routes.js +297 -0
  29. package/lib/api-routes/webhook-route-routes.js +152 -0
  30. package/lib/email-client/gmail-client.js +14 -0
  31. package/lib/email-client/imap/mailbox.js +34 -11
  32. package/lib/email-client/imap/subconnection.js +20 -12
  33. package/lib/email-client/imap/sync-operations.js +130 -2
  34. package/lib/email-client/imap-client.js +116 -58
  35. package/lib/email-client/outlook-client.js +85 -13
  36. package/lib/export.js +60 -19
  37. package/lib/imapproxy/imap-core/lib/commands/starttls.js +18 -0
  38. package/lib/imapproxy/imap-core/lib/imap-command.js +7 -2
  39. package/lib/imapproxy/imap-core/lib/imap-connection.js +113 -23
  40. package/lib/imapproxy/imap-core/lib/imap-server.js +25 -1
  41. package/lib/imapproxy/imap-core/lib/imap-stream.js +26 -0
  42. package/lib/imapproxy/imap-server.js +92 -29
  43. package/lib/message-port-stream.js +113 -16
  44. package/lib/reject-worker-calls.js +42 -0
  45. package/lib/routes-ui.js +37 -8778
  46. package/lib/schemas.js +26 -1
  47. package/lib/tools.js +73 -0
  48. package/lib/ui-routes/account-routes.js +40 -210
  49. package/lib/ui-routes/admin-config-routes.js +913 -487
  50. package/lib/ui-routes/admin-entities-routes.js +1 -0
  51. package/lib/ui-routes/auth-routes.js +1339 -0
  52. package/lib/ui-routes/dashboard-routes.js +188 -0
  53. package/lib/ui-routes/document-store-routes.js +800 -0
  54. package/lib/ui-routes/export-routes.js +217 -0
  55. package/lib/ui-routes/internals-routes.js +354 -0
  56. package/lib/ui-routes/network-config-routes.js +759 -0
  57. package/lib/ui-routes/{oauth-routes.js → oauth-config-routes.js} +371 -91
  58. package/lib/ui-routes/route-helpers.js +316 -0
  59. package/lib/ui-routes/smtp-test-routes.js +236 -0
  60. package/lib/ui-routes/unsubscribe-routes.js +234 -0
  61. package/lib/webhook-request.js +36 -0
  62. package/package.json +17 -17
  63. package/sbom.json +1 -1
  64. package/server.js +217 -19
  65. package/static/licenses.html +52 -182
  66. package/translations/messages.pot +131 -151
  67. package/views/dashboard.hbs +7 -26
  68. package/views/internals/index.hbs +15 -0
  69. package/views/tokens/index.hbs +9 -0
  70. package/workers/api.js +198 -4401
  71. package/workers/export.js +87 -54
  72. package/workers/imap.js +29 -13
  73. package/workers/submit.js +20 -11
  74. package/workers/webhooks.js +6 -20
@@ -0,0 +1,16 @@
1
+ name: "EmailEngine CodeQL config"
2
+
3
+ # Exclude code that is not part of the production runtime from analysis.
4
+ # These paths generate only false-positive noise:
5
+ # - test fixtures intentionally disable TLS verification, build shell
6
+ # commands from fixed paths, etc.
7
+ # - vendored third-party browser assets (Bootstrap and other bundles) ship
8
+ # with their own known patterns and are not maintained here
9
+ # - developer helper scripts (e.g. test-token refresh) deliberately print
10
+ # tokens to the console for local debugging
11
+ paths-ignore:
12
+ - test
13
+ - '**/test/**'
14
+ - scripts
15
+ - static/bootstrap-4.6.2-dist
16
+ - static/vendor
@@ -0,0 +1,102 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL Advanced"
13
+
14
+ on:
15
+ push:
16
+ branches: [ "master" ]
17
+ pull_request:
18
+ branches: [ "master" ]
19
+ schedule:
20
+ - cron: '40 17 * * 6'
21
+
22
+ jobs:
23
+ analyze:
24
+ name: Analyze (${{ matrix.language }})
25
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
26
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
27
+ # - https://gh.io/supported-runners-and-hardware-resources
28
+ # - https://gh.io/using-larger-runners (GitHub.com only)
29
+ # Consider using larger runners or machines with greater resources for possible analysis time improvements.
30
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
31
+ permissions:
32
+ # required for all workflows
33
+ security-events: write
34
+
35
+ # required to fetch internal or private CodeQL packs
36
+ packages: read
37
+
38
+ # only required for workflows in private repositories
39
+ actions: read
40
+ contents: read
41
+
42
+ strategy:
43
+ fail-fast: false
44
+ matrix:
45
+ include:
46
+ - language: actions
47
+ build-mode: none
48
+ - language: javascript-typescript
49
+ build-mode: none
50
+ # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
51
+ # Use `c-cpp` to analyze code written in C, C++ or both
52
+ # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
53
+ # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
54
+ # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
55
+ # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
56
+ # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
57
+ # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
58
+ steps:
59
+ - name: Checkout repository
60
+ uses: actions/checkout@v4
61
+
62
+ # Add any setup steps before running the `github/codeql-action/init` action.
63
+ # This includes steps like installing compilers or runtimes (`actions/setup-node`
64
+ # or others). This is typically only required for manual builds.
65
+ # - name: Setup runtime (example)
66
+ # uses: actions/setup-example@v1
67
+
68
+ # Initializes the CodeQL tools for scanning.
69
+ - name: Initialize CodeQL
70
+ uses: github/codeql-action/init@v4
71
+ with:
72
+ languages: ${{ matrix.language }}
73
+ build-mode: ${{ matrix.build-mode }}
74
+ config-file: ./.github/codeql/codeql-config.yml
75
+ # If you wish to specify custom queries, you can do so here or in a config file.
76
+ # By default, queries listed here will override any specified in a config file.
77
+ # Prefix the list here with "+" to use these queries and those in the config file.
78
+
79
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
80
+ # queries: security-extended,security-and-quality
81
+
82
+ # If the analyze step fails for one of the languages you are analyzing with
83
+ # "We were unable to automatically build your code", modify the matrix above
84
+ # to set the build mode to "manual" for that language. Then modify this step
85
+ # to build your code.
86
+ # ℹ️ Command-line programs to run using the OS shell.
87
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
88
+ - name: Run manual build steps
89
+ if: matrix.build-mode == 'manual'
90
+ shell: bash
91
+ run: |
92
+ echo 'If you are using a "manual" build mode for one or more of the' \
93
+ 'languages you are analyzing, replace this with the commands to build' \
94
+ 'your code, for example:'
95
+ echo ' make bootstrap'
96
+ echo ' make release'
97
+ exit 1
98
+
99
+ - name: Perform CodeQL Analysis
100
+ uses: github/codeql-action/analyze@v4
101
+ with:
102
+ category: "/language:${{matrix.language}}"
@@ -5,6 +5,9 @@ on:
5
5
 
6
6
  name: Deploy test instance and Docker image
7
7
 
8
+ permissions:
9
+ contents: read
10
+
8
11
  concurrency:
9
12
  group: ${{ github.workflow }}-${{ github.ref }}
10
13
  cancel-in-progress: true
@@ -13,6 +16,7 @@ jobs:
13
16
  deploy:
14
17
  name: Deploy Demo App
15
18
  runs-on: ubuntu-24.04
19
+ timeout-minutes: 15
16
20
 
17
21
  steps:
18
22
  - name: Checkout
@@ -60,6 +64,10 @@ jobs:
60
64
  docker:
61
65
  name: Build Docker Image
62
66
  runs-on: ubuntu-24.04
67
+ timeout-minutes: 30
68
+ permissions:
69
+ contents: read
70
+ packages: write
63
71
 
64
72
  steps:
65
73
  - name: Checkout
@@ -63,6 +63,10 @@ jobs:
63
63
  runs-on: ubuntu-latest
64
64
  needs: release_please
65
65
  if: ${{needs.release_please.outputs.release_created}}
66
+ # Multi-arch builds run the arm64 leg under QEMU emulation, which can
67
+ # hang indefinitely on a misbehaving step. A normal build finishes in
68
+ # ~4 minutes, so fail fast instead of burning the 6-hour default limit.
69
+ timeout-minutes: 30
66
70
  steps:
67
71
  - run: echo version v${{needs.release_please.outputs.major}}.${{needs.release_please.outputs.minor}}.${{needs.release_please.outputs.patch}}
68
72
 
@@ -4,6 +4,9 @@ on:
4
4
  push:
5
5
  pull_request:
6
6
 
7
+ permissions:
8
+ contents: read
9
+
7
10
  concurrency:
8
11
  group: ${{ github.workflow }}-${{ github.ref }}
9
12
  cancel-in-progress: true
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.69.0](https://github.com/postalsys/emailengine/compare/v2.68.1...v2.69.0) (2026-06-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * add label/category filtering to message search ([c171c4e](https://github.com/postalsys/emailengine/commit/c171c4e1efe582f7e532b69108110a1c48aa3ede))
9
+ * detect unsafe Redis eviction config in dashboard warnings ([712d5d6](https://github.com/postalsys/emailengine/commit/712d5d69b99cc45c6f3e123a084ef054236e0110))
10
+ * rebuild lost IMAP sync state without replaying messageNew ([9fe391e](https://github.com/postalsys/emailengine/commit/9fe391e9d6350485aded780709305d698a4c65fb))
11
+ * surface token hash for identification in API, UI, and logs ([8e2a5a9](https://github.com/postalsys/emailengine/commit/8e2a5a9f2bb5194e92575c34c0006a8c4e21888d))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * avoid 500 on GET /admin/totp without a partial-auth session ([b6f2394](https://github.com/postalsys/emailengine/commit/b6f2394182379d94a05f95c84f5ce04ae6f87a05))
17
+ * bound inbound line length in the IMAP proxy parser ([9fb8a5e](https://github.com/postalsys/emailengine/commit/9fb8a5e301564dee46b48ca32a5b84e4832d96e8))
18
+ * close leaks and a worker crash found in the connection hardening review ([e97950e](https://github.com/postalsys/emailengine/commit/e97950e7e907ac5b5549302b8865c5d2bfb0d6e2))
19
+ * count undecodable export queue entries as skipped ([72e2498](https://github.com/postalsys/emailengine/commit/72e2498390f17ad24d5f7b1577540674d7e1fe09))
20
+ * distinguish SO_REUSEPORT fallback cause in logs and admin UI ([99ad9f3](https://github.com/postalsys/emailengine/commit/99ad9f3359eab5bafc4726535584874d4823f7bd))
21
+ * drain webhook response body on every delivery path ([2cc3e7a](https://github.com/postalsys/emailengine/commit/2cc3e7aef114b5b913dea5c363278e1a883d5a48))
22
+ * extract gettext strings from the decomposed ui-routes modules ([227e60f](https://github.com/postalsys/emailengine/commit/227e60ff4f354c5d0f55534877c8ab6fbdd92c7d))
23
+ * guard cross-thread download streams against early producer errors ([191ec62](https://github.com/postalsys/emailengine/commit/191ec6204887c3abe78da7a9944e3165b19c0b87))
24
+ * guard IMAP notification handlers against malformed events ([0248953](https://github.com/postalsys/emailengine/commit/0248953a5dcccba5d46704bf7113a70fb5ac3150))
25
+ * guard webhook notifications in BullMQ failure handlers ([e90048e](https://github.com/postalsys/emailengine/commit/e90048e7b7bd92916219602f92305342234df4a3))
26
+ * harden IMAP proxy STARTTLS against command injection ([3661ddd](https://github.com/postalsys/emailengine/commit/3661dddbf216b29ec8f074d3f6422dd4db5d9e7b))
27
+ * harden inter-thread IPC (worker-death call rejection, webhook guards, stream cleanup) ([0111a8e](https://github.com/postalsys/emailengine/commit/0111a8e2bf139c3412cc37e5e933fd5316cee8fc))
28
+ * harden message export against data loss and corruption ([540d867](https://github.com/postalsys/emailengine/commit/540d8678ec25f62e8123fc78fa929f4e4713b5d4))
29
+ * harden multiple-API-worker startup and centralize proxy-agent reload ([7f2db9e](https://github.com/postalsys/emailengine/commit/7f2db9e557d960aa1b7523b7d8678598683c2f29))
30
+ * list Outlook folders with slashes in the name ([dd35e7b](https://github.com/postalsys/emailengine/commit/dd35e7b262a47f9395811d75c4bcc1d9ab4c6ec0))
31
+ * only reseed lost IMAP sync state when no stored state exists ([78e0141](https://github.com/postalsys/emailengine/commit/78e0141e9f3478d2a2a1206c805258c6807a45d8))
32
+ * prevent IMAP worker crash on download stream errors ([d9d5396](https://github.com/postalsys/emailengine/commit/d9d539632cc245a8e2a14585fc7918186bee00b6))
33
+ * prevent post-BYE command dispatch in IMAP proxy teardown ([d51e92d](https://github.com/postalsys/emailengine/commit/d51e92d298e9373a6dc5c2eb0919885e161b45c7))
34
+ * re-arm a generous idle timeout for proxied IMAP connections ([627c6d4](https://github.com/postalsys/emailengine/commit/627c6d472e50f6fc3e958e4774226557a526018d))
35
+ * reject in-flight worker calls when a worker thread terminates ([08e7b01](https://github.com/postalsys/emailengine/commit/08e7b0128a4b8e9f9e5a96fefe49110bd032bd2c))
36
+ * release cross-thread download streams on abort and error ([6514249](https://github.com/postalsys/emailengine/commit/65142499d095bbcb5f0bb49755a2e24f6e52fa62))
37
+ * release MessagePort streams when message/attachment downloads fail ([787f4f5](https://github.com/postalsys/emailengine/commit/787f4f5ef6364ec1389c15269c8f7f8eaaa2142a))
38
+ * return 422 for unsupported label search and surface Outlook categories ([d981359](https://github.com/postalsys/emailengine/commit/d981359b78b61d2c8a03e4e35086a4c9674ce53a))
39
+ * return the append destination folder from uploadMessage ([96aed6b](https://github.com/postalsys/emailengine/commit/96aed6b1144e1d3ee9da79b3a1fb03606245f83c))
40
+ * send correctly typed SMTP and IMAP proxy state change notifications ([3fbd27f](https://github.com/postalsys/emailengine/commit/3fbd27fc08bb74f9738a3fc95d4177fa4978c263))
41
+ * tear down subconnections when deleting an IMAP account ([bd81a33](https://github.com/postalsys/emailengine/commit/bd81a33caf9400cbee2781a9180f1a901c1d6e48))
42
+
43
+ ## [2.68.1](https://github.com/postalsys/emailengine/compare/v2.68.0...v2.68.1) (2026-06-01)
44
+
45
+
46
+ ### Bug Fixes
47
+
48
+ * harden mergeObjects and tighten CodeQL scanning ([d35025d](https://github.com/postalsys/emailengine/commit/d35025daabf8fe7b3f5200b0000f52c3a012f4eb))
49
+ * prevent IMAP proxy worker crashes and connection leaks ([#596](https://github.com/postalsys/emailengine/issues/596)) ([4453330](https://github.com/postalsys/emailengine/commit/4453330ad38fb3e64692add1357d48227e1956e0))
50
+ * stop referencing prepared password string in error log ([50cdb89](https://github.com/postalsys/emailengine/commit/50cdb8998e27f33f700f267cb39ddba39e7d32d0))
51
+
3
52
  ## [2.68.0](https://github.com/postalsys/emailengine/compare/v2.67.3...v2.68.0) (2026-05-26)
4
53
 
5
54
 
package/SECURITY.md ADDED
@@ -0,0 +1,80 @@
1
+ # Security Policy
2
+
3
+ EmailEngine is a self-hosted email integration platform that stores email
4
+ account credentials and proxies access to IMAP/SMTP, the Gmail API, and the
5
+ Microsoft Graph API. Because it handles sensitive credentials and message
6
+ content, we take security reports seriously and aim to respond quickly.
7
+
8
+ ## Supported Versions
9
+
10
+ Security fixes are released only against the latest version. We do not backport
11
+ patches to older releases - upgrading to the current release line is the
12
+ supported way to receive security updates.
13
+
14
+ | Version | Supported |
15
+ | ------- | ------------------ |
16
+ | 2.x | :white_check_mark: |
17
+ | < 2.0 | :x: |
18
+
19
+ If you are on an older version, please upgrade. See the release notes at
20
+ <https://github.com/postalsys/emailengine/releases> before updating.
21
+
22
+ ## Reporting a Vulnerability
23
+
24
+ **Please do not report security vulnerabilities through public GitHub issues,
25
+ pull requests, or discussions.**
26
+
27
+ Report privately through one of the following channels:
28
+
29
+ 1. **GitHub Security Advisories (preferred).** Open a private report at
30
+ <https://github.com/postalsys/emailengine/security/advisories/new>. This keeps
31
+ the discussion private until a fix is published and lets us credit you.
32
+ 2. **Email.** Send details to **andris@postalsys.com** (the contact listed in
33
+ [`SECURITY.txt`](SECURITY.txt)). Encrypt sensitive details if possible.
34
+
35
+ When reporting, please include as much of the following as you can:
36
+
37
+ - The affected version(s) and environment (EmailEngine version, Node.js version,
38
+ OS, deployment method - npm, Docker, or prebuilt binary).
39
+ - The component involved (e.g. REST API, admin web UI, OAuth2 flows, IMAP/SMTP
40
+ proxy server, webhook delivery, credential encryption, the export pipeline).
41
+ - A clear description of the issue and its impact (e.g. authentication bypass,
42
+ privilege escalation, credential disclosure, SSRF, injection, information
43
+ disclosure, denial of service).
44
+ - A minimal proof of concept or reproduction steps.
45
+ - Any suggested remediation, if you have one.
46
+
47
+ We are a small team, so there is no guaranteed response time - sometimes reports
48
+ are handled within hours, sometimes they take longer. Accepted issues are fixed
49
+ in a new release and coordinated through a GitHub Security Advisory, and
50
+ reporters who wish to be named are credited.
51
+
52
+ ## CVEs
53
+
54
+ We track and disclose vulnerabilities through GitHub Security Advisories. We do
55
+ not request or manage CVE identifiers ourselves. If you need a CVE assigned for a
56
+ reported issue, please request one yourself - for example, through GitHub's own
57
+ CVE request flow on the published advisory, or another CNA.
58
+
59
+ ## Scope
60
+
61
+ In scope: the EmailEngine application source in this repository - the REST API
62
+ and admin web UI (authentication, session and token handling, CSRF protection),
63
+ OAuth2 application handling, credential encryption at rest, the IMAP/SMTP and
64
+ IMAP proxy servers, webhook delivery (including custom filter/transform
65
+ functions), the export pipeline, and inter-worker communication.
66
+
67
+ Out of scope:
68
+
69
+ - Vulnerabilities in your own application code that integrates with EmailEngine.
70
+ - Misconfiguration of your deployment - for example, exposing the admin
71
+ interface or REST API to untrusted networks, weak service secrets, an
72
+ unauthenticated or publicly reachable Redis instance, or missing TLS.
73
+ - Issues that require an already-compromised host or pre-existing administrator
74
+ access.
75
+ - Vulnerabilities in third-party email providers and services that EmailEngine
76
+ connects to (Gmail, Microsoft 365, arbitrary IMAP/SMTP servers).
77
+ - Social-engineering reports and missing security headers without a
78
+ demonstrated, concrete impact.
79
+
80
+ Thank you for helping keep EmailEngine and its users safe.
package/SECURITY.txt ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN PGP SIGNED MESSAGE-----
2
+ Hash: SHA256
3
+
4
+ Contact: https://github.com/postalsys/emailengine/security/advisories/new
5
+ Contact: mailto:andris@postalsys.com
6
+ Expires: 2027-06-01T00:00:00.000Z
7
+ Encryption: https://keys.openpgp.org/vks/v1/by-fingerprint/5D952A46E1D8C931F6364E01DC6C83F4D584D364
8
+ Preferred-Languages: en, et
9
+ Canonical: https://github.com/postalsys/emailengine/blob/master/SECURITY.txt
10
+ Policy: https://github.com/postalsys/emailengine/blob/master/SECURITY.md
11
+ -----BEGIN PGP SIGNATURE-----
12
+
13
+ iQJPBAEBCAA5FiEEXZUqRuHYyTH2Nk4B3GyD9NWE02QFAmodKpsbFIAAAAAABAAO
14
+ bWFudTIsMi41KzEuMTIsMCwzAAoJENxsg/TVhNNkbcEP/j1v3M9ebklJgaCxkVEl
15
+ xij24q4p+28s1ywf4y/aquBtcVkWd+y6vjG/f4RUUUtaSVChvnI5en14X8QuuSnk
16
+ egsRARvCY9E2dbodMSyDUMWS8slRcFTDczQJhtXBD8Fu+/tWPj1UfQ8xR87ZUZ5b
17
+ nOgCfCFnvSuDh9KBauUKZcSyuLCJ5saBuZ3RACzmz57wpzFE3vi4/q4tQAaBM5Za
18
+ m4293Yx8ioX01S3nR5VRL8dlpdMEFy0dj66v/OFu4p3MjGf+0cpmZ7YKC8U5PhNQ
19
+ FcQMTNkfBtkgc5lj42cKV8BBhCtN2og3u+Bhih0h0XItv4b/o9rWBtivXIQcHdtl
20
+ WyGrrbgfGl503aVj8N0cIQ1eaWAbuRRJVHwF/G11sX6ZvhiLHdHf5YSjOrmyhSmx
21
+ 6t2gsKMrrvoODRUOqpu8ll8Rosu6m/EaNWgh9KXJyYqgQqGS+NeQHcC51fWMLjxd
22
+ 7zvYrNKqhMEU8e6siyLdJJgqAJCCtl9vS/kLItPz9hBk3KE2/kXfvQc6OF4I4ziU
23
+ 3/Edf1v37koEnT07P5HdiGgUo+u4Vo8OUEm5Ih88EWaWijHGLPeJ0NyMfkwsveTw
24
+ n1z7pmCdZ+B9pglRGx3Mae7P8L1s1LeL8cQWo4QZ2nUcA5vtOiWfDAywlrN0mSNv
25
+ lfeE0/subU9kC04LRJ6NUhZp
26
+ =8lqK
27
+ -----END PGP SIGNATURE-----
@@ -1,6 +1,8 @@
1
1
 
2
2
  [workers]
3
3
  imap = 4
4
+ # Number of API/HTTP workers. Values >1 require SO_REUSEPORT (Linux); on other platforms it falls back to 1.
5
+ api = 1
4
6
  webhooks = 1
5
7
  "imapProxy" = 1
6
8
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "creationTime": "2026-05-25T14:45:59.000000",
2
+ "creationTime": "2026-06-09T14:45:50.000000",
3
3
  "prefixes": [
4
4
  {
5
5
  "ipv6Prefix": "2001:4860:4801:2008::/64"
@@ -307,6 +307,12 @@
307
307
  {
308
308
  "ipv6Prefix": "2001:4860:4801:207d::/64"
309
309
  },
310
+ {
311
+ "ipv6Prefix": "2001:4860:4801:207e::/64"
312
+ },
313
+ {
314
+ "ipv6Prefix": "2001:4860:4801:207f::/64"
315
+ },
310
316
  {
311
317
  "ipv6Prefix": "2001:4860:4801:2080::/64"
312
318
  },
@@ -790,6 +796,12 @@
790
796
  {
791
797
  "ipv4Prefix": "74.125.219.160/27"
792
798
  },
799
+ {
800
+ "ipv4Prefix": "74.125.219.192/27"
801
+ },
802
+ {
803
+ "ipv4Prefix": "74.125.219.224/27"
804
+ },
793
805
  {
794
806
  "ipv4Prefix": "74.125.219.32/27"
795
807
  },
package/lib/account.js CHANGED
@@ -1084,22 +1084,45 @@ class Account {
1084
1084
  };
1085
1085
  }
1086
1086
 
1087
+ // Creates the consumer side of a cross-thread download stream, with a guard listener:
1088
+ // the producer can post {error} before Hapi attaches its own 'error' listeners (or
1089
+ // while the setup call is still pending). Without a listener that emission is an
1090
+ // uncaught exception that kills the API worker.
1091
+ createDownloadStream(failureLogMessage) {
1092
+ const { port1, port2 } = new MessageChannel();
1093
+ const stream = new MessagePortReadable(port1);
1094
+
1095
+ stream.on('error', err => {
1096
+ this.logger.error({ msg: failureLogMessage, account: this.account, err });
1097
+ });
1098
+
1099
+ return { stream, port2 };
1100
+ }
1101
+
1087
1102
  async getRawMessage(message) {
1088
1103
  await this.loadAccountData(this.account, true);
1089
1104
 
1090
- const { port1, port2 } = new MessageChannel();
1091
- const stream = new MessagePortReadable(port1);
1105
+ const { stream, port2 } = this.createDownloadStream('Message source stream failed');
1092
1106
 
1093
- let streamCreated = await this.call(
1094
- {
1095
- cmd: 'getRawMessage',
1096
- account: this.account,
1097
- message,
1098
- timeout: this.timeout,
1099
- port: port2
1100
- },
1101
- [port2]
1102
- );
1107
+ let streamCreated;
1108
+ try {
1109
+ streamCreated = await this.call(
1110
+ {
1111
+ cmd: 'getRawMessage',
1112
+ account: this.account,
1113
+ message,
1114
+ timeout: this.timeout,
1115
+ port: port2
1116
+ },
1117
+ [port2]
1118
+ );
1119
+ } catch (err) {
1120
+ // The setup call failed (timeout, worker gone, 404). Destroy the reader so
1121
+ // its MessagePort + listener are released instead of leaking, and so any
1122
+ // late producer data is not buffered forever.
1123
+ stream.destroy();
1124
+ throw err;
1125
+ }
1103
1126
 
1104
1127
  if (streamCreated && streamCreated.headers) {
1105
1128
  stream.headers = streamCreated.headers;
@@ -1111,19 +1134,27 @@ class Account {
1111
1134
  async getAttachment(attachment) {
1112
1135
  await this.loadAccountData(this.account, true);
1113
1136
 
1114
- const { port1, port2 } = new MessageChannel();
1115
- const stream = new MessagePortReadable(port1);
1137
+ const { stream, port2 } = this.createDownloadStream('Attachment stream failed');
1116
1138
 
1117
- let streamCreated = await this.call(
1118
- {
1119
- cmd: 'getAttachment',
1120
- account: this.account,
1121
- attachment,
1122
- timeout: this.timeout,
1123
- port: port2
1124
- },
1125
- [port2]
1126
- );
1139
+ let streamCreated;
1140
+ try {
1141
+ streamCreated = await this.call(
1142
+ {
1143
+ cmd: 'getAttachment',
1144
+ account: this.account,
1145
+ attachment,
1146
+ timeout: this.timeout,
1147
+ port: port2
1148
+ },
1149
+ [port2]
1150
+ );
1151
+ } catch (err) {
1152
+ // The setup call failed (timeout, worker gone, 404). Destroy the reader so
1153
+ // its MessagePort + listener are released instead of leaking, and so any
1154
+ // late producer data is not buffered forever.
1155
+ stream.destroy();
1156
+ throw err;
1157
+ }
1127
1158
 
1128
1159
  if (streamCreated && streamCreated.headers) {
1129
1160
  stream.headers = streamCreated.headers;
@@ -1547,14 +1578,20 @@ class Account {
1547
1578
 
1548
1579
  // download large inline attachments not stored in ES
1549
1580
  for (let attachment of attachmentsToDownload) {
1581
+ let downloadStream;
1550
1582
  try {
1551
- let downloadStream = await this.getAttachment(attachment.id);
1583
+ downloadStream = await this.getAttachment(attachment.id);
1552
1584
  if (downloadStream) {
1553
1585
  let content = await download(downloadStream);
1554
1586
  this.logger.trace({ msg: 'Fetched attachment content', account: this.account, attachment, size: content.length });
1555
1587
  attachment.content = content.toString('base64');
1556
1588
  }
1557
1589
  } catch (err) {
1590
+ // Release the reader if download() failed mid-stream so its
1591
+ // MessagePort is not left open (destroy() is idempotent).
1592
+ if (downloadStream) {
1593
+ downloadStream.destroy();
1594
+ }
1558
1595
  this.logger.error({ msg: 'Failed to fetch attachment content', account: this.account, attachment, err });
1559
1596
  }
1560
1597
  }