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

server

package
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 52 Imported by: 0

Documentation

Overview

Package server is dromas-server's HTTP API (PROTOCOL.md §11, the status map in §11.3) over a Store.

Index

Constants

View Source
const DefaultMailboxCache = 10000

DefaultMailboxCache is how many mailbox records memory holds by default, as many account records, and as many mailboxes remembered as missing.

View Source
const ReceiveCeiling = time.Second / 10

ReceiveCeiling is the shortest refill of any mailbox's budget of new messages: no mailbox takes new messages faster than one each, ten a second, whatever its account lifts, and dromas-server serve does not start with flags that would (decided). A mailbox's messages are one item collection of the messages table, which its LSI holds to 10 GB on one partition, and every message leaves an item there until it expires (ARCHITECTURE.md § Storage).

Variables

View Source
var (
	ErrNotFound = errors.New("not found")
	ErrExists   = errors.New("already exists")
	ErrStale    = errors.New("the record changed since it was read")
	// ErrKeyRefused is an adopt key that is spent, expired or gone.
	ErrKeyRefused = errors.New("the adopt key is spent, expired or revoked")
	// ErrUnknown is a write's error that leaves open whether it was
	// applied: an answer lost, a timeout, a fault of the backend's own, or
	// any error a backend did not state as a refusal. It wraps the cause.
	// A count reserved for the write is kept (LIMITS.md rule 4).
	ErrUnknown = errors.New("the write may have been applied")
)

Store errors. A write's error is one of two outcomes (Memory.write): a refusal the store stated, of a write it did not make, which these name (and errNotStored, errRefused, a duplicate); or ErrUnknown, which leaves open whether the write was applied. A write that returns nil was applied.

Functions

func AWSLogger

func AWSLogger(w io.Writer) logging.Logger

AWSLogger is the logger for the AWS SDK's own lines, written to w through Redacting: its default writes to stderr apart from the redaction (dromas-dev/dromas-server#72).

func HTTPServer

func HTTPServer(addr string, h http.Handler, l Limits, idle time.Duration) *http.Server

