skymail

package
v1.3.105 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: AGPL-3.0 Imports: 37 Imported by: 0

Documentation

Overview

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

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/outbox.go c4-app-mail

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

Parking: mail for a visor the Outbox cannot reach is also left, sealed for that visor, on its dmsg-async inbox hosts, so it arrives when that visor comes back even if this one is offline by then. The visor fetches parked mail and files it through the same checks as mail over SMTP.

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

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

Package skymail pkg/skymail/seen.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 (
	TokenFile   = "control-token"
	TokenHeader = "X-Skymail-Token" //nolint:gosec // a header name, not a credential
)

TokenFile holds the token a control request over TCP must carry in TokenHeader. The app writes it beside the mail when it binds a TCP address, so only who can read the mail can use it.

View Source
const (
	DefaultMaxMessageSize int64 = 10 << 20 // 10 MiB, room for a few phone photos
	DefaultMaxTotalSize   int64 = 64 << 20 // 64 MiB, every folder 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"
	FolderOutbox = "Outbox"
)

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

View Source
const (
	NetDmsg   = "dmsg"
	NetSkynet = "skynet"
)

Networks a Service listens on, in the order it opens them.

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.

View Source
const Port = 25

Port is SMTP's port, on dmsg and skynet alike.

View Source
const RelayHeader = "X-Skymail-Relay"

RelayHeader names the visor that delivered a sealed envelope for its signer. The mailbox writes it on the line after PeerHeader, and only a header in that place counts.

View Source
const SealedContentType = "application/vnd.skywire.skymail-sealed"

SealedContentType marks a sealed envelope.

View Source
const SealedExtension = "SKYMAIL-SEALED"

SealedExtension is the EHLO keyword of a mailbox that opens envelopes.

View Source
const SettingsFile = "settings.json"

SettingsFile keeps the settings changed at runtime beside the mail, like the whitelist. It wins over the baseline the mailbox was started with, so a change survives a restart and, in a browser tab, a reload that regenerates the visor config.

Variables

View Source
var (
	ErrNotSealed   = errors.New("skymail: not a sealed envelope")
	ErrNotForUs    = errors.New("skymail: sealed for another visor")
	ErrSealBroken  = errors.New("skymail: sealed envelope does not open")
	ErrBadSealSign = errors.New("skymail: sealed envelope signature does not verify")
)

Errors an envelope can fail to open with.

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

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

View Source
var ErrNoOutbox = errors.New("skymail: this mailbox has no outbox")

ErrNoOutbox answers Retry on a mailbox opened without an Outbox.

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 IsSealed

func IsSealed(body []byte) bool

IsSealed reports whether body is a sealed envelope.

func OpenSealed

func OpenSealed(body []byte, sk cipher.SecKey) ([]byte, cipher.PubKey, error)

OpenSealed opens an envelope sealed to the holder of sk and returns the message and the PK that signed 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.

func Seal

func Seal(msg []byte, sk cipher.SecKey, rcpt cipher.PubKey) ([]byte, error)

Seal signs msg with sk and seals it so only rcpt can open it.

func SealedRecipient

func SealedRecipient(body []byte) (cipher.PubKey, bool)

SealedRecipient reads the recipient PK an envelope is sealed for, which a carrier can see without opening it.

func SealedSize

func SealedSize(n int64) int64

SealedSize is the size of the envelope Seal makes for an n byte message, for a size limit that has to admit it.

Types

type Age

type Age time.Duration

Age is a duration written as text, such as "168h0m0s". It also reads the nanosecond number the wasm visor once wrote.

func (Age) MarshalJSON

func (a Age) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*Age) UnmarshalJSON

func (a *Age) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

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
	// Outbox queues mail for a recipient that cannot take it yet and
	// retries it (RunOutbox). Off, such a send fails at once.
	Outbox bool
	// Sealer, when set, seals and opens envelopes as PK. The mailbox then
	// opens sealed envelopes (sealed.go) that any visor delivers, and files
	// them under the PK that signed them.
	Sealer Sealer
	// Helpers, with Sealer and the Outbox, names the visors that get a
	// sealed copy of queued mail to deliver too (helpers.go).
	Helpers func() []cipher.PubKey
	// Parker, with Sealer and the Outbox, parks queued mail on the recipient's
	// dmsg-async inbox hosts and fetches the mail parked for this visor
	// (parked.go).
	Parker Parker
	// RelayFor, with Sealer and the Outbox, reports whether this mailbox
	// carries sealed mail that pk hands over for someone else.
	RelayFor func(pk cipher.PubKey) bool
	// 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 Control

type Control struct {
	Do func(*http.Request) (*http.Response, error)
}

Control calls a mailbox's control surface. Do sends one request and is where the caller decides how to reach the app: its in-process handler, or its TCP address with the token.

func (Control) Attachment

func (c Control) Attachment(folder, id string, n int) (*AttachmentData, error)

Attachment decodes attachment n of a message.

func (Control) Delete

func (c Control) Delete(folder, id string) error

Delete removes one message. In the Outbox that cancels it.

func (Control) List

func (c Control) List(folder string) ([]Summary, error)

