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

skymail

package
v1.3.104-alpha1 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 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