Audience: Windows / IT admins deploying go-mapi at scale — RDS, Citrix, managed desktops, group policy.
Operational facts
- Single-file NSIS installer (
go-mapi-setup.exe) — All Users only, no MSI - Silent install via
/S; optional auto-update Scheduled Task via/AUTOUPDATE=1 - ~40–50 MB RAM per signed-in session (idle, after 10 min — see RAM sizing below)
- SHA-256 checksums published with every release (
SHA256SUMS.txt) - Per-user OAuth tokens in Windows Credential Manager (DPAPI-scoped)
- Outbound network: Gmail API + Google OAuth + GitHub Releases (update check). Nothing else.
Positioning
- LGPL-3.0-or-later — FOSS, no per-seat licensing, source on GitHub
- No telemetry, no content retention, no analytics
v3.0.0 is unsigned. The release pipeline supports SignPath.io OSS
signing, but v3.0.0 was published from a runner without the SignPath
secrets configured and used the unsigned staged/ build path.
Practical consequences for managed deployments:
- SmartScreen will warn users on first interactive run. The dismissal
flow is More info → Run anyway. Silent installs (
/S) bypass the SmartScreen dialog because there is no interactive shell to display it. - Microsoft Defender Application Control / WDAC policies that require signed binaries will block go-mapi. You'll need to allow the binary by hash, by file path, or by publisher (not applicable while unsigned).
- AppLocker Publisher rules are not usable while unsigned; use Hash
rules against the SHA-256 in
SHA256SUMS.txt, or Path rules anchored at%ProgramFiles%\go-mapi\.
To suppress SmartScreen warnings for managed deployments:
- Group Policy:
Computer Configuration → Administrative Templates → Windows Components → File Explorer → Configure Windows Defender SmartScreen— set to Disable or Warn but allow per your policy. - Intune (Win32 app): pre-stage the installer via the Intune Win32 app pipeline; the trust override at deployment time avoids the per-user SmartScreen prompt.
- GPO file allow-list (per-binary): use Software Restriction Policies
or AppLocker Hash rules against the published
SHA256SUMS.txthash.
A future signed release will replace this section with verification
guidance (Authenticode chain, Get-AuthenticodeSignature snippet).
go-mapi-setup.exe is All Users only. go-mapi registers itself as the
machine-wide MAPI handler under HKLM\SOFTWARE\Clients\Mail\go-mapi, which is
inherently machine-wide. There is no per-user install path.
The installer requires UAC elevation. Run as an administrator or via a managed-deployment context that elevates.
Default install directory: %ProgramFiles%\go-mapi (64-bit).
The 32-bit MAPI DLL is placed separately at %ProgramFiles(x86)%\go-mapi
so that both 32-bit and 64-bit MAPI callers are routed correctly.
Registry footprint:
HKLM\SOFTWARE\Clients\Mail\go-mapi(native/64-bit MAPI registration)HKLM\SOFTWARE\WOW6432Node\Clients\Mail\go-mapi(32-bit MAPI registration)HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\go-mapi(Add/Remove Programs)
The installer backs up the previous default mail client name to
%ProgramData%\go-mapi\uninst\previous-mail-client.json and restores it on
uninstall.
Silent install with all defaults (no automatic updates):
go-mapi-setup.exe /S
Silent install to a custom path:
go-mapi-setup.exe /S /D=C:\Program Files\go-mapi
Note:
/Dmust be the last parameter and must not be quoted even if the path contains spaces (NSIS restriction).
Enable the automatic-update Scheduled Task at install time:
go-mapi-setup.exe /S /AUTOUPDATE=1
The installer is idempotent — running it over an existing install upgrades in place (the previous mail client backup is preserved across upgrades).
NSIS uses standard process exit codes. The installer issues Abort on
the failure paths below, which translates to a non-zero exit code; success
is 0.
| Exit code | Meaning | Source |
|---|---|---|
0 |
Install completed successfully | NSIS default on SectionEnd |
1 |
User cancelled the wizard, or interactive EnsureAppNotRunning cancelled |
NSIS default on Quit/Abort |
2 |
Installation failed (NSIS internal error — file copy, registry write, script error) | NSIS default for SetErrorLevel 2 and runtime aborts |
The installer's explicit Abort paths (interactive cancel of "go-mapi
already running" prompt; go-mapi.exe still running after a 10-second
graceful-close poll on silent install) surface as exit 1 and exit 2
respectively, matching NSIS conventions. SCCM / Intune detection rules
should treat any non-zero exit as install failure and key the success
detection off the HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\go-mapi
key existing after the run.
Log output goes to the Windows installer log rather than a file; add
/LOG=C:\path\to\install.log if your deployment tooling needs a log file.
When installed with /AUTOUPDATE=1 (or with the "Enable automatic updates"
checkbox ticked during an interactive install), the installer registers a
Windows Scheduled Task:
| Property | Value |
|---|---|
| Task name | go-mapi Auto Update |
| Path | \go-mapi Auto Update (root of Task Scheduler) |
| Run as | SYSTEM (no per-user credential, no logon required) |
| Schedule | Daily 03:00 with ±30 minute random delay |
| Also runs | At system startup (5 minute delay) |
| Network | RunOnlyIfNetworkAvailable=true (skips offline runs) |
| Catch-up | StartWhenAvailable=true (runs after wake/reboot if missed) |
| Concurrency | MultipleInstancesPolicy=IgnoreNew (no overlapping runs) |
| Time limit | 12 hours per run (ExecutionTimeLimit=PT12H) |
The task fires go-mapi.exe --update-check-silent, which fetches the latest
release, verifies the SHA-256 digest before writing anything, and atomically
replaces the binary. The running interactive go-mapi instance keeps working
until the next launch; the task does not forcibly restart it.
Update logs are written to %ProgramData%\go-mapi\updates\update.log
(admin-readable; no PII, no message content).
The silent updater (src/app/updates_silent.go) follows a verify-then-swap
pattern designed to be safe against running user sessions:
- Stage: assets are downloaded into
%ProgramData%\go-mapi\updates\staging\and SHA-256-verified in-memory before any byte hits the install directory. - Atomic swap (NTFS rename-while-running): for each of
go-mapi.exe,go-mapi-x64.dll, andgo-mapi-x86.dll, the updater performs a two-step rename —installed → installed.old.<pid>followed bystaged → installed. This usesMoveFileExand works while the running process holds the file open (NTFS allows rename of a file under loader lock; deletion would be blocked). - In-memory binary handoff: any currently running
go-mapi.exekeeps running from the loader-pinned old binary in memory. New launches — the next user session, the next time the user clicks the tray icon after a quit, or the next reboot — pick up the new binary. No user process is killed by the updater (D-13 in the silent-updater plan makes this explicit; the retry loop is the back-pressure, notWM_CLOSE). - Orphan cleanup: the
*.old.<pid>files are removed on the next silent-update cycle, after the running process has exited. - Retry budget: if a swap fails (
ERROR_SHARING_VIOLATIONfrom Defender / a filter driver briefly holding the file), the updater retries with exponential backoff up to a 12-hour wall clock; the next Scheduled Task trigger picks up where it left off.
Operational impact: a user who is signed in and idle when an update lands sees nothing. The next time they launch go-mapi (after quitting from the tray, after reboot, or at next logon), they get the new version. There is no forced re-login or restart.
Admin debug log: %ProgramData%\go-mapi\updates\update.log (capped at
1 MB, rotated by truncation; records version transitions, swap
attempts, retry counts, verify success/failure — no message content,
no credentials, no hex digests for failed verifications).
Disable (e.g. during maintenance windows):
schtasks /change /tn "go-mapi Auto Update" /disable
Re-enable:
schtasks /change /tn "go-mapi Auto Update" /enable
Force an immediate update check:
schtasks /run /tn "go-mapi Auto Update"
Query last run and status:
schtasks /query /tn "go-mapi Auto Update" /v /fo LIST
The task is removed automatically by the go-mapi uninstaller.
To add automatic updates to an existing notify-only install, re-run the
installer over the existing install with /AUTOUPDATE=1 — idempotent.
To disable automatic updates on a managed host, either install with
/AUTOUPDATE=0 (the default) or delete the task after install:
schtasks /delete /tn "go-mapi Auto Update" /f
Every release publishes SHA256SUMS.txt alongside the installer:
https://github.com/marcfargas/go-mapi/releases/latest/download/SHA256SUMS.txt
Format follows the sha256sum convention (<lowercase-hex> <filename>).
The automatic updater verifies downloads before applying them. For manual
verification before deployment (returns success or throws on mismatch —
suitable for use in a deployment pipeline):
$base = "https://github.com/marcfargas/go-mapi/releases/download/vX.Y.Z"
$sums = (Invoke-WebRequest "$base/SHA256SUMS.txt").Content
$expected = ($sums -split "`n" |
Where-Object { $_ -match 'go-mapi-setup\.exe' } |
ForEach-Object { ($_ -split '\s+')[0] })
$actual = (Get-FileHash .\go-mapi-setup.exe -Algorithm SHA256).Hash.ToLower()
if ($actual -eq $expected) {
Write-Output "OK ($actual)"
} else {
throw "Checksum mismatch: expected $expected, got $actual"
}Replace vX.Y.Z with the tagged release you're verifying. The script
exits non-zero (via throw) on mismatch — wire this into your SCCM /
Intune pre-install step.
go-mapi uses a standard NSIS installer with a silent mode and a documented registry footprint, which makes it compatible with most Windows software distribution tooling:
- Intune / SCCM: deploy
go-mapi-setup.exe /Sas a Win32 app. Detection rule: keyHKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\go-mapiexists. Treat any non-zero exit code as failure (see Exit codes above). - Group Policy (Software Installation): NSIS produces an EXE, not an MSI. Use a GP startup script or a third-party EXE-to-MSI wrapper if your policy requires MSI format.
- Chocolatey / Scoop: no official package yet. Use the GitHub Releases URL directly in your internal feed.
go-mapi makes network calls only to:
https://github.com/marcfargas/go-mapi/releases/latest/download/...(update check and asset download — only when automatic updates are enabled or when the user clicks "download update")- Google OAuth endpoints (sign-in and token refresh)
- Gmail API (
https://gmail.googleapis.com/) — only when the user is signed in
No telemetry. No content retention. Email content is never stored outside of
Gmail's own API. The silent-update log at %ProgramData%\go-mapi\updates\update.log
records version transitions and download success/failure only — no message
bodies, no recipient data, no credential material.
Credential storage: each user's OAuth tokens are stored in the Windows Credential Manager (DPAPI-scoped, per user). go-mapi never stores tokens in shared locations.
The "~40–50 MB RAM per session" figure is measured idle, signed-in, with
the tray window hidden, after 10 minutes of running on Windows 11 22H2
(working set, Get-Process go-mapi). Memory grows modestly during draft
creation and returns to baseline once the draft is posted to Gmail. There
is no observed long-running growth — the watcher and OAuth refresh paths
are stateless w.r.t. heap retention.
For RDS / Citrix capacity planning, treat 50 MB / concurrent signed-in session as a working ceiling. Sessions that have not yet signed in (the first-launch SignInScreen state) consume noticeably less.
The uninstaller scrubs the uninstalling admin's profile and all machine-wide
locations (HKLM\SOFTWARE\Clients\Mail\go-mapi, %ProgramFiles%\go-mapi\,
%ProgramData%\go-mapi\, and the Scheduled Task). It does not enumerate
every user profile on the host.
Residue that persists per user after uninstall:
%APPDATA%\go-mapi\(settings, log) — per user, not touched by the uninstaller- Windows Credential Manager target
go-mapi:oauth-tokens— per user (DPAPI-scoped)
Is the residue harmful? No. The Credential Manager entry holds a
DPAPI-encrypted Google refresh token scoped to that user's profile. Without
the matching go-mapi binary on the host, the token has no privileged
caller; without that user's interactive logon, DPAPI will not decrypt it.
Leaving the residue in place after uninstall does not grant access to the
user's Gmail account — Google will rotate / revoke unused refresh tokens
on its own schedule. The %APPDATA%\go-mapi\ residue is a settings file
and a log; neither contains message content or credentials.
If your security policy requires per-user cleanup anyway, the most operationally tractable patterns on RDS / Citrix are:
- At-logon scheduled task (per user): deploy a logon-triggered task
via GPO that runs:
This runs in the user's own session context, so DPAPI and Credential Manager are reachable without impersonation.
Remove-Item -Recurse -Force "$env:APPDATA\go-mapi" -ErrorAction SilentlyContinue cmdkey /list:go-mapi:oauth-tokens 2>$null | Out-Null if ($LASTEXITCODE -eq 0) { cmdkey /delete:go-mapi:oauth-tokens | Out-Null }
- Off-session impersonation (e.g.
psexec -i -s -u <user>) is possible but operationally expensive on a 60-host farm and is generally not worth the effort given the residue is harmless.
go-mapi's OAuth sign-in opens a short-lived loopback listener on 127.0.0.1
with an ephemeral port. The installer creates a Windows Firewall inbound rule
(go-mapi OAuth loopback, scoped to the binary) to suppress the first-bind
consent prompt. On RDS hosts, that prompt appears on the server console (not in
the user's RDP session) if the rule is absent or blocked by group policy. If
your GPO blocks netsh advfirewall writes, pre-create the rule via policy
before deploying go-mapi.
The exact rule the installer creates is:
netsh advfirewall firewall add rule ^
name="go-mapi OAuth loopback" ^
dir=in ^
program="%ProgramFiles%\go-mapi\go-mapi.exe" ^
action=allow ^
profile=any
Translated to PowerShell New-NetFirewallRule / GPO equivalents:
| Property | Value |
|---|---|
| Name / DisplayName | go-mapi OAuth loopback |
| Direction | Inbound |
| Action | Allow |
| Profile | Any (Domain + Private + Public) |
| Program | %ProgramFiles%\go-mapi\go-mapi.exe (full path required by GPO; resolve %ProgramFiles% to C:\Program Files if your policy editor doesn't expand) |
| Protocol | Any (the rule is program-scoped, not protocol-scoped) |
| Local port | Any (the OAuth listener binds an ephemeral port on 127.0.0.1) |
| Remote address | Any (loopback is not enforced at firewall layer; the binary itself binds 127.0.0.1 only) |
PowerShell equivalent for GPO startup scripts or pre-staging:
New-NetFirewallRule `
-DisplayName "go-mapi OAuth loopback" `
-Direction Inbound `
-Action Allow `
-Program "$env:ProgramFiles\go-mapi\go-mapi.exe" `
-Profile AnyPre-creating the rule via GPO before go-mapi is deployed avoids the
first-bind UI prompt entirely; the installer's own netsh call becomes a
no-op (the rule already exists with the same name) and a non-fatal
non-zero return is logged but ignored.
The installer is an NSIS EXE. There is no MSI wrapper. This is a known limitation for GPO-based Software Installation policies.
For end-user installation and usage, see README.md. For contributors and maintainers, see DEVELOPMENT.md.