List lists a folder, newest first.

func (Control) Raw

func (c Control) Raw(folder, id string) ([]byte, error)

Raw returns one message as stored.

func (Control) Read

func (c Control) Read(folder, id string) (*Rendered, error)

Read renders one message and marks it read.

func (Control) Reload

func (c Control) Reload() error

Reload applies SettingsFile as it now stands.

func (Control) Retry

func (c Control) Retry(id string) error

Retry makes an Outbox message due now, or every one when id is empty.

func (Control) Send

func (c Control) Send(out Outgoing) (*SendResult, error)

Send delivers out, as Mailbox.Send does.

func (Control) SetWhitelist

func (c Control) SetWhitelist(pks []cipher.PubKey) error

SetWhitelist replaces the PKs allowed to deliver.

func (Control) Status

func (c Control) Status() (Status, error)

Status reports the mailbox's state.

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 Host

type Host struct {
	PK cipher.PubKey
	// Listen opens Port on one of the Networks.
	Listen func(network string) (net.Listener, error)
	// Dialers reach a peer's Port, keyed by address suffix.
	Dialers map[string]skymailbridge.Dialer
	// PeerPK names the authenticated remote PK of an inbound connection.
	PeerPK func(net.Conn) (cipher.PubKey, bool)
	// Sealer seals and opens envelopes as PK. Nil turns sealed mail off.
	Sealer Sealer
	// Peers are the visors this one manages as a hypervisor and its own
	// hypervisors, the default helpers and relay-for lists.
	Peers func() (managed, hypervisors []cipher.PubKey)
	// Parker reaches the visor's dmsg-async inbox. Nil turns parking off.
	Parker    Parker
	OnDeliver func(Delivered)
	Log       logrus.FieldLogger
}

Host is what a running mailbox needs from the visor it serves.

type KeySealer

type KeySealer cipher.SecKey

KeySealer is a Sealer that holds the secret key itself.

func (KeySealer) Open

func (k KeySealer) Open(body []byte) ([]byte, cipher.PubKey, error)

Open implements Sealer.

func (KeySealer) Seal

func (k KeySealer) Seal(msg []byte, rcpt cipher.PubKey) ([]byte, error)

Seal implements Sealer.

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. In the Outbox that cancels its delivery.

func (*Mailbox) Expire

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

Expire deletes every Inbox and Sent message older than MaxAge and reports how many. The Outbox gives up on its own, with a bounce.

func (*Mailbox) HasOutbox

func (mb *Mailbox) HasOutbox() bool

HasOutbox reports whether the mailbox was opened with an Outbox.

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) Queued

func (mb *Mailbox) Queued() int

Queued is how many messages wait in the Outbox.

func (*Mailbox) Raw

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

Raw returns a message exactly as stored.

func (*Mailbox) Retry

func (mb *Mailbox) Retry(id string) error

Retry makes an Outbox message due now, or every one when id is empty.

func (*Mailbox) RunExpiry

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

RunExpiry sweeps now and then every expiryInterval until ctx ends.

func (*Mailbox) RunOutbox

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

RunOutbox delivers queued mail until ctx ends: each message when its next attempt is due, and at once when Retry asks or its recipient has just delivered mail here, which shows that visor is back.

func (*Mailbox) RunParked

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

RunParked fetches the mail parked for this visor when it starts and every unparkInterval 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. A recipient that cannot take it yet is queued in the Outbox, and Send succeeds when every recipient was either delivered or queued.

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) Unpark

func (mb *Mailbox) Unpark(ctx context.Context) int

Unpark files the mail parked for this visor and removes what it filed or can never file. Mail that may file later, such as into a full mailbox, stays parked. It reports how many messages it filed.

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 Parked

type Parked struct {
	ID   string
	Host cipher.PubKey
	From cipher.PubKey
	Body []byte
}

Parked is a sealed envelope that waited for this visor on an inbox host.

type Parker

type Parker interface {
	// Park leaves env, sealed for rcpt, on rcpt's inbox hosts and returns the
	// host that took it. ref names the message across retries.
	Park(ctx context.Context, rcpt cipher.PubKey, env []byte, ref string) (cipher.PubKey, error)
	// Unpark returns the mail parked for this visor without removing it.
	Unpark(ctx context.Context) ([]Parked, error)
	// Ack removes parked mail.
	Ack(ctx context.Context, done []Parked) error
}

Parker reaches the visor's dmsg-async inbox (pkg/dmsg/dmsgasync).

type Queued

type Queued struct {
	// Pending are the recipients still to reach.
	Pending     []string  `json:"pending"`
	Attempts    int       `json:"attempts"`
	LastError   string    `json:"last_error,omitempty"`
	NextAttempt time.Time `json:"next_attempt"`
	// GiveUp is when the Outbox stops trying. Zero means never.
	GiveUp time.Time `json:"give_up,omitzero"`
	// Handed are the helpers that hold a sealed copy to deliver too.
	Handed []string `json:"handed,omitempty"`
	// CarriedFor is set on sealed mail this visor delivers for the
	// visor that signed it. This visor cannot read it.
	CarriedFor string `json:"carried_for,omitempty"`
	// Parked are the recipient visors whose inbox hosts hold a sealed copy.
	Parked []string `json:"parked,omitempty"`
}

