Help us shape the future of Go! Take the 2026 Go Developer Survey

store

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT-0 Imports: 20 Imported by: 0

Documentation

Overview

Package store owns perch's SQLite schema, blobs, messages, and search index.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound           = errors.New("store: not found")
	ErrDuplicate          = errors.New("store: already exists")
	ErrNoRecipient        = errors.New("store: recipient does not map to an account")
	ErrMailboxNotFound    = errors.New("store: mailbox not found")
	ErrMailboxHasChildren = errors.New("store: mailbox has children")
	ErrAccountDisabled    = errors.New("store: account is disabled")
)

Common store errors.

View Source
var ErrBlobUnavailable = errors.New("store: blob body is not local")

ErrBlobUnavailable reports that the message body exists only in remote storage and the caller must provide a blob store.

Functions

func FTSQuery

func FTSQuery(text string) string

FTSQuery converts free text into a conservative FTS5 prefix query. Each token is quoted, and tokens are ANDed. It returns "" when no token is at least three characters, because short terms are poor FTS candidates.

func NormalizeAddress

func NormalizeAddress(address string) (string, error)

NormalizeAddress lowercases and canonicalizes an RFC 5322 mailbox.

func SpecialUse

func SpecialUse(name string) string

SpecialUse returns the IMAP special-use attribute for a standard mailbox name, or an empty string.

func StripPlus

func StripPlus(address string) (string, bool)

StripPlus removes a plus-addressing tag from an address local part.

Types

type Account

type Account struct {
	ID             int64
	PrimaryAddress string
	DisplayName    string
	PasswordHash   string
	Disabled       bool
	CreatedAt      time.Time
}

Account is one local mailbox account.

type Address

type Address struct {
	Name    string `json:"name,omitempty"`
	Address string `json:"address"`
}

Address is a decoded RFC 5322 mailbox.

type Blob

type Blob struct {
	ID       int64
	SHA256   string
	Size     int64
	Kind     string
	Ref      string
	RefCount int64
}

Blob is the raw-message location shared by one or more messages.

type CopyResult

type CopyResult struct {
	SourceUIDs  []uint32
	DestUIDs    []uint32
	UIDValidity uint32
}

CopyResult reports a COPY or MOVE.

type Flags

type Flags uint32

Flags is the system flag bitmask stored for a message.

const (
	FlagSeen Flags = 1 << iota
	FlagAnswered
	FlagFlagged
	FlagDeleted
	FlagDraft
)

func (Flags) Has

func (f Flags) Has(other Flags) bool

Has reports whether all bits in other are set.

type FlagsOp

type FlagsOp int

FlagsOp is a STORE operation.

const (
	FlagsSet FlagsOp = iota
	FlagsAdd
	FlagsDel
)
type Header struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

Header is one raw header field.

type Mailbox

type Mailbox struct {
	ID            int64
	AccountID     int64
	Name          string
	UIDValidity   uint32
	UIDNext       uint32
	HighestModSeq uint64
	Subscribed    bool
	SpecialUse    string
	CreatedAt     time.Time
}

Mailbox is one IMAP mailbox belonging to an account.

type MailboxStatus

type MailboxStatus struct {
	Mailbox     Mailbox
	Messages    uint32
	Unseen      uint32
	Deleted     uint32
	Size        int64
	FirstUnseen uint32
}

MailboxStatus summarises a mailbox for SELECT and STATUS.

type Message

type Message struct {
	ID            int64
	AccountID     int64
	MailboxID     int64
	UID           uint32
	ModSeq        uint64
	BlobID        int64
	Size          int64
	InternalDate  time.Time
	MessageID     string
	Subject       string
	From          []Address
	To            []Address
	Cc            []Address
	Bcc           []Address
	ReplyTo       []Address
	InReplyTo     []string
	References    []string
	SentAt        *time.Time
	Headers       []Header
	MIMETree      string
	RawSHA256     string
	S3Key         string
	TaskID        string
	CorrelationID string
	IsSpam        bool
	Flags         Flags
	Keywords      []string
}

Message is a stored message with its metadata, flags, and keywords.

type MessageInput

