forwardemail 0.0.1 → 0.14.17
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.
- package/LICENSE.md +83 -0
- package/README.md +376 -0
- package/dist/forwardemail.cjs +6903 -0
- package/dist/notifier/LICENSE-ntfytoast.txt +21 -0
- package/dist/notifier/LICENSE-terminal-notifier +9 -0
- package/dist/notifier/LICENSE-toasted-notifier.md +21 -0
- package/dist/notifier/icon.png +0 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/Info.plist +63 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier +0 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/PkgInfo +1 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/Resources/Terminal.icns +0 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- package/dist/notifier/mac.noindex/terminal-notifier.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- package/dist/notifier/ntfytoast.exe +0 -0
- package/package.json +39 -8
package/LICENSE.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Copyright](#copyright)
|
|
6
|
+
- [Disclaimer](#disclaimer)
|
|
7
|
+
- [Business Source License 1.1](#business-source-license-11)
|
|
8
|
+
|
|
9
|
+
## Copyright
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Forward Email LLC • <support@forwardemail.net> • <https://forwardemail.net>
|
|
12
|
+
|
|
13
|
+
## Disclaimer
|
|
14
|
+
|
|
15
|
+
This license applies apart from third-party linked files published with their own license.
|
|
16
|
+
|
|
17
|
+
All files are licensed under the [Business Source License 1.1](#business-source-license-11) below.
|
|
18
|
+
|
|
19
|
+
## Business Source License 1.1
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
|
|
23
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
|
|
27
|
+
Licensor: Forward Email LLC
|
|
28
|
+
Licensed Work: Forward Email. The Licensed Work is (c) 2026 Forward Email LLC
|
|
29
|
+
Additional Use Grant: You may make production use of the Licensed Work,
|
|
30
|
+
provided such use does not include offering the Licensed Work
|
|
31
|
+
to third parties on a hosted or embedded basis which is
|
|
32
|
+
competitive with Forward Email's products.
|
|
33
|
+
Change Date: Four years from the date the Licensed Work is published.
|
|
34
|
+
Change License: Mozilla Public License 2.0 (MPL-2.0) <https://mozilla.org/MPL/2.0/>
|
|
35
|
+
|
|
36
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
37
|
+
please contact <support@forwardemail.net>.
|
|
38
|
+
|
|
39
|
+
Notice
|
|
40
|
+
|
|
41
|
+
Business Source License 1.1
|
|
42
|
+
|
|
43
|
+
Terms
|
|
44
|
+
|
|
45
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
46
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
47
|
+
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
|
48
|
+
|
|
49
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
50
|
+
available distribution of a specific version of the Licensed Work under this
|
|
51
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
52
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
53
|
+
above terminate.
|
|
54
|
+
|
|
55
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
56
|
+
currently in effect as described in this License, you must purchase a
|
|
57
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
58
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
59
|
+
|
|
60
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
61
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
62
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
63
|
+
for each version of the Licensed Work released by Licensor.
|
|
64
|
+
|
|
65
|
+
You must conspicuously display this License on each original or modified copy
|
|
66
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
67
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
68
|
+
License apply to your use of that work.
|
|
69
|
+
|
|
70
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
71
|
+
terminate your rights under this License for the current and all other
|
|
72
|
+
versions of the Licensed Work.
|
|
73
|
+
|
|
74
|
+
This License does not grant you any right in any trademark or logo of
|
|
75
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
76
|
+
Licensor as expressly required by this License).
|
|
77
|
+
|
|
78
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
79
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
80
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
81
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
82
|
+
TITLE.
|
|
83
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# Forward Email in the Terminal
|
|
2
|
+
|
|
3
|
+
`forwardemail` runs the Forward Email webmail (mail, calendar, contacts and settings) inside a terminal. It is the same Svelte application as [mail.forwardemail.net](https://mail.forwardemail.net), with the same sign-in, encryption, workers and keyboard shortcuts. Instead of a browser engine, [TermDOM](https://github.com/bikeshaving/termdom) lays out and draws the page as text cells.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install -g forwardemail
|
|
7
|
+
forwardemail
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Install](#install)
|
|
13
|
+
- [Standalone executable (no Node.js needed)](#standalone-executable-no-nodejs-needed)
|
|
14
|
+
- [npm](#npm)
|
|
15
|
+
- [Manual download](#manual-download)
|
|
16
|
+
- [Usage](#usage)
|
|
17
|
+
- [Commands and options](#commands-and-options)
|
|
18
|
+
- [Keys and mouse](#keys-and-mouse)
|
|
19
|
+
- [Plain text](#plain-text)
|
|
20
|
+
- [Notifications](#notifications)
|
|
21
|
+
- [Environment variables](#environment-variables)
|
|
22
|
+
- [Where data is stored](#where-data-is-stored)
|
|
23
|
+
- [Custom styles](#custom-styles)
|
|
24
|
+
- [Self-hosted servers](#self-hosted-servers)
|
|
25
|
+
- [Updates](#updates)
|
|
26
|
+
- [Verifying downloads](#verifying-downloads)
|
|
27
|
+
- [Uninstall](#uninstall)
|
|
28
|
+
- [Differences from the browser](#differences-from-the-browser)
|
|
29
|
+
- [Troubleshooting](#troubleshooting)
|
|
30
|
+
- [How it works](#how-it-works)
|
|
31
|
+
- [Development](#development)
|
|
32
|
+
- [Releasing](#releasing)
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
### Standalone executable (no Node.js needed)
|
|
37
|
+
|
|
38
|
+
**macOS and Linux:**
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
curl -fsSL https://github.com/forwardemail/mail.forwardemail.net/releases/latest/download/install.sh | sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The executable goes to `~/.local/bin/forwardemail`. The script tells you if that directory is not on your `PATH`.
|
|
45
|
+
|
|
46
|
+
**Windows (PowerShell):**
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
irm https://github.com/forwardemail/mail.forwardemail.net/releases/latest/download/install.ps1 | iex
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The executable goes to `%LOCALAPPDATA%\Programs\forwardemail\forwardemail.exe`, and that folder is added to your user `PATH`. Open a new terminal afterwards.
|
|
53
|
+
|
|
54
|
+
Both installers download the build for your system and CPU from the latest [GitHub release](https://github.com/forwardemail/mail.forwardemail.net/releases/latest). They check it against the release's `SHA256SUMS.txt` and refuse to install it if the checksum does not match. Both accept the same settings:
|
|
55
|
+
|
|
56
|
+
| Variable | Effect |
|
|
57
|
+
| :------------------------- | :------------------------------------------------------------- |
|
|
58
|
+
| `FORWARDEMAIL_VERSION` | Install this version (for example `0.15.0`) instead of latest. |
|
|
59
|
+
| `FORWARDEMAIL_INSTALL_DIR` | Install into this directory instead of the default. |
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
curl -fsSL https://github.com/forwardemail/mail.forwardemail.net/releases/latest/download/install.sh | FORWARDEMAIL_INSTALL_DIR=/usr/local/bin sh
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Standalone builds are available for:
|
|
66
|
+
|
|
67
|
+
| System | x64 | arm64 |
|
|
68
|
+
| :------ | :------------------------- | :--------------------------- |
|
|
69
|
+
| Linux | `forwardemail-linux-x64` | `forwardemail-linux-arm64` |
|
|
70
|
+
| macOS | `forwardemail-darwin-x64` | `forwardemail-darwin-arm64` |
|
|
71
|
+
| Windows | `forwardemail-win-x64.exe` | `forwardemail-win-arm64.exe` |
|
|
72
|
+
|
|
73
|
+
The Linux builds need glibc. On Alpine and other musl systems, install with npm instead. On an Apple Silicon Mac, the installer picks the arm64 build even when the shell runs under Rosetta.
|
|
74
|
+
|
|
75
|
+
### npm
|
|
76
|
+
|
|
77
|
+
Requires Node.js 22 or later.
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
npm install -g forwardemail
|
|
81
|
+
# or
|
|
82
|
+
pnpm add -g forwardemail
|
|
83
|
+
# or, without installing
|
|
84
|
+
npx forwardemail --demo
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The npm package is a single JavaScript file of about 7 MB, plus the desktop notifier programs for macOS and Windows, with no dependencies. It runs anywhere Node.js does, including musl Linux, FreeBSD and other CPUs.
|
|
88
|
+
|
|
89
|
+
### Manual download
|
|
90
|
+
|
|
91
|
+
Download `forwardemail-<system>-<cpu>.gz` (or `.exe.gz`) from the [releases page](https://github.com/forwardemail/mail.forwardemail.net/releases/latest), then unpack it and make it executable:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
gunzip forwardemail-linux-x64.gz
|
|
95
|
+
chmod +x forwardemail-linux-x64
|
|
96
|
+
mv forwardemail-linux-x64 ~/.local/bin/forwardemail
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A file downloaded with a browser on macOS is quarantined by Gatekeeper; clear the flag with `xattr -d com.apple.quarantine ~/.local/bin/forwardemail`. Files fetched with `curl`, as the install script does, are not quarantined.
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
forwardemail # sign in and read your mail
|
|
105
|
+
forwardemail --demo # look around with sample data, no account needed
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Commands and options
|
|
109
|
+
|
|
110
|
+
| Command | What it does |
|
|
111
|
+
| :--------------------------- | :------------------------------------------------------------------------------------- |
|
|
112
|
+
| `forwardemail` | Opens Forward Email. |
|
|
113
|
+
| `forwardemail update` | Updates to the latest version (`upgrade` works too). |
|
|
114
|
+
| `forwardemail logout` | Signs out and deletes the session and local data stored on this device. |
|
|
115
|
+
| `forwardemail notifications` | Shows whether desktop notifications work here and what shows them. `--test` sends one. |
|
|
116
|
+
| `forwardemail help` | Prints help. |
|
|
117
|
+
|
|
118
|
+
| Option | What it does |
|
|
119
|
+
| :------------------- | :------------------------------------------------------------------- |
|
|
120
|
+
| `--demo` | Opens the demo account with sample mail, contacts and events. |
|
|
121
|
+
| `--api <url>` | Uses another API server, such as a self-hosted Forward Email. |
|
|
122
|
+
| `--data-dir <path>` | Keeps settings and the session in this directory (see below). |
|
|
123
|
+
| `--no-update-check` | Skips the daily update check. |
|
|
124
|
+
| `--no-hints` | Hides the key hints on the bottom row. |
|
|
125
|
+
| `--no-notifications` | Shows no desktop notifications. New mail still shows inside the app. |
|
|
126
|
+
| `-v`, `--version` | Prints the version. |
|
|
127
|
+
| `-h`, `--help` | Prints help, including where this machine keeps its data. |
|
|
128
|
+
|
|
129
|
+
`forwardemail` needs an interactive terminal. Running it with input or output redirected exits with an error.
|
|
130
|
+
|
|
131
|
+
### Keys and mouse
|
|
132
|
+
|
|
133
|
+
The bottom row always shows the keys for what is on screen, for example with a message open:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
Esc Back r Reply a Reply all f Forward e Archive s Star m Read/unread Del Delete ? Shortcuts
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Click a hint to press its key. Press `?` for every shortcut. To change one, choose **Change keys in Settings** in the bar while that list is open, or go to **Settings › Keyboard Shortcuts**. There, choose **Edit** next to the shortcut and press the new key (Esc cancels). The hint bar and the `?` list show the new key straight away. Hide the bar with `--no-hints`.
|
|
140
|
+
|
|
141
|
+
What you type right after a new-message, reply or forward shortcut (Ctrl+N, `r`, `a`, `f`, or the keys you gave them) goes into the new message, even before its window has appeared.
|
|
142
|
+
|
|
143
|
+
| Key | Action |
|
|
144
|
+
| :------------------ | :------------------------------------------------------------------------------- |
|
|
145
|
+
| `Tab` / `Shift+Tab` | Move between controls. The focused control is highlighted. |
|
|
146
|
+
| `Enter` / `Space` | Activate the focused button or link. |
|
|
147
|
+
| `↑` / `↓` | Move through the message list. |
|
|
148
|
+
| `Esc` | Close a dialog or menu; otherwise go back (from a message, a contact, settings). |
|
|
149
|
+
| `Ctrl+N` | Compose a new message. |
|
|
150
|
+
| `r` / `a` / `f` | Reply, reply all, forward. |
|
|
151
|
+
| `e` / `s` / `Del` | Archive, star, delete. |
|
|
152
|
+
| `?` | Show every keyboard shortcut. |
|
|
153
|
+
| `Ctrl+C` | Quit and give the terminal back. |
|
|
154
|
+
|
|
155
|
+
`forwardemail` uses the terminal's alternate screen, as `vim` and `less` do. The wheel scrolls the app, not your shell's history, and quitting puts your shell's screen back as it was.
|
|
156
|
+
|
|
157
|
+
A scroll bar on the right edge of the message list, a message, the compose window and other long panes shows when there is more to see. Click the bar above or below its thumb to scroll a page, or drag the thumb. In the compose window the message grows as you type, the window scrolls to keep the cursor in view, and the arrow keys scroll it back.
|
|
158
|
+
|
|
159
|
+
Clicking and scrolling with the wheel work with the mouse in any terminal that reports mouse events. That includes most modern terminals: iTerm2, Terminal.app, GNOME Terminal, Konsole, kitty, WezTerm, Alacritty, foot and Windows Terminal. A terminal of 120 columns or more shows the full desktop layout. At 80 columns or fewer (the webmail's 640 px phone breakpoint), the folder list moves behind the `≡` menu, as on a phone.
|
|
160
|
+
|
|
161
|
+
Drag over text, in a message or anywhere else, to select it. Releasing the button copies it to your clipboard, and the bottom row says **Copied**. The copy goes through the terminal's clipboard sequence (OSC 52), so it also works over SSH. In tmux, add `set -g set-clipboard on`. iTerm2 asks first: allow it under **Settings › General › Selection › Applications in terminal may access clipboard**. To use your terminal's own selection instead, hold its modifier while you drag: Option in iTerm2, Fn in Terminal.app, Shift in most others.
|
|
162
|
+
|
|
163
|
+
With the pointer on a button, the bottom row shows the button's name. Ghostty, kitty, WezTerm and foot also change the pointer to a hand over links and buttons and to an I-beam over text fields. `FORWARDEMAIL_POINTER=1` asks other terminals to do the same, and `FORWARDEMAIL_POINTER=0` stops it.
|
|
164
|
+
|
|
165
|
+
Links in messages, and links to other sites, open in your default browser. Files you save (attachments, exported contacts and calendars, a message as `.eml`) go to your Downloads folder, and a notice shows where.
|
|
166
|
+
|
|
167
|
+
### Plain text
|
|
168
|
+
|
|
169
|
+
Messages are shown, and new messages written, as plain text by default in the terminal. A reply quotes the original with `>` in front of each line. The **T** button next to **Save draft** in the compose window switches that message to rich text, with the formatting toolbar, and back. To change the default, use **Settings › Appearance › Use plain text by default** and **Settings › Privacy & Security › View emails as plain text**. The browser, desktop and mobile apps keep rich text as their default.
|
|
170
|
+
|
|
171
|
+
### Notifications
|
|
172
|
+
|
|
173
|
+
New mail that arrives while the terminal is in the background shows as a desktop notification, as it does in a browser tab you are not looking at. While you are using the terminal, it shows inside the app instead. Calendar reminders work the same way.
|
|
174
|
+
|
|
175
|
+
Turn them on in **Settings › Account › Notifications › Allow notifications**, or with **Turn on** in the offer the app shows once after you sign in. **Turn off notifications** in the same place turns them off again. `forwardemail notifications --test` sends a test notification from the command line.
|
|
176
|
+
|
|
177
|
+
| System | Shown by |
|
|
178
|
+
| :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
179
|
+
| macOS | Notification Center, through [terminal-notifier](https://github.com/julienXX/terminal-notifier) (included). Clicking one brings your terminal to the front. Allow it in System Settings › Notifications if macOS asks. terminal-notifier is an Intel program, so Apple Silicon Macs need Rosetta 2 (`softwareupdate --install-rosetta`). |
|
|
180
|
+
| Linux | Your desktop's notification service, through `notify-send` (package `libnotify-bin` on Debian and Ubuntu, `libnotify` on Fedora and Arch). |
|
|
181
|
+
| Windows | Windows notifications, through [ntfytoast](https://github.com/Aetherinox/ntfy-toast) (included). Clicking one opens the message. |
|
|
182
|
+
|
|
183
|
+
The client knows whether its terminal is in the foreground from the terminal's focus reports, which most terminals send. In tmux, add `set -g focus-events on` to `~/.tmux.conf`. A terminal that sends no focus reports counts as in the background after two minutes without a key press or mouse event.
|
|
184
|
+
|
|
185
|
+
Notifications are off over SSH, since they would appear on the remote computer, and on Linux without a desktop session. `FORWARDEMAIL_NOTIFICATIONS=1` turns them on anyway, and `FORWARDEMAIL_NOTIFICATIONS=0` (or `--no-notifications`) turns them off everywhere. Notifications come from the running app: nothing arrives after you quit it.
|
|
186
|
+
|
|
187
|
+
### Environment variables
|
|
188
|
+
|
|
189
|
+
| Variable | Effect |
|
|
190
|
+
| :----------------------------- | :----------------------------------------------------------------------------------- |
|
|
191
|
+
| `FORWARDEMAIL_HOME` | Same as `--data-dir`: the directory for settings and the session (see below). |
|
|
192
|
+
| `FORWARDEMAIL_API_URL` | Same as `--api`. |
|
|
193
|
+
| `FORWARDEMAIL_NO_UPDATE_CHECK` | Same as `--no-update-check`. |
|
|
194
|
+
| `FORWARDEMAIL_NO_HINTS` | Same as `--no-hints`. |
|
|
195
|
+
| `FORWARDEMAIL_NOTIFICATIONS` | `0` is the same as `--no-notifications`. `1` shows them over SSH too. |
|
|
196
|
+
| `FORWARDEMAIL_POINTER` | `1` asks any terminal to change the pointer shape, `0` none (see above). |
|
|
197
|
+
| `FORWARDEMAIL_DOWNLOADS` | Where saved files go, instead of `~/Downloads` (or your home directory without one). |
|
|
198
|
+
| `FORWARDEMAIL_DEBUG` | Writes the app's console output to `forwardemail.log` in the data directory. |
|
|
199
|
+
|
|
200
|
+
### Where data is stored
|
|
201
|
+
|
|
202
|
+
| System | Default directory |
|
|
203
|
+
| :------ | :---------------------------------------------------------------------------- |
|
|
204
|
+
| Linux | `$XDG_CONFIG_HOME/forwardemail`, or `~/.config/forwardemail` if that is unset |
|
|
205
|
+
| macOS | `~/Library/Application Support/forwardemail` |
|
|
206
|
+
| Windows | `%APPDATA%\forwardemail` |
|
|
207
|
+
|
|
208
|
+
To keep the data somewhere else, such as an encrypted volume or a synced folder, pass `--data-dir <path>` or set `FORWARDEMAIL_HOME`. A leading `~` means your home directory, and the directory is created on first use. Use the same setting each time you run `forwardemail` (an alias such as `alias forwardemail='forwardemail --data-dir ~/secure/forwardemail'` helps), since each directory holds its own session. `forwardemail --help` prints the directory in use.
|
|
209
|
+
|
|
210
|
+
| File | Contents |
|
|
211
|
+
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
212
|
+
| `local-storage.json` | What the webmail keeps in a browser's `localStorage`: your session and settings. Readable only by you (mode `0600`). It holds the same data as a signed-in browser profile. |
|
|
213
|
+
| `update.json` | When updates were last checked. |
|
|
214
|
+
| `notifications.json` | Whether you allowed desktop notifications. |
|
|
215
|
+
| `notifier/` | The standalone executable writes its notifier files here (the notifier program on macOS and Windows, the app icon) the first time it needs them. |
|
|
216
|
+
| `user.css` | Optional styles of your own (see below). |
|
|
217
|
+
| `forwardemail.log` | Debug log, only written with `FORWARDEMAIL_DEBUG=1`. |
|
|
218
|
+
|
|
219
|
+
Mail, contacts and the search index are cached in memory for the session. So are drafts and changes made while the network is down: they are sent when the connection is back, which the client checks every few seconds. Quitting before then loses them. Like the webmail, they are fetched again from the server when you start the app. `forwardemail logout` removes `local-storage.json`, `update.json`, `notifications.json`, `notifier/` and `forwardemail.log`, and leaves `user.css` alone. Downloads go to your Downloads folder, not here.
|
|
220
|
+
|
|
221
|
+
### Custom styles
|
|
222
|
+
|
|
223
|
+
Put CSS in `user.css` in the data directory to restyle the terminal client. It is loaded after the built-in styles. Lengths are written in browser units: one column is `8px` (`0.5rem`) and one row is `16px` (`1rem`). Colors are drawn in true color where the terminal supports it.
|
|
224
|
+
|
|
225
|
+
```css
|
|
226
|
+
/* An amber accent for buttons, links and the focused control */
|
|
227
|
+
:root,
|
|
228
|
+
.dark {
|
|
229
|
+
--fe-primary: #b45309;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Hide the storage meter in the sidebar */
|
|
233
|
+
a[href$='/my-account/billing'] {
|
|
234
|
+
display: none;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Self-hosted servers
|
|
239
|
+
|
|
240
|
+
The terminal client talks to `https://api.forwardemail.net` by default. To use a self-hosted Forward Email instance, point it at that server's API:
|
|
241
|
+
|
|
242
|
+
```sh
|
|
243
|
+
forwardemail --api https://api.mail.example.com
|
|
244
|
+
# or
|
|
245
|
+
export FORWARDEMAIL_API_URL=https://api.mail.example.com
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Updates
|
|
249
|
+
|
|
250
|
+
**Standalone executables update themselves.** At most once a day, `forwardemail` checks the latest GitHub release in the background while you use it. If there is a newer version, it downloads the build for your system, verifies its SHA-256 checksum against the release's `SHA256SUMS.txt`, and replaces the executable atomically. The new version runs the next time you start the app, and a one-line note is printed when you quit. To update right away, run `forwardemail update`.
|
|
251
|
+
|
|
252
|
+
**npm installs** check the same way but do not change anything themselves. When an update is available, a note on exit says to run `forwardemail update`, which runs `npm install -g forwardemail@latest`.
|
|
253
|
+
|
|
254
|
+
Turn the check off with `--no-update-check` or `FORWARDEMAIL_NO_UPDATE_CHECK=1`. A failed check (offline, rate-limited) is silent and is retried at the next start.
|
|
255
|
+
|
|
256
|
+
## Verifying downloads
|
|
257
|
+
|
|
258
|
+
Every release lists the SHA-256 of each asset in `SHA256SUMS.txt`, and each executable has a [GitHub build provenance attestation](https://docs.github.com/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds). Together they show that the file was built by this repository's release workflow from the tagged source.
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
# The checksum (the installers and the updater do this for you)
|
|
262
|
+
grep forwardemail-linux-x64.gz SHA256SUMS.txt | sha256sum -c -
|
|
263
|
+
|
|
264
|
+
# The provenance, with the GitHub CLI
|
|
265
|
+
gh attestation verify forwardemail-linux-x64.gz --repo forwardemail/mail.forwardemail.net
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The Windows executables are also Authenticode-signed by Forward Email LLC with the same certificate as the desktop installers. In PowerShell, `Get-AuthenticodeSignature forwardemail.exe` shows the signer.
|
|
269
|
+
|
|
270
|
+
The npm package is published with [npm provenance](https://docs.npmjs.com/generating-provenance-statements). Run `npm audit signatures` after installing to check it.
|
|
271
|
+
|
|
272
|
+
## Uninstall
|
|
273
|
+
|
|
274
|
+
Sign out first, so no session is left behind, then remove the program:
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
forwardemail logout
|
|
278
|
+
rm ~/.local/bin/forwardemail # standalone, macOS and Linux
|
|
279
|
+
npm uninstall -g forwardemail # npm
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
On Windows, delete `%LOCALAPPDATA%\Programs\forwardemail` and remove it from your user `PATH`. To remove every trace, also delete the data directory listed under [Where data is stored](#where-data-is-stored).
|
|
283
|
+
|
|
284
|
+
## Differences from the browser
|
|
285
|
+
|
|
286
|
+
The terminal client runs the webmail's own code, so features behave the same way they do on the web. These are the exceptions, all due to what a terminal can draw:
|
|
287
|
+
|
|
288
|
+
- **Images, video and canvas** are not drawn. Icons appear as text glyphs, and messages are shown as formatted text with their links.
|
|
289
|
+
- **Plain text** is the default for reading and writing (see [Plain text](#plain-text)).
|
|
290
|
+
- **Fonts and font sizes** are the terminal's. Headings stand out by weight and color, not size.
|
|
291
|
+
- **Passkeys** (WebAuthn) need a browser or the desktop app. To use App Lock in the terminal, unlock it with its PIN.
|
|
292
|
+
- **Push notifications and the service worker** are unavailable. New mail arrives over the WebSocket connection while the app is open, and shows as a [desktop notification](#notifications) when the terminal is in the background.
|
|
293
|
+
- **Hover tooltips** appear in the bottom row rather than over the page, where they would cover the controls around them.
|
|
294
|
+
- **Transparency and animation** are flattened: translucent colors are blended with the page background, and transitions jump to their end.
|
|
295
|
+
- **Esc followed by a key** in quick succession counts as two presses, not as Alt plus the key (some terminals send Alt that way). The exceptions are Alt+Backspace and Alt+B/F/D, which keep their word-editing meaning in text fields. The webmail has no other Alt shortcuts.
|
|
296
|
+
|
|
297
|
+
## Troubleshooting
|
|
298
|
+
|
|
299
|
+
**The layout is garbled or characters overlap.** Use a font with box-drawing characters and a terminal that supports Unicode. On Windows, use Windows Terminal rather than the legacy console.
|
|
300
|
+
|
|
301
|
+
**Colors look wrong.** Set `COLORTERM=truecolor` if your terminal supports 24-bit color but does not advertise it. Inside tmux, add `set -g default-terminal "tmux-256color"` and `set -as terminal-features ",*:RGB"`.
|
|
302
|
+
|
|
303
|
+
**Clicks do nothing.** Turn on mouse reporting in your terminal (in tmux: `set -g mouse on`). Keyboard navigation always works.
|
|
304
|
+
|
|
305
|
+
**No desktop notifications.** Run `forwardemail notifications --test`. It says why they are off (for example over SSH, or `notify-send` missing on Linux) or sends a test. Check that they are turned on in **Settings › Account › Notifications**, and that your system allows them for terminal-notifier (macOS) or NtfyToast (Windows).
|
|
306
|
+
|
|
307
|
+
**`forwardemail: command not found` after installing.** Add the install directory to your `PATH`. For the default on macOS and Linux:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.profile
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Something else is wrong.** Run `FORWARDEMAIL_DEBUG=1 forwardemail`, reproduce the problem, and attach `forwardemail.log` from the data directory to an [issue](https://github.com/forwardemail/mail.forwardemail.net/issues). Check the log for anything private before posting it.
|
|
314
|
+
|
|
315
|
+
## How it works
|
|
316
|
+
|
|
317
|
+
The webmail is built a second time for Node.js, and the result is a single CommonJS file, `cli/dist/forwardemail.cjs`:
|
|
318
|
+
|
|
319
|
+
1. **App bundle.** [`vite.cli.config.js`](../vite.cli.config.js) builds `src/main.ts` with the same Svelte components, stores and workers as the web build. Lucide icons are swapped for text glyphs ([`src/cli/components/LucideIcon.svelte`](../src/cli/components/LucideIcon.svelte)). The stylesheets are collected into one string.
|
|
320
|
+
2. **Stylesheet conversion.** [`scripts/build-cli.mjs`](../scripts/build-cli.mjs) drops what a terminal cannot draw: animations, shadows, fonts, print styles and unused custom properties ([`src/cli/prune-css.js`](../src/cli/prune-css.js)). It then converts lengths to terminal cells ([`src/cli/px-to-cells.js`](../src/cli/px-to-cells.js)). The app sees a window 8 px per column and 16 px per row, so its media queries and layout code work unchanged. [`src/cli/terminal.css`](../src/cli/terminal.css) adds terminal-specific rules on top: focus highlighting, one-row inputs and hidden images. [`src/cli/terminal-utilities.css`](../src/cli/terminal-utilities.css) goes inside Tailwind's utilities layer, so the app's responsive variants (`md:hidden`) still override it.
|
|
321
|
+
3. **Launcher.** [`src/cli/index.ts`](../src/cli/index.ts) handles the command line and updates, then [`src/cli/app.ts`](../src/cli/app.ts) boots the page. [`src/cli/environment.ts`](../src/cli/environment.ts) makes Node look like the browser tab the webmail expects:
|
|
322
|
+
- a TermDOM `window` and `document`
|
|
323
|
+
- `localStorage` saved to disk
|
|
324
|
+
- IndexedDB from `fake-indexeddb`
|
|
325
|
+
- Web Workers on `worker_threads` ([`src/cli/worker.ts`](../src/cli/worker.ts))
|
|
326
|
+
- `history` and `location` ([`src/cli/history.ts`](../src/cli/history.ts))
|
|
327
|
+
- sandboxed message frames ([`src/cli/frames.ts`](../src/cli/frames.ts))
|
|
328
|
+
- downloads and outside links ([`src/cli/links.ts`](../src/cli/links.ts))
|
|
329
|
+
- geometry scaled to the virtual pixel grid ([`src/cli/viewport.ts`](../src/cli/viewport.ts))
|
|
330
|
+
|
|
331
|
+
The application code under `src/` is untouched. Everything specific to the terminal lives in `src/cli/`, and the browser and desktop builds do not include it.
|
|
332
|
+
|
|
333
|
+
The standalone executables are [Node.js single executable applications](https://nodejs.org/api/single-executable-applications.html): the bundle is injected into a copy of the `node` binary by [`scripts/build-sea.mjs`](../scripts/build-sea.mjs).
|
|
334
|
+
|
|
335
|
+
## Development
|
|
336
|
+
|
|
337
|
+
```sh
|
|
338
|
+
pnpm install
|
|
339
|
+
pnpm build:cli # cli/dist/forwardemail.cjs
|
|
340
|
+
pnpm cli --demo # run the build
|
|
341
|
+
pnpm test:cli # build, then run tests/cli
|
|
342
|
+
pnpm build:sea # standalone executable for this machine in cli/dist/
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`CLI_BUILD_DEV=1 pnpm build:cli` builds with `import.meta.env.DEV` set. `FORWARDEMAIL_DEBUG=1` logs console output and focus changes to `forwardemail.log`. `FORWARDEMAIL_DEBUG_DOM=<file>` writes the document's markup to a file every two seconds, which helps when a layout does not match the browser.
|
|
346
|
+
|
|
347
|
+
The interactive tests in [`tests/cli/cli.e2e.test.js`](../tests/cli/cli.e2e.test.js) run the built client in a real pseudo-terminal (util-linux `script`) and read the screen through a headless xterm.js, as a user's terminal would. They click, type and wait for text on screen. They run on Linux; on other systems only the command-line tests run.
|
|
348
|
+
|
|
349
|
+
When a component looks wrong in the terminal, fix it in `src/cli/terminal.css`, or in the stylesheet conversion when the cause is general, rather than in the component. That keeps the web and desktop apps unchanged.
|
|
350
|
+
|
|
351
|
+
## Releasing
|
|
352
|
+
|
|
353
|
+
The CLI shares the app's version and is released with it: nothing extra to run. `pnpm release` (np) bumps `cli/package.json` along with the rest, commits, and pushes the `v*` tag; the build fails if the two versions differ. np itself does not publish to npm (`"publish": false`); CI publishes the package it built and tested.
|
|
354
|
+
|
|
355
|
+
For every `v*` tag, [`release.yml`](../.github/workflows/release.yml) calls [`release-cli.yml`](../.github/workflows/release-cli.yml), which:
|
|
356
|
+
|
|
357
|
+
1. builds and checks the bundle;
|
|
358
|
+
2. builds the six standalone executables on native runners, smoke-tests each one, and uploads them gzipped with provenance attestations;
|
|
359
|
+
3. uploads `install.sh` and `install.ps1`;
|
|
360
|
+
4. publishes the npm package `forwardemail` at the same version, with [provenance](https://docs.npmjs.com/generating-provenance-statements). A prerelease version (`1.2.0-beta.1`) goes to the `next` dist-tag, as np would publish it.
|
|
361
|
+
|
|
362
|
+
`SHA256SUMS.txt` is written afterwards by the release's checksums job. The installers and the updater find the release through GitHub's `releases/latest`, so they only see a release after it is published (not while it is a draft or prerelease).
|
|
363
|
+
|
|
364
|
+
To rebuild the CLI assets of an existing release, run **Release CLI** from the Actions tab with the version, then re-run the release's checksums job. A version already on npm is left as it is (npm versions cannot be replaced).
|
|
365
|
+
|
|
366
|
+
Before the first release, give the workflow a way to publish to npm. The `forwardemail` package already exists there, so the simplest is [trusted publishing](https://docs.npmjs.com/trusted-publishers): on the package's settings page on npmjs.com, add GitHub Actions with organization `forwardemail`, repository `mail.forwardemail.net`, workflow `release.yml` and environment `release`. No secret is needed then. A manual **Release CLI** run is not covered by that (npm checks the workflow that started the run) and needs an `NPM_TOKEN`. [Secrets › npm publishing](./SECRETS.md#npm-publishing-for-the-terminal-client) has both setups step by step.
|
|
367
|
+
|
|
368
|
+
If neither is set up, the npm job fails, and with it the release run's summary; the GitHub release and its executables are published regardless. Set one up and re-run **Release CLI** for that version.
|
|
369
|
+
|
|
370
|
+
A manual run for a version older than npm's `latest` publishes it under the `previous` dist-tag, so `latest` never moves backwards.
|
|
371
|
+
|
|
372
|
+
| Setting | Kind | Purpose |
|
|
373
|
+
| :------------------------------------------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
374
|
+
| `NPM_TOKEN` | Secret | npm granular token, in the `release` environment. Not needed with trusted publishing, except for manual **Release CLI** runs. |
|
|
375
|
+
| `ESIGNER_USERNAME`, `ESIGNER_PASSWORD`, `ESIGNER_CREDENTIAL_ID`, `ESIGNER_TOTP_SECRET` | Secrets | Sign the Windows executables with the same SSL.com eSigner certificate as the desktop installers; see [Secrets](./SECRETS.md#windows-code-signing-secrets). |
|
|
376
|
+
| `ALLOW_UNSIGNED_WINDOWS` | Variable | Shared with the desktop release: `true` ships the Windows executables unsigned when the eSigner secrets are missing, instead of failing. |
|