No description
Find a file
2026-06-17 12:01:59 +02:00
acceptance_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
append.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
append_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
client.go chore: post-review cleanup ai:claude-sonnet-4-5 2026-06-17 12:00:34 +02:00
client_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
conn.go chore: post-review cleanup ai:claude-sonnet-4-5 2026-06-17 12:00:34 +02:00
doc.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
errors.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
examine_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
fetch.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
fixes_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
go.mod feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
go.sum feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
idle.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
LICENSE feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
limiter_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
list.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
list_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
namespace.go chore: post-review cleanup ai:claude-sonnet-4-5 2026-06-17 12:00:34 +02:00
namespace_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
NOTICE feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
notify.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
parser.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
password.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
password_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
peek.go chore: post-review cleanup ai:claude-sonnet-4-5 2026-06-17 12:00:34 +02:00
peek_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
README.md feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
sanitize.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
sanitize_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
search.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
search_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
security.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
security_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
select.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
store.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
testserver_test.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
types.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
utf7.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00
validate.go feat: initial extraction of imapclient package ai:claude-sonnet-4-5 2026-06-17 11:54:56 +02:00

imapclient

A small, dependency-light IMAP client for Go, built directly on RFC 3501 with optional CONDSTORE (RFC 7162) and NAMESPACE (RFC 2342) support. Context-cancellable I/O on every operation; typed values throughout; credentials wrapped in a self-redacting Password type.

The only runtime dependency outside the standard library is golang.org/x/time/rate (a token-bucket command rate limiter).

Status: pre-1.0 (v0.x). The API may change.

Install

go get go.schlittermann.de/heiko/imapclient

Requires Go 1.26 or newer.

Quick start

ctx := context.Background()

cfg := imapclient.Config{
	Name:     "primary",
	Host:     "imap.example.com",
	Port:     993,
	TLS:      true,
	Username: "alice@example.com",
	Password: imapclient.NewPassword(os.Getenv("IMAP_PASSWORD")),
	Mailbox:  "INBOX",
}

c, mbox, err := imapclient.Connect(ctx, cfg, false /* condStore */)
if err != nil {
	log.Fatal(err)
}
defer c.Close()

uids, err := c.SearchUIDs(ctx, imapclient.SearchCriteria{Since: time.Now().AddDate(0, 0, -7)})
if err != nil {
	log.Fatal(err)
}

for _, uid := range uids {
	body, err := c.FetchBody(ctx, uid)
	if err != nil {
		log.Fatal(err)
	}
	_ = body // parse with net/mail, etc.

	if err := c.Store(ctx, uid, imapclient.FlagAdd, imapclient.FlagSeen); err != nil {
		log.Fatal(err)
	}
}

_ = mbox // *MailboxInfo from the SELECT

Transport security

The zero value of Config is safe: with neither TLS nor InsecurePlaintext set, Dial requires the server to advertise STARTTLS before sending any credentials. If the server does not advertise it, Dial returns ErrSTARTTLSUnsupported before LOGIN.

  • Implicit TLS (Config.TLS = true, port 993): TLS before any IMAP bytes.
  • Mandatory STARTTLS (default, port 143): plaintext upgraded before LOGIN.
  • Plaintext (Config.InsecurePlaintext = true): no encryption — local testing only.

There is no "STARTTLS optional" downgrade mode by design.

Each insecure opt-out fires Config.OnSecurityEvent once per connect:

Field Meaning SecurityEventKind
TLSNoVerify skip certificate verification TLSNoVerifyActive
InsecurePlaintext stay plaintext for the whole session STARTTLSDisabled
InsecureDebug show cleartext password in DebugWriter InsecureDebugActive
cfg.OnSecurityEvent = func(e imapclient.SecurityEvent) {
	log.Printf("imap insecure transport: %s source=%s host=%s", e.Kind, e.Name, e.Host)
}

The package imports no logging framework; routing the signal is the caller's choice.

Credentials

Password redacts in every formatting, logging, and marshalling path. Only Password.Reveal returns the plaintext, and the package calls it exactly once — just before writing the LOGIN command.

License

Apache-2.0. See LICENSE and NOTICE.