type MessageInput struct {
	AccountID     int64
	MailboxID     int64
	Raw           []byte
	RawSHA256     string
	Kind          string // local or s3; inferred from Raw/S3Key when empty
	S3Key         string
	StoreData     bool // keep a local copy of Raw even for an S3 blob
	Size          int64
	InternalDate  time.Time
	MessageID     string
	Subject       string
	From          []Address
	To            []Address
	Cc            []Address
	Bcc           []Address
	ReplyTo       []Address
	InReplyTo     []string
	References    []string
	SentAt        *time.Time
	Headers       []Header
	MIMETree      string
	TaskID        string
	CorrelationID string
	IsSpam        bool
	Flags         Flags
	Keywords      []string
	Search        SearchContent
	// DedupeRaw rejects an insert when the same account already has a message
	// with this raw SHA-256. Ingest and S3 reconciliation set it; APPEND does
	// not, so a client can intentionally append the same message twice.
	DedupeRaw bool
}

MessageInput describes a new message. Raw may be nil when the message is already in S3 and only metadata is being indexed.

type MessageState

type MessageState struct {
	UID      uint32
	Flags    Flags
	Keywords []string
}

MessageState is the lightweight per-message state used for SELECT and IDLE polling.

type Outbound

type Outbound struct {
	ID             int64
	AccountID      int64
	MessageRef     string
	TaskID         string
	FromAddress    string
	Recipients     []string
	Subject        string
	Size           int64
	Status         string
	Attempt        uint64
	SMTPCode       int
	EnhancedStatus string
	RemoteResponse string
	Reason         string
	AcceptedAt     time.Time
	OccurredAt     time.Time
	NextRetryAt    time.Time
	CreatedAt      time.Time
	UpdatedAt      time.Time
}

Outbound is one submitted message and its latest delivery status.

type SearchContent

type SearchContent struct {
	Subject     string
	Sender      string
	Recipients  string
	Headers     string
	Body        string
	Attachments string
}

SearchContent is the text indexed for FTS.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the persistent SQLite state. Reads use a small connection pool; writes use a single connection so transactions are serialized without SQLITE_BUSY upgrade races.

func Open

func Open(path string) (*Store, error)

Open opens or creates the database at path, applies migrations, and returns a ready store. The caller must call Close.

func (*Store) AccountByAddress

func (s *Store) AccountByAddress(ctx context.Context, address string) (Account, error)

AccountByAddress returns the account owning address, including aliases and catch-all domains.

func (*Store) AccountByID

func (s *Store) AccountByID(ctx context.Context, id int64) (Account, error)

AccountByID returns one account.

func (*Store) AddAlias

func (s *Store) AddAlias(ctx context.Context, accountID int64, address string) error

AddAlias adds an address alias to accountID.

func (*Store) AddCatchAll

func (s *Store) AddCatchAll(ctx context.Context, accountID int64, domain string) error

AddCatchAll routes every otherwise-unmatched address at domain to accountID.

func (*Store) AllMessages

func (s *Store) AllMessages(ctx context.Context, mailboxID int64) ([]Message, error)

AllMessages returns every message in a mailbox ordered by UID.

func (*Store) Backup

func (s *Store) Backup(ctx context.Context, path string) error

Backup writes a consistent copy of the database to path using SQLite's VACUUM INTO.

func (*Store) BindS3ByMessageID

func (s *Store) BindS3ByMessageID(ctx context.Context, messageID int64, objectKey string) error

BindS3ByMessageID binds an existing message to an S3 object key.

func (*Store) BlobByRef

func (s *Store) BlobByRef(ctx context.Context, ref string) (Blob, error)

BlobByRef returns a blob by its S3 object reference, including refcount-zero tombstones. It returns ErrNotFound when no such object has ever been seen.

func (*Store) Close

func (s *Store) Close() error

Close checkpoints the WAL and closes both pools.

func (*Store) CopyMessages

func (s *Store) CopyMessages(ctx context.Context, sourceMailboxID, destMailboxID int64, uids []uint32) (CopyResult, error)

CopyMessages copies messages to destMailbox in one transaction.

func (*Store) CountDeadLetters

func (s *Store) CountDeadLetters(ctx context.Context) (int64, error)

CountDeadLetters returns the number of stored dead letters.

func (*Store) CountIndexed

func (s *Store) CountIndexed(ctx context.Context) (int, error)

CountIndexed returns the number of rows in the FTS index. It is used by doctor to verify that the FTS5 module is available.

func (*Store) CountOutbound

func (s *Store) CountOutbound(ctx context.Context) (map[string]int, error)

CountOutbound returns outbound status counts for doctor.

func (*Store) CreateAccount

func (s *Store) CreateAccount(ctx context.Context, address, displayName, passwordHash string) (Account, error)

CreateAccount creates an account, its primary address, and the default mailbox set in one transaction. passwordHash must already be hashed.

func (*Store) CreateMailbox

