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

skymail

package
v1.3.102 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: AGPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package skymail pkg/skymail/limits.go c4-app-mail

Package skymail pkg/skymail/mailbox.go c4-app-mail

Package skymail pkg/skymail/maildir.go c4-app-mail

Package skymail is a mailbox a visor hosts for its own PK: mail to <anything>@<base32-pk>.skynet (or .dmsg) arrives over plain SMTP on port 25, is stamped with the sender's authenticated PK, and is kept verbatim in a Maildir. Nothing about it needs a certificate, a domain or a public address, so it runs as well in a browser tab as on a server. The SMTP protocol loop and the relay are shared with pkg/skymailbridge, which is what a host already running Postfix uses.

Package skymail pkg/skymail/render.go c4-app-mail

Package skymail pkg/skymail/send.go c4-app-mail

Package skymail pkg/skymail/units.go c4-app-mail

Index

Constants

View Source
const (
	DefaultMaxMessageSize int64 = 10 << 20 // 10 MiB, room for a few phone photos
	DefaultMaxTotalSize   int64 = 64 << 20 // 64 MiB, Inbox and Sent together
	DefaultMaxAge               = 7 * 24 * time.Hour
)

Default limits. The mailbox is on by default and open to every PK, so they are low enough that nobody can fill a browser's storage with it: a full mailbox refuses mail until expiry or a delete frees room.

View Source
const (
	FolderInbox = "INBOX"
	FolderSent  = "Sent"
)

Folder names. Inbox is the Maildir root; others are Maildir++ subfolders ("." + name), which is what Dovecot or mutt would open.

View Source
const PeerHeader = "X-Skymail-Peer"

PeerHeader carries the sender PK the receiving visor authenticated. The mailbox writes it at the top of every message it accepts, so the first occurrence is always its own; one a sender forged sits below.

Variables

View Source
var ErrMailboxFull = errors.New("skymail: mailbox full")

ErrMailboxFull is returned when a message does not fit the quota.

View Source
var ErrNotFound = errors.New("skymail: no such message")

ErrNotFound is returned for a message id that is not in the folder.

View Source
var Suffixes = []string{".skynet", ".dmsg"}

Suffixes are the address TLDs a mailbox answers to. Both name the same PK; they differ only in which network a sender dials.

Functions

func FormatAge

func FormatAge(d time.Duration) string

FormatAge writes a duration the way ParseAge reads it.

func FormatSize

func FormatSize(n int64) string

FormatSize writes a size the way ParseSize reads it.

func ParseAge

func ParseAge(s string) (time.Duration, error)

ParseAge reads a Go duration, also taking days ("7d"), "default" (0) or "none" (-1).

func ParseSize

func ParseSize(s string) (int64, error)

ParseSize reads a limit written "16MiB", "500KB" or "1048576", or "default" (0) or "none" (-1), the values Limits takes.

Types

type Attachment

type Attachment struct {
	Name        string `json:"name"`
	ContentType string `json:"content_type"`
	Size        int    `json:"size"`
}

Attachment names a non-text part of a message.

type AttachmentData

type AttachmentData struct {
	Name        string `json:"name"`
	ContentType string `json:"content_type"`
	Data        []byte `json:"data"`
}

AttachmentData is one attachment, decoded.

func ExtractAttachment

func ExtractAttachment(raw []byte, n int) (*AttachmentData, error)

ExtractAttachment decodes attachment n, numbered as Render lists them.

type Config

type Config struct {
	// Dir is the Maildir root. Inbox lives here, Sent in Dir/.Sent.
	Dir string
	// PK is the mailbox owner: mail is accepted only for <x>@<PK>.
	PK cipher.PubKey
	// PeerPK names the authenticated remote PK of an inbound
	// connection. The visor supplies it (dmsg and skynet addresses
	// differ); ok=false means the caller cannot be identified.
	PeerPK func(net.Conn) (cipher.PubKey, bool)
	// Dialers reach a peer's port 25, keyed by address suffix. A
	// suffix with no dialer cannot be sent to.
	Dialers map[string]skymailbridge.Dialer
	// Limits bound what the mailbox keeps; zero fields take defaults.
	Limits Limits
	// OnDeliver, when set, is told of each message the network files in the
	// inbox, once it is stored. It must not block.
	OnDeliver func(Delivered)
	Log       logrus.FieldLogger
}

Config is what a Mailbox needs from its host.

type Delivered added in v1.3.101

type Delivered struct {
	ID      string
	From    string
	Subject string
	PeerPK  cipher.PubKey
}

Delivered is a message the network just filed in the inbox.

type Limits

type Limits struct {
	MaxMessageSize int64         `json:"max_message_size"`
	MaxTotalSize   int64         `json:"max_total_size"`
	MaxAge         time.Duration `json:"max_age"`
}

Limits bound what a mailbox keeps. A zero field means its default; a negative one removes that bound.

func (Limits) WithDefaults

func (l Limits) WithDefaults() Limits

WithDefaults resolves zero fields to the defaults.

type Mailbox

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

Mailbox is one PK's mail: a Maildir, an SMTP receiver policy, and a sender. Safe for concurrent use.

func Open

func Open(cfg Config) (*Mailbox, error)

Open creates (or reopens) the mailbox under cfg.Dir.

func (*Mailbox) Address

func (mb *Mailbox) Address(local, suffix string) string

Address returns the mailbox's address for local part and suffix, e.g. "me@<base32-pk>.skynet".

func (*Mailbox) Attachment

func (mb *Mailbox) Attachment(folder, id string, n int) (*AttachmentData, error)

