Documentation
¶
Index ¶
- Variables
- func GenericWebhook(recorder ReputationRecorder, cfg GenericWebhookConfig) http.Handler
- func IsFreeProviderDomain(domain string) bool
- func IsRoleAccountLocal(local string) bool
- func MailgunWebhook(recorder ReputationRecorder, cfg MailgunWebhookConfig) http.Handler
- func PostmarkWebhook(recorder ReputationRecorder, cfg PostmarkWebhookConfig) http.Handler
- func SESWebhook(recorder ReputationRecorder, cfg SESWebhookConfig) http.Handler
- func SendGridWebhook(recorder ReputationRecorder, cfg SendGridWebhookConfig) http.Handler
- type AddressOptions
- type BulkItem
- type BulkOptions
- type BulkProgress
- type BulkResult
- type CatchAllStatus
- type Config
- type DNSChecker
- type DNSResolver
- type DNSResult
- type DisposableDetector
- type DomainMapper
- type FileReputationOptions
- type FileReputationStore
- type GenericWebhookConfig
- type MXRecord
- type MailboxStatus
- type MailgunWebhookConfig
- type MemoryReputationStore
- type Observer
- type PostmarkWebhookConfig
- type Reason
- type ReputationEvent
- type ReputationEventType
- type ReputationRecorder
- type ReputationStats
- type ReputationStore
- type Result
- type SESWebhookConfig
- type SMTPChecker
- type SMTPDialer
- type SMTPIPResolver
- type SMTPOptions
- type SMTPResult
- type Scorer
- type ScorerFunc
- type SendGridWebhookConfig
- type Status
- type SyntaxResult
- type ValidationError
- type Validator
- func (v *Validator) Close() error
- func (v *Validator) RecordReputation(ctx context.Context, email string, typ ReputationEventType, at time.Time) error
- func (v *Validator) RecordReputationEvent(ctx context.Context, event ReputationEvent) error
- func (v *Validator) Validate(ctx context.Context, input string) (Result, error)
- func (v *Validator) ValidateBulk(ctx context.Context, emails []string, opts BulkOptions) (BulkResult, error)
- func (v *Validator) ValidateMany(ctx context.Context, emails []string, workers int) ([]Result, error)
- func (v *Validator) ValidateStream(ctx context.Context, input <-chan string, opts BulkOptions) <-chan BulkItem
- type Verdict
- type WebhookResponse
Constants ¶
This section is empty.
Variables ¶
var ( ErrClosed = errors.New("emailvalidator: closed") ErrReputationDisabled = errors.New("emailvalidator: reputation store is not configured") )
Functions ¶
func GenericWebhook ¶
func GenericWebhook(recorder ReputationRecorder, cfg GenericWebhookConfig) http.Handler
GenericWebhook accepts {"events":[{"email":"...","type":"hard_bounce", "timestamp":"..."}]}. Signature = hex(HMAC-SHA256(secret, timestamp + "." + raw_body)).
func IsFreeProviderDomain ¶
IsFreeProviderDomain reports whether a domain is a common consumer mailbox provider.
func IsRoleAccountLocal ¶
IsRoleAccountLocal reports whether a local part is a generic role account.
func MailgunWebhook ¶
func MailgunWebhook(recorder ReputationRecorder, cfg MailgunWebhookConfig) http.Handler
func PostmarkWebhook ¶
func PostmarkWebhook(recorder ReputationRecorder, cfg PostmarkWebhookConfig) http.Handler
func SESWebhook ¶
func SESWebhook(recorder ReputationRecorder, cfg SESWebhookConfig) http.Handler
func SendGridWebhook ¶
func SendGridWebhook(recorder ReputationRecorder, cfg SendGridWebhookConfig) http.Handler
Types ¶
type AddressOptions ¶
type AddressOptions struct {
AllowQuotedLocal bool
AllowDomainLiteral bool
AllowSMTPUTF8 bool
DomainMapper DomainMapper
}
type BulkItem ¶
type BulkItem struct {
Index int `json:"index"`
Email string `json:"email"`
Result Result `json:"result"`
Error string `json:"error,omitempty"`
}
BulkItem is one completed item. Index always refers to the original input.
type BulkOptions ¶
type BulkOptions struct {
Workers int
QueueSize int
Deduplicate bool
Ordered bool
ContinueOnError bool
PerItemTimeout time.Duration
ProgressEvery int
OnProgress func(BulkProgress)
}
BulkOptions controls high-throughput validation. Zero values select safe production defaults. Results preserve input order unless Ordered is false.
type BulkProgress ¶
type BulkProgress struct {
Total int `json:"total"`
Submitted int `json:"submitted"`
Completed int `json:"completed"`
Unique int `json:"unique"`
Duplicates int `json:"duplicates"`
Failed int `json:"failed"`
Elapsed time.Duration `json:"elapsed_ns"`
RatePerSecond float64 `json:"rate_per_second"`
}
BulkProgress is a lock-free snapshot emitted during a bulk run.
type BulkResult ¶
type BulkResult struct {
Items []BulkItem `json:"items"`
Total int `json:"total"`
Unique int `json:"unique"`
Duplicates int `json:"duplicates"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
Verdicts map[Verdict]int `json:"verdicts"`
Duration time.Duration `json:"duration_ns"`
RatePerSecond float64 `json:"rate_per_second"`
}
BulkResult contains ordered results and aggregate performance information.
type CatchAllStatus ¶
type CatchAllStatus string
CatchAllStatus captures whether a domain accepts randomly generated recipients.
const ( CatchAllNotChecked CatchAllStatus = "not_checked" CatchAllYes CatchAllStatus = "yes" CatchAllNo CatchAllStatus = "no" CatchAllUnknown CatchAllStatus = "unknown" )
type Config ¶
type Config struct {
Address AddressOptions
CheckDNS bool
DNS *DNSChecker
SMTP *SMTPChecker
Disposable *DisposableDetector
Reputation ReputationStore
RoleAccountDetector func(local string) bool
FreeProviderDetector func(domain string) bool
Scorer Scorer
Observer Observer
Now func() time.Time
}
Config configures the layered validator.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns conservative production defaults. DNS checking is enabled. Active SMTP probing remains disabled until explicitly enabled.
type DNSChecker ¶
type DNSChecker struct {
Resolver DNSResolver
Timeout time.Duration
PositiveTTL time.Duration
NegativeTTL time.Duration
CacheSize int
// contains filtered or unexported fields
}
func NewDNSChecker ¶
func NewDNSChecker() *DNSChecker
type DNSResolver ¶
type DNSResolver interface {
LookupMX(context.Context, string) ([]*net.MX, error)
LookupIP(context.Context, string, string) ([]net.IP, error)
}
DNSResolver permits deterministic testing and custom resolver integration.
type DNSResult ¶
type DNSResult struct {
Status Status `json:"status"`
MX []MXRecord `json:"mx,omitempty"`
ImplicitMX bool `json:"implicit_mx,omitempty"`
NullMX bool `json:"null_mx,omitempty"`
HasA bool `json:"has_a,omitempty"`
HasAAAA bool `json:"has_aaaa,omitempty"`
Error string `json:"error,omitempty"`
Duration time.Duration `json:"duration_ns,omitempty"`
}
DNSResult describes domain mail-routing capability.
type DisposableDetector ¶
type DisposableDetector struct {
// contains filtered or unexported fields
}
DisposableDetector supports lock-free reads and atomic list replacement.
func NewDisposableDetector ¶
func NewDisposableDetector() *DisposableDetector
func (*DisposableDetector) Count ¶
func (d *DisposableDetector) Count() int
func (*DisposableDetector) IsDisposable ¶
func (d *DisposableDetector) IsDisposable(domain string) bool
IsDisposable checks exact domains and parent suffixes. An allowlist always overrides the bundled or custom blocklist.
func (*DisposableDetector) ReplaceBlocklist ¶
func (d *DisposableDetector) ReplaceBlocklist(r io.Reader) error
ReplaceBlocklist atomically replaces the blocklist from newline-delimited domains. Blank lines and # comments are ignored.
func (*DisposableDetector) SetAllowlist ¶
func (d *DisposableDetector) SetAllowlist(domains []string) error
SetAllowlist atomically replaces the allowlist.
type DomainMapper ¶
AddressOptions controls syntax acceptance. The defaults intentionally reject uncommon address forms that are technically legal but frequently unsafe in web registration flows. DomainMapper can provide full IDNA/UTS #46 processing without forcing a dependency on applications that only accept ASCII domains. Compatible implementations expose ToASCII, such as golang.org/x/net/idna profiles.
type FileReputationOptions ¶
type FileReputationOptions struct {
Path string
Pepper []byte
SyncWrites bool
CompactAfterRecords uint64
MaxLogBytes int64
BreakStaleLockAfter time.Duration
// IdempotencyRetention controls how long HMAC event markers are retained.
// Zero defaults to 30 days.
IdempotencyRetention time.Duration
}
FileReputationOptions configures the durable hashed reputation log.
type FileReputationStore ¶
type FileReputationStore struct {
// contains filtered or unexported fields
}
FileReputationStore is an exclusive-process, append-only store with CRC32C recovery and atomic compaction. The on-disk file contains hashes and counters only; no email address, domain, reason, or message body is persisted.
func OpenFileReputationStore ¶
func OpenFileReputationStore(opts FileReputationOptions) (*FileReputationStore, error)
func (*FileReputationStore) Close ¶
func (s *FileReputationStore) Close() error
func (*FileReputationStore) Compact ¶
func (s *FileReputationStore) Compact() error
func (*FileReputationStore) Lookup ¶
func (s *FileReputationStore) Lookup(ctx context.Context, email, domain string) (ReputationStats, error)
func (*FileReputationStore) Record ¶
func (s *FileReputationStore) Record(ctx context.Context, e ReputationEvent) error
type GenericWebhookConfig ¶
GenericWebhookConfig configures a compact HMAC-signed webhook endpoint.
type MailboxStatus ¶
type MailboxStatus string
MailboxStatus is the SMTP mailbox-level outcome.
const ( MailboxNotChecked MailboxStatus = "not_checked" MailboxExists MailboxStatus = "exists" MailboxDoesNotExist MailboxStatus = "does_not_exist" MailboxAcceptAll MailboxStatus = "accept_all" MailboxFull MailboxStatus = "mailbox_full" MailboxDisabled MailboxStatus = "mailbox_disabled" MailboxBlocked MailboxStatus = "blocked" MailboxUnknown MailboxStatus = "unknown" )
type MailgunWebhookConfig ¶
type MemoryReputationStore ¶
type MemoryReputationStore struct {
// contains filtered or unexported fields
}
MemoryReputationStore is useful for tests and ephemeral services. It applies the same HMAC privacy model as the file store.
func NewMemoryReputationStore ¶
func NewMemoryReputationStore(pepper []byte) (*MemoryReputationStore, error)
func (*MemoryReputationStore) Close ¶
func (s *MemoryReputationStore) Close() error
func (*MemoryReputationStore) Lookup ¶
func (s *MemoryReputationStore) Lookup(ctx context.Context, email, domain string) (ReputationStats, error)
func (*MemoryReputationStore) Record ¶
func (s *MemoryReputationStore) Record(ctx context.Context, e ReputationEvent) error
type Observer ¶
Observer receives operational events. Implementations should return quickly and must be safe for concurrent use.
type PostmarkWebhookConfig ¶
type Reason ¶
type Reason struct {
Code string `json:"code"`
Message string `json:"message"`
Weight int `json:"weight,omitempty"`
}
Reason is a stable machine-readable explanation for a verdict or risk.
type ReputationEvent ¶
type ReputationEvent struct {
// ID is an optional provider-scoped event identifier. When present, durable
// stores use its HMAC as an idempotency marker so retries are not counted
// twice, including after process restarts.
ID string
Email string
Domain string
Type ReputationEventType
At time.Time
}
ReputationEvent contains transient clear-text input. Stores hash the address before persistence and never persist Email or Domain.
type ReputationEventType ¶
type ReputationEventType string
ReputationEventType is a normalized ESP signal.
const ( EventDelivered ReputationEventType = "delivered" EventHardBounce ReputationEventType = "hard_bounce" EventSoftBounce ReputationEventType = "soft_bounce" EventComplaint ReputationEventType = "complaint" EventUnsubscribe ReputationEventType = "unsubscribe" EventDeferred ReputationEventType = "deferred" )
type ReputationRecorder ¶
type ReputationRecorder interface {
RecordReputationEvent(context.Context, ReputationEvent) error
}
ReputationRecorder is implemented by Validator.
type ReputationStats ¶
type ReputationStats struct {
Delivered uint64 `json:"delivered"`
HardBounces uint64 `json:"hard_bounces"`
SoftBounces uint64 `json:"soft_bounces"`
Complaints uint64 `json:"complaints"`
Unsubscribes uint64 `json:"unsubscribes"`
Deferred uint64 `json:"deferred"`
LastEventAt time.Time `json:"last_event_at,omitempty"`
Score int `json:"score"`
}
ReputationStats contains only aggregate counters. Implementations must not persist the clear-text address.
type ReputationStore ¶
type ReputationStore interface {
Record(context.Context, ReputationEvent) error
Lookup(context.Context, string, string) (ReputationStats, error)
Close() error
}
ReputationStore persists and retrieves aggregate delivery signals.
type Result ¶
type Result struct {
Input string `json:"input"`
Normalized string `json:"normalized,omitempty"`
Verdict Verdict `json:"verdict"`
RiskScore int `json:"risk_score"`
Syntax SyntaxResult `json:"syntax"`
DNS DNSResult `json:"dns"`
SMTP SMTPResult `json:"smtp"`
Disposable bool `json:"disposable"`
RoleAccount bool `json:"role_account"`
FreeProvider bool `json:"free_provider"`
Reputation ReputationStats `json:"reputation"`
Reasons []Reason `json:"reasons,omitempty"`
CheckedAt time.Time `json:"checked_at"`
Duration time.Duration `json:"duration_ns"`
}
Result is a complete validation result.
type SESWebhookConfig ¶
type SMTPChecker ¶
type SMTPChecker struct {
Options SMTPOptions
Dialer SMTPDialer
IPResolver SMTPIPResolver
// contains filtered or unexported fields
}
func NewSMTPChecker ¶
func NewSMTPChecker(opts SMTPOptions) *SMTPChecker
func (*SMTPChecker) Check ¶
func (s *SMTPChecker) Check(ctx context.Context, address, domain string, dns DNSResult, smtpUTF8 bool) SMTPResult
type SMTPDialer ¶
SMTPDialer makes SMTP transport testable and allows proxy/custom-network use.
type SMTPIPResolver ¶
SMTPIPResolver is used to resolve MX hosts once before dialing. This prevents DNS rebinding between policy checks and the TCP connection.
type SMTPOptions ¶
type SMTPOptions struct {
Enabled bool
Port int
Timeout time.Duration
HELODomain string
MailFrom string
TrySTARTTLS bool
RequireTLS bool
InsecureSkipVerify bool
CatchAllSamples int
MaxMXAttempts int
MaxConcurrent int
MinDomainInterval time.Duration
MaxResponseBytes int
DisableCatchAll bool
AllowPrivateIPs bool
PositiveTTL time.Duration
NegativeTTL time.Duration
UnknownTTL time.Duration
CatchAllTTL time.Duration
CatchAllUnknownTTL time.Duration
TLSConfig *tls.Config
}
SMTPOptions controls active mailbox probing. SMTP probing should only be enabled where outbound TCP/25 is permitted and after reviewing the target providers' acceptable-use policies.
func DefaultSMTPOptions ¶
func DefaultSMTPOptions() SMTPOptions
type SMTPResult ¶
type SMTPResult struct {
Status Status `json:"status"`
Mailbox MailboxStatus `json:"mailbox"`
CatchAll CatchAllStatus `json:"catch_all"`
Host string `json:"host,omitempty"`
Code int `json:"code,omitempty"`
EnhancedCode string `json:"enhanced_code,omitempty"`
Message string `json:"message,omitempty"`
TLS bool `json:"tls,omitempty"`
Cached bool `json:"cached,omitempty"`
Attempts int `json:"attempts,omitempty"`
Duration time.Duration `json:"duration_ns,omitempty"`
Error string `json:"error,omitempty"`
}
SMTPResult describes an SMTP RCPT probe. A positive response is evidence of acceptance, not proof that a human-owned mailbox exists.
type Scorer ¶
type Scorer interface {
Score(*Result)
}
Scorer converts completed stage results into a business verdict and reasons. Implementations must be safe for concurrent use.
type ScorerFunc ¶
type ScorerFunc func(*Result)
ScorerFunc adapts a function to Scorer.
func (ScorerFunc) Score ¶
func (f ScorerFunc) Score(r *Result)
type SendGridWebhookConfig ¶
type SyntaxResult ¶
type SyntaxResult struct {
Status Status `json:"status"`
Normalized string `json:"normalized,omitempty"`
Local string `json:"local,omitempty"`
Domain string `json:"domain,omitempty"`
SMTPUTF8 bool `json:"smtp_utf8,omitempty"`
DomainLiteral bool `json:"domain_literal,omitempty"`
Error string `json:"error,omitempty"`
}
SyntaxResult describes parsing and normalization.
func ValidateSyntax ¶
func ValidateSyntax(input string, opts AddressOptions) SyntaxResult
ValidateSyntax performs syntax and normalization only.
type ValidationError ¶
ValidationError identifies an input error without conflating it with DNS or SMTP uncertainty.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator is safe for concurrent use.
func NewDefault ¶
func NewDefault() *Validator
NewDefault constructs a validator with DNS enabled and SMTP disabled.
func (*Validator) RecordReputation ¶
func (v *Validator) RecordReputation(ctx context.Context, email string, typ ReputationEventType, at time.Time) error
RecordReputation normalizes an address and records an ESP signal. The store receives clear text only in memory and hashes it before persistence.
func (*Validator) RecordReputationEvent ¶
func (v *Validator) RecordReputationEvent(ctx context.Context, event ReputationEvent) error
RecordReputationEvent records an event with an optional provider-scoped ID. Supplying ID enables restart-safe idempotency in the bundled stores.
func (*Validator) Validate ¶
Validate runs all configured checks. DNS and SMTP uncertainty are represented in the result rather than returned as fatal errors; errors are reserved for cancellation or a closed validator.
func (*Validator) ValidateBulk ¶
func (v *Validator) ValidateBulk(ctx context.Context, emails []string, opts BulkOptions) (BulkResult, error)
ValidateBulk validates a bounded in-memory batch. It de-duplicates identical normalized inputs, performs one validation for each unique address, fans the result back to all original positions, and never creates one goroutine per item.
func (*Validator) ValidateMany ¶
func (v *Validator) ValidateMany(ctx context.Context, emails []string, workers int) ([]Result, error)
ValidateMany is retained for compatibility and delegates to the optimized bulk engine with ordered output and de-duplication enabled.
func (*Validator) ValidateStream ¶
func (v *Validator) ValidateStream(ctx context.Context, input <-chan string, opts BulkOptions) <-chan BulkItem
ValidateStream validates an arbitrarily large stream with bounded memory. Input is consumed until closed; output arrives in completion order. The caller must drain the returned channel. Cancellation closes output promptly.
type WebhookResponse ¶
type WebhookResponse struct {
Accepted int `json:"accepted"`
Ignored int `json:"ignored"`
Errors []string `json:"errors,omitempty"`
}
WebhookResponse summarizes ingestion without echoing recipient addresses.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
emailvalidator
command
|
|
|
emailvalidatord
command
|
|
|
examples
|
|
|
bulk
command
|
|
|
library
command
|
|
|
stream
command
|
|