Documentation
¶
Overview ¶
Package store owns the SQLite database: opening it, migrating it and, later, the repositories.
Index ¶
- Constants
- Variables
- func Migrate(ctx context.Context, db *sql.DB) error
- func Open(ctx context.Context, dataDir string) (*sql.DB, error)
- type Account
- type Action
- type ActivityFilter
- type ActivityRow
- type Batch
- type CleanupBatch
- type Contact
- type Correction
- type Decision
- type FeedCursor
- type Folder
- type Identity
- type Ledger
- type Message
- type NewRule
- type RuleStat
- type RuleUse
- type SenderDecision
- type SenderSeen
- type Signals
- type Snapshot
- type Store
- func (s *Store) Account(ctx context.Context, id int64) (Account, error)
- func (s *Store) AccountSecret(ctx context.Context, master []byte, id int64) (string, error)
- func (s *Store) AccountTenant(ctx context.Context, accountID int64) (int64, error)
- func (s *Store) Accounts(ctx context.Context) ([]Account, error)
- func (s *Store) Action(ctx context.Context, id int64) (Action, error)
- func (s *Store) Activity(ctx context.Context, v Viewer, f ActivityFilter) ([]ActivityRow, error)
- func (s *Store) ActivityFor(ctx context.Context, v Viewer, messageID int64) (ActivityRow, error)
- func (s *Store) AddBatchProgress(ctx context.Context, id int64, done, skipped, tokens int, costUSD float64) error
- func (s *Store) AddCorrection(ctx context.Context, tenantID, userID int64, c Correction, always string) (int64, error)
- func (s *Store) AddDecision(ctx context.Context, d Decision, state string) (int64, error)
- func (s *Store) AddFolder(ctx context.Context, accountID int64, name string) error
- func (s *Store) AddLearnedSenderRule(ctx context.Context, tenantID, userID int64, address string, ruleID, now int64) (bool, error)
- func (s *Store) AddUsage(ctx context.Context, tenantID int64, operator bool, ...) error
- func (s *Store) AdvanceFolder(ctx context.Context, ref mail.MsgRef) error
- func (s *Store) Batch(ctx context.Context, id int64) (Batch, error)
- func (s *Store) BatchActionCounts(ctx context.Context, v Viewer, batchID int64) (map[string]int, error)
- func (s *Store) BatchActions(ctx context.Context, batchID int64) ([]Action, error)
- func (s *Store) Batches(ctx context.Context, v Viewer, kind string, before int64, limit int) ([]Batch, error)
- func (s *Store) CanSee(ctx context.Context, v Viewer, accountID int64) (bool, error)
- func (s *Store) ClaimSummary(ctx context.Context, userID, dueAt, now int64, maxAttempts int) (id int64, attempt int, ok bool, err error)
- func (s *Store) Corrections(ctx context.Context, v Viewer, limit int) ([]Correction, error)
- func (s *Store) CountMessages(ctx context.Context, v Viewer, state string) (int, error)
- func (s *Store) CreateAccount(ctx context.Context, master []byte, a Account, secret string) (Account, error)
- func (s *Store) CreateBatch(ctx context.Context, tenantID int64, kind, status string, now int64) (int64, error)
- func (s *Store) CreateCleanupBatch(ctx context.Context, accountID int64, folder string, since int64, ...) (Batch, error)
- func (s *Store) CreateCleanupRun(ctx context.Context, cs []CleanupBatch, now int64) ([]Batch, error)
- func (s *Store) CreateFirstUser(ctx context.Context, email, passwordHash string, now int64) (User, error)
- func (s *Store) CreateRule(ctx context.Context, tenantID int64, r rules.Rule, now int64) (rules.Rule, error)
- func (s *Store) CreateRules(ctx context.Context, tenantID, userID int64, added []NewRule, now int64) ([]rules.Rule, error)
- func (s *Store) CreateSession(ctx context.Context, tokenHash string, userID, now, expiresAt int64) error
- func (s *Store) CreateTenant(ctx context.Context, provider, externalID string, now int64) (int64, error)
- func (s *Store) CreateUser(ctx context.Context, tenantID int64, email, passwordHash string, now int64) (User, error)
- func (s *Store) DeleteAccount(ctx context.Context, id, now int64) ([]rules.Rule, error)
- func (s *Store) DeleteRule(ctx context.Context, tenantID, id int64) error
- func (s *Store) DeleteSenderRule(ctx context.Context, tenantID int64, matchType, value string) error
- func (s *Store) DeleteSession(ctx context.Context, tokenHash string) error
- func (s *Store) DoneActionsSince(ctx context.Context, v Viewer, ruleID, since int64) ([]Action, error)
- func (s *Store) DryRun(ctx context.Context, tenantID int64, def bool) (bool, error)
- func (s *Store) DueMessages(ctx context.Context, accountID, now int64) ([]Message, error)
- func (s *Store) EndIdentitySessions(ctx context.Context, provider, subject string) error
- func (s *Store) ExtendSession(ctx context.Context, tokenHash string, expiresAt int64) error
- func (s *Store) FailRunningBatches(ctx context.Context) error
- func (s *Store) FinishAction(ctx context.Context, a Action) error
- func (s *Store) FinishSummary(ctx context.Context, id int64, sent bool, periodStart, periodEnd, now int64) error
- func (s *Store) FirstUser(ctx context.Context) (User, error)
- func (s *Store) Folder(ctx context.Context, accountID int64, name string) (Folder, error)
- func (s *Store) Folders(ctx context.Context, accountID int64) ([]Folder, error)
- func (s *Store) HasUsers(ctx context.Context) (bool, error)
- func (s *Store) HitSenderRule(ctx context.Context, id int64) error
- func (s *Store) ImportRules(ctx context.Context, tenantID, userID int64, imported []rules.Rule, now int64) (created, updated int, err error)
- func (s *Store) IngestMessage(ctx context.Context, ref mail.MsgRef, now int64) (m Message, fresh bool, err error)
- func (s *Store) InsertAction(ctx context.Context, a Action) (int64, error)
- func (s *Store) IsContact(ctx context.Context, accountID int64, address string) (bool, error)
- func (s *Store) LastMailAt(ctx context.Context, accountID int64) (int64, error)
- func (s *Store) LastSentSummary(ctx context.Context, userID int64) (Summary, error)
- func (s *Store) LeaveOwnMail(ctx context.Context, tenantID int64) (bool, error)
- func (s *Store) LiveBatch(ctx context.Context, tenantID int64, now time.Time) (int64, error)
- func (s *Store) MarkActionUndone(ctx context.Context, a Action, restored mail.MsgRef, now int64) error
- func (s *Store) Message(ctx context.Context, id int64) (Message, error)
- func (s *Store) MessageActions(ctx context.Context, messageID int64) ([]Action, error)
- func (s *Store) MessageActivity(ctx context.Context, messageID int64) (ActivityRow, error)
- func (s *Store) MessageByMessageID(ctx context.Context, accountID int64, messageID string, except int64) (Message, error)
- func (s *Store) MessageCorrections(ctx context.Context, messageID int64) ([]Correction, error)
- func (s *Store) MessageDecisions(ctx context.Context, messageID int64) ([]Decision, error)
- func (s *Store) Ping(ctx context.Context) error
- func (s *Store) PutSenderRule(ctx context.Context, tenantID int64, sr rules.SenderRule, now int64) (rules.SenderRule, error)
- func (s *Store) QuietRules(ctx context.Context, v Viewer, since int64) (int, error)
- func (s *Store) Rebind(ctx context.Context, fresh, known int64, ref mail.MsgRef) (bool, error)
- func (s *Store) RecentSenderDecisions(ctx context.Context, v Viewer, address string, n int) ([]SenderDecision, error)
- func (s *Store) ReconcileRemovedMailboxes(ctx context.Context, now int64) ([]rules.Rule, error)
- func (s *Store) ReorderRules(ctx context.Context, tenantID int64, ids []int64) error
- func (s *Store) Retain(ctx context.Context, tenantID, now int64, retentionDays int) (blanked, deleted int64, err error)
- func (s *Store) Rule(ctx context.Context, tenantID, id int64) (rules.Rule, error)
- func (s *Store) RuleCosts(ctx context.Context, v Viewer, since int64) ([]RuleUse, error)
- func (s *Store) RuleStats(ctx context.Context, v Viewer, since int64) (map[int64]RuleStat, error)
- func (s *Store) Rules(ctx context.Context, tenantID int64) ([]rules.Rule, error)
- func (s *Store) SaveFolders(ctx context.Context, accountID int64, folders []Folder) error
- func (s *Store) SaveMessageSummary(ctx context.Context, m Message) error
- func (s *Store) SenderRules(ctx context.Context, tenantID int64) ([]rules.SenderRule, error)
- func (s *Store) SendersSeen(ctx context.Context, v Viewer, since int64) ([]SenderSeen, error)
- func (s *Store) SessionUser(ctx context.Context, tokenHash string, now int64) (User, int64, error)
- func (s *Store) SetAccountCapabilities(ctx context.Context, id int64, capabilities []string) error
- func (s *Store) SetAccountSecret(ctx context.Context, master []byte, id int64, secret string) error
- func (s *Store) SetAccountShared(ctx context.Context, id int64, shared bool) error
- func (s *Store) SetAccountStatus(ctx context.Context, id int64, status, lastError string, now int64) error
- func (s *Store) SetBatchProgress(ctx context.Context, id int64, total, done int) error
- func (s *Store) SetBatchStatus(ctx context.Context, id int64, status string) error
- func (s *Store) SetDryRun(ctx context.Context, tenantID int64, on bool) error
- func (s *Store) SetFolderPosition(ctx context.Context, accountID int64, name string, uidValidity, lastUID uint32) error
- func (s *Store) SetMessageState(ctx context.Context, id int64, state string, attempts int, nextAttemptAt int64) error
- func (s *Store) SetPassword(ctx context.Context, userID int64, passwordHash string) error
- func (s *Store) SetSetting(ctx context.Context, tenantID int64, key, value string) error
- func (s *Store) SetSettings(ctx context.Context, tenantID int64, values map[string]*string) error
- func (s *Store) SetTrashToFolder(ctx context.Context, tenantID int64, on bool) error
- func (s *Store) Setting(ctx context.Context, tenantID int64, key string) (string, error)
- func (s *Store) Settings(ctx context.Context, tenantID int64) (map[string]string, error)
- func (s *Store) SettingsLike(ctx context.Context, prefix string) (map[int64]map[string]string, error)
- func (s *Store) SignInIdentity(ctx context.Context, id Identity, now int64) (User, error)
- func (s *Store) StatsTotals(ctx context.Context, v Viewer, since int64) (Totals, error)
- func (s *Store) SummaryCost(ctx context.Context, v Viewer, from, to int64) (costUSD float64, calls int, err error)
- func (s *Store) SummaryReview(ctx context.Context, v Viewer, limit int) ([]SummaryEmail, int, error)
- func (s *Store) SummaryRules(ctx context.Context, v Viewer, from, to int64) ([]RuleUse, error)
- func (s *Store) SummaryTrashed(ctx context.Context, v Viewer, from, to int64, limit int) ([]SummaryEmail, int, error)
- func (s *Store) TenantAccounts(ctx context.Context, tenantID int64) ([]Account, error)
- func (s *Store) TenantMembers(ctx context.Context, tenantID int64) (int, error)
- func (s *Store) Tenants(ctx context.Context) ([]int64, error)
- func (s *Store) TopRules(ctx context.Context, v Viewer, since int64, limit int) ([]RuleUse, error)
- func (s *Store) TrashToFolder(ctx context.Context, tenantID int64) (bool, error)
- func (s *Store) UpdateAccount(ctx context.Context, a Account) error
- func (s *Store) UpdateRule(ctx context.Context, tenantID int64, r rules.Rule, now int64) error
- func (s *Store) UpsertContacts(ctx context.Context, accountID int64, contacts []Contact) error
- func (s *Store) Usage(ctx context.Context, tenantID int64, sinceDay string) ([]UsageRow, error)
- func (s *Store) User(ctx context.Context, id int64) (User, error)
- func (s *Store) UserByEmail(ctx context.Context, email string) (User, error)
- func (s *Store) Users(ctx context.Context) ([]User, error)
- func (s *Store) VisibleAccount(ctx context.Context, v Viewer, id int64) (Account, error)
- func (s *Store) VisibleAccounts(ctx context.Context, v Viewer) ([]Account, error)
- func (s *Store) VisibleAction(ctx context.Context, v Viewer, id int64) (Action, error)
- func (s *Store) VisibleBatch(ctx context.Context, v Viewer, id int64) (Batch, error)
- func (s *Store) VisibleBatchActions(ctx context.Context, v Viewer, batchID int64) ([]Action, error)
- func (s *Store) VisibleMessage(ctx context.Context, v Viewer, id int64) (Message, error)
- type Summary
- type SummaryEmail
- type Totals
- type UsageRow
- type User
- type Viewer
Constants ¶
const ( ActionDone = "done" ActionDryRun = "dry_run" ActionFailed = "failed" ActionUndone = "undone" )
Action statuses, as stored in actions.status.
const ( BatchLive = "live" BatchCleanup = "cleanup" BatchCorrection = "correction" BatchUndo = "undo" BatchRunning = "running" BatchDone = "done" BatchFailed = "failed" BatchUndone = "undone" )
Batch kinds and statuses, as stored in batches.
const ( SettingTrashToFolder = "trash_to_folder" DefaultTrashToFolder = true )
SettingTrashToFolder is the settings key of the switch that sends trashed mail to an ordinary folder of MailRules' own instead of the server's Trash, which providers empty on their own. DefaultTrashToFolder is in force until it is set, for a new install and an existing one alike.
const ( SettingLeaveOwnMail = "leave_own_mail" DefaultLeaveOwnMail = true )
SettingLeaveOwnMail is the settings key of the switch that leaves mail sent from the mailbox's own address alone (Account.OwnAddresses): no rule, model or action touches it. DefaultLeaveOwnMail is in force until it is set.
const ( StateNew = "new" // ingested, not yet decided StateDecided = "decided" // a decision is recorded, its actions are not finished StateActed = "acted" // the decision's actions ran (or were recorded as dry-run) StateReview = "review" // waiting in Needs review StateSkipped = "skipped" // nothing to do: no rule matched, or the message was gone StateError = "error" // failed; the retry job runs it again at NextAttemptAt )
Message states, as stored in messages.state.
const ( CorrectionFix = "correction" CorrectionReview = "review" )
What a correction was: a fix made from the feed, or an answer given in Needs review.
const ( SummarySending = "sending" SummarySent = "sent" SummaryFailed = "failed" )
Summary statuses, as stored in summaries.status.
const KeepMessagesDays = 180
KeepMessagesDays is how long a message's row is kept. After that it is deleted with its decisions and actions, unless one of its actions can still be undone: a move or a flag change that is in effect and was made in the last UndoDays. A recorded "keep" changed nothing worth putting back, so it does not hold a row.
NewAccountShared is whether a mailbox someone adds starts shared with everyone in their tenant. It starts private: only its owner sees it, its mail and what MailRules did to it, until the owner shares it.
const SelfHostTenant int64 = 1
SelfHostTenant is the one tenant of a self-hosted install, made by migration 0011.
const SettingSummary = "summary"
SettingSummary holds the summary email's settings, one JSON object (internal/summary).
const UndoDays = 30
UndoDays is how long an action stays undoable. Older ones are history: undo refuses them (actions.ErrTooOld) and they no longer hold their message's row.
Variables ¶
var ( // ErrTenantMismatch is a returning identity that arrived under a different organisation // than the one it signed up with. ErrTenantMismatch = errors.New("identity belongs to another tenant") // ErrEmailInUse is an email another user already has. ErrEmailInUse = errors.New("email in use by another user") )
var ErrAlreadySetUp = errors.New("already set up")
ErrAlreadySetUp means the first-run admin account already exists.
var ErrNotFound = errors.New("not found")
ErrNotFound means the row does not exist (or, for a session, has expired).
var ErrRuleSet = errors.New("the list must name every rule exactly once")
ErrRuleSet means a reorder did not name each of the tenant's rules exactly once.
var Outcomes = []string{"sorted", "trashed", "inbox", "review"}
Outcomes are the four places a processed message ends up: the groups of the Overview bar (Totals.Went*) and the values of ActivityFilter.Outcome.
Functions ¶
Types ¶
type Account ¶
type Account struct {
ID int64
UserID int64 // the owner: who added it
TenantID int64 // the owner's tenant
Label string
Preset string
Host string
Port int
TLSMode string
Username string
WatchFolder string
Status string // new | live | reconnecting | auth_failed | error | paused
LastError string
LastEventAt int64
Capabilities []string
CreatedAt int64
}
Account is one mailbox the daemon watches. Its password is never part of this struct: read it with AccountSecret at the moment a connection is made.
func (Account) OwnAddresses ¶
OwnAddresses returns the account's own email addresses, lowercase: the username when it is a full address and, for a provider whose domains are known (presets.Preset.Domains), the name before "@" at each of them, so an iCloud username of only "jane" is jane@icloud.com, jane@me.com and jane@mac.com. Aliases are not known. It is nil when no address can be told: a username without "@" on a provider whose domains are not known.
type Action ¶
type Action struct {
ID int64
DecisionID int64 // 0 = none
BatchID int64 // 0 = none
MessageID int64
AccountID int64
Kind string // move | trash | archive | junk | flag | unflag | read | unread | keep | review
Folder string // params.folder: the destination of a move, or of a trash sent to MailRules' own folder (trash_to_folder)
Before Snapshot
After *Snapshot // nil unless the action is done (or was, before an undo)
Status string
Error string
CreatedAt int64
UndoneAt int64
}
Action is one row of actions: one step taken (or, in dry-run, not taken) on a message.
type ActivityFilter ¶
type ActivityFilter struct {
ID int64 // only this message
AccountID int64
RuleID int64
Stage string
State string // a message state; StateReview lists Needs review
Action string // an action kind: only messages with such an action
Outcome string // one of Outcomes: where the message ended up, as the stats count it
After FeedCursor
Limit int // 0 = 50
}
ActivityFilter narrows Activity. Zero fields do not filter.
type ActivityRow ¶
ActivityRow is a message with its latest decision and every action taken on it, oldest first; Decision is nil until one is made.
func (ActivityRow) Cursor ¶
func (r ActivityRow) Cursor() FeedCursor
Cursor is the position of this row in the feed, for the page after it.
type Batch ¶
type Batch struct {
ID int64
TenantID int64
Kind string // live | cleanup | review | correction | undo
Status string // running | done | failed | undone
Total int // how many items the batch set out to handle; 0 = not counted (live)
Done int // how many it has handled
CreatedAt int64
// What a cleanup batch sorted, and what its model calls have cost so far. Zero for
// the other kinds.
AccountID int64
Folder string
Since int64 // only mail received from this time on; 0 = all of it
Tokens int // model tokens, in and out
CostUSD float64
// Skipped is how many selected emails a cleanup Sort passed over because they were no
// longer where its check found them (MAI-44). 0 for the other kinds.
Skipped int
// ScanLimit is the most emails the cleanup's check was allowed to cover, and ScanMatched
// how many the folder held in the range before that cut (MAI-48). ScanLimit 0 = not
// recorded (a batch made before this), and then ScanMatched means nothing.
ScanLimit int
ScanMatched int
// RunID joins the batches of one manual run over several mailboxes, one per mailbox
// (MAI-43): it is the id of the run's first batch, the same on every one. 0 = not part of
// such a run, which is every other batch and a cleanup of one mailbox.
RunID int64
}
Batch is one row of batches: a group of actions that can be undone together.
type CleanupBatch ¶ added in v0.1.2
type CleanupBatch struct {
AccountID int64
Folder string
Since int64 // only mail received from this time on; 0 = all of it
// ScanLimit and ScanMatched say how much mail the check behind the batch covered: the
// most emails it was allowed and how many the range held before that cut. ScanLimit 0
// records nothing.
ScanLimit, ScanMatched int
Total int // emails the batch sorts
}
CleanupBatch is what a cleanup batch is opened with.
type Correction ¶
type Correction struct {
ID int64
MessageID int64
WrongRuleID int64 // 0 = no rule had been applied
RightRuleID int64 // 0 = keep in the inbox
RightRule string // that rule's name, when read back; empty for keep, or once the rule is deleted
Kind string // CorrectionFix | CorrectionReview; empty is stored as CorrectionFix
Example string // JSON message.Summary, the few-shot example
CreatedAt int64
}
Correction is one row of corrections: the user said a message belongs to another rule.
type Decision ¶
type Decision struct {
ID int64
MessageID int64
Stage string // sender | condition | decider | fallback | none
RuleID int64 // 0 = no rule; in Needs review, the rule the model suggested
RuleName string // the rule's name: as it is now, or as it was when decided if the rule is gone
RuleVersion int
Confidence float64
Reason string
Model string
TokensIn int
TokensOut int
CostUSD float64
LatencyMS int64
CreatedAt int64
// Probabilities is what the decision model gave each candidate rule, by rule id, with
// 0 for "none of these". nil when the model gives no such spread, or none was asked.
Probabilities map[int64]float64
}
Decision is one row of decisions: what was decided for a message, and what it cost.
type FeedCursor ¶
FeedCursor is where a page of Activity ends: the ActedAt and ID of its last row. The next page starts after it in the feed's order. The zero value is the first page.
type Folder ¶
type Folder struct {
AccountID int64
Name string
Delimiter string
SpecialUse string // \Junk \Trash \Archive \Sent \Drafts or empty
UIDValidity uint32
LastUID uint32 // highest UID processed
}
Folder is one folder of an account, with the watch position when it is watched.
type Identity ¶ added in v0.2.0
type Identity struct {
Provider, Subject, Email, Tenant string
}
Identity is a person as a sign-in service names them: Subject is the service's stable id for them and Tenant the id of the organisation they signed in under.
type Ledger ¶ added in v0.2.0
Ledger books model usage for one tenant (models.UsageStore). Operator says, by the provider a call went to (models.Usage.Provider), whether it used a key the operator holds; nil marks none.
type Message ¶
type Message struct {
ID int64
AccountID int64
Folder string // where it arrived; with UIDValidity and UID, the dedupe key
UIDValidity uint32
UID uint32
MessageID string // RFC 5322 Message-ID, without angle brackets
FromAddr string
FromName string // the From display name; may be empty
FromDomain string
ToAddrs []string
Subject string
Snippet string
ReceivedAt int64
ListID string
HasAttachment bool
Size int64
Signals Signals
State string
Attempts int // retries made after a failure
NextAttemptAt int64 // 0 = not waiting for the retry job
CreatedAt int64
// ActedAt is when MailRules last decided the message, or CreatedAt until a decision is
// made. The feed is ordered by it.
ActedAt int64
// Where the executor last left the message; zero while it is still where it arrived.
CurFolder string
CurUIDValidity uint32
CurUID uint32
}
Message is one row of messages: what is kept of an email. The body never is.
type RuleStat ¶
type RuleStat struct {
Hits int // messages it was applied to since the time asked for
LastMatchAt int64 // 0 = never
}
RuleStat is how often a rule has been applied.
type RuleUse ¶
type RuleUse struct {
RuleID int64
RuleName string
Emails int
Calls int // decisions a model was asked for
CostUSD float64
}
RuleUse is what one rule did, or cost. RuleID is 0 with a name for a rule that has been deleted since, and 0 without one for "no rule".
type SenderDecision ¶
type SenderDecision struct {
Stage string
RuleID int64
Confidence float64
Corrected bool // the user corrected that message afterwards
}
SenderDecision is one past decision for a sender, as the learner reads it.
type SenderSeen ¶
type SenderSeen struct {
Address string
Name string // the From display name of the newest email; may be empty
Messages int // emails seen since the time asked for
LastSeenAt int64
ListUnsubscribe bool // at least one of them carried a List-Unsubscribe header
}
SenderSeen is what the messages table knows about one sender address.
type Signals ¶
type Signals struct {
Bulk bool `json:"bulk"`
Noreply bool `json:"noreply"`
Contact bool `json:"is_contact"`
RepliedBefore bool `json:"replied_before"`
DMARC string `json:"dmarc"`
// ListUnsubscribe is set when the email carries a List-Unsubscribe header.
ListUnsubscribe bool `json:"list_unsubscribe"`
}
Signals is the JSON in messages.signals.
type Snapshot ¶
type Snapshot struct {
Folder string `json:"folder"`
UIDValidity uint32 `json:"uidvalidity"`
UID uint32 `json:"uid"`
Flags []string `json:"flags"`
}
Snapshot is the JSON in actions.before and actions.after: where a message was and which flags it had.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the repository over the database. All times are unix seconds.
func (*Store) Account ¶
Account returns one account, or ErrNotFound. It does not look at who asks: it is for internal work on a known account (the worker, the executor). A request reads VisibleAccount.
func (*Store) AccountSecret ¶
AccountSecret decrypts an account's app password or token. It returns crypto.ErrDecrypt when the master key is not the one the secret was sealed under.
func (*Store) AccountTenant ¶ added in v0.2.0
AccountTenant returns the tenant an account belongs to, or ErrNotFound.
func (*Store) Accounts ¶
Accounts lists every account of every tenant, oldest first: for the daemon's own work (starting the watchers, the master key check), never for a request.
func (*Store) Action ¶
Action returns one action, or ErrNotFound. It does not look at who asks: a request reads VisibleAction.
func (*Store) Activity ¶
func (s *Store) Activity(ctx context.Context, v Viewer, f ActivityFilter) ([]ActivityRow, error)
Activity lists the messages of the mailboxes the viewer sees, each with its latest decision, by when MailRules acted on them (ActedAt), newest first; messages acted on in the same second come newest row first.
func (*Store) ActivityFor ¶
ActivityFor returns the activity row of one message the viewer sees, or ErrNotFound.
func (*Store) AddBatchProgress ¶
func (s *Store) AddBatchProgress(ctx context.Context, id int64, done, skipped, tokens int, costUSD float64) error
AddBatchProgress records that a cleanup batch handled more emails, passed over some that had moved, and what their model calls cost.
func (*Store) AddCorrection ¶
func (s *Store) AddCorrection(ctx context.Context, tenantID, userID int64, c Correction, always string) (int64, error)
AddCorrection records a correction in one transaction: it inserts the row, forgets the tenant's learned sender rule for that message's sender and marks the message acted. With always (rules.MatchAddress or rules.MatchDomain; "" = neither) it also stores a user sender rule of the tenant, made by userID, that sends the address, or its whole domain, to the right rule (or keeps it in the inbox), replacing whatever that address or domain had. The caller checks that the message and the right rule are the tenant's.
func (*Store) AddDecision ¶
AddDecision records a decision and moves its message to state in one transaction, and returns the decision's id. The message's ActedAt becomes the decision's time.
func (*Store) AddFolder ¶
AddFolder records a folder MailRules itself just created on the server, so it shows up before the next folder discovery. A folder already on record is left as it is.
func (*Store) AddLearnedSenderRule ¶
func (s *Store) AddLearnedSenderRule(ctx context.Context, tenantID, userID int64, address string, ruleID, now int64) (bool, error)
AddLearnedSenderRule routes an address to one of the tenant's rules unless the address already has a sender rule there, which is never overwritten. userID is who it is learnt for. It reports whether a rule was created.
func (*Store) AddUsage ¶
func (s *Store) AddUsage(ctx context.Context, tenantID int64, operator bool, day, provider, model, purpose string, calls, tokensIn, tokensOut int, costUSD float64) error
AddUsage adds model calls to the tenant's cost-ledger row for that day, provider, model and purpose (decide | escalate | compose | test | cleanup), creating it if needed. operator marks calls made with a key the operator holds (MAILRULES_MODE=cloud): they are booked on rows of their own, so billing can charge for them.
func (*Store) AdvanceFolder ¶
AdvanceFolder raises a watched folder's last processed UID to ref's. It never lowers it, except when the folder's UIDVALIDITY changed, where the old position means nothing.
func (*Store) Batch ¶
Batch returns one batch, or ErrNotFound. It does not look at who asks: a request reads VisibleBatch.
func (*Store) BatchActionCounts ¶
func (s *Store) BatchActionCounts(ctx context.Context, v Viewer, batchID int64) (map[string]int, error)
BatchActionCounts counts a batch's own actions on the mailboxes the viewer sees, by status.
func (*Store) BatchActions ¶
BatchActions lists a batch's actions, oldest first, whoever's mailbox they are on: for internal work. A request reads VisibleBatchActions.
func (*Store) Batches ¶
func (s *Store) Batches(ctx context.Context, v Viewer, kind string, before int64, limit int) ([]Batch, error)
Batches lists the batches the viewer sees, newest first: of one kind, or of every kind when kind is empty. before is the cursor (only batches with a smaller id; 0 = from the newest).
func (*Store) CanSee ¶ added in v0.2.0
CanSee reports whether the viewer sees the account: the visibility rule for one id.
func (*Store) ClaimSummary ¶
func (s *Store) ClaimSummary(ctx context.Context, userID, dueAt, now int64, maxAttempts int) (id int64, attempt int, ok bool, err error)
ClaimSummary records an attempt at the summary due at dueAt and reports whether this caller is to make it, and which attempt it is. It is one statement, so of two callers at once only one gets it. It is not to be made when it was sent, when an attempt is running or was cut off (status sending), after maxAttempts, or while the last failed attempt is younger than its back-off: a minute after the first, doubling each time.
func (*Store) Corrections ¶
Corrections lists the newest corrections made to mail in the mailboxes the viewer sees, newest first, with the example each stored: the pool few-shot retrieval ranks.
func (*Store) CountMessages ¶
CountMessages counts the messages in a state in the mailboxes the viewer sees, e.g. StateReview for the Needs review badge.
func (*Store) CreateAccount ¶
func (s *Store) CreateAccount(ctx context.Context, master []byte, a Account, secret string) (Account, error)
CreateAccount stores an account with its secret encrypted under the master key. The account goes into its owner's (a.UserID's) tenant and starts shared or not as NewAccountShared says; a.TenantID and a.Shared are ignored. The ciphertext is bound to the row id, which only exists after the insert, so the row is inserted, sealed and updated in one transaction: no reader ever sees it without a secret.
func (*Store) CreateBatch ¶
func (s *Store) CreateBatch(ctx context.Context, tenantID int64, kind, status string, now int64) (int64, error)
CreateBatch opens a batch of the tenant, with no mailbox, and returns its id.
func (*Store) CreateCleanupBatch ¶
func (s *Store) CreateCleanupBatch(ctx context.Context, accountID int64, folder string, since int64, scanLimit, scanMatched, total int, now int64) (Batch, error)
CreateCleanupBatch opens the batch of one cleanup run over total emails, running.
scanLimit and scanMatched say how much mail the check behind it covered: the most emails it was allowed and how many the range held before that cut. scanLimit 0 records nothing.
func (*Store) CreateCleanupRun ¶ added in v0.1.2
func (s *Store) CreateCleanupRun(ctx context.Context, cs []CleanupBatch, now int64) ([]Batch, error)
CreateCleanupRun opens one running cleanup batch per mailbox for a manual run over several mailboxes, all or none: they share a RunID, the id of the first, so they read back as one run. A run of one mailbox is a plain cleanup batch with no RunID.
func (*Store) CreateFirstUser ¶
func (s *Store) CreateFirstUser(ctx context.Context, email, passwordHash string, now int64) (User, error)
CreateFirstUser inserts the admin account, in the self-host tenant, only if no user exists yet.
func (*Store) CreateRule ¶
func (s *Store) CreateRule(ctx context.Context, tenantID int64, r rules.Rule, now int64) (rules.Rule, error)
CreateRule inserts a rule of the tenant at version 1 and returns it with its id. r.UserID is its author. The caller validates the rule first.
func (*Store) CreateRules ¶
func (s *Store) CreateRules(ctx context.Context, tenantID, userID int64, added []NewRule, now int64) ([]rules.Rule, error)
CreateRules inserts rules of the tenant, written by userID, in one transaction and returns them as saved, in the order given. Each lands at its position in the priority order, or at the end; the priorities of all the tenant's rules are then renumbered from 1, which is not an edit, so versions stay. The caller validates the rules first.
func (*Store) CreateSession ¶
func (s *Store) CreateSession(ctx context.Context, tokenHash string, userID, now, expiresAt int64) error
CreateSession stores a login session by the hash of its token, and sweeps expired ones.
func (*Store) CreateTenant ¶ added in v0.2.0
func (s *Store) CreateTenant(ctx context.Context, provider, externalID string, now int64) (int64, error)
CreateTenant adds a tenant for a sign-in service's organisation and returns its id.
func (*Store) CreateUser ¶ added in v0.2.0
func (s *Store) CreateUser(ctx context.Context, tenantID int64, email, passwordHash string, now int64) (User, error)
CreateUser adds a user to a tenant. An empty passwordHash makes a user who cannot sign in with a password (one made from a sign-in service's identity).
func (*Store) DeleteAccount ¶
DeleteAccount wipes an account with its secret, folders, contacts, messages, decisions, actions and corrections: each references the account with ON DELETE CASCADE. The rules are kept: first, in the same transaction, every user's rules that name the mailbox are rewritten as rules.Rule.DropAccount has it, so a rule limited to it is switched off and marked, with no mailbox, before the cascade could reach it. It returns those rules.
func (*Store) DeleteRule ¶
DeleteRule removes one of the tenant's rules. The schema takes the sender rules that route to it along (ON DELETE CASCADE) and keeps its decisions and corrections, with their rule set to NULL.
func (*Store) DeleteSenderRule ¶
func (s *Store) DeleteSenderRule(ctx context.Context, tenantID int64, matchType, value string) error
DeleteSenderRule removes one of the tenant's sender verdicts; deleting a missing one is not an error.
func (*Store) DeleteSession ¶
DeleteSession ends a session; deleting a missing one is not an error.
func (*Store) DoneActionsSince ¶
func (s *Store) DoneActionsSince(ctx context.Context, v Viewer, ruleID, since int64) ([]Action, error)
DoneActionsSince lists the actions on the mailboxes the viewer sees made at or after since that are still in effect, newest first: the ones "undo everything since" reverses. With ruleID, only those a decision for that rule led to.
func (*Store) DryRun ¶
DryRun reports whether the tenant's dry-run switch is on. def is used until the switch has been set (MAILRULES_DRY_RUN).
func (*Store) DueMessages ¶
DueMessages lists an account's failed messages whose retry time has come, oldest first.
func (*Store) EndIdentitySessions ¶ added in v0.2.0
EndIdentitySessions deletes every session of the identity's user. An identity nobody has is not an error.
func (*Store) ExtendSession ¶
ExtendSession slides a session's expiry forward.
func (*Store) FailRunningBatches ¶
FailRunningBatches marks every batch still running as failed. The daemon calls it at startup: a batch that was running when the daemon stopped will never finish.
func (*Store) FinishAction ¶
FinishAction stores how an action ended. For a done action, after is the message's state afterwards, and the message's location is updated with it in the same transaction.
func (*Store) FinishSummary ¶
func (s *Store) FinishSummary(ctx context.Context, id int64, sent bool, periodStart, periodEnd, now int64) error
FinishSummary records how a claimed attempt ended: sent, with the time it covered, or failed.
func (*Store) FirstUser ¶
FirstUser returns the admin account, or ErrNotFound before first-run setup. The command line acts as this user, in its tenant.
func (*Store) HitSenderRule ¶
HitSenderRule counts one more email settled by a sender rule.
func (*Store) ImportRules ¶
func (s *Store) ImportRules(ctx context.Context, tenantID, userID int64, imported []rules.Rule, now int64) (created, updated int, err error)
ImportRules stores the rules of a YAML file in the tenant in one transaction; userID is the author of the rules it adds. A rule whose name is already in use replaces that rule, including its mailbox (AccountID, which the caller has set to the one to keep when the file says nothing), and keeps its place, id and author; the others are added after the existing rules, in file order. Rules the file does not name are left alone. The caller validates the rules first.
func (*Store) IngestMessage ¶
func (s *Store) IngestMessage(ctx context.Context, ref mail.MsgRef, now int64) (m Message, fresh bool, err error)
IngestMessage records that a message arrived, once. fresh is false when the row was already there (the watcher repeats a message after a crash); the stored row is returned either way. A known message that the executor itself put at ref (an undo back into the watched folder gets a new UID) is not new mail either: its existing row is returned.
func (*Store) InsertAction ¶
InsertAction writes an action row and returns its id.
func (*Store) LastMailAt ¶
LastMailAt is when the newest email MailRules has seen in an account arrived (its received date, or when it was first seen if it carried none); 0 when it has seen none.
func (*Store) LastSentSummary ¶
LastSentSummary returns the user's latest summary that went out, or ErrNotFound.
func (*Store) LeaveOwnMail ¶
LeaveOwnMail reports whether, in the tenant, mail from the mailbox's own address is left alone. The pipeline reads it for every email.
func (*Store) LiveBatch ¶
LiveBatch returns the batch that collects the tenant's actions of live processing for now's day (UTC), creating it on the day's first action, so "undo today" is one batch undo. Once a day's batch has been undone, later mail that day starts a new one.
func (*Store) MarkActionUndone ¶
func (s *Store) MarkActionUndone(ctx context.Context, a Action, restored mail.MsgRef, now int64) error
MarkActionUndone marks a done action undone and records where its message is now.
func (*Store) MessageActions ¶
MessageActions lists every action taken on a message, oldest first.
func (*Store) MessageActivity ¶ added in v0.2.0
MessageActivity returns one message's activity row, whoever's it is, or ErrNotFound: for internal work on a known message (the executor, the event publishers).
func (*Store) MessageByMessageID ¶
func (s *Store) MessageByMessageID(ctx context.Context, accountID int64, messageID string, except int64) (Message, error)
MessageByMessageID returns the account's newest row, other than except, for the email with this Message-ID header, or ErrNotFound.
func (*Store) MessageCorrections ¶
MessageCorrections lists the corrections the user made to a message, oldest first.
func (*Store) MessageDecisions ¶
MessageDecisions lists every decision made for a message, oldest first. A message has more than one when the retry job ran it again.
func (*Store) PutSenderRule ¶
func (s *Store) PutSenderRule(ctx context.Context, tenantID int64, sr rules.SenderRule, now int64) (rules.SenderRule, error)
PutSenderRule stores the tenant's verdict for a sender address or domain, replacing any earlier one for the same sender, and returns it with its id. sr.UserID is who made it. A rule_id that is not one of the tenant's rules is refused as ErrNotFound.
func (*Store) QuietRules ¶
QuietRules counts the enabled rules of the viewer's tenant that were applied to no email of the mailboxes the viewer sees since `since`.
func (*Store) Rebind ¶
Rebind records that the email of row known is now at ref, where it was just ingested as row fresh, and removes fresh, so the email keeps one row. fresh must be a row MailRules has done nothing with yet: when it has a decision, an action or a correction, Rebind changes nothing and reports false.
func (*Store) RecentSenderDecisions ¶
func (s *Store) RecentSenderDecisions(ctx context.Context, v Viewer, address string, n int) ([]SenderDecision, error)
RecentSenderDecisions returns the decisions of the newest n decided emails from one address across the mailboxes the viewer sees, newest first. An email decided more than once (a retry, a cleanup run) counts once, by its latest decision. A decision whose actions were recorded in dry-run is left out, so a dry-run period teaches the learner nothing, then or once MailRules is live.
func (*Store) ReconcileRemovedMailboxes ¶ added in v0.1.3
ReconcileRemovedMailboxes fixes the rules that still name a mailbox removed before DeleteAccount kept them (MAI-132): every mailbox a rule names that has no account any more is dropped from it as DeleteAccount would have, for every tenant, in one transaction. It returns the rules it changed; a second run changes nothing.
func (*Store) ReorderRules ¶
ReorderRules sets the priorities of the tenant's rules to the order of ids, in one transaction. A reorder is not an edit, so versions stay as they are.
func (*Store) Retain ¶
func (s *Store) Retain(ctx context.Context, tenantID, now int64, retentionDays int) (blanked, deleted int64, err error)
Retain is the retention job's one step for one tenant: it blanks the snippet of every message of the tenant's mailboxes first seen more than retentionDays ago (the tenant's retention_days), and deletes the tenant's messages first seen more than KeepMessagesDays ago that have no action left to undo, that is, none in effect from the last UndoDays (their decisions, actions and corrections go with them). It returns how many rows each part touched.
func (*Store) RuleCosts ¶
RuleCosts lists, per rule, the emails of the mailboxes the viewer sees decided for it since `since` and what the model decisions among them cost, dearest first. Every decision counts here, a retry too: it was paid for.
func (*Store) RuleStats ¶
RuleStats counts, per rule of the viewer's tenant, the messages it was applied to since `since` in the mailboxes the viewer sees, with the time of the latest one ever. Mail waiting in Needs review does not count: nothing was applied.
func (*Store) Rules ¶
Rules returns all of the tenant's rules, disabled ones included, in priority order.
func (*Store) SaveFolders ¶
SaveFolders replaces an account's folder list with what discovery found. Watch positions of folders that still exist are kept; folders that are gone are removed.
func (*Store) SaveMessageSummary ¶
SaveMessageSummary stores the parsed header fields, snippet and signals of a message.
func (*Store) SenderRules ¶
SenderRules returns all of the tenant's sender rules.
func (*Store) SendersSeen ¶
SendersSeen lists every address the mailboxes the viewer sees have had mail from since `since` (by when MailRules first saw the email).
func (*Store) SessionUser ¶
SessionUser returns the user behind a live session and when it expires.
func (*Store) SetAccountCapabilities ¶
SetAccountCapabilities stores what the server advertised at the last login.
func (*Store) SetAccountSecret ¶
SetAccountSecret replaces an account's app password, sealed under a fresh data key.
func (*Store) SetAccountShared ¶ added in v0.2.0
SetAccountShared shares an account with everyone in its tenant, or makes it private to its owner again. The caller checks that the owner asks.
func (*Store) SetAccountStatus ¶
func (s *Store) SetAccountStatus(ctx context.Context, id int64, status, lastError string, now int64) error
SetAccountStatus records the connection state; lastError is cleared by passing "".
func (*Store) SetBatchProgress ¶
SetBatchProgress records how far a batch has come.
func (*Store) SetBatchStatus ¶
SetBatchStatus moves a batch to a status.
func (*Store) SetDryRun ¶
SetDryRun turns the tenant's dry-run switch on or off. A running daemon reads it before every action, so it takes effect at once.
func (*Store) SetFolderPosition ¶
func (s *Store) SetFolderPosition(ctx context.Context, accountID int64, name string, uidValidity, lastUID uint32) error
SetFolderPosition records how far a watched folder has been processed.
func (*Store) SetMessageState ¶
func (s *Store) SetMessageState(ctx context.Context, id int64, state string, attempts int, nextAttemptAt int64) error
SetMessageState moves a message to a state. nextAttemptAt is when the retry job should run it again; 0 means it is not waiting.
func (*Store) SetPassword ¶ added in v0.1.2
SetPassword replaces a user's password hash and, in the same transaction, ends every session the user has: a session someone else holds does not outlive the password that opened it. A missing user is ErrNotFound.
func (*Store) SetSetting ¶
SetSetting stores a tenant's JSON value under key, replacing any previous one.
func (*Store) SetSettings ¶
SetSettings stores several of a tenant's settings in one transaction, so a change is never half applied. A nil value removes the key, which puts its default back in force.
func (*Store) SetTrashToFolder ¶
SetTrashToFolder turns the tenant's trash_to_folder switch on or off.
func (*Store) SettingsLike ¶ added in v0.2.0
func (s *Store) SettingsLike(ctx context.Context, prefix string) (map[int64]map[string]string, error)
SettingsLike returns, across every tenant, the stored settings whose key starts with prefix, as tenant id and key to JSON value. It is for checks over the whole database (the master key check counts every stored provider key), never for showing.
func (*Store) SignInIdentity ¶ added in v0.2.0
SignInIdentity finds the user an identity belongs to, or makes one, in one transaction. The tenant is the one of (Provider, Tenant), made when there is none yet. A new identity gets a user in that tenant who cannot sign in with a password; a known one keeps its user, whose email becomes id.Email. It fails, changing nothing, with ErrTenantMismatch when a known identity's user is in another tenant, and with ErrEmailInUse when another user has the email.
func (*Store) StatsTotals ¶
StatsTotals counts the emails of the mailboxes the viewer sees decided since `since`.
func (*Store) SummaryCost ¶
func (s *Store) SummaryCost(ctx context.Context, v Viewer, from, to int64) (costUSD float64, calls int, err error)
SummaryCost is what the model decisions made in [from, to) on the mail of the mailboxes the viewer sees cost, and how many asked a model.
func (*Store) SummaryReview ¶
func (s *Store) SummaryReview(ctx context.Context, v Viewer, limit int) ([]SummaryEmail, int, error)
SummaryReview lists the emails of the mailboxes the viewer sees waiting in Needs review now, newest first, at most limit of them, and how many there are.
func (*Store) SummaryRules ¶
SummaryRules lists, per rule, the emails of the mailboxes the viewer sees a rule was applied to (sorted or trashed, as the Overview counts them) whose latest decision was made in [from, to), most first.
func (*Store) SummaryTrashed ¶
func (s *Store) SummaryTrashed(ctx context.Context, v Viewer, from, to int64, limit int) ([]SummaryEmail, int, error)
SummaryTrashed lists the emails of the mailboxes the viewer sees trashed (or, in dry-run, recorded as trashed) in [from, to) and not undone since, newest first, at most limit of them, and how many there are.
func (*Store) TenantAccounts ¶ added in v0.2.0
TenantAccounts lists every account of a tenant, private ones included, oldest first. It is for checks that span the tenant without showing what they find (a mailbox already connected), never for listing to a person.
func (*Store) TenantMembers ¶ added in v0.2.0
TenantMembers counts the users in a tenant.
func (*Store) Tenants ¶ added in v0.2.0
Tenants lists every tenant's id, oldest first: what per-tenant jobs (retention) walk.
func (*Store) TopRules ¶
TopRules lists the rules applied to the most emails of the mailboxes the viewer sees since `since`, most first.
func (*Store) TrashToFolder ¶
TrashToFolder reports whether the tenant's trashed mail goes to MailRules' own folder rather than the server's Trash. The executor reads it before every trash action.
func (*Store) UpdateAccount ¶
UpdateAccount stores an account's label, watch folder and status (the fields the user edits).
func (*Store) UpdateRule ¶
UpdateRule rewrites the editable columns of one of the tenant's rules and bumps its version, so past decisions keep pointing at the wording they were made under. The author (UserID) stays as it is. A rule of another tenant is ErrNotFound.
func (*Store) UpsertContacts ¶
UpsertContacts adds addresses to an account's contacts, keeping the latest sent time.
func (*Store) Usage ¶
Usage lists the tenant's cost ledger from sinceDay (YYYY-MM-DD) on, oldest day first.
func (*Store) UserByEmail ¶
UserByEmail looks an account up by its (case-insensitive) email.
func (*Store) Users ¶ added in v0.2.0
Users lists every user, oldest first: who the summary email is checked for.
func (*Store) VisibleAccount ¶ added in v0.2.0
VisibleAccount returns one account the viewer sees, or ErrNotFound: one they do not see is answered as missing.
func (*Store) VisibleAccounts ¶ added in v0.2.0
VisibleAccounts lists the accounts the viewer sees, oldest first.
func (*Store) VisibleAction ¶ added in v0.2.0
VisibleAction returns one action on a mailbox the viewer sees, or ErrNotFound.
func (*Store) VisibleBatch ¶ added in v0.2.0
VisibleBatch returns one batch the viewer sees, or ErrNotFound.
func (*Store) VisibleBatchActions ¶ added in v0.2.0
VisibleBatchActions lists a batch's actions on the mailboxes the viewer sees, oldest first. A tenant's batch without a mailbox (the day's live batch, a correction or an undo) can hold actions on mailboxes of several people; each sees only theirs and shared ones.
type Summary ¶
type Summary struct {
ID int64
UserID int64
DueAt int64
Status string
Attempts int
LastAttemptAt int64
PeriodStart int64 // 0 until sent
PeriodEnd int64
SentAt int64
}
Summary is one row of summaries: a scheduled summary email.
type SummaryEmail ¶
type SummaryEmail struct {
MessageID int64
FromName string
FromAddr string
Subject string
Folder string // a trashed email: the folder the trash moved it to; "" = the server's Trash
DryRun bool // a trashed email: the trash was only recorded, in dry-run
}
SummaryEmail is one email as a summary lists it: who sent it, its subject and, for a trashed one, where it went. Never its text.
type Totals ¶
type Totals struct {
Processed int // emails decided
Sorted int // of those, a rule or sender rule was applied (or recorded, in dry-run) and not undone since
WithoutModel int // of those, a sender rule or condition rule acted on, with no model asked; never more than Processed
Trashed int // emails with a trash action that is in effect (or recorded, in dry-run)
// Where the processed emails ended up; the four add up to Processed.
WentSorted int // a rule was applied and it did not trash the email
WentTrash int // a rule was applied and it trashed the email
WentReview int // waiting in Needs review
WentNowhere int // left in the inbox: no rule matched, it was the mailbox's own mail, it could not be handled, or all that was done to it was undone
}
Totals are the counts of the stats tiles.
type UsageRow ¶
type UsageRow struct {
Day string // YYYY-MM-DD, UTC
Provider string
Model string
Purpose string // decide | escalate | compose | test | cleanup | suggest
Operator bool // made with a key the operator holds (MAILRULES_MODE=cloud)
Calls int
TokensIn int
TokensOut int
CostUSD float64
}
UsageRow is one row of the cost ledger.
type Viewer ¶ added in v0.2.0
type Viewer struct {
UserID, TenantID int64
}
Viewer is the signed-in person a user-facing read or change is made for. They see an account when it is in their tenant and they own it or it is shared (visibleAccounts), and through it its messages, decisions, actions, corrections, contacts, folders, batches, stats, activity and Needs review queue. Rules, sender rules, settings and the usage ledger belong to the tenant.