GoWebMail

A self-hosted, multi-user, encrypted web email client written entirely in Go. Supports Gmail and Outlook via OAuth2, plus any standard IMAP/SMTP provider (Fastmail, ProtonMail Bridge, iCloud, etc.).

Notes:

  • Work still in progress (Gmail and Outlook OAuth2 not yet fully tested in production)
  • AI-assisted development — suggestions and contributions very welcome!

Features

Email

  • Unified inbox — view emails from all connected accounts in one stream
  • Gmail & Outlook OAuth2 — modern token-based auth (no raw passwords stored for these providers)
  • IMAP/SMTP — connect any standard provider with username/password credentials
  • Auto-detect mail settings — MX lookup + common port patterns to pre-fill IMAP/SMTP config
  • Send / Reply / Forward / Draft — full compose workflow with floating draggable compose window
  • Attachments — view inline images, download individual files or all at once
  • Forward as attachment — attach original .eml as message/rfc822
  • Folder navigation — per-account folder/label browsing with right-click context menu
  • Full-text search — across all accounts and folders locally (no server-side search required)
  • Message filtering — unread only, starred, has attachment, from/to filters
  • Bulk operations — multi-select with Ctrl+click / Shift+range; bulk mark read/delete
  • Drag-and-drop — move messages to folders; attach files in compose
  • Starred messages — virtual folder across all accounts
  • EML download — download raw message as .eml
  • Raw headers view — fetches full RFC 822 headers from IMAP on demand

Security

  • AES-256-GCM encryption — all email content, credentials and OAuth tokens encrypted at rest in SQLite (field-level, not whole-DB encryption)
  • bcrypt password hashing — GoWebMail account passwords hashed with cost=12
  • TOTP MFA — custom implementation, no external library; ±60s window for clock skew tolerance
  • Brute-force IP blocking — auto-blocks IPs after configurable failed login attempts (default: 5 attempts in 30 min → 12h ban); permanent blocks supported
  • Geo-blocking — deny or allow-only access by country via ip-api.com (no API key needed); 24h in-memory cache
  • Per-user IP access rules — each user configures their own IP allow-list or brute-force bypass list independently of global rules
  • Security alert emails — notifies the targeted user when their account is brute-forced; supports STARTTLS, implicit TLS, and plain relay
  • DNS rebinding protectionHostCheckMiddleware rejects requests with unexpected Host headers
  • Security headers — CSP, X-Frame-Options, Referrer-Policy, Permissions-Policy, X-XSS-Protection on all responses
  • Sandboxed HTML email rendering — emails rendered in CSP-sandboxed <iframe>; external links require confirmation before opening
  • Remote image blocking — images blocked by default with per-sender whitelist
  • Styled HTTP error pages — 403/404/405 served as themed pages matching the app (not plain browser defaults)

Admin Panel (/admin)

  • User management — create, edit (role, active status, password reset), delete users
  • Audit log — paginated, filterable event log for all security-relevant actions
  • Security dashboard — live blocked IPs table with attacker country, login attempt history per IP, manual block/unblock controls
  • App settings — all runtime configuration editable from the UI; changes are written back to gowebmail.conf
  • MFA disable — admin can disable MFA for any locked-out user
  • Password reset — admin can reset any user's password from the web UI

User Settings

  • Profile — change username and email address (password confirmation required)
  • Password change — change own password
  • TOTP MFA setup — enable/disable via QR code scan
  • Sync interval — per-user background sync frequency
  • Compose popup mode — toggle floating window vs. browser popup window
  • Per-user IP rules — three modes: disabled (global rules apply), brute_skip (listed IPs bypass lockout counter), allow_only (only listed IPs may log in to this account)

UI

  • Dark-themed SPA — clean, responsive vanilla-JS single-page app; no JavaScript framework
  • OS / browser notifications — permission requested once; slide-in toast + OS push notification on new mail
  • Folder context menu — right-click: sync, enable/disable sync, hide, empty trash/spam, move contents, delete
  • Compose window — draggable floating window or browser popup; tag-input for To/CC/BCC; auto-saves draft every 60s
Inbox view Compose Admin Security panel Settings

Quick Start

Option 1: Build executable

git clone https://github.com/ghostersk/gowebmail && cd gowebmail
go build -o gowebmail ./cmd/server
# Smaller binary (strip debug info):
go build -ldflags="-s -w" -o gowebmail ./cmd/server
./gowebmail

Visit http://localhost:8080. Default login: admin / admin.

Option 2: Run directly

git clone https://github.com/ghostersk/gowebmail && cd gowebmail
go run ./cmd/server/main.go
# Check ./data/gowebmail.conf on first run — update as needed, then restart.

Admin CLI Commands

All commands open the database directly without starting the HTTP server. They require the same environment variables or data/gowebmail.conf as the server.

# List all admin accounts with MFA status
./gowebmail --list-admin

# USERNAME                  EMAIL                                 MFA
# --------                  -----                                 ---
# admin                     admin@example.com                     ON

# Reset an admin's password (minimum 8 characters)
./gowebmail --pw admin "NewSecurePass123"

# Disable MFA for a locked-out admin
./gowebmail --mfa-off admin

# List all currently blocked IPs
# Shows: IP, username attempted, attempt count, blocked-at, expiry, time remaining
./gowebmail --blocklist

# IP                  USERNAME USED         TRIES  BLOCKED AT              EXPIRES                 REMAINING
# --                  -------------         -----  ----------              -------                 ---------
# 1.2.3.4             bob                   7      2026-03-08 14:22:01     2026-03-09 02:22:01     11h 34m
# 5.6.7.8             admin                 12     2026-03-07 09:10:00     permanent               ∞  (manual unblock)

