mail

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package mail defines the engine-neutral contract the control plane uses to manage an in-house mail server. Adapters live in subpackages.

Index

Constants

View Source
const (
	PasswordLengthEnv = "APP_MAIL_PASSWORD_LENGTH" //nolint:gosec // env var name, not a credential

	// SuspendedMessage is what the engine tells a client that tries to log in.
	SuspendedMessage = "This mailbox is suspended. Ask your team admin."
)

PasswordLengthEnv sets the length of generated mailbox passwords.

View Source
const (
	SendCapEnv = "APP_MAIL_DEFAULT_SEND_CAP_PER_HOUR"
)

SendCapEnv sets the default per-account hourly send cap carried on AccountSpec. It is data only; nothing enforces it yet.

Variables

View Source
var (
	ErrNotFound     = errors.New("mail: not found")
	ErrExists       = errors.New("mail: already exists")
	ErrUnauthorized = errors.New("mail: engine rejected the admin credentials")
	ErrUnsupported  = errors.New("mail: not supported by this engine")
	ErrInvalid      = errors.New("mail: invalid request")
)

Typed failures every adapter maps its engine errors onto.

View Source
var ErrHasMail = errors.New("mail: the mailbox still holds mail")

ErrHasMail refuses a delete while the account still holds mail.

Functions

func AccountNameFor

func AccountNameFor(address string) string

AccountNameFor derives the engine account name from an address. Names are the full lowercase address so two domains never collide.

func CreateAccount

func CreateAccount(ctx context.Context, e Engine, address string, sendCapPerHour int) (string, error)

CreateAccount creates the account with a generated password and returns the password once. The caller shows it to the admin and never stores it.

func DefaultSendCapPerHour

func DefaultSendCapPerHour() int

DefaultSendCapPerHour reads the default cap from the environment.

func DeleteAccount

func DeleteAccount(ctx context.Context, e Engine, account, confirm string, deleteMail bool) error

DeleteAccount removes the account. confirm must repeat the account name, and an account that still uses disk is refused unless deleteMail is set.

func GeneratePassword

func GeneratePassword() (string, error)

GeneratePassword returns a random password from crypto/rand.

func PasswordLength

func PasswordLength() int

PasswordLength reads the generated password length, never below the minimum.

func ResumeAccount

func ResumeAccount(ctx context.Context, e Engine, account string) error

ResumeAccount allows logins again.

func SetAccountPassword

func SetAccountPassword(ctx context.Context, e Engine, account string) (string, error)

SetAccountPassword rotates the password and returns the new one once.

func SplitAddress

func SplitAddress(addr string) (local, domain string, err error)

SplitAddress returns the localpart and lowercase domain of an address.

func SuspendAccount

func SuspendAccount(ctx context.Context, e Engine, account string) error

SuspendAccount refuses logins and keeps receiving mail (ADR 059 section 2.5).

Types

type Account

type Account struct {
	Name string `json:"name"`
	// Disabled is true while the engine refuses logins for the account.
	Disabled bool `json:"disabled"`
}

Account is a mailbox owner. Quota is reported only when the engine exposes it.

func ListAccounts

func ListAccounts(ctx context.Context, e Engine) ([]Account, error)

ListAccounts lists every account with its login state.

type AccountDetail

type AccountDetail struct {
	Name           string   `json:"name"`
	Addresses      []string `json:"addresses"`
	LoginDisabled  bool     `json:"login_disabled"`
	DiskUsageBytes int64    `json:"disk_usage_bytes"`
}

AccountDetail is one account's management view: addresses, login state and disk usage. It never carries a password or message content.

type AccountSpec

type AccountSpec struct {
	Name     string
	Address  string
	Password string
	// SendCapPerHour is advisory data for a later enforcement slice; 0 means
	// use DefaultSendCapPerHour.
	SendCapPerHour int
}

AccountSpec creates an account with its first address.

func (AccountSpec) GoString

func (a AccountSpec) GoString() string

GoString covers %#v with the same redaction.

func (AccountSpec) LogValue

func (a AccountSpec) LogValue() slog.Value

LogValue redacts the password for slog.

func (AccountSpec) String

func (a AccountSpec) String() string

String redacts the password so a printed spec never leaks it.

type Alias

type Alias struct {
	Address string   `json:"address"`
	Targets []string `json:"targets"`
}

Alias forwards an address to targets.

func ListAliases

func ListAliases(ctx context.Context, e Engine, domain string) ([]Alias, error)

ListAliases lists one domain's aliases.

type CredentialSource