HTTPServer is the API's HTTP server, at addr, with h as its handler. It reads a request's head in at most l.HeadTimeout and to l.HeadBytes, no further, before any handler sees it, and so before any count (LIMITS.md, rule 1); every request after its head reaches h, OPTIONS * among them, which net/http would otherwise answer itself, uncounted (dromas-dev/dromas-server#75). An idle keep-alive connection is closed after idle.

func IsThrottle

func IsThrottle(err error) bool

IsThrottle reports whether err is AWS refusing for capacity: a throttle, or the SDK's own retry quota running out.

func NewAccountID

func NewAccountID() (string, error)

NewAccountID returns a minted account id: 16 random bytes, in hex.

func NewAdoptKey

func NewAdoptKey(account string) (string, [32]byte, error)

NewAdoptKey returns a new adopt key into account, and the hash it is kept by.

func Redacting

func Redacting(w io.Writer) io.Writer

Redacting is w, redacting each line written to it (redact): the writer under every log dromas-server keeps, net/http's own among them.

func SiteBackend

func SiteBackend(raw string) (*url.URL, error)

SiteBackend reads the URL of a site's web server: http or https and a host, with no path, query, fragment or user.

Types

type Account

type Account struct {
	ID        string
	Unmetered bool
	Rev       uint64
}

Account is an account's record (PROTOCOL.md §11.16). An account pays, from prepaid credit, for its mailboxes' sends past the free limits and for size over the cap; with no credit they have the free limits. An unmetered account's mailboxes have no daily limit, the largest cap and new messages at ReceiveCeiling, and are never charged: the echo account. Credit is not built yet, so a metered account's mailboxes have the free limits.

func NewAccount

func NewAccount(id string, unmetered bool) (*Account, error)

NewAccount checks an account id and returns its record.

type AdoptKey

type AdoptKey struct {
	Account string
	Hash    [32]byte // SHA-256 of the key
	Uses    int      // left
	Expires time.Time
}

AdoptKey is what is kept of an adopt key: its hash, never the key (adoptkey.go).

type Backend

type Backend interface {
	// ReadAddress reads what is held of address a, strongly consistent:
	// its mailbox's record and its identity's revocations, each nil if absent.
	ReadAddress(ctx context.Context, a protocol.Address) (*Mailbox, *Revocations, error)
	// ReadLive reads a mailbox's live messages, strongly consistent: what a
	// mailbox's load counts (Memory.readMailbox).
	ReadLive(ctx context.Context, mailbox protocol.Address) ([]Header, error)
	// List returns up to limit live headers, oldest first by (ReceivedAt,
	// ID), after the given key (nil: from the start); strongly consistent
	// where consistent is set.
	List(ctx context.Context, mailbox protocol.Address, after *Cursor, limit int, consistent bool) ([]Header, error)
	// Message returns a live message and its body, from byte from on where
	// from is inside it, above 0 and below its Size, and otherwise whole; or
	// ErrNotFound; or errNoBody for a live item whose body is gone.
	Message(ctx context.Context, mailbox protocol.Address, id [32]byte, from int64) (*Header, []byte, error)
	// Dedupe returns the header of the live message mailbox holds with this
	// id, or ErrNotFound.
	Dedupe(ctx context.Context, mailbox protocol.Address, id [32]byte) (*Header, error)
	// Account reads an account's record, strongly consistent, or returns
	// ErrNotFound.
	Account(ctx context.Context, id string) (*Account, error)
	// AdoptKey reads an adopt key's state, strongly consistent, or returns
	// ErrNotFound.
	AdoptKey(ctx context.Context, account string, hash [32]byte) (*AdoptKey, error)

	// CreateMailbox stores b's record, at b.Rev, if the address has none;
	// otherwise ErrExists.
	CreateMailbox(ctx context.Context, b *Mailbox) error
	// UpdateMailbox stores b's record at b.Rev+1 if the stored one is at b.Rev;
	// otherwise ErrStale. It writes nothing of the revocations, and leaves b
	// as it is.
	UpdateMailbox(ctx context.Context, b *Mailbox) error
	// DeleteMailbox deletes a's record, and keeps its revocations.
	DeleteMailbox(ctx context.Context, a protocol.Address) error
	// Revoke keeps what the identity of address a revoked (Store.Revoke),
	// and returns the address's revocations as they now are; ErrStale if a
	// revocation of certificates through as high a serial was stored first,
	// or if the address's revocations item is not what want asks: there, or
	// not. The item exists only holding a revocation, since none is written
	// that holds neither, so that is whether any is held.
	Revoke(ctx context.Context, a protocol.Address, rv *Revocations, want Expect) (*Revocations, error)
	// Adopt moves mailbox b, at b.Rev, into account, spending a use of the
	// account's adopt key whose hash is key, in one write: ErrStale,
	// ErrKeyRefused.
	Adopt(ctx context.Context, b *Mailbox, account string, key [32]byte) error
	// Leave takes mailbox b, at b.Rev, out of its account, or returns ErrStale.
	Leave(ctx context.Context, b *Mailbox) error
	// CreateAccount stores a new account at a.Rev, or returns ErrExists.
	CreateAccount(ctx context.Context, a *Account) error
	// PutAdoptKey stores a new adopt key's state, or returns ErrExists.
	PutAdoptKey(ctx context.Context, k *AdoptKey) error
	// PutMessage stores a message: its body, then its item. A live item of
	// another send of the id refuses it (duplicate).
	PutMessage(ctx context.Context, mailbox protocol.Address, h *Header, body []byte) error
	// Retire deletes a live message's item, so it leaves the list and
	// nothing of it is kept, and returns when it was received; ErrNotFound
	// if it is not live.
	Retire(ctx context.Context, mailbox protocol.Address, id [32]byte) (time.Time, error)
	// DeleteMessage acks a message: its body goes, then it is retired.
	DeleteMessage(ctx context.Context, mailbox protocol.Address, id [32]byte) (time.Time, error)
	// DeleteAll deletes, as acks do, every live message in a mailbox, and hands
	// each retired as its item is deleted. A message already gone is no
	// error.
	DeleteAll(ctx context.Context, mailbox protocol.Address, retired func(id [32]byte, received time.Time)) error

	// Start hands every change to what the backend keeps, from then on, to
	// apply: in order for each item, and at least once. It returns once the
	// feed is followed, and the feed ends with ctx.
	Start(ctx context.Context, apply func(Change)) error
	// Ready reports whether the feed is followed.
	Ready() bool
}

Backend is what a store keeps durably, and the change feed of it: the durable operations only. Memory, the one Store, holds over it what a poll needs, and applies to that what each write and each change did (ARCHITECTURE.md § Storage). A backend maps its storage to these operations and their three outcomes, and nothing else: dynamoBackend (DynamoDB and S3 Express) and memBackend (maps in this process).

Each write has exactly one of three outcomes, read in one place (Memory.write):

  • applied: it returns nil (and what it returns beside);
  • refused, as the store stated, of a write it did not make: ErrExists, ErrStale, ErrNotFound, ErrKeyRefused, a send's body not stored (errNotStored), a send that met another's item (duplicate, with that item's header), or errRefused (refused);
  • unknown, which may have applied: ErrUnknown, wrapping its cause. Any other error is taken as unknown too.

A write of several steps is one operation, whose outcome is its item's write: a send writes its body, then its item; an ack deletes its body, then its item; DeleteAll finds a mailbox's messages, deletes their bodies, then each item, and hands each it took to retired as it lands. A step that fails before the item's write is a refusal: it changed nothing a reader of the list sees.

type Change

type Change struct {
	Kind ChangeKind
	// Addr is the record's address, the revocations', or the message's mailbox.
	Addr protocol.Address
	// AccountID is the account's id.
	AccountID string
	// Gone is an item deleted.
	Gone bool
	// Err is an item whose image does not read.
	Err error

	Mailbox     *Mailbox     // RecordChange
	Revocations *Revocations // RevocationsChange
	Header      *Header      // MessageChange: of an item deleted, its ID alone
	Live        bool         // MessageChange: in the list
	Account     *Account     // AccountChange
}

Change is one change to what a backend keeps, as its feed hands it over: what the item now holds, decoded by the backend, or that it is gone. An item that does not read is carried with Err, and applied as one gone, whatever its kind: memory then holds nothing of it, and its next use reads it again (dromas-dev/dromas-client#407 item 1).

type ChangeKind

type ChangeKind int

ChangeKind is what a Change is a change to.

const (
	// RecordChange is a mailbox's record.
	RecordChange ChangeKind = iota + 1
	// RevocationsChange is an address's revocations.
	RevocationsChange
	// MessageChange is a message's item.
	MessageChange
	// AccountChange is an account's record.
	AccountChange
)

The kinds of change, each with one rule in Memory.apply.

func (ChangeKind) String

func (k ChangeKind) String() string

type Config

type Config struct {
	Store  Store
	Limits Limits
	Now    func() time.Time // time.Now if nil
	Log    *log.Logger      // no access log if nil
	// Ready, when set, reports whether the store has loaded its memory; until
	// it has, every call is answered 503 rather than wrong (ARCHITECTURE.md
	// § Startup).
	Ready func() bool
	// TrustedProxies are the peers whose X-Forwarded-For names the client
	// (clientIP): behind a reverse proxy every request arrives from the
	// proxy's address. From any other peer the header is ignored. None:
	// the peer is always the client.
	TrustedProxies []netip.Prefix
	// Names are the names clients call this server by: the host of its URL,
	// lowercase, with its port only when that is not the scheme's default. A
	// pass is taken only on a request whose Host, written so, is one of them
	// (PROTOCOL.md §8). None: any Host
	// is taken, which is for the dev server and tests only, since a pass
	// made for another server would then pass here.
	Names []string
	// IdentityTypes are the identity key types this server registers: some
	// of protocol.IdentityTypes, ssh-ed25519 among them, since that is the
	// one every client makes and verifies (PROTOCOL.md §4.1). None: every type
	// this build verifies.
	IdentityTypes []string
	// SiteBackend, when set, is the web server every request outside /v1/
	// and /to/, the API's and the link page's, is passed to (SiteBackend,
	// siteProxy). None: those paths are 404.
	SiteBackend *url.URL
	// Metrics, when set, count what the server does and refuses
	// (NewMetrics). None: nothing is counted.
	Metrics *Metrics
}

Config configures a Server.

type Cursor

type Cursor struct {
	ReceivedAt time.Time
	ID         [32]byte
}

Cursor is a position in a mailbox's list: the last key returned.

type DynamoConfig

type DynamoConfig struct {
	AWS                                                  aws.Config
	MailboxesTable, MessagesTable, AccountsTable, Bucket string
	Now                                                  func() time.Time // time.Now if nil
	// MailboxCache is how many mailbox records memory holds, as many account
	// records, and as many mailboxes remembered as missing; 0: none.
	MailboxCache   int
	StreamInterval time.Duration // how often each stream shard is read
	Log            *log.Logger   // nil: no log
	// ConsistentReads makes every read strongly consistent, at twice the
	// read units: for tests, which read back what they wrote within
	// milliseconds. Otherwise only the reads that fill memory are, a mailbox's
	// load and a read of what is held of an address, since a stale copy
	// there would decide later answers, and the page of a list asked to
	// wait, which a client may hold on: a strongly consistent read costs
	// twice the read units, and polls are the load, which never fill. A
	// stale read elsewhere costs a retry or one poll; the conditional writes
	// keep the data right.
	ConsistentReads bool
	// Fatal is called if a stream follower loses its place in its stream.
	// Memory can no longer be shown current; the process must restart, which
	// rebuilds it from the tables.
	Fatal func(error)
	// Metrics, if set, count the caches' hits, misses and evictions, how far
	// behind each stream memory follows, and how large each mailbox's item
	// collection a message write grows is. Calls to AWS are counted by
	// Metrics.InstrumentAWS on the AWS config.
	Metrics *Metrics
}

DynamoConfig configures a store over DynamoDB and S3 Express.

type Expect

type Expect int

Expect is what a conditional write asks of its target as it writes: what a request's If-Match or If-None-Match held of the target at its read (expected; PROTOCOL.md §11.2), so that a change between the two refuses the write, ErrStale, and the request is answered 409 mailbox_changed. It is the write's own condition, beside any other it has, and asks nothing of a request that carried neither.

const (
	// ExpectAnything asks nothing.
	ExpectAnything Expect = iota
	// ExpectHeld asks that the target exist: an If-Match held.
	ExpectHeld
	// ExpectNone asks that it not: an If-None-Match of "*" held.
	ExpectNone
)
type Header struct {
	ID         [32]byte // SHA-256 of the ciphertext
	Size       int
	ReceivedAt time.Time // to the millisecond
	ExpiresAt  time.Time
}

Header is one message's metadata: everything but its body.

type Indebted

type Indebted struct {
	Key    string `json:"key"`
	FullIn int64  `json:"full_in"` // seconds, rounded up
}

Indebted is a key a limit holds, and how long until its bucket is full again: the deeper in debt, the longer.

type Limits

type Limits struct {
	MaxSize        int           // the cap on a ciphertext, in bytes, and what a send reads of its body; nothing may depend on its value
	MailboxCap     int           // total per mailbox, a storage ceiling [tunable]
	SendsPerDay    int           // per sender, a number a day: that many at once, one back each day divided by it (decided: 20)
	PollBurst      int           // polls a mailbox may make at once (decided: 10); also the fetches of messages already fetched
	PollRefill     time.Duration // one more poll each (decided: 50 s; once a minute is stated); also one more such fetch
	PollAfter      int           // seconds, told to clients (decided: 60)
	MaxWait        time.Duration // the longest a list is held, whatever its wait asks (PROTOCOL.md §11.11) [tunable]
	MaxHeldLists   int           // lists this process holds at once; past it a held list is answered 503 (0: none held) [tunable]
	AccountMaxSize int           // the most an account lifts MaxSize to, so what a send reads first; up to MaxSize, nothing
	RenewalsPerDay int           // prekey renewals per mailbox, a number a day as SendsPerDay (decided: 10), so no mailbox can spend the mailboxes table's write capacity
	RetiredKept    int           // retired prekeys a mailbox record keeps for the new-key rule, newest first (decided: 10), so a record stays small

	RegistrationsPerHour      int // per IPv4 address or IPv6 /64 [tunable]
	RangeRegistrationsPerHour int // per IPv6 /48, beside the /64's (decided: 500, Let's Encrypt's ratio of the two, until measured)
	SendsPerMinutePerIP       int // the pre-auth send rate per IP [tunable]
	MissesPerMinute           int // per IP: reads of the mailboxes table that find no mailbox, and checks that fail (decided: 10 for now)

	// Requests is the flood budget of an IPv4 address or IPv6 /64: every call
	// with no pass and every request no route matches (decided: 100 at once,
	// 50 a second).
	RequestBurst  int
	RequestRefill time.Duration

	// Budgets: a burst at once and one more each refill (LIMITS.md).
	ReceiveBurst                 int           // per mailbox: new messages it takes, from all senders together (decided: 50, on, and never off: ReceiveCeiling)
	ReceiveRefill                time.Duration // (decided: 6 s, ten a minute; never shorter than ReceiveCeiling, which an account lifts it to)
	DrainBurst                   int           // per mailbox: its first fetches of held messages, spent only by its own calls (decided: 50, on); an account lifts it
	DrainRefill                  time.Duration // (decided: 6 s, ten a minute)
	CallBurst                    int           // per mailbox: its calls with its pass that no other budget counts [tunable]; an account lifts it
	CallRefill                   time.Duration // [tunable]
	CertificatesPerDay           int           // per mailbox, a number a day: renewals under a later certificate, but for one that replaces a revoked certificate (decided: 10, as renewals)
	CertificateRevocationsPerDay int           // per mailbox, a number a day: raises of its revocation of certificates (decided: a count of its own, apart from CertificatesPerDay; 10, as that one) [tunable]

	// ACLEntries is the most entries a mailbox's ACL holds, its two lists
	// together [tunable]: a mailbox's record, with its ACL, is held in the mailbox
	// cache, so this bounds what one record costs memory there. Never off,
	// and at most protocol.MaxACLEntries: dromas-server serve does not start
	// otherwise.
	ACLEntries int

	// How long a body may take: BodyTimeout, and one second more for each
	// BodyMinRate bytes its handler reads, as Apache's RequestReadTimeout
	// counts; each write of a response the same, for the bytes of it already
	// written. A body that stops coming is cut off after BodyTimeout. What the
	// server drains of a body after its handler has what the handler's reads
	// left, and the answer's time runs from the end of the drain (paced). It
	// is also the longest a send waits for the one large read.
	BodyTimeout time.Duration // [tunable]
	BodyMinRate int           // bytes a second [tunable]

	// HeadBytes is the most of a request's head, its request line and its
	// headers, that the server reads (http.Server.MaxHeaderBytes): what the
	// longest call needs, a pass of protocol.MaxPassAuthorization bytes
	// with the request line and the other headers a client and a proxy in
	// front of it send, and no more (decided: 8 KiB, least need).
	// A head is read before any count, since the address a call is counted
	// by may be one of its headers (LIMITS.md, rule 1).
	HeadBytes int
	// HeadTimeout is how long a request's head may take to arrive
	// (http.Server.ReadHeaderTimeout), before any handler sees it.
	HeadTimeout time.Duration
}

Limits are the server's numbers. Decided ones are marked; the rest are tunables, and a zero turns that limit off.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are the decided numbers, and the proposed values of the tunables that have one. Tunables with no proposed value are off.

type Mailbox added in v0.1.5

type Mailbox struct {
	Addr     protocol.Address
	Identity []byte // the identity document, exactly as registered or as the revocations carried it
	State    State
	// The current prekey: its statement, the working key's signature over it,
	// which carries the key's certificate, and the statement parsed.
	Statement []byte
	Signature []byte
	Prekey    *protocol.Prekey
	// The certificate Signature carries, which is the mailbox's current one: its
	// serial, and the SHA-256 of its bytes. A pass is taken only under it,
	// and a prekey only under it or a later one (PROTOCOL.md §5). A mailbox's
	// statements are ordered by this serial first, and by the prekey's own
	// second.
	CertSerial uint64
	Cert       [32]byte
	// SenderKeyTypes is what the mailbox declared with its current prekey: the
	// identity key types of the senders it takes messages from, or
	// protocol.AnySender (PROTOCOL.md §7). Only names this build verifies
	// are kept (declared): a mailbox of any other type cannot send here.
	SenderKeyTypes []string
	// Retired holds the newest retired prekeys (Limits.RetiredKept), each
	// until its not_after: its key may not come back.
	Retired []Retired
	Rev     uint64 // the record's revision, 0 with no record; a write must name the one it read
	Created time.Time
	// Expires is when the record may go: DynamoDB's TTL deletes it some time
	// after (PROTOCOL.md §13). Every registration and renewal moves it to
	// when nothing of the mailbox is valid any more. Zero: never.
	Expires time.Time
	// Account is the account the mailbox belongs to, or "" (PROTOCOL.md §11.16).
	// Only Adopt and Leave change it.
	Account string
	// What the identity revoked, from the address's revocations item
	// (Revocations): kept for good and served to whoever asks.
	//
	// Revocation is the identity's signature that revoked the address
	// (PROTOCOL.md §10.1): nil unless State is Revoked. With Identity it
	// proves itself, so nobody takes this server's word that the mailbox is
	// revoked.
	Revocation []byte
	// RevokedCerts is the identity's highest revocation of its certificates,
	// exactly as it signed it, and RevokedCertsSig its signature
	// (PROTOCOL.md §10.2): nil until one is published. The address goes on.
	// Served with Identity to whoever asks, it proves itself as Revocation
	// does. It was verified when it was stored, and only one through a higher
	// serial replaces it.
	RevokedCerts    []byte
	RevokedCertsSig []byte
	// ACL is the mailbox's sender ACL (PROTOCOL.md §11.14): who may send to it,
	// deny read first and then allow. nil until the mailbox puts one, and a mailbox
	// with none takes messages from everyone. It is replaced whole, and never
	// removed.
	ACL *protocol.ACL
}

Mailbox is what a server holds of an address: its mailbox's record, if it has one, and what its identity revoked, if anything, which is kept in an item of its own (Revocations) with or without a mailbox. A revocation of the address is the State whatever the record says.

type Memory

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

Memory is the store: what this process holds in memory, over a Backend that holds the durable state (ARCHITECTURE.md § Memory). What a poll needs lives here (index), loaded per mailbox on its first use, and bounded caches of mailbox and account records serve sends and reads of a mailbox (recCache). Every write goes through one function (write), which applies its outcome, and every change the backend's feed hands over goes through one (apply). It is the one implementation of Store, whatever the backend: NewDynamoStore and NewMemStore make it.

func NewDynamoStore

func NewDynamoStore(c DynamoConfig) *Memory

NewDynamoStore returns the store over DynamoDB and S3 Express: Memory over the DynamoDB backend, not ready until Start returns.

func NewMemStore

func NewMemStore(now func() time.Time) *Memory

NewMemStore returns the dev server's store: Memory over a backend of maps in this process (memBackend), on the given clock, nothing persisted. Memory follows its feed from the start.

func (*Memory) Account

func (s *Memory) Account(ctx context.Context, id string) (*Account, error)

Account implements Store: the cache, or a strongly consistent read, so a change made before it is in it (one made after reaches the cache by the feed, or, where it was counted after the read's token, keeps the fill out: recCache.fill).

func (*Memory) Adopt

func (s *Memory) Adopt(ctx context.Context, b *Mailbox, account string, key [32]byte) error

Adopt implements Store.

func (*Memory) AdoptKey

func (s *Memory) AdoptKey(ctx context.Context, account string, hash [32]byte) (*AdoptKey, error)

AdoptKey implements Store: a strongly consistent read, so a key just made is found.

func (*Memory) After

func (s *Memory) After(ctx context.Context, mailbox protocol.Address, c *Cursor) (int, error)

After implements Store: from memory, after the mailbox's load on its first use.

func (*Memory) Begin

func (s *Memory) Begin(a protocol.Address) uint64

Begin implements Store: a's token in the record cache (recCache.begin).

func (*Memory) Changed

func (s *Memory) Changed(a protocol.Address) <-chan struct{}

Changed returns a channel closed at mailbox a's next change in memory (Store.Changed).

func (*Memory) Count

func (s *Memory) Count(ctx context.Context, mailbox protocol.Address) (int, error)

Count implements Store: from memory, after the mailbox's load on its first use.

func (*Memory) CreateAccount

func (s *Memory) CreateAccount(ctx context.Context, a *Account) error

CreateAccount implements Store.

func (*Memory) CreateMailbox added in v0.1.5

func (s *Memory) CreateMailbox(ctx context.Context, b *Mailbox) error

CreateMailbox implements Store.

func (*Memory) Dedupe

func (s *Memory) Dedupe(ctx context.Context, mailbox protocol.Address, id [32]byte) (*Header, error)

Dedupe implements Store: the live message's item. A stale miss is caught by PutMessage's conditional write.

func (*Memory) DeleteMailbox added in v0.1.5

func (s *Memory) DeleteMailbox(ctx context.Context, a protocol.Address) error

DeleteMailbox implements Store.

func (*Memory) DeleteMessage

func (s *Memory) DeleteMessage(ctx context.Context, mailbox protocol.Address, id [32]byte) error

DeleteMessage implements Store (ack): the body, then the item. Both steps are safe to repeat, so a crash between them is finished by the client's next ack, and a fetch in between finds no body and retires it.

func (*Memory) DeleteMessages

func (s *Memory) DeleteMessages(ctx context.Context, mailbox protocol.Address) error

DeleteMessages implements Store (a revocation): every live message in the mailbox, as acks, each out of memory as its item is deleted.

func (*Memory) Fetched

func (s *Memory) Fetched(ctx context.Context, mailbox protocol.Address, id [32]byte) (held, again bool, err error)

Fetched implements Store, from memory as Holds does.

func (*Memory) Held

func (s *Memory) Held(a protocol.Address) (Standing, bool)

Held implements Store: the standing of a mailbox memory has loaded.

func (*Memory) Holds

func (s *Memory) Holds(ctx context.Context, mailbox protocol.Address, id [32]byte) (bool, error)

Holds implements Store: from memory, after the mailbox's load on its first use. A message another process stored is held once the feed has brought it.

func (*Memory) Known

func (s *Memory) Known(a protocol.Address) (*Mailbox, bool)

Known implements Store: the record cache, or the memory of a mailbox a read did not find.

func (*Memory) Leave

func (s *Memory) Leave(ctx context.Context, b *Mailbox) error

Leave implements Store.

func (*Memory) List

func (s *Memory) List(ctx context.Context, mailbox protocol.Address, after *Cursor, limit int, consistent bool) ([]Header, error)

List implements Store: a page read eventually consistent just after a write may miss it while memory's count has it, and a client lists at every poll, so the next one has it; a held list's, which no later change may wake, is read strongly consistent (Server.list).

func (*Memory) Mailbox added in v0.1.5

func (s *Memory) Mailbox(ctx context.Context, a protocol.Address) (*Mailbox, error)

Mailbox implements Store: the cache, or a read. An address the read finds nothing of is remembered as missing for a while (missCache), so naming it again costs no read. The read fills the cache, so it is strongly consistent, as a mailbox's load is: a write made before it is in it, and one made after reaches the cache by the feed, or, where it was counted after the read's token, keeps the fill out (recCache.fill, missed).

func (*Memory) Message

func (s *Memory) Message(ctx context.Context, mailbox protocol.Address, id [32]byte, from int64) (*Header, []byte, error)

Message implements Store. A live message whose body is gone (a half-finished ack, or a body the lifecycle rule took early) is retired and answered as gone.

func (*Memory) NextExpiry added in v0.1.3

func (s *Memory) NextExpiry(a protocol.Address) time.Time

NextExpiry implements Store, from the index.

func (*Memory) PutAdoptKey

func (s *Memory) PutAdoptKey(ctx context.Context, k *AdoptKey) error

PutAdoptKey implements Store. Memory holds no adopt key.

func (*Memory) PutMessage

func (s *Memory) PutMessage(ctx context.Context, mailbox protocol.Address, h *Header, body []byte) (*Header, error)

PutMessage implements Store: stored, the message's mark joins memory; met by another send's live item of the same ciphertext, that item's header is returned.

func (*Memory) Ready

func (s *Memory) Ready() bool

Ready reports whether the backend's feed is followed.

func (*Memory) Revoke

func (s *Memory) Revoke(ctx context.Context, a protocol.Address, rv *Revocations, want Expect) error

Revoke implements Store.

func (*Memory) Start

func (s *Memory) Start(ctx context.Context) error

Start follows the backend's feed: from then on every change it hands over is applied (apply). Memory starts empty, and a mailbox is loaded on its first use, after the feed is followed: a change made before its load is in the load, one made after reaches memory by the feed, or both, and applying it twice is harmless (ARCHITECTURE.md § Startup). Nothing is kept across restarts.

func (*Memory) UpdateMailbox added in v0.1.5

func (s *Memory) UpdateMailbox(ctx context.Context, b *Mailbox) error

UpdateMailbox implements Store.

type Metrics

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

Metrics count what this server does and refuses, for its operator (dromas-dev/dromas-server#59; ARCHITECTURE.md § Metrics), as Prometheus counts a service: its requests, errors and latency, every failure beside its attempts. They are served on a listener of their own (dromas-server serve --metrics-listen), never the API's.

No label is a value a caller chooses: no address, IP, mailbox, message id, cursor or path as sent, each of which would be a series per caller and is what the logs leave out. Every label is from a set this code defines: a route is the router's pattern, a code the protocol's, a limit the name a refusal gives it. A nil *Metrics counts nothing.

func NewMetrics

func NewMetrics(reg prometheus.Registerer) *Metrics

NewMetrics makes the server's metrics and registers them with reg.

func (*Metrics) ConnState

func (m *Metrics) ConnState(_ net.Conn, st http.ConnState)

ConnState counts the API listener's open connections, as http.Server's ConnState.

func (*Metrics) InstrumentAWS

func (m *Metrics) InstrumentAWS(cfg *aws.Config)

InstrumentAWS counts and times every call the clients made from cfg make, each with its retries, by service, operation and outcome.

type Retired

type Retired struct {
	Type     protocol.KeyType
	NotAfter time.Time
	KeyHash  [32]byte // SHA-256 of the age recipient
}

Retired is what is kept of a retired prekey while it is live.

type Revocations

type Revocations struct {
	Identity []byte
	// Address is the identity's signature that revoked the address, or nil.
	Address []byte
	// Certs and CertsSig are its highest revocation of its certificates, or
	// nil; Through is the serial it revokes through, 0 with none.
	Certs, CertsSig []byte
	Through         uint64
}

Revocations is what an address's identity has revoked, as kept in an item of its own beside the mailbox's record, mailbox or none (PROTOCOL.md §10.1, §10.2): the revocation of the address once, and the highest revocation of its certificates. Nothing in it is ever taken away, so two copies merge to the larger of each.

type Server

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

Server is the API.

func New

func New(c Config) *Server

New returns a Server.

func (*Server) AdminHandler

func (s *Server) AdminHandler() http.Handler

AdminHandler is the operator's interface:

  • POST /limits/clear?ip=<address> answers {"address":"<the key cleared>","cleared":[<the limits that held anything of it>]} (ClearAddress);
  • GET /limits/top?limit=<name>&n=<count> answers {"limit":"<name>", "keys":[{"key":"<a mailbox or an address>","full_in":<seconds>}]}, deepest in debt first (Top).

func (*Server) ClearAddress

func (s *Server) ClearAddress(ip string) (key string, cleared []string, ok bool)

ClearAddress forgets what the limits keyed by a caller's address hold of ip: its misses, its registrations, by its IPv4 address or IPv6 /64 and by its IPv6 /48, its sends before their pass and its calls with no checked pass, each bucket full again as if the address had never been seen. ip is an IPv4 address or any IPv6 address of a /64, with or without a zone, which is how the limits key a caller (ipKey). It returns that key and the names of the limits that held anything of it, and false if ip is no address.

func (*Server) HTTPServer added in v0.1.3

func (s *Server) HTTPServer(addr string, idle time.Duration) *http.Server

HTTPServer is the API's HTTP server for s, at addr: HTTPServer with s's handler and limits, which answers every list s holds 304 at once when it shuts down (StopHolds, by http.Server.RegisterOnShutdown), so its clients list again at once and Shutdown waits for none of them.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the API's routes, the page a mailbox's link opens, and the site, if there is one. Every answer is paced, and every request is counted before anything is done for it (LIMITS.md, rule 1): each handler is put on its router by mount, through the gate, and a request no route answers is counted instead by unrouted. Both pages are answered before the API is ready, since neither reads a store.

func (*Server) StopHolds

func (s *Server) StopHolds()

StopHolds answers every list held, now and from now on, at once: for http.Server.RegisterOnShutdown, so the clients list again at once and Shutdown waits for none of them.

func (*Server) Top

func (s *Server) Top(name string, n int) (top []Indebted, ok bool)

Top returns, for the limit named name, the n keys it holds deepest in debt now, deepest first: which mailboxes or addresses spend it most. It reads memory and keeps nothing (dromas-dev/dromas-server#59). ok is false for a name that is no limit's.

type Standing

type Standing struct {
	State State
	Key   string   // the identity's key in SSH wire form, of whatever type: what line 2 of its document holds
	Cert  [32]byte // Mailbox.Cert
	// CertSerial is Mailbox.CertSerial, which Listed is judged by.
	CertSerial uint64
	// Listed is true while the mailbox's current certificate is one its identity
	// has revoked (PROTOCOL.md §10.2): no pass of the mailbox is taken until it
	// publishes a prekey under a new certificate.
	Listed bool
	// InAccount is true for a mailbox in an account, whose limits are read from
	// it; a mailbox in none has the server's, with nothing to read.
	InAccount bool
}

Standing is what checking a mailbox's pass needs of it (PROTOCOL.md §8): its state, its identity's public key, which certificate is its current one, and whether its identity has revoked that certificate.

type State

type State string

State is what a server holds of an address (PROTOCOL.md §11.6). A mailbox's record is Active, the one state a record is written in or read back as; Revoked and NoMailbox are what the store answers from the address's revocations alone.

const (
	// Active is a registered mailbox.
	Active State = "active"
	// Revoked is an address its identity revoked. Its record is deleted,
	// and the revocation is kept for good.
	Revoked State = "revoked"
	// NoMailbox is an address with no mailbox whose identity revoked certificates:
	// a later registration is held above them.
	NoMailbox State = "none"
)

The states.

type Store

type Store interface {
	// Held returns the standing of a mailbox memory holds, with no read. ok is
	// false for a mailbox this process has not loaded, which only its record
	// answers for: Known, then Mailbox (ARCHITECTURE.md § Memory).
	Held(a protocol.Address) (st Standing, ok bool)
	// Changed returns a channel closed at mailbox a's next change in memory, by
	// a write of this process or one the streams bring: what a held list
	// waits on (PROTOCOL.md §11.11). It reads nothing.
	Changed(a protocol.Address) <-chan struct{}
	// NextExpiry returns when the first of mailbox a's messages in memory
	// expires, by the store's clock, with no read: past, for one that
	// expired since the mailbox was last read; zero for a mailbox with none, or one
	// memory has not loaded. An expiry is no change memory applies, so a
	// held list waits until it, and no longer (PROTOCOL.md §11.11).
	NextExpiry(a protocol.Address) time.Time
	// Mailbox returns what is held of an address (view): its mailbox's record and
	// its identity's revocations, or ErrNotFound if neither.
	Mailbox(ctx context.Context, a protocol.Address) (*Mailbox, error)
	// Known returns what memory holds of an address, with no read: what Mailbox
	// returns, or nil for an address it holds as missing. ok is false when
	// only a read can tell, which is what Mailbox then does.
	Known(a protocol.Address) (b *Mailbox, ok bool)
	// Begin returns a token of address a, which only grows: what Mailbox
	// answers once it is taken holds every write this process applied to a
	// before it.
	Begin(a protocol.Address) uint64
	// CreateMailbox stores a new mailbox's record at Rev 1, or returns ErrExists if
	// the address has one.
	CreateMailbox(ctx context.Context, b *Mailbox) error
	// UpdateMailbox stores b's record if the stored one is still at b.Rev, and
	// bumps b.Rev; otherwise ErrStale. It writes nothing of the revocations.
	UpdateMailbox(ctx context.Context, b *Mailbox) error
	// DeleteMailbox deletes a's record, and keeps its revocations.
	DeleteMailbox(ctx context.Context, a protocol.Address) error
	// Revoke keeps what the identity of address a revoked, mailbox or none, in
	// the address's revocations item: rv's revocation of the address, kept
	// once, and its revocation of certificates, which only one through a
	// higher serial replaces: ErrStale if one through as high a serial was
	// stored first, or if the address's revocations are not what want asks
	// of them, some held or none.
	Revoke(ctx context.Context, a protocol.Address, rv *Revocations, want Expect) error

	// Dedupe returns the header of the live message mailbox holds with this
	// id, or ErrNotFound: nothing is kept of one acked or expired.
	Dedupe(ctx context.Context, mailbox protocol.Address, id [32]byte) (*Header, error)
	// PutMessage stores a message: its body, then its header. If a live
	// message of the id appeared first, it returns that one's header
	// instead.
	PutMessage(ctx context.Context, mailbox protocol.Address, h *Header, body []byte) (*Header, error)
	// List returns up to limit live headers, oldest first by (ReceivedAt,
	// ID), after the given key (nil: from the start). Consistent reads them
	// strongly consistent: every write applied to memory before the read is
	// in the page.
	List(ctx context.Context, mailbox protocol.Address, after *Cursor, limit int, consistent bool) ([]Header, error)
	// Message returns a live message and its body, or ErrNotFound: the body
	// from byte from on where from is inside it, above 0 and below its Size,
	// and otherwise whole, as a fetch answers a range (PROTOCOL.md §11.12).
	// So a fetch that resumes reads only the rest from where a body is kept.
	Message(ctx context.Context, mailbox protocol.Address, id [32]byte, from int64) (*Header, []byte, error)
	// DeleteMessage acks a message: its body goes, then its item, and
	// nothing of it is kept (PROTOCOL.md §13). ErrNotFound if it is not
	// live.
	DeleteMessage(ctx context.Context, mailbox protocol.Address, id [32]byte) error
	// DeleteMessages deletes every live message in a mailbox, as acks do (a
	// revocation of its address). A message already gone is no error.
	DeleteMessages(ctx context.Context, mailbox protocol.Address) error

	// Account returns an account's record, or ErrNotFound: read on its
	// first use, then answered from memory.
	Account(ctx context.Context, id string) (*Account, error)
	// CreateAccount stores a new account at Rev 1, or returns ErrExists.
	CreateAccount(ctx context.Context, a *Account) error
	// PutAdoptKey stores a new adopt key's state, or returns ErrExists.
	PutAdoptKey(ctx context.Context, k *AdoptKey) error
	// AdoptKey returns the state of account's adopt key whose hash is given,
	// or ErrNotFound. It reads it strongly consistent, so a key just made is
	// found.
	AdoptKey(ctx context.Context, account string, hash [32]byte) (*AdoptKey, error)
	// Adopt moves mailbox b, as read, into account, spending a use of account's
	// adopt key whose hash is given, in one write: ErrStale if the record
	// changed, ErrKeyRefused if the key is spent, expired or gone. It sets
	// b.Account and bumps b.Rev.
	Adopt(ctx context.Context, b *Mailbox, account string, key [32]byte) error
	// Leave takes mailbox b, as read, out of its account, or returns ErrStale.
	// It clears b.Account and bumps b.Rev.
	Leave(ctx context.Context, b *Mailbox) error

	// Count returns the number of live messages in a mailbox, from memory: the
	// mailbox is loaded on its first use, and answered from memory after that.
	Count(ctx context.Context, mailbox protocol.Address) (n int, err error)
	// After returns how many live messages a mailbox holds past a list position,
	// from memory as Count does: what a list from that cursor would return.
	After(ctx context.Context, mailbox protocol.Address, c *Cursor) (int, error)
	// Holds reports whether a mailbox holds a live message with this id, from
	// memory as Count does. False is certain; true may be another message
	// whose id starts alike, so the caller still reads it.
	Holds(ctx context.Context, mailbox protocol.Address, id [32]byte) (bool, error)
	// Fetched is Holds for a fetch: it also reports whether this process has
	// served the message before, and counts this serving. The count lives
	// with the message in memory, so a restart forgets it.
	Fetched(ctx context.Context, mailbox protocol.Address, id [32]byte) (held, again bool, err error)
}

Store is the server's state: mailbox records, message headers and bodies, and what a poll needs of them in memory. Its one implementation is Memory, over a Backend: DynamoDB and S3 Express (NewDynamoStore), or maps in this process (NewMemStore, the dev server).

Jump to

Keyboard shortcuts

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