func (s *Store) CreateMailbox(ctx context.Context, accountID int64, name, specialUse string) (Mailbox, error)

CreateMailbox creates a mailbox and any missing parent mailboxes.

func (*Store) DeadLetter

func (s *Store) DeadLetter(ctx context.Context, address, taskID, reason string, raw []byte) error

DeadLetter stores a message that could not be routed to an account.

func (*Store) DeleteBlob

func (s *Store) DeleteBlob(ctx context.Context, id int64) error

DeleteBlob removes a blob row by ID.

func (*Store) DeleteMailbox

func (s *Store) DeleteMailbox(ctx context.Context, accountID int64, name string) error

DeleteMailbox deletes an empty-of-children mailbox and all its messages. It refuses to delete INBOX.

func (*Store) EnforceSpoolLimit

func (s *Store) EnforceSpoolLimit(ctx context.Context, maxBytes int64) (int, int64, error)

EnforceSpoolLimit evicts confirmed S3 spool copies until their compressed size is at most maxBytes. Unconfirmed copies are never evicted.

func (*Store) EvictLocalData

func (s *Store) EvictLocalData(ctx context.Context, messageID int64) error

EvictLocalData removes a message's local raw copy after an S3 object has been confirmed. The blob row and S3 reference remain.

func (*Store) Expunge

func (s *Store) Expunge(ctx context.Context, mailboxID int64, uids []uint32) ([]uint32, error)

Expunge deletes messages with \Deleted set, optionally restricted to uids. It returns the expunged UIDs in ascending order.

func (*Store) FTSCandidates

func (s *Store) FTSCandidates(ctx context.Context, mailboxID int64, term string, limit int) ([]int64, error)

FTSCandidates returns message IDs matching term. It is a candidate prefilter, not a substitute for IMAP's substring semantics; callers must verify the full criteria against SearchContent.

func (*Store) GCLocalOrphans

func (s *Store) GCLocalOrphans(ctx context.Context, dryRun bool) (int, int64, error)

GCLocalOrphans removes local blobs that are no longer referenced by any message. When dryRun is true it only reports what would be removed.

func (*Store) IncrementalVacuum

func (s *Store) IncrementalVacuum(ctx context.Context) error

IncrementalVacuum releases free pages without a full VACUUM.

func (*Store) InsertMessage

func (s *Store) InsertMessage(ctx context.Context, in MessageInput) (Message, bool, error)

InsertMessage stores a message and its blob, assigning the next UID. It is idempotent for non-empty TaskID: a duplicate returns the existing message with inserted=false.

func (*Store) IntegrityCheck

func (s *Store) IntegrityCheck(ctx context.Context) error

IntegrityCheck runs SQLite's quick_check. It returns nil when the database reports ok.

func (*Store) ListAccounts

func (s *Store) ListAccounts(ctx context.Context) ([]Account, error)

ListAccounts returns every account ordered by address.

func (*Store) ListAddresses

func (s *Store) ListAddresses(ctx context.Context, accountID int64) ([]string, error)

ListAddresses returns all addresses for an account.

func (*Store) ListMailboxes

func (s *Store) ListMailboxes(ctx context.Context, accountID int64) ([]Mailbox, error)

ListMailboxes returns all mailboxes for an account ordered by name.

func (*Store) ListOutbound

func (s *Store) ListOutbound(ctx context.Context, accountID int64, limit int) ([]Outbound, error)

ListOutbound returns recent outbound messages, optionally filtered by account.

func (*Store) MailboxByID

func (s *Store) MailboxByID(ctx context.Context, id int64) (Mailbox, error)

MailboxByID returns one mailbox by database identifier.

func (*Store) MailboxByName

func (s *Store) MailboxByName(ctx context.Context, accountID int64, name string) (Mailbox, error)

MailboxByName returns one mailbox. INBOX is matched case-insensitively.

func (*Store) MailboxState

func (s *Store) MailboxState(ctx context.Context, mailboxID int64) ([]MessageState, error)

MailboxState returns the UID, flags, and keywords for every message in a mailbox, ordered by UID.

func (*Store) MessageByRawSHA

func (s *Store) MessageByRawSHA(ctx context.Context, accountID int64, sha string) (Message, error)

MessageByRawSHA returns the first message for an account with the given raw SHA-256.

func (*Store) MessageByS3Key

func (s *Store) MessageByS3Key(ctx context.Context, key string) (Message, error)

MessageByS3Key returns the message bound to an S3 object key.

func (*Store) MessageByTaskID

func (s *Store) MessageByTaskID(ctx context.Context, taskID string) (Message, error)