Attachment decodes attachment n of a message.

func (*Mailbox) Delete

func (mb *Mailbox) Delete(folder, id string) error

Delete removes a message.

func (*Mailbox) Expire

func (mb *Mailbox) Expire(now time.Time) (int, error)

Expire deletes every message older than MaxAge and reports how many.

func (*Mailbox) Limits

func (mb *Mailbox) Limits() Limits

Limits returns the effective limits.

func (*Mailbox) List

func (mb *Mailbox) List(folder string) ([]Summary, error)

List returns a folder's messages, newest first.

func (*Mailbox) MarkSeen

func (mb *Mailbox) MarkSeen(folder, id string) error

MarkSeen flags a message as read.

func (*Mailbox) MaxMessageSize

func (mb *Mailbox) MaxMessageSize() int64

MaxMessageSize is what the SMTP session advertises and enforces.

func (*Mailbox) Raw

func (mb *Mailbox) Raw(folder, id string) ([]byte, error)

Raw returns a message exactly as stored.

func (*Mailbox) RunExpiry

func (mb *Mailbox) RunExpiry(ctx context.Context)

RunExpiry sweeps now and then every expiryInterval until ctx ends.

func (*Mailbox) Send

func (mb *Mailbox) Send(ctx context.Context, out Outgoing) (*SendResult, error)

Send delivers out to every recipient's visor now, over the network its address names, and files a copy in Sent if any delivery worked. Recipients at this mailbox's own PK are filed directly.

func (*Mailbox) Serve

func (mb *Mailbox) Serve(ctx context.Context, lis net.Listener) error

Serve accepts SMTP on lis until ctx ends or lis closes.

func (*Mailbox) SetLimits

func (mb *Mailbox) SetLimits(l Limits)

SetLimits replaces the limits; zero fields take their defaults. It applies at once: the next session advertises the new size, and a lowered MaxAge expires what it now excludes on the next sweep.

func (*Mailbox) SetWhitelist

func (mb *Mailbox) SetWhitelist(pks []cipher.PubKey) error

SetWhitelist replaces the set of PKs allowed to deliver and saves it. An empty set accepts mail from any PK, as an unrestricted `serve` port does.

func (*Mailbox) Usage

func (mb *Mailbox) Usage() (int64, error)

Usage is the bytes held by every folder.

func (*Mailbox) Whitelist

func (mb *Mailbox) Whitelist() []cipher.PubKey

Whitelist returns the PKs allowed to deliver; empty means everyone.

type Outgoing

type Outgoing struct {
	// From is a local part ("me") or a full address of this mailbox.
	// Empty means "mail".
	From      string   `json:"from,omitempty"`
	To        []string `json:"to"`
	Cc        []string `json:"cc,omitempty"`
	Subject   string   `json:"subject"`
	Body      string   `json:"body"`
	InReplyTo string   `json:"in_reply_to,omitempty"`
	// Attachments go after the text as a multipart/mixed message.
	Attachments []OutgoingAttachment `json:"attachments,omitempty"`
}

Outgoing is a plain-text message to send.

type OutgoingAttachment

type OutgoingAttachment struct {
	Name        string `json:"name"`
	ContentType string `json:"content_type,omitempty"` // default application/octet-stream
	Data        []byte `json:"data"`
}

OutgoingAttachment is one file to send.

type RcptResult

type RcptResult struct {
	Rcpt string `json:"rcpt"`
	Err  string `json:"error,omitempty"`
	// Via is the network that carried it: "skynet", "dmsg", or "local"
	// for this mailbox's own PK.
	Via string `json:"via,omitempty"`
}

RcptResult is the outcome for one recipient. Delivery is immediate: a recipient whose visor cannot be reached now gets an error, not a queue entry.

type Rendered

type Rendered struct {
	From        string       `json:"from"`
	To          string       `json:"to"`
	Cc          string       `json:"cc,omitempty"`
	Subject     string       `json:"subject"`
	Date        string       `json:"date"`
	MessageID   string       `json:"message_id,omitempty"`
	PeerPK      string       `json:"peer_pk,omitempty"`
	Verified    bool         `json:"from_verified"`
	Text        string       `json:"text"`
	FromHTML    bool         `json:"from_html,omitempty"` // Text was reduced from an HTML-only part
	Attachments []Attachment `json:"attachments,omitempty"`
}

Rendered is a message reduced to what a reader shows.

func Render

func Render(raw []byte) (*Rendered, error)

Render parses raw into headers, a plain-text body, and the names of its attachments. It prefers text/plain; an HTML-only message is reduced to text, never rendered, so nothing in a message can run.

type SendResult

type SendResult struct {
	ID         string       `json:"id,omitempty"` // the Sent copy, when anything was delivered
	MessageID  string       `json:"message_id"`
	Recipients []RcptResult `json:"recipients"`
}

SendResult is what Send reports.

type Summary

type Summary struct {
	ID      string    `json:"id"`
	Folder  string    `json:"folder"`
	From    string    `json:"from"`
	To      string    `json:"to"`
	Subject string    `json:"subject"`
	Date    time.Time `json:"date"`
	Seen    bool      `json:"seen"`
	Size    int64     `json:"size"`
	// PeerPK is the sender PK the transport authenticated (inbox only).
	PeerPK string `json:"peer_pk,omitempty"`
	// FromVerified is true when the From address names PeerPK itself.
	FromVerified bool `json:"from_verified"`
}

Summary is one message as a list shows it.

Jump to

Keyboard shortcuts

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