git » chasquid » commit f3b01cb

docs: Add missing docstrings, adjust wording to match standard style

author Alberto Bertogli
2018-03-04 15:54:34 UTC
committer Alberto Bertogli
2018-03-04 16:00:06 UTC
parent 40ae9b5f693ef67800ddeeb9fb27eb26f4f9d31e

docs: Add missing docstrings, adjust wording to match standard style

This patch adds a missing docstrings for exported identifiers, and
adjust some of the existing ones to match the standard style.

In some cases, the identifiers were un-exported after noticing they had
no external users.

Besides improving documentation, it also reduces the linter noise
significantly.

cmd/chasquid-util/chasquid-util.go +13 -12
internal/aliases/aliases.go +13 -1
internal/auth/auth.go +9 -4
internal/config/config.go +1 -0
internal/courier/procmail.go +2 -0
internal/courier/smtp.go +2 -0
internal/domaininfo/domaininfo.go +3 -0
internal/dovecot/dovecot.go +10 -6
internal/dovecot/dovecot_test.go +2 -2
internal/envelope/envelope.go +4 -0
internal/maillog/maillog.go +20 -2
internal/normalize/normalize.go +3 -3
internal/protoio/protoio.go +3 -0
internal/queue/queue.go +5 -1
internal/safeio/safeio.go +1 -1
internal/set/set.go +4 -0
internal/smtp/smtp.go +2 -1
internal/smtpsrv/conn.go +17 -2
internal/smtpsrv/server.go +15 -1
internal/trace/trace.go +13 -0
internal/userdb/userdb.go +12 -10

diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go
index a617e0e..5a9b145 100644
--- a/cmd/chasquid-util/chasquid-util.go
+++ b/cmd/chasquid-util/chasquid-util.go
@@ -56,12 +56,12 @@ func main() {
 	}
 
 	commands := map[string]func(){
-		"user-add":        UserAdd,
-		"user-remove":     UserRemove,
-		"authenticate":    Authenticate,
-		"check-userdb":    CheckUserDB,
-		"aliases-resolve": AliasesResolve,
-		"print-config":    PrintConfig,
+		"user-add":        userAdd,
+		"user-remove":     userRemove,
+		"authenticate":    authenticate,
+		"check-userdb":    checkUserDB,
+		"aliases-resolve": aliasesResolve,
+		"print-config":    printConfig,
 	}
 
 	for cmd, f := range commands {
@@ -71,6 +71,7 @@ func main() {
 	}
 }
 