MessageByTaskID returns one message by mxRaven task identifier.

func (*Store) MessageByUID

func (s *Store) MessageByUID(ctx context.Context, mailboxID int64, uid uint32) (Message, error)

MessageByUID returns one message.

func (*Store) MessageCount

func (s *Store) MessageCount(ctx context.Context, mailboxID int64) (uint32, error)

MessageCount returns the number of messages in a mailbox.

func (*Store) MessagesByIDs

func (s *Store) MessagesByIDs(ctx context.Context, mailboxID int64, ids []int64) ([]Message, error)

MessagesByIDs returns messages in a mailbox by database ID, ordered by UID.

func (*Store) MessagesByUIDs

func (s *Store) MessagesByUIDs(ctx context.Context, mailboxID int64, uids []uint32) ([]Message, error)

MessagesByUIDs returns messages for the given UIDs, ordered by UID.

func (*Store) MoveMessages

func (s *Store) MoveMessages(ctx context.Context, sourceMailboxID, destMailboxID int64, uids []uint32) (CopyResult, error)

MoveMessages copies messages and removes the source rows atomically.

func (*Store) PurgeDeadLetters

func (s *Store) PurgeDeadLetters(ctx context.Context, before time.Time) (int, error)

PurgeDeadLetters deletes dead letters older than before.

func (*Store) RawBlob

func (s *Store) RawBlob(ctx context.Context, messageID int64) ([]byte, Blob, error)

RawBlob returns the raw message bytes for a message. It prefers a local copy and returns ErrBlobUnavailable when only a remote reference exists.

func (*Store) RecordOutboundAttempt

func (s *Store) RecordOutboundAttempt(ctx context.Context, out Outbound) error

RecordOutboundAttempt updates the latest attempt status for a submission.

func (*Store) RemoveAddress

func (s *Store) RemoveAddress(ctx context.Context, address string) error

RemoveAddress removes an alias or catch-all. Primary addresses cannot be removed.

func (*Store) RenameMailbox

func (s *Store) RenameMailbox(ctx context.Context, accountID int64, oldName, newName string) error

RenameMailbox renames a mailbox and its descendants. INBOX cannot be renamed; clients should create a new mailbox and move messages instead.

func (*Store) ResolveRecipient

func (s *Store) ResolveRecipient(ctx context.Context, address string) (Account, string, error)

ResolveRecipient maps an envelope recipient to an account. It tries an exact address, then the plus-tag-stripped form, then a domain catch-all.

func (*Store) S3Tombstones

func (s *Store) S3Tombstones(ctx context.Context) ([]Blob, error)

S3Tombstones returns deleted-message blob rows whose S3 objects still exist.

func (*Store) SearchContentByIDs

func (s *Store) SearchContentByIDs(ctx context.Context, ids []int64) (map[int64]SearchContent, error)

SearchContentByIDs loads FTS text for the given message IDs.

func (*Store) SetAccountDisabled

func (s *Store) SetAccountDisabled(ctx context.Context, accountID int64, disabled bool) error

SetAccountDisabled enables or disables an account.

func (*Store) SetPasswordHash

func (s *Store) SetPasswordHash(ctx context.Context, accountID int64, hash string) error

SetPasswordHash replaces an account's password hash.

func (*Store) SetS3Key

func (s *Store) SetS3Key(ctx context.Context, taskID, objectKey string) (Message, error)

SetS3Key binds a message to its mxRaven S3 object. taskID may be either the accepted task ID or the S3 attempt's correlation ID.

func (*Store) SetSubscribed

func (s *Store) SetSubscribed(ctx context.Context, accountID int64, name string, subscribed bool) error

SetSubscribed changes a mailbox's subscription state.

func (*Store) SpoolStats

func (s *Store) SpoolStats(ctx context.Context) (int64, int, error)

SpoolStats reports the compressed size and count of local spool copies for S3-backed messages.

func (*Store) Status

func (s *Store) Status(ctx context.Context, mailboxID int64) (MailboxStatus, error)

Status returns counts and size for a mailbox.

func (*Store) StoreFlags

func (s *Store) StoreFlags(ctx context.Context, mailboxID int64, uids []uint32, op FlagsOp, flags Flags, keywords []string) ([]Message, error)

StoreFlags updates system flags and keywords on messages.

func (*Store) UpsertOutboundAccepted

func (s *Store) UpsertOutboundAccepted(ctx context.Context, out Outbound) error

UpsertOutboundAccepted records the submission acceptance row.

Jump to

Keyboard shortcuts

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