Documentation
¶
Overview ¶
Package server is dromasd's HTTP API (PROTOCOL.md §11, the status map in §11.3) over a Store.
Index ¶
- Constants
- Variables
- func AWSLogger(w io.Writer) logging.Logger
- func HTTPServer(addr string, h http.Handler, l Limits, idle time.Duration) *http.Server
- func IsThrottle(err error) bool
- func NewAccountID() (string, error)
- func NewAdoptKey(account string) (string, [32]byte, error)
- func Redacting(w io.Writer) io.Writer
- func SiteBackend(raw string) (*url.URL, error)
- type Account
- type AdoptKey
- type Backend
- type Box
- type Change
- type ChangeKind
- type Config
- type Cursor
- type DynamoConfig
- type Expect
- type Header
- type Indebted
- type Limits
- type Memory
- func (s *Memory) Account(ctx context.Context, id string) (*Account, error)
- func (s *Memory) Adopt(ctx context.Context, b *Box, account string, key [32]byte) error
- func (s *Memory) AdoptKey(ctx context.Context, account string, hash [32]byte) (*AdoptKey, error)
- func (s *Memory) After(ctx context.Context, box protocol.Address, c *Cursor) (int, error)
- func (s *Memory) Begin(a protocol.Address) uint64
- func (s *Memory) Box(ctx context.Context, a protocol.Address) (*Box, error)
- func (s *Memory) Changed(a protocol.Address) <-chan struct{}
- func (s *Memory) Count(ctx context.Context, box protocol.Address) (int, error)
- func (s *Memory) CreateAccount(ctx context.Context, a *Account) error
- func (s *Memory) CreateBox(ctx context.Context, b *Box) error
- func (s *Memory) Dedupe(ctx context.Context, box protocol.Address, id [32]byte) (*Header, error)
- func (s *Memory) DeleteBox(ctx context.Context, a protocol.Address) error
- func (s *Memory) DeleteMessage(ctx context.Context, box protocol.Address, id [32]byte) error
- func (s *Memory) DeleteMessages(ctx context.Context, box protocol.Address) error
- func (s *Memory) Fetched(ctx context.Context, box protocol.Address, id [32]byte) (held, again bool, err error)
- func (s *Memory) Held(a protocol.Address) (Standing, bool)
- func (s *Memory) Holds(ctx context.Context, box protocol.Address, id [32]byte) (bool, error)
- func (s *Memory) Known(a protocol.Address) (*Box, bool)
- func (s *Memory) Leave(ctx context.Context, b *Box) error
- func (s *Memory) List(ctx context.Context, box protocol.Address, after *Cursor, limit int) ([]Header, error)
- func (s *Memory) Message(ctx context.Context, box protocol.Address, id [32]byte, from int64) (*Header, []byte, error)
- func (s *Memory) PutAdoptKey(ctx context.Context, k *AdoptKey) error
- func (s *Memory) PutMessage(ctx context.Context, box protocol.Address, h *Header, body []byte) (*Header, error)
- func (s *Memory) Ready() bool
- func (s *Memory) Revoke(ctx context.Context, a protocol.Address, rv *Revocations, want Expect) error
- func (s *Memory) Start(ctx context.Context) error
- func (s *Memory) UpdateBox(ctx context.Context, b *Box) error
- type Metrics
- type Retired
- type Revocations
- type Server
- type Standing
- type State
- type Store
Constants ¶
const DefaultBoxCache = 10000
DefaultBoxCache is how many box records memory holds by default, as many account records, and as many boxes remembered as missing.
const ReceiveCeiling = time.Second / 10
ReceiveCeiling is the shortest refill of any box's budget of new mail: no box takes new mail faster than one each, ten a second, whatever its account lifts, and dromasd serve does not start with flags that would (decided). A box'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 ¶
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 ¶
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 ¶
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 ¶
IsThrottle reports whether err is AWS refusing for capacity: a throttle, or the SDK's own retry quota running out.
func NewAccountID ¶
NewAccountID returns a minted account id: 16 random bytes, in hex.
func NewAdoptKey ¶
NewAdoptKey returns a new adopt key into account, and the hash it is kept by.
Types ¶
type Account ¶
Account is an account's record (PROTOCOL.md §11.16). An account pays, from prepaid credit, for its boxes' sends past the free limits and for size over the cap; with no credit they have the free limits. An unmetered account's boxes have no daily limit, the largest cap and new mail at ReceiveCeiling, and are never charged: the echo account. Credit is not built yet, so a metered account's boxes have the free limits.
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 box's record and its identity's revocations, each nil if absent.
ReadAddress(ctx context.Context, a protocol.Address) (*Box, *Revocations, error)
// ReadLive reads a box's live messages, strongly consistent: what a
// box's load counts (Memory.readBox).
ReadLive(ctx context.Context, box protocol.Address) ([]Header, error)
// List returns up to limit live headers, oldest first by (ReceivedAt,
// ID), after the given key (nil: from the start).
List(ctx context.Context, box protocol.Address, after *Cursor, limit int) ([]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, box protocol.Address, id [32]byte, from int64) (*Header, []byte, error)
// Dedupe returns the header of the live message box holds with this
// id, or ErrNotFound.
Dedupe(ctx context.Context, box 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)
// CreateBox stores b's record, at b.Rev, if the address has none;
// otherwise ErrExists.
CreateBox(ctx context.Context, b *Box) error
// UpdateBox 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.
UpdateBox(ctx context.Context, b *Box) error
// DeleteBox deletes a's record, and keeps its revocations.
DeleteBox(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 box 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 *Box, account string, key [32]byte) error
// Leave takes box b, at b.Rev, out of its account, or returns ErrStale.
Leave(ctx context.Context, b *Box) 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, box 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, box protocol.Address, id [32]byte) (time.Time, error)
// DeleteMessage acks a message: its body goes, then it is retired.
DeleteMessage(ctx context.Context, box protocol.Address, id [32]byte) (time.Time, error)
// DeleteAll deletes, as acks do, every live message in a box, and hands
// each retired as its item is deleted. A message already gone is no
// error.
DeleteAll(ctx context.Context, box 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 box'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 Box ¶
type Box 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 box'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 box's
// statements are ordered by this serial first, and by the prekey's own
// second.
CertSerial uint64
Cert [32]byte
// SenderKeyTypes is what the box declared with its current prekey: the
// identity key types of the senders it takes mail from, or
// protocol.AnySender (PROTOCOL.md §7). Only names this build verifies
// are kept (declared): a box 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 box is valid any more. Zero: never.
Expires time.Time
// Account is the account the box 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 box 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 box's sender ACL (PROTOCOL.md §11.14): who may send to it,
// deny read first and then allow. nil until the box puts one, and a box
// with none takes mail from everyone. It is replaced whole, and never
// removed.
ACL *protocol.ACL
}
Box is what a server holds of an address: its box'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 box. A revocation of the address is the State whatever the record says.
type Change ¶
type Change struct {
Kind ChangeKind
// Addr is the record's address, the revocations', or the message's box.
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
Box *Box // 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 box'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 DynamoConfig ¶
type DynamoConfig struct {
AWS aws.Config
BoxesTable, MsgsTable, AccountsTable, Bucket string
Now func() time.Time // time.Now if nil
// BoxCache is how many box records memory holds, as many account
// records, and as many boxes remembered as missing; 0: none.
BoxCache 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 box's
// load and a read of what is held of an address, since a stale copy
// there would decide later answers: 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 box'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 box_changed. It is the write's own condition, beside any other it has, and asks nothing of a request that carried neither.
type Header ¶
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
BoxCap int // total per box, 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 box 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)
HoldMax time.Duration // the longest a list is held, whatever its wait asks (PROTOCOL.md §11.11) [tunable]
Holds 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 box, a number a day as SendsPerDay (decided: 10), so no box can spend the boxes table's write capacity
RetiredKept int // retired prekeys a box 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 boxes table that find no box, 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 box: 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 box: 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 box: its calls with its pass that no other budget counts [tunable]; an account lifts it
CallRefill time.Duration // [tunable]
CertificatesPerDay int // per box, a number a day: renewals under a later certificate, but for one that replaces a revoked certificate (decided: 10, as renewals)
CertificateRevocationsPerDay int // per box, 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 box's ACL holds, its two lists
// together [tunable]: a box's record, with its ACL, is held in the box
// cache, so this bounds what one record costs memory there. Never off,
// and at most protocol.MaxACLEntries: dromasd 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 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 box on its first use, and bounded caches of box and account records serve sends and reads of a box (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 ¶
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 ¶
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) AdoptKey ¶
AdoptKey implements Store: a strongly consistent read, so a key just made is found.
func (*Memory) Box ¶
Box 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 box'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) Changed ¶
Changed returns a channel closed at box a's next change in memory (Store.Changed).
func (*Memory) CreateAccount ¶
CreateAccount implements Store.
func (*Memory) Dedupe ¶
Dedupe implements Store: the live message's item. A stale miss is caught by PutMessage's conditional write.
func (*Memory) DeleteMessage ¶
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 ¶
DeleteMessages implements Store (a revocation): every live message in the box, as acks, each out of memory as its item is deleted.
func (*Memory) Fetched ¶
func (s *Memory) Fetched(ctx context.Context, box protocol.Address, id [32]byte) (held, again bool, err error)
Fetched implements Store, from memory as Holds does.
func (*Memory) Holds ¶
Holds implements Store: from memory, after the box's load on its first use. A message another process stored is held once the feed has brought it.
func (*Memory) Known ¶
Known implements Store: the record cache, or the memory of a box a read did not find.
func (*Memory) List ¶
func (s *Memory) List(ctx context.Context, box protocol.Address, after *Cursor, limit int) ([]Header, error)
List implements Store: a page read just after a write may miss it while memory's count has it; a client lists at every poll, so the next one has it.
func (*Memory) Message ¶
func (s *Memory) Message(ctx context.Context, box 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) PutAdoptKey ¶
PutAdoptKey implements Store. Memory holds no adopt key.
func (*Memory) PutMessage ¶
func (s *Memory) PutMessage(ctx context.Context, box 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) Revoke ¶
func (s *Memory) Revoke(ctx context.Context, a protocol.Address, rv *Revocations, want Expect) error
Revoke implements Store.
func (*Memory) Start ¶
Start follows the backend's feed: from then on every change it hands over is applied (apply). Memory starts empty, and a box 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.
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 (dromasd serve -metrics-listen), never the API's.
No label is a value a caller chooses: no address, IP, box, 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 ¶
ConnState counts the API listener's open connections, as http.Server's ConnState.
func (*Metrics) InstrumentAWS ¶
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 box's record, box 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 (*Server) AdminHandler ¶
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 box or an address>","full_in":<seconds>}]}, deepest in debt first (Top).
func (*Server) ClearAddress ¶
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) Handler ¶
Handler returns the API's routes, the page a box'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.
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 // Box.Cert
// CertSerial is Box.CertSerial, which Listed is judged by.
CertSerial uint64
// Listed is true while the box's current certificate is one its identity
// has revoked (PROTOCOL.md §10.2): no pass of the box is taken until it
// publishes a prekey under a new certificate.
Listed bool
// InAccount is true for a box in an account, whose limits are read from
// it; a box in none has the server's, with nothing to read.
InAccount bool
}
Standing is what checking a box'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 box's record is Active, the one state a record is written in or read back as; Revoked and NoBox are what the store answers from the address's revocations alone.
const ( // Active is a registered box. Active State = "active" // Revoked is an address its identity revoked. Its record is deleted, // and the revocation is kept for good. Revoked State = "revoked" // NoBox is an address with no box whose identity revoked certificates: // a later registration is held above them. NoBox State = "none" )
The states.
type Store ¶
type Store interface {
// Held returns the standing of a box memory holds, with no read. ok is
// false for a box this process has not loaded, which only its record
// answers for: Known, then Box (ARCHITECTURE.md § Memory).
Held(a protocol.Address) (st Standing, ok bool)
// Changed returns a channel closed at box 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{}
// Box returns what is held of an address (view): its box's record and
// its identity's revocations, or ErrNotFound if neither.
Box(ctx context.Context, a protocol.Address) (*Box, error)
// Known returns what memory holds of an address, with no read: what Box
// returns, or nil for an address it holds as missing. ok is false when
// only a read can tell, which is what Box then does.
Known(a protocol.Address) (b *Box, ok bool)
// Begin returns a token of address a, which only grows: what Box
// answers once it is taken holds every write this process applied to a
// before it.
Begin(a protocol.Address) uint64
// CreateBox stores a new box's record at Rev 1, or returns ErrExists if
// the address has one.
CreateBox(ctx context.Context, b *Box) error
// UpdateBox 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.
UpdateBox(ctx context.Context, b *Box) error
// DeleteBox deletes a's record, and keeps its revocations.
DeleteBox(ctx context.Context, a protocol.Address) error
// Revoke keeps what the identity of address a revoked, box 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 box holds with this
// id, or ErrNotFound: nothing is kept of one acked or expired.
Dedupe(ctx context.Context, box 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, box 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).
List(ctx context.Context, box protocol.Address, after *Cursor, limit int) ([]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, box 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, box protocol.Address, id [32]byte) error
// DeleteMessages deletes every live message in a box, as acks do (a
// revocation of its address). A message already gone is no error.
DeleteMessages(ctx context.Context, box 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 box 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 *Box, account string, key [32]byte) error
// Leave takes box b, as read, out of its account, or returns ErrStale.
// It clears b.Account and bumps b.Rev.
Leave(ctx context.Context, b *Box) error
// Count returns the number of live messages in a box, from memory: the
// box is loaded on its first use, and answered from memory after that.
Count(ctx context.Context, box protocol.Address) (n int, err error)
// After returns how many live messages a box holds past a list position,
// from memory as Count does: what a list from that cursor would return.
After(ctx context.Context, box protocol.Address, c *Cursor) (int, error)
// Holds reports whether a box 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, box 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, box protocol.Address, id [32]byte) (held, again bool, err error)
}
Store is the server's state: box 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).