Queued is where an Outbox message stands.

type RcptResult

type RcptResult struct {
	Rcpt   string `json:"rcpt"`
	Err    string `json:"error,omitempty"`
	Queued bool   `json:"queued,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. Err says why it was not delivered. With Queued set that is only for now: the Outbox retries.

type Relay

type Relay struct {
	Helpers  *[]cipher.PubKey `json:"helpers,omitempty"`
	RelayFor *[]cipher.PubKey `json:"relay_for,omitempty"`
}

Relay is who carries this mailbox's queued mail and whose mail it carries. nil is the default, empty is none.

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"`
	RelayPK     string       `json:"relay_pk,omitempty"`
	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 Sealer

type Sealer interface {
	Seal(msg []byte, rcpt cipher.PubKey) ([]byte, error)
	Open(body []byte) ([]byte, cipher.PubKey, error)
}

Sealer seals and opens envelopes as the mailbox's owner. The owner's secret key stays with whoever implements it, which for the skymail app is the visor.

type SendReply

type SendReply struct {
	Result *SendResult `json:"result,omitempty"`
	Error  string      `json:"error,omitempty"`
}

SendReply is the control surface's answer to a send. Send can fail with a result, which says why each recipient was refused.

type SendResult

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

SendResult is what Send reports.

type Service

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

Service is a mailbox that serves Port: the receiving and sending side of the skymail app, apart from how the app reaches its visor.

func StartService

func StartService(ctx context.Context, cfg ServiceConfig) (*Service, error)

StartService opens the mailbox and serves it on every network the host can listen on. dmsg is required. A visor without skynet still takes mail over dmsg, as a .skynet address cannot reach it anyway.

func (*Service) Close

func (s *Service) Close()

Close stops serving and waits for the mailbox's loops, keeping its mail. Port is free again when it returns.

func (*Service) Handler

func (s *Service) Handler(token string) http.Handler

Handler is the control surface of s, which the visor's mail API calls. token, when set, must arrive in TokenHeader.

func (*Service) Helpers

func (s *Service) Helpers() []cipher.PubKey

Helpers is who gets this mailbox's queued mail, and whether that is the default: the visors it manages as a hypervisor, which trust it already.

func (*Service) Mailbox

func (s *Service) Mailbox() *Mailbox

Mailbox is the mailbox the Service serves.

func (*Service) RelayFor

func (s *Service) RelayFor() ([]cipher.PubKey, bool)

RelayFor is whose sealed mail this mailbox carries, and whether that is the default: its hypervisors, which can already do anything on it.

func (*Service) Reload

func (s *Service) Reload() error

Reload applies SettingsFile as it now stands, then offers queued mail to a helper that may be new.

func (*Service) Status

func (s *Service) Status() Status

Status reports the mailbox's state now.

type ServiceConfig

type ServiceConfig struct {
	Host
	// Dir is the Maildir root, which also holds SettingsFile.
	Dir string
	// Base are the limits SettingsFile overrides.
	Base Limits
	// Outbox queues mail for a visor that cannot take it yet.
	Outbox bool
}

ServiceConfig starts a Service.

type Settings

type Settings struct {
	Enable         *bool  `json:"enable,omitempty"`
	MaxMessageSize *int64 `json:"max_message_size,omitempty"`
	MaxTotalSize   *int64 `json:"max_total_size,omitempty"`
	MaxAge         *Age   `json:"max_age,omitempty"`
	Relay
}

Settings is what SettingsFile holds. A nil field is unset.

func LoadSettings

func LoadSettings(dir string) (Settings, error)

LoadSettings reads dir's SettingsFile. A missing file is no settings.

func (Settings) Over

func (s Settings) Over(base Limits) Limits

Over returns base with the limits s sets in place of base's.

func (Settings) Save

func (s Settings) Save(dir string) error

Save writes s to dir's SettingsFile in one step.

type Status

type Status struct {
	Address     string          `json:"address"`
	AddressDmsg string          `json:"address_dmsg"`
	Dir         string          `json:"dir"`
	Limits      Limits          `json:"limits"`
	Usage       int64           `json:"usage"`
	Whitelist   []cipher.PubKey `json:"whitelist"`
	Unread      int             `json:"unread"`
	Total       int             `json:"total"`
	HasOutbox   bool            `json:"has_outbox"`
	Outbox      int             `json:"outbox"`
	Sealing     bool            `json:"sealing"`

	Helpers         []cipher.PubKey `json:"helpers"`
	HelpersDefault  bool            `json:"helpers_default"`
	RelayFor        []cipher.PubKey `json:"relay_for"`
	RelayForDefault bool            `json:"relay_for_default"`
}

Status is the state of a running mailbox.

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"`
	// RelayPK is the visor that delivered a sealed envelope for PeerPK.
	RelayPK string `json:"relay_pk,omitempty"`
	// Queue is where an Outbox message stands.
	Queue *Queued `json:"queue,omitempty"`
}

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