+// Fatalf prints the given message, then exits the program with an error code.
 func Fatalf(s string, arg ...interface{}) {
 	fmt.Printf(s+"\n", arg...)
 	os.Exit(1)
@@ -109,7 +110,7 @@ func userDBFromArgs(create bool) (string, string, *userdb.DB) {
 }
 
 // chasquid-util check-userdb <domain>
-func CheckUserDB() {
+func checkUserDB() {
 	_, err := userdb.Load(userDBForDomain(""))
 	if err != nil {
 		Fatalf("Error loading database: %v", err)
@@ -119,7 +120,7 @@ func CheckUserDB() {
 }
 
 // chasquid-util user-add <username> [--password=<password>]
-func UserAdd() {
+func userAdd() {
 	user, _, db := userDBFromArgs(true)
 	password := getPassword()
 
@@ -137,7 +138,7 @@ func UserAdd() {
 }
 
 // chasquid-util authenticate <username> [--password=<password>]
-func Authenticate() {
+func authenticate() {
 	user, _, db := userDBFromArgs(false)
 
 	password := getPassword()
@@ -177,7 +178,7 @@ func getPassword() string {
 }
 
 // chasquid-util user-remove <username>
-func UserRemove() {
+func userRemove() {
 	user, _, db := userDBFromArgs(false)
 
 	present := db.RemoveUser(user)
@@ -194,7 +195,7 @@ func UserRemove() {
 }
 
 // chasquid-util aliases-resolve <address>
-func AliasesResolve() {
+func aliasesResolve() {
 	conf, err := config.Load(configDir + "/chasquid.conf")
 	if err != nil {
 		Fatalf("Error reading config")
@@ -238,7 +239,7 @@ func AliasesResolve() {
 }
 
 // chasquid-util print-config
-func PrintConfig() {
+func printConfig() {
 	conf, err := config.Load(configDir + "/chasquid.conf")
 	if err != nil {
 		Fatalf("Error reading config")
diff --git a/internal/aliases/aliases.go b/internal/aliases/aliases.go
index 31ef18b..6635e72 100644
--- a/internal/aliases/aliases.go
+++ b/internal/aliases/aliases.go
@@ -72,6 +72,7 @@ type Recipient struct {
 	Type RType
 }
 
+// RType represents a recipient type, see the contants below for valid values.
 type RType string
 
 // Valid recipient types.
@@ -81,6 +82,8 @@ const (
 )
 
 var (
+	// ErrRecursionLimitExceeded is returned when the resolving lookup
+	// exceeded the recursion limit. Usually caused by aliases loops.
 	ErrRecursionLimitExceeded = fmt.Errorf("recursion limit exceeded")
 
 	// How many levels of recursions we allow during lookups.
@@ -109,6 +112,7 @@ type Resolver struct {
 	mu sync.Mutex
 }
 
+// NewResolver returns a new, empty Resolver.
 func NewResolver() *Resolver {
 	return &Resolver{
 		files:   map[string][]string{},
@@ -117,6 +121,8 @@ func NewResolver() *Resolver {
 	}
 }
 
+// Resolve the given address, returning the list of corresponding recipients
+// (if any).
 func (v *Resolver) Resolve(addr string) ([]Recipient, error) {
 	v.mu.Lock()
 	defer v.mu.Unlock()
@@ -184,14 +190,17 @@ func (v *Resolver) cleanIfLocal(addr string) string {
 	return user + "@" + domain
 }
 
+// AddDomain to the resolver, registering its existence.
 func (v *Resolver) AddDomain(domain string) {
 	v.mu.Lock()
 	v.domains[domain] = true
 	v.mu.Unlock()
 }
 
+// AddAliasesFile to the resolver. The file will be parsed, and an error
+// returned if it does not exist or parse correctly.
 func (v *Resolver) AddAliasesFile(domain, path string) error {
-	// We inconditionally add the domain and file on our list.
+	// We unconditionally add the domain and file on our list.
 	// Even if the file does not exist now, it may later. This makes it be
 	// consider when doing Reload.
 	// Adding it to the domains mean that we will do drop character and suffix
@@ -219,10 +228,13 @@ func (v *Resolver) AddAliasesFile(domain, path string) error {
 	return nil
 }
 
+// AddAliasForTesting adds an alias to the resolver, for testing purposes.
+// Not for use in production code.
 func (v *Resolver) AddAliasForTesting(addr, rcpt string, rType RType) {
 	v.aliases[addr] = append(v.aliases[addr], Recipient{rcpt, rType})
 }
 
+// Reload aliases files for all known domains.
 func (v *Resolver) Reload() error {
 	newAliases := map[string][]Recipient{}
 
diff --git a/internal/auth/auth.go b/internal/auth/auth.go
index 6ab74c2..c9b543f 100644
--- a/internal/auth/auth.go
+++ b/internal/auth/auth.go
@@ -13,16 +13,16 @@ import (
 	"blitiri.com.ar/go/chasquid/internal/normalize"
 )
 
-// Interface for authentication backends.
+// Backend is the common interface for all authentication backends.
 type Backend interface {
 	Authenticate(user, password string) (bool, error)
 	Exists(user string) (bool, error)
 	Reload() error
 }
 
-// Interface for authentication backends that don't need to emit errors.
-// This allows backends to avoid unnecessary complexity, in exchange for a bit
-// more here.
+// NoErrorBackend is the interface for authentication backends that don't need
+// to emit errors.  This allows backends to avoid unnecessary complexity, in
+// exchange for a bit more here.
 // They can be converted to normal Backend using WrapNoErrorBackend (defined
 // below).
 type NoErrorBackend interface {
@@ -31,6 +31,8 @@ type NoErrorBackend interface {
 	Reload() error
 }
 
+// Authenticator tracks the backends for each domain, and allows callers to
+// query them with a more practical API.
 type Authenticator struct {
 	// Registered backends, map of domain (string) -> Backend.
 	// Backend operations will _not_ include the domain in the username.
@@ -48,6 +50,7 @@ type Authenticator struct {
 	AuthDuration time.Duration
 }
 
+// NewAuthenticator returns a new Authenticator with no backends.
 func NewAuthenticator() *Authenticator {
 	return &Authenticator{
 		backends:     map[string]Backend{},
@@ -55,6 +58,7 @@ func NewAuthenticator() *Authenticator {
 	}
 }
 
+// Register a backend to use for the given domain.
 func (a *Authenticator) Register(domain string, be Backend) {
 	a.backends[domain] = be
 }
@@ -87,6 +91,7 @@ func (a *Authenticator) Authenticate(user, domain, password string) (bool, error
 	return false, nil
 }
 
+// Exists checks that user@domain exists.
 func (a *Authenticator) Exists(user, domain string) (bool, error) {
 	if be, ok := a.backends[domain]; ok {
 		ok, err := be.Exists(user)
diff --git a/internal/config/config.go b/internal/config/config.go
index 5bf4abd..ef35308 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -81,6 +81,7 @@ func Load(path string) (*Config, error) {
 	return c, nil
 }
 
+// LogConfig logs the given configuration, in a human-friendly way.
 func LogConfig(c *Config) {
 	log.Infof("Configuration:")
 	log.Infof("  Hostname: %q", c.Hostname)
diff --git a/internal/courier/procmail.go b/internal/courier/procmail.go
index 0a0aa6e..9f2dc7e 100644
--- a/internal/courier/procmail.go
+++ b/internal/courier/procmail.go
@@ -29,6 +29,8 @@ type Procmail struct {
 	Timeout time.Duration // Timeout for each invocation.
 }
 
+// Deliver an email. On failures, returns an error, and whether or not it is
+// permanent.
 func (p *Procmail) Deliver(from string, to string, data []byte) (error, bool) {
 	tr := trace.New("Courier.Procmail", to)
 	defer tr.Finish()
diff --git a/internal/courier/smtp.go b/internal/courier/smtp.go
index 7f1ce25..3df9a5c 100644
--- a/internal/courier/smtp.go
+++ b/internal/courier/smtp.go
@@ -41,6 +41,8 @@ type SMTP struct {
 	Dinfo *domaininfo.DB
 }
 
+// Deliver an email. On failures, returns an error, and whether or not it is
+// permanent.
 func (s *SMTP) Deliver(from string, to string, data []byte) (error, bool) {
 	a := &attempt{
 		courier:  s,
diff --git a/internal/domaininfo/domaininfo.go b/internal/domaininfo/domaininfo.go
index 841f459..2d7ff42 100644
--- a/internal/domaininfo/domaininfo.go
+++ b/internal/domaininfo/domaininfo.go
@@ -13,6 +13,7 @@ import (
 // Command to generate domaininfo.pb.go.
 //go:generate protoc --go_out=. domaininfo.proto
 
+// DB represents the persistent domain information database.
 type DB struct {
 	// Persistent store with the list of domains we know.
 	store *protoio.Store
@@ -23,6 +24,8 @@ type DB struct {
 	ev *trace.EventLog
 }
 
+// New opens a domain information database on the given dir, creating it if
+// necessary. The returned database will not be loaded.
 func New(dir string) (*DB, error) {
 	st, err := protoio.NewStore(dir)
 	if err != nil {
diff --git a/internal/dovecot/dovecot.go b/internal/dovecot/dovecot.go
index 855cfee..f6d2c13 100644
--- a/internal/dovecot/dovecot.go
+++ b/internal/dovecot/dovecot.go
@@ -21,12 +21,12 @@ import (
 	"unicode"
 )
 
-// Default timeout to use. We expect Dovecot to be quite fast, but don't want
+// DefaultTimeout to use. We expect Dovecot to be quite fast, but don't want
 // to hang forever if something gets stuck.
 const DefaultTimeout = 5 * time.Second
 
 var (
-	ErrUsernameNotSafe = errors.New("username not safe (contains spaces)")
+	errUsernameNotSafe = errors.New("username not safe (contains spaces)")
 )
 
 var defaultUserdbPaths = []string{
@@ -60,6 +60,7 @@ func NewAuth(userdb, client string) *Auth {
 	}
 }
 
+// String representation of this Auth, for human consumption.
 func (a *Auth) String() string {
 	return fmt.Sprintf("DovecotAuth(%q, %q)", a.userdbAddr, a.clientAddr)
 }
@@ -79,10 +80,10 @@ func (a *Auth) Check() error {
 	return nil
 }
 
-// Does user exist?
+// Exists returns true if the user exists, false otherwise.
 func (a *Auth) Exists(user string) (bool, error) {
 	if !isUsernameSafe(user) {
-		return false, ErrUsernameNotSafe
+		return false, errUsernameNotSafe
 	}
 
 	conn, err := a.dial("unix", a.userdbAddr)
@@ -126,10 +127,11 @@ func (a *Auth) Exists(user string) (bool, error) {
 	return false, fmt.Errorf("invalid response: %q", resp)
 }
 
-// Is the password valud for the user?
+// Authenticate returns true if the password is valid for the user, false
+// otherwise.
 func (a *Auth) Authenticate(user, passwd string) (bool, error) {
 	if !isUsernameSafe(user) {
-		return false, ErrUsernameNotSafe
+		return false, errUsernameNotSafe
 	}
 
 	conn, err := a.dial("unix", a.clientAddr)
@@ -182,6 +184,8 @@ func (a *Auth) Authenticate(user, passwd string) (bool, error) {
 	return false, fmt.Errorf("invalid response: %q", resp)
 }
 
+// Reload the authenticator. It's a no-op for dovecot, but it is needed to
+// conform with the auth.Backend interface.
 func (a *Auth) Reload() error {
 	return nil
 }
diff --git a/internal/dovecot/dovecot_test.go b/internal/dovecot/dovecot_test.go
index 4fcbfc7..f2fd887 100644
--- a/internal/dovecot/dovecot_test.go
+++ b/internal/dovecot/dovecot_test.go
@@ -19,12 +19,12 @@ func TestUsernameNotSafe(t *testing.T) {
 		"a b", " ab", "ab ", "a\tb", "a\t", " ", "\t", "\t "}
 	for _, c := range cases {
 		ok, err := a.Authenticate(c, "passwd")
-		if ok || err != ErrUsernameNotSafe {
+		if ok || err != errUsernameNotSafe {
 			t.Errorf("Authenticate(%q, _): got %v, %v", c, ok, err)
 		}
 
 		ok, err = a.Exists(c)
-		if ok || err != ErrUsernameNotSafe {
+		if ok || err != errUsernameNotSafe {
 			t.Errorf("Exists(%q): got %v, %v", c, ok, err)
 		}
 	}
diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go
index c504ad0..ec04bf9 100644
--- a/internal/envelope/envelope.go
+++ b/internal/envelope/envelope.go
@@ -19,16 +19,19 @@ func Split(addr string) (string, string) {
 	return ps[0], ps[1]
 }
 
+// UserOf user@domain returns user.
 func UserOf(addr string) string {
 	user, _ := Split(addr)
 	return user
 }
 
+// DomainOf user@domain returns domain.
 func DomainOf(addr string) string {
 	_, domain := Split(addr)
 	return domain
 }
 
+// DomainIn checks that the domain of the address is on the given set.
 func DomainIn(addr string, locals *set.String) bool {
 	domain := DomainOf(addr)
 	if domain == "" {
@@ -38,6 +41,7 @@ func DomainIn(addr string, locals *set.String) bool {
 	return locals.Has(domain)
 }
 
+// AddHeader adds (prepends) a MIME header to the message.
 func AddHeader(data []byte, k, v string) []byte {
 	if len(v) > 0 {
 		// If the value contains newlines, indent them properly.
diff --git a/internal/maillog/maillog.go b/internal/maillog/maillog.go
index 568bc6f..322d8b3 100644
--- a/internal/maillog/maillog.go
+++ b/internal/maillog/maillog.go
@@ -24,20 +24,26 @@ type timedWriter struct {
 	w io.Writer
 }
 
+// Write the given buffer, prepending timing information.
 func (t timedWriter) Write(b []byte) (int, error) {
 	fmt.Fprintf(t.w, "%s  ", time.Now().Format("2006-01-02 15:04:05.000000"))
 	return t.w.Write(b)
 }
 
+// Logger contains a backend used to log data to, such as a file or syslog.
+// It implements various user-friendly methods for logging mail information to
+// it.
 type Logger struct {
 	w    io.Writer
 	once sync.Once
 }
 
+// New creates a new Logger which will write messages to the given writer.
 func New(w io.Writer) *Logger {
 	return &Logger{w: timedWriter{w}}
 }
 
+// NewSyslog creates a new Logger which will write messages to syslog.
 func NewSyslog() (*Logger, error) {
 	w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_MAIL, "chasquid")
 	if err != nil {
@@ -58,10 +64,12 @@ func (l *Logger) printf(format string, args ...interface{}) {
 	}
 }
 
+// Listening logs that the daemon is listening on the given address.
 func (l *Logger) Listening(a string) {
 	l.printf("daemon listening on %s\n", a)
 }
 
+// Auth logs an authentication request.
 func (l *Logger) Auth(netAddr net.Addr, user string, successful bool) {
 	res := "succeeded"
 	if !successful {
@@ -72,6 +80,7 @@ func (l *Logger) Auth(netAddr net.Addr, user string, successful bool) {
 	authLog.Debugf(msg)
 }
 
+// Rejected logs that we've rejected an email.
 func (l *Logger) Rejected(netAddr net.Addr, from string, to []string, err string) {
 	if from != "" {
 		from = fmt.Sprintf(" from=%s", from)
@@ -83,10 +92,12 @@ func (l *Logger) Rejected(netAddr net.Addr, from string, to []string, err string
 	l.printf("%s rejected%s%s - %v\n", netAddr, from, toStr, err)
 }
 
+// Queued logs that we have queued an email.
 func (l *Logger) Queued(netAddr net.Addr, from string, to []string, id string) {
 	l.printf("%s from=%s queued ip=%s to=%v\n", id, from, netAddr, to)
 }
 
+// SendAttempt logs that we have attempted to send an email.
 func (l *Logger) SendAttempt(id, from, to string, err error, permanent bool) {
 	if err == nil {
 		l.printf("%s from=%s to=%s sent\n", id, from, to)
@@ -99,6 +110,7 @@ func (l *Logger) SendAttempt(id, from, to string, err error, permanent bool) {
 	}
 }
 
+// QueueLoop logs that we have completed a queue loop.
 func (l *Logger) QueueLoop(id, from string, nextDelay time.Duration) {
 	if nextDelay > 0 {
 		l.printf("%s from=%s completed loop, next in %v\n", id, from, nextDelay)
@@ -107,29 +119,35 @@ func (l *Logger) QueueLoop(id, from string, nextDelay time.Duration) {
 	}
 }
 
-// The default logger used in the following top-level functions.
-var Default *Logger = New(ioutil.Discard)
+// Default logger, used in the following top-level functions.
+var Default = New(ioutil.Discard)
 
+// Listening logs that the daemon is listening on the given address.
 func Listening(a string) {
 	Default.Listening(a)
 }
 
+// Auth logs an authentication request.
 func Auth(netAddr net.Addr, user string, successful bool) {
 	Default.Auth(netAddr, user, successful)
 }
 
+// Rejected logs that we've rejected an email.
 func Rejected(netAddr net.Addr, from string, to []string, err string) {
 	Default.Rejected(netAddr, from, to, err)
 }
 
+// Queued logs that we have queued an email.
 func Queued(netAddr net.Addr, from string, to []string, id string) {
 	Default.Queued(netAddr, from, to, id)
 }
 
+// SendAttempt logs that we have attempted to send an email.
 func SendAttempt(id, from, to string, err error, permanent bool) {
 	Default.SendAttempt(id, from, to, err, permanent)
 }
 
+// QueueLoop logs that we have completed a queue loop.
 func QueueLoop(id, from string, nextDelay time.Duration) {
 	Default.QueueLoop(id, from, nextDelay)
 }
diff --git a/internal/normalize/normalize.go b/internal/normalize/normalize.go
index 569dc76..df68226 100644
--- a/internal/normalize/normalize.go
+++ b/internal/normalize/normalize.go
@@ -41,7 +41,7 @@ func Domain(domain string) (string, error) {
 	return d, nil
 }
 
-// Name normalizes an email address, applying User and Domain to its
+// Addr normalizes an email address, applying User and Domain to its
 // respective components.
 // On error, it will also return the original address to simplify callers.
 func Addr(addr string) (string, error) {
@@ -60,8 +60,8 @@ func Addr(addr string) (string, error) {
 	return user + "@" + domain, nil
 }
 
-// Take an address with an ASCII domain, and convert it to Unicode as per
-// IDNA, including basic normalization.
+// DomainToUnicode takes an address with an ASCII domain, and convert it to
+// Unicode as per IDNA, including basic normalization.
 // The user part is unchanged.
 func DomainToUnicode(addr string) (string, error) {
 	if addr == "<>" {
diff --git a/internal/protoio/protoio.go b/internal/protoio/protoio.go
index 4f5ac10..f066955 100644
--- a/internal/protoio/protoio.go
+++ b/internal/protoio/protoio.go
@@ -72,10 +72,12 @@ func (s *Store) idToFname(id string) string {
 	return s.dir + "/" + storeIDPrefix + url.QueryEscape(id)
 }
 
+// Put a message into the store.
 func (s *Store) Put(id string, m proto.Message) error {
 	return WriteTextMessage(s.idToFname(id), m, 0660)
 }
 
+// Get a message from the store.
 func (s *Store) Get(id string, m proto.Message) (bool, error) {
 	err := ReadTextMessage(s.idToFname(id), m)
 	if os.IsNotExist(err) {
@@ -84,6 +86,7 @@ func (s *Store) Get(id string, m proto.Message) (bool, error) {
 	return err == nil, err
 }
 
+// ListIDs in the store.
 func (s *Store) ListIDs() ([]string, error) {
 	ids := []string{}
 
diff --git a/internal/queue/queue.go b/internal/queue/queue.go
index 7b3d4e3..cd92e58 100644
--- a/internal/queue/queue.go
+++ b/internal/queue/queue.go
@@ -146,6 +146,7 @@ func (q *Queue) Load() error {
 	return nil
 }
 
+// Len returns the number of elements in the queue.
 func (q *Queue) Len() int {
 	q.mu.RLock()
 	defer q.mu.RUnlock()
@@ -253,7 +254,7 @@ func (q *Queue) DumpString() string {
 	return s
 }
 
-// An item in the queue.
+// An Item in the queue.
 type Item struct {
 	// Base the item on the protobuf message.
 	// We will use this for serialization, so any fields below are NOT
@@ -267,6 +268,7 @@ type Item struct {
 	CreatedAt time.Time
 }
 
+// ItemFromFile loads an item from the given file.
 func ItemFromFile(fname string) (*Item, error) {
 	item := &Item{}
 	err := protoio.ReadTextMessage(fname, &item.Message)
@@ -278,6 +280,7 @@ func ItemFromFile(fname string) (*Item, error) {
 	return item, err
 }
 
+// WriteTo saves an item to the given directory.
 func (item *Item) WriteTo(dir string) error {
 	item.Lock()
 	defer item.Unlock()
@@ -294,6 +297,7 @@ func (item *Item) WriteTo(dir string) error {
 	return protoio.WriteTextMessage(path, &item.Message, 0600)
 }
 
+// SendLoop repeatedly attempts to send the item.
 func (item *Item) SendLoop(q *Queue) {
 	tr := trace.New("Queue.SendLoop", item.ID)
 	defer tr.Finish()
diff --git a/internal/safeio/safeio.go b/internal/safeio/safeio.go
index a78302d..dc742f7 100644
--- a/internal/safeio/safeio.go
+++ b/internal/safeio/safeio.go
@@ -9,7 +9,7 @@ import (
 	"syscall"
 )
 
-// Type FileOp represents an operation on a file (passed by its name).
+// FileOp represents an operation on a file (passed by its name).
 type FileOp func(fname string) error
 
 // WriteFile writes data to a file named by filename, atomically.
diff --git a/internal/set/set.go b/internal/set/set.go
index e53308a..dfaf9c9 100644
--- a/internal/set/set.go
+++ b/internal/set/set.go
@@ -1,16 +1,19 @@
 // Package set implement sets for various types. Well, only string for now :)
 package set
 
+// String set.
 type String struct {
 	m map[string]struct{}
 }
 
+// NewString returns a new string set, with the given values in it.
 func NewString(values ...string) *String {
 	s := &String{}
 	s.Add(values...)
 	return s
 }
 
+// Add values to the string set.
 func (s *String) Add(values ...string) {
 	if s.m == nil {
 		s.m = map[string]struct{}{}
@@ -21,6 +24,7 @@ func (s *String) Add(values ...string) {
 	}
 }
 
+// Has checks if the set has the given value.
 func (s *String) Has(value string) bool {
 	// We explicitly allow s to be nil *in this function* to simplify callers'
 	// code.  Note that Add will not tolerate it, and will panic.
diff --git a/internal/smtp/smtp.go b/internal/smtp/smtp.go
index aa67696..bb91b85 100644
--- a/internal/smtp/smtp.go
+++ b/internal/smtp/smtp.go
@@ -22,6 +22,7 @@ type Client struct {
 	*smtp.Client
 }
 
+// NewClient uses the given connection to create a new Client.
 func NewClient(conn net.Conn, host string) (*Client, error) {
 	c, err := smtp.NewClient(conn, host)
 	if err != nil {
@@ -129,7 +130,7 @@ func isASCII(s string) bool {
 	return true
 }
 
-// ErrIsPermanent returns true if the error is permanent, and false otherwise.
+// IsPermanent returns true if the error is permanent, and false otherwise.
 // If it can't tell, it returns false.
 func IsPermanent(err error) bool {
 	terr, ok := err.(*textproto.Error)
diff --git a/internal/smtpsrv/conn.go b/internal/smtpsrv/conn.go
index 42a8cd7..14aee1c 100644
--- a/internal/smtpsrv/conn.go
+++ b/internal/smtpsrv/conn.go
@@ -52,7 +52,7 @@ var (
 	disableSPFForTesting = false
 )
 
-// Mode for a socket (listening or connection).
+// SocketMode represents the mode for a socket (listening or connection).
 // We keep them distinct, as policies can differ between them.
 type SocketMode struct {
 	// Is this mode submission?
@@ -81,7 +81,7 @@ var (
 	ModeSubmissionTLS = SocketMode{IsSubmission: true, TLS: true}
 )
 
-// Incoming SMTP connection.
+// Conn represents an incoming SMTP connection.
 type Conn struct {
 	// Main hostname, used for display only.
 	hostname string
@@ -146,10 +146,13 @@ type Conn struct {
 	commandTimeout time.Duration
 }
 
+// Close the connection.
 func (c *Conn) Close() {
 	c.conn.Close()
 }
 
+// Handle implements the main protocol loop (reading commands, sending
+// replies).
 func (c *Conn) Handle() {
 	defer c.Close()
 
@@ -265,6 +268,7 @@ loop:
 	}
 }
 
+// HELO SMTP command handler.
 func (c *Conn) HELO(params string) (code int, msg string) {
 	if len(strings.TrimSpace(params)) == 0 {
 		return 501, "Invisible customers are not welcome!"
@@ -282,6 +286,7 @@ func (c *Conn) HELO(params string) (code int, msg string) {
 	return 250, msg
 }
 
+// EHLO SMTP command handler.
 func (c *Conn) EHLO(params string) (code int, msg string) {
 	if len(strings.TrimSpace(params)) == 0 {
 		return 501, "Invisible customers are not welcome!"
@@ -303,10 +308,12 @@ func (c *Conn) EHLO(params string) (code int, msg string) {
 	return 250, buf.String()
 }
 
+// HELP SMTP command handler.
 func (c *Conn) HELP(params string) (code int, msg string) {
 	return 214, "hoy por ti, maƱana por mi"
 }
 
+// RSET SMTP command handler.
 func (c *Conn) RSET(params string) (code int, msg string) {
 	c.resetEnvelope()
 
@@ -319,6 +326,7 @@ func (c *Conn) RSET(params string) (code int, msg string) {
 	return 250, msgs[rand.Int()%len(msgs)]
 }
 
+// VRFY SMTP command handler.
 func (c *Conn) VRFY(params string) (code int, msg string) {
 	// 252 can be used for cases like ours, when we don't really want to
 	// confirm or deny anything.
@@ -326,6 +334,7 @@ func (c *Conn) VRFY(params string) (code int, msg string) {
 	return 252, "You have a strange feeling for a moment, then it passes."
 }
 
+// EXPN SMTP command handler.
 func (c *Conn) EXPN(params string) (code int, msg string) {
 	// 252 can be used for cases like ours, when we don't really want to
 	// confirm or deny anything.
@@ -333,10 +342,12 @@ func (c *Conn) EXPN(params string) (code int, msg string) {
 	return 252, "You feel disoriented for a moment."
 }
 
+// NOOP SMTP command handler.
 func (c *Conn) NOOP(params string) (code int, msg string) {
 	return 250, "You hear a faint typing noise."
 }
 
+// MAIL SMTP command handler.
 func (c *Conn) MAIL(params string) (code int, msg string) {
 	// params should be: "FROM:<name@host>", and possibly followed by
 	// options such as "BODY=8BITMIME" (which we ignore).
@@ -466,6 +477,7 @@ func (c *Conn) secLevelCheck(addr string) bool {
 	return ok
 }
 
+// RCPT SMTP command handler.
 func (c *Conn) RCPT(params string) (code int, msg string) {
 	// params should be: "TO:<name@host>", and possibly followed by options
 	// such as "NOTIFY=SUCCESS,DELAY" (which we ignore).
@@ -531,6 +543,7 @@ func (c *Conn) RCPT(params string) (code int, msg string) {
 	return 250, "You have an eerie feeling..."
 }
 
+// DATA SMTP command handler.
 func (c *Conn) DATA(params string) (code int, msg string) {
 	if c.ehloAddress == "" {
 		return 503, "Invisible customers are not welcome!"
@@ -796,6 +809,7 @@ func boolToStr(b bool) string {
 	return "0"
 }
 
+// STARTTLS SMTP command handler.
 func (c *Conn) STARTTLS(params string) (code int, msg string) {
 	if c.onTLS {
 		return 503, "You are already wearing that!"
@@ -840,6 +854,7 @@ func (c *Conn) STARTTLS(params string) (code int, msg string) {
 	return 0, ""
 }
 
+// AUTH SMTP command handler.
 func (c *Conn) AUTH(params string) (code int, msg string) {
 	if !c.onTLS {
 		return 503, "You feel vulnerable"
diff --git a/internal/smtpsrv/server.go b/internal/smtpsrv/server.go
index 640a17e..f3cb0e6 100644
--- a/internal/smtpsrv/server.go
+++ b/internal/smtpsrv/server.go
@@ -28,6 +28,7 @@ var (
 		"how often to reload, ONLY FOR TESTING")
 )
 
+// Server represents an SMTP server instance.
 type Server struct {
 	// Main hostname, used for display only.
 	Hostname string
@@ -70,6 +71,7 @@ type Server struct {
 	PostDataHook string
 }
 
+// NewServer returns a new empty Server.
 func NewServer() *Server {
 	return &Server{
 		addrs:          map[SocketMode][]string{},
@@ -83,6 +85,7 @@ func NewServer() *Server {
 	}
 }
 
+// AddCerts (TLS) to the server.
 func (s *Server) AddCerts(certPath, keyPath string) error {
 	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
 	if err != nil {
@@ -92,36 +95,44 @@ func (s *Server) AddCerts(certPath, keyPath string) error {
 	return nil
 }
 
+// AddAddr adds an address for the server to listen on.
 func (s *Server) AddAddr(a string, m SocketMode) {
 	s.addrs[m] = append(s.addrs[m], a)
 }
 
+// AddListeners adds listeners for the server to listen on.
 func (s *Server) AddListeners(ls []net.Listener, m SocketMode) {
 	s.listeners[m] = append(s.listeners[m], ls...)
 }
 
+// AddDomain adds a local domain to the server.
 func (s *Server) AddDomain(d string) {
 	s.localDomains.Add(d)
 	s.aliasesR.AddDomain(d)
 }
 
+// AddUserDB adds a userdb.DB instance as backend for the domain.
 func (s *Server) AddUserDB(domain string, db *userdb.DB) {
 	s.authr.Register(domain, auth.WrapNoErrorBackend(db))
 }
 
+// AddAliasesFile adds an aliases file for the given domain.
 func (s *Server) AddAliasesFile(domain, f string) error {
 	return s.aliasesR.AddAliasesFile(domain, f)
 }
 
+// SetAuthFallback sets the authentication backend to use as fallback.
 func (s *Server) SetAuthFallback(be auth.Backend) {
 	s.authr.Fallback = be
 }
 
+// SetAliasesConfig sets the aliases configuration options.
 func (s *Server) SetAliasesConfig(suffixSep, dropChars string) {
 	s.aliasesR.SuffixSep = suffixSep
 	s.aliasesR.DropChars = dropChars
 }
 
+// InitDomainInfo initializes the domain info database.
 func (s *Server) InitDomainInfo(dir string) *domaininfo.DB {
 	var err error
 	s.dinfo, err = domaininfo.New(dir)
@@ -137,6 +148,7 @@ func (s *Server) InitDomainInfo(dir string) *domaininfo.DB {
 	return s.dinfo
 }
 
+// InitQueue initializes the queue.
 func (s *Server) InitQueue(path string, localC, remoteC courier.Courier) {
 	q := queue.New(path, s.localDomains, s.aliasesR, localC, remoteC)
 	err := q.Load()
@@ -151,7 +163,7 @@ func (s *Server) InitQueue(path string, localC, remoteC courier.Courier) {
 		})
 }
 
-// PeriodicallyReload some of the server's information, such as aliases and
+// periodicallyReload some of the server's information, such as aliases and
 // the user databases.
 func (s *Server) periodicallyReload() {
 	for range time.Tick(*reloadEvery) {
@@ -167,6 +179,8 @@ func (s *Server) periodicallyReload() {
 	}
 }
 
+// ListenAndServe on the addresses and listeners that were previously added.
+// This function will not return.
 func (s *Server) ListenAndServe() {
 	if len(s.tlsConfig.Certificates) == 0 {
 		// chasquid assumes there's at least one valid certificate (for things
diff --git a/internal/trace/trace.go b/internal/trace/trace.go
index e2b004c..84880c9 100644
--- a/internal/trace/trace.go
+++ b/internal/trace/trace.go
@@ -10,12 +10,14 @@ import (
 	nettrace "golang.org/x/net/trace"
 )
 
+// A Trace represents an active request.
 type Trace struct {
 	family string
 	title  string
 	t      nettrace.Trace
 }
 
+// New trace.
 func New(family, title string) *Trace {
 	t := &Trace{family, title, nettrace.New(family, title)}
 
@@ -26,6 +28,7 @@ func New(family, title string) *Trace {
 	return t
 }
 
+// Printf adds this message to the trace's log.
 func (t *Trace) Printf(format string, a ...interface{}) {
 	t.t.LazyPrintf(format, a...)
 
@@ -33,6 +36,7 @@ func (t *Trace) Printf(format string, a ...interface{}) {
 		quote(fmt.Sprintf(format, a...)))
 }
 
+// Debugf adds this message to the trace's log, with a debugging level.
 func (t *Trace) Debugf(format string, a ...interface{}) {
 	t.t.LazyPrintf(format, a...)
 
@@ -40,6 +44,7 @@ func (t *Trace) Debugf(format string, a ...interface{}) {
 		t.family, t.title, quote(fmt.Sprintf(format, a...)))
 }
 
+// Errorf adds this message to the trace's log, with an error level.
 func (t *Trace) Errorf(format string, a ...interface{}) error {
 	// Note we can't just call t.Error here, as it breaks caller logging.
 	err := fmt.Errorf(format, a...)
@@ -51,6 +56,8 @@ func (t *Trace) Errorf(format string, a ...interface{}) error {
 	return err
 }
 
+// Error marks the trace as having seen an error, and also logs it to the
+// trace's log.
 func (t *Trace) Error(err error) error {
 	t.t.SetError()
 	t.t.LazyPrintf("error: %v", err)
@@ -61,20 +68,24 @@ func (t *Trace) Error(err error) error {
 	return err
 }
 
+// Finish the trace. It should not be changed after this is called.
 func (t *Trace) Finish() {
 	t.t.Finish()
 }
 
+// EventLog is used for tracing long-lived objects.
 type EventLog struct {
 	family string
 	title  string
 	e      nettrace.EventLog
 }
 
+// NewEventLog returns a new EventLog.
 func NewEventLog(family, title string) *EventLog {
 	return &EventLog{family, title, nettrace.NewEventLog(family, title)}
 }
 
+// Printf adds the message to the EventLog.
 func (e *EventLog) Printf(format string, a ...interface{}) {
 	e.e.Printf(format, a...)
 
@@ -82,6 +93,7 @@ func (e *EventLog) Printf(format string, a ...interface{}) {
 		quote(fmt.Sprintf(format, a...)))
 }
 
+// Debugf adds the message to the EventLog, with a debugging level.
 func (e *EventLog) Debugf(format string, a ...interface{}) {
 	e.e.Printf(format, a...)
 
@@ -89,6 +101,7 @@ func (e *EventLog) Debugf(format string, a ...interface{}) {
 		quote(fmt.Sprintf(format, a...)))
 }
 
+// Errorf adds the message to the EventLog, with an error level.
 func (e *EventLog) Errorf(format string, a ...interface{}) error {
 	err := fmt.Errorf(format, a...)
 	e.e.Errorf("error: %v", err)
diff --git a/internal/userdb/userdb.go b/internal/userdb/userdb.go
index 0359fbb..81b04bc 100644
--- a/internal/userdb/userdb.go
+++ b/internal/userdb/userdb.go
@@ -45,6 +45,7 @@ import (
 	"blitiri.com.ar/go/chasquid/internal/protoio"
 )
 
+// DB represents a single user database.
 type DB struct {
 	fname string
 	db    *ProtoDB
@@ -53,10 +54,7 @@ type DB struct {
 	mu sync.RWMutex
 }
 
-var (
-	ErrInvalidUsername = errors.New("invalid username")
-)
-
+// New returns a new user database, on the given file name.
 func New(fname string) *DB {
 	return &DB{
 		fname: fname,
@@ -106,7 +104,8 @@ func (db *DB) Write() error {
 	return protoio.WriteTextMessage(db.fname, db.db, 0660)
 }
 
-// Is this password valid for the user?
+// Authenticate returns true if the password is valid for the user, false
+// otherwise.
 func (db *DB) Authenticate(name, plainPassword string) bool {
 	db.mu.RLock()
 	passwd, ok := db.db.Users[name]
@@ -119,6 +118,7 @@ func (db *DB) Authenticate(name, plainPassword string) bool {
 	return passwd.PasswordMatches(plainPassword)
 }
 
+// PasswordMatches returns true if the given password is a match.
 func (p *Password) PasswordMatches(plain string) bool {
 	switch s := p.Scheme.(type) {
 	case nil:
@@ -132,11 +132,11 @@ func (p *Password) PasswordMatches(plain string) bool {
 	}
 }
 
-// Add a user to the database. If the user is already present, override it.
+// AddUser to the database. If the user is already present, override it.
 // Note we enforce that the name has been normalized previously.
 func (db *DB) AddUser(name, plainPassword string) error {
 	if norm, err := normalize.User(name); err != nil || name != norm {
-		return ErrInvalidUsername
+		return errors.New("invalid username")
 	}
 
 	s := &Scrypt{
@@ -178,7 +178,7 @@ func (db *DB) RemoveUser(name string) bool {
 	return present
 }
 
-// Exists returns true if the user is present, False otherwise.
+// Exists returns true if the user is present, false otherwise.
 func (db *DB) Exists(name string) bool {
 	db.mu.Lock()
 	_, present := db.db.Users[name]
@@ -190,7 +190,8 @@ func (db *DB) Exists(name string) bool {
 // Encryption schemes
 //
 
-// Plain text scheme. Useful mostly for testing and debugging.
+// PasswordMatches implementation for the plain text scheme.
+// Useful mostly for testing and debugging.
 // TODO: Do we really need this? Removing it would make accidents less likely
 // to happen. Consider doing so when we add another scheme, so we a least have
 // two and multi-scheme support does not bit-rot.
@@ -198,7 +199,8 @@ func (p *Plain) PasswordMatches(plain string) bool {
 	return plain == string(p.Password)
 }
 
-// scrypt scheme, which we use by default.
+// PasswordMatches implementation for the scrypt scheme, which we use by
+// default.
 func (s *Scrypt) PasswordMatches(plain string) bool {
 	dk, err := scrypt.Key([]byte(plain), s.Salt,
 		1<<s.LogN, int(s.R), int(s.P), int(s.KeyLen))