# Remove a block immediately
./gowebmail --unblock 1.2.3.4

Note: --list-admin, --pw, and --mfa-off only work on admin accounts. Regular user management is done through the web UI at /admin. --blocklist and --unblock are particularly useful if you have locked yourself out.


Configuration

On first run, data/gowebmail.conf is auto-generated with all defaults and inline comments. All keys can also be set via environment variables. The Admin → Settings UI can edit and save most values at runtime, writing changes back to gowebmail.conf.

Core

Key Default Description
HOSTNAME localhost Public hostname for BaseURL construction and Host header validation
LISTEN_ADDR :8080 Bind address
SECURE_COOKIE false Set true when running behind HTTPS
TRUSTED_PROXIES (blank) Comma-separated IPs/CIDRs allowed to set X-Forwarded-For
ENCRYPTION_KEY (auto-generated) AES-256 key — back this up immediately; losing it makes the DB unreadable
SESSION_MAX_AGE 604800 Session lifetime in seconds (default: 7 days)

Brute Force Protection

Key Default Description
BRUTE_ENABLED true Enable automatic IP blocking on failed logins
BRUTE_MAX_ATTEMPTS 5 Failed login attempts before ban triggers
BRUTE_WINDOW_MINUTES 30 Rolling window in minutes for counting failures
BRUTE_BAN_HOURS 12 Ban duration in hours; 0 = permanent block (manual unblock required)
BRUTE_WHITELIST_IPS (blank) Comma-separated IPs never blocked — add your own IP here

Geo Blocking

Key Default Description
GEO_BLOCK_COUNTRIES (blank) ISO country codes to deny outright (e.g. CN,RU,KP). Evaluated first — takes priority over allow list.
GEO_ALLOW_COUNTRIES (blank) ISO country codes to allow exclusively (e.g. SK,CZ,DE). All other countries are denied.

Geo lookups use ip-api.com (free tier, no API key, ~45 req/min limit). Results are cached in-memory for 24 hours. Private/loopback IPs always bypass geo checks.

Security Notification Emails

Key Default Description
NOTIFY_ENABLED true Send alert email to user when a brute-force attack targets their account
NOTIFY_SMTP_HOST (blank) SMTP hostname for sending alert emails
NOTIFY_SMTP_PORT 587 465 = implicit TLS · 587 = STARTTLS · 25 = plain relay (no auth)
NOTIFY_FROM (blank) Sender address (e.g. security@yourdomain.com)
NOTIFY_USER (blank) SMTP auth username — leave blank for unauthenticated relay
NOTIFY_PASS (blank) SMTP auth password — leave blank for unauthenticated relay

Setting up OAuth2

Gmail

  1. Go to Google Cloud Console → New project
  2. Enable Gmail API
  3. Create OAuth 2.0 Client ID (Web application type)
  4. Add Authorized redirect URI: <BASE_URL>/auth/gmail/callback
  5. Add scope https://mail.google.com/ (required for full IMAP access)
  6. Add test users while the app is in "Testing" mode
  7. Set in config: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET

Outlook / Microsoft 365

  1. Go to Azure portal → App registrations → New registration
  2. Set redirect URI: <BASE_URL>/auth/outlook/callback
  3. Under API permissions add:
    • https://outlook.office.com/IMAP.AccessAsUser.All
    • https://outlook.office.com/SMTP.Send
    • offline_access, openid, profile, email
  4. Create a Client secret under Certificates & secrets
  5. Set in config: MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, MICROSOFT_TENANT_ID

Security Notes

  • ENCRYPTION_KEY is critical — back it up. Without it the encrypted SQLite database is permanently unreadable.
  • Email content (subject, from, to, body), IMAP/SMTP credentials, and OAuth tokens are all encrypted at rest with AES-256-GCM at the field level.
  • GoWebMail user passwords are bcrypt hashed (cost=12). Session tokens are 32-byte crypto/rand hex strings.
  • All HTTP responses include security headers (CSP, X-Frame-Options, Referrer-Policy, etc.).
  • HTML emails render in a CSP-sandboxed <iframe> — external links trigger a confirmation dialog before opening in a new tab.
  • In production, run behind a reverse proxy with HTTPS (nginx / Caddy) and set SECURE_COOKIE=true.
  • Add your own IP to BRUTE_WHITELIST_IPS to avoid ever locking yourself out. If it does happen, use ./gowebmail --unblock <ip> — no server restart needed.

Building for Production

CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w" -o gowebmail ./cmd/server

CGO is required by mattn/go-sqlite3. Cross-compilation for other platforms requires a C cross-compiler (or use zig cc as a drop-in).

Docker (example)

FROM golang:1.22-alpine AS builder
RUN apk add gcc musl-dev sqlite-dev
WORKDIR /app
COPY . .
RUN CGO_ENABLED=1 go build -ldflags="-s -w" -o gowebmail ./cmd/server

FROM alpine:latest
RUN apk add --no-cache sqlite-libs ca-certificates
WORKDIR /app
COPY --from=builder /app/gowebmail .
EXPOSE 8080
CMD ["./gowebmail"]

Dependencies

Package Purpose
github.com/emersion/go-imap v1.2.1 IMAP client
github.com/emersion/go-smtp SMTP client
github.com/emersion/go-message MIME parsing
github.com/gorilla/mux HTTP router
github.com/mattn/go-sqlite3 SQLite driver (CGO required)
golang.org/x/crypto bcrypt
golang.org/x/oauth2 OAuth2 + Google/Microsoft endpoints

License

This project is licensed under the GPL-3.0 license.

Description
No description provided
Readme GPL-3.0 1.2 MiB
Languages
Go 59.8%
JavaScript 23.7%
HTML 9.8%
CSS 6.6%
Dockerfile 0.1%