Documentation
¶
Overview ¶
Package pull fetches a signed JSON document, checks its signature against keys it was handed, and keeps the last good copy on disk so that the next run has something to work with when the fetch does not go through.
THE SHAPE IT EXISTS FOR. A document this package serves changes rarely and is needed on every run; the source that publishes it is not always reachable. The whole point is that a caller can ask for the document on any run and get an answer: either a fresh one, or the last good one with an error saying why it is not fresh. The network is consulted only when the cache is older than the caller's patience, and every failure the source can throw — a dead host, a timeout, a bad signature, a rollback — falls back to the cache rather than leaving the caller empty.
WHAT MAKES A DOCUMENT GOOD is written down in [Puller.good] and nowhere else, because it is the one contract a reader can spot on its own: the document is JSON, an object, and carries an integer "version"; its ".sig" verifies under one of the keys the caller trusts; and its version is not lower than the one the cache already holds, because a copy of yesterday served today is the one attack a reader can catch without trusting the source. A good document is cached and returned; a bad one returns whatever the cache had, together with the error that says why the fresh one was refused.
THE CACHE IS NOT A TRUSTED STORE. Bytes in Puller.CacheDir that do not parse, or whose signature no longer verifies under Puller.Keys, are treated as if nothing were cached: no error of their own, no panic, and the next good fetch replaces them. The cache is a copy of a good fetch, never a source of truth in its own right.
Index ¶
Constants ¶
const ( // MaxDoc is the largest document Pull will read. A document larger than // this is refused rather than read to the end, because a source that answers // with an unbounded body is a source that can exhaust the reader. MaxDoc = 4 << 20 // 4 MiB // DefaultBudget is what a Pull with a zero Budget spends. It is short on // purpose: this package serves a document that is needed on the path of a // keystroke, and a caller who can afford to wait longer sets its own. DefaultBudget = 2 * time.Second )
Variables ¶
var ErrBadSignature = errors.New("pull: signature does not verify")
ErrBadSignature is a fetched document whose signature does not verify under any of the keys in hand. It is its own error because a caller deciding whether to look elsewhere for the same document has to tell a document that failed its check from a source that did not answer: a copy somewhere else cannot vouch for bytes whose signature just failed.
Functions ¶
func Verify ¶
Verify answers whether sig is a detached ed25519 signature over doc under ANY ONE of keys. sig is read as base64 text in the standard encoding; space around it is ignored. No keys, an unreadable base64, a signature of the wrong length or a signature nobody's key made is false. A caller who trusts nobody trusts nothing.
The key-length guard is here because ed25519.Verify panics on a wrong-length key, and a key the caller mis-hands is a misconfiguration, not a crash.
Types ¶
type Puller ¶
type Puller struct {
URL string
Keys []ed25519.PublicKey
CacheDir string
TTL time.Duration
Budget time.Duration // 0 means DefaultBudget
Client *http.Client // nil means a client the package makes
Now func() time.Time // nil means time.Now
}
Puller fetches one document from one URL, verifies it against one set of keys, and keeps the last good copy in one directory. The zero value is not usable: URL, Keys and CacheDir must be set before Pull is called.
Client and Now have nil defaults the package fills: a plain http.Client with no timeout of its own (the budget governs) and time.Now. A caller that wants deterministic clocks or a test transport sets both.