type CredentialSource interface {
	AdminPassword(ctx context.Context) (string, error)
}

CredentialSource yields the engine admin password at call time so callers can read it from internal/secrets and the adapter never stores it long term.

type DKIMKey

type DKIMKey struct {
	Selector  string `json:"selector"`
	PublicKey string `json:"public_key"`
	TXT       string `json:"txt"`
}

DKIMKey is a DKIM selector and public key found in the engine's records. PublicKey is the bare p= value, ready for mailrecords.Spec.DKIMPublicKey.

type DNSRecord

type DNSRecord struct {
	Name  string `json:"name"`
	TTL   int    `json:"ttl,omitempty"`
	Type  string `json:"type"`
	Value string `json:"value"`
}

DNSRecord is one parsed zone-file line from the engine.

type Domain

type Domain struct {
	Name string `json:"name"`
}

Domain is a mail domain the engine serves.

type DomainSpec

type DomainSpec struct {
	Name      string
	Account   string
	Localpart string
}

DomainSpec creates a domain. Account is an existing account that receives the domain's default address; leave Localpart empty to reuse it, or set both to create a new account with that first address.

type Engine

type Engine interface {
	Domains(ctx context.Context) ([]Domain, error)
	AddDomain(ctx context.Context, d DomainSpec) error
	RemoveDomain(ctx context.Context, name string) error
	DNSRecords(ctx context.Context, domain string) ([]DNSRecord, error)
	DKIMKeys(ctx context.Context, domain string) ([]DKIMKey, error)
	Accounts(ctx context.Context) ([]Account, error)
	AddAccount(ctx context.Context, a AccountSpec) error
	RemoveAccount(ctx context.Context, name string) error
	AddAddress(ctx context.Context, address, account string) error
	SetPassword(ctx context.Context, account, password string) error
	SetQuota(ctx context.Context, account string, q Quota) error
	AddAlias(ctx context.Context, a Alias) error
	RemoveAlias(ctx context.Context, address string) error
	// Aliases lists the aliases of one domain.
	Aliases(ctx context.Context, domain string) ([]Alias, error)
	// AccountDetail reads one account's addresses, login state and disk usage.
	AccountDetail(ctx context.Context, name string) (AccountDetail, error)
	// SetLoginDisabled refuses logins with message as the reason; "" allows them again.
	SetLoginDisabled(ctx context.Context, account, message string) error
	QueueList(ctx context.Context, limit int) ([]QueueEntry, error)
	// HoldMessages sets hold on the listed queue ids and returns how many changed.
	HoldMessages(ctx context.Context, ids []int64, hold bool) (int, error)
	QueueStats(ctx context.Context) (QueueStats, error)
	// HoldQueue pauses delivery of every queued message; ReleaseQueue resumes it.
	HoldQueue(ctx context.Context) error
	ReleaseQueue(ctx context.Context) error
	Health(ctx context.Context) error
}

Engine is the control plane's view of a mail server.

type Error

type Error struct {
	Kind    error
	Code    string
	Message string
}

Error carries the engine's own message alongside the typed sentinel.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type QueueEntry

type QueueEntry struct {
	ID              int64     `json:"id"`
	Queued          time.Time `json:"queued"`
	Hold            bool      `json:"hold"`
	SenderAccount   string    `json:"sender_account"`
	RecipientDomain string    `json:"recipient_domain"`
	Attempts        int       `json:"attempts"`
	NextAttempt     time.Time `json:"next_attempt"`
	LastError       string    `json:"last_error,omitempty"`
}

QueueEntry is one queued outbound message, limited to delivery metadata: never the subject, message id or body (ADR 059 section 2.8).

func ListQueue

func ListQueue(ctx context.Context, e Engine, limit int) ([]QueueEntry, error)

ListQueue lists queued messages, metadata only.

type QueueStats

type QueueStats struct {
	Queued  int `json:"queued"`
	Failing int `json:"failing"`
}

QueueStats summarises the outbound queue.

type Quota

type Quota struct {
	MaxBytes int64
}

Quota is a per-account size limit in bytes (0 means unlimited).

Directories

Path Synopsis
Package demo is a dev-only, in-memory stand-in for a mail server and its DNS zone, so the Mail page can be exercised without a container, provider or domain.
Package demo is a dev-only, in-memory stand-in for a mail server and its DNS zone, so the Mail page can be exercised without a container, provider or domain.
Package mox adapts the mox admin API (Sherpa JSON-RPC) to mail.Engine.
Package mox adapts the mox admin API (Sherpa JSON-RPC) to mail.Engine.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL