The norms and papers behind the mechanisms¶
Docket implements a small number of well-known mechanisms, and a reader deserves to know which standard each one follows — not just which file it lives in. This page maps each mechanism to the normative document or the original publication it comes from, says how strong that source is, and states where Docket follows it exactly, where it implements a smaller subset, and where it deliberately departs.
It exists for the same reason docs/production-coverage.md exists: a
mechanism named without a source is a claim about someone else's idea. The difference is direction —
that map asks "is the mechanism here?", this one asks "is it the mechanism the standard describes?"
How the strength column reads. primary, read means the normative text was read while writing this page (RFCs and the one web page below are freely readable). primary, not read here means the document is named and its content is well established, but the full text (a paywalled paper, a book, a PDF) was not read in this environment; the cell says only what the abstract, DOI record or a reliable summary states. practitioner canon means it is the source that introduced a practice and is cited as such throughout the industry, without being a formal standard. Nothing here is a substitute for the source: where a cell matters to you, follow the link.
| mechanism | source | strength | what it establishes, and where Docket stands |
|---|---|---|---|
| Hash chain per event, receipt committing to the head | Lamport, Time, Clocks, and the Ordering of Events in a Distributed System, CACM 21(7), 1978; Crosby & Wallach, Efficient Data Structures for Tamper-Evident Logging, USENIX Security 2009 | primary, not read here | Ordering is defined by causality (each event names its predecessor), not by a clock, and a hash chain makes past records detectable-if-changed rather than merely readable. Docket follows this: the chain is the order, the receipt commits to the head, and docket verify recomputes both. What it does not claim is the stronger property the word "tamper-evident" invites — a chain local to one file proves internal consistency, not that a copy was never rolled back, and docs/threat-model.md says so |
| Canonical JSON with integers only, in everything hashed | RFC 8785 (JSON Canonicalization Scheme); RFC 8259 §6 (JSON numbers) | primary, not read here | RFC 8785 defines a canonical form so two implementations hash the same bytes; RFC 8259 §6 warns that fractional numbers are interoperable-ish by courtesy, not by specification. Docket follows the goal and departs on the numbers: it refuses floating point in hashed structures entirely and carries money as a decimal string (ADR 0002), because a format that a JSON implementation may round is not a format a stranger can reproduce |
| Merkle inclusion proofs exposed to verifiers | RFC 9162 §2.1.3.2 (Certificate Transparency v2.0) | primary, not read here | Defines the inclusion proof a log hands a verifier, and the tree construction it must use. Docket's proofs are computed for the receipt's usage rows against the same construction principles, but no cell here claims CT compatibility: the receipt format is its own (docket/receipt/v1) and the proof is over Docket's leaf encoding, not RFC 6962/9162's |
429 plus Retry-After when a rate limit refuses a call |
RFC 6585 §4 (429 Too Many Requests); RFC 9110 §10.2.3 (Retry-After) | primary, §4 read in full (rfc-editor.org, 2026-10-15); RFC 9110 §10.2.3 not read here — its two forms via MDN's Retry-After page (modified 2025-11-21), which cites the section | §4 defines 429 as "the user has sent too many requests in a given amount of time (\"rate limiting\")", says the representation SHOULD explain the condition and MAY carry Retry-After, and closes with the sentence that settles a question two secondary summaries answered in opposite directions: "Responses with the 429 status code MUST NOT be stored by a cache." Docket's boundary answers a throttled call with 429 and a seconds-valued Retry-After, records the refusal as an event, and stores only 2xx in its cache — a rule now traceable to §4 rather than to taste, and pinned by a test that a 429 is refused with a reason. It departs on one half: an HTTP-date Retry-After is ignored, because deciding "is that in the future?" requires comparing a remote clock to a local one, and a skewed pair turns a wait into either a hot loop or an hour's stall. The comment in resilience.py records that reasoning where a reader will find it |
| Token-bucket rate limiting | RFC 2697 / RFC 2698 (single- and two-rate token buckets) | primary, not read here | The token bucket is the standard traffic-shaping algorithm: a bucket refilling at a fixed rate, each request consuming a token, excess refused rather than queued. Docket's RateLimiter is a token bucket with a per-second and a per-minute ceiling, and pacing is max(next_call + pace, now) — the schedule can never run ahead of the clock |
| Circuit breaker with a half-open probe | Nygard, Release It! (2007; 2nd ed. 2018) | practitioner canon | Names and defines the pattern: closed → open on sustained failure → half-open to test recovery → closed. Docket implements that state machine, and two details its tests pin are the ones naive implementations get wrong: the threshold comparison must be strict, and an open breaker must not block a cache hit (the hit never reaches the breaker's protected resource) |
| Retry with exponential backoff and jitter | Brooker, Exponential Backoff And Jitter, AWS Architecture Blog, March 2015 | practitioner canon | Quantifies why backoff without jitter synchronises a fleet into a second wave (the retries arrive together, later and no smaller), and why "full jitter" spreads them best. Docket does not retry on the user's behalf; its reference agent reproduces the storm (retry, backoff modes) so a game day can show what it does to a run's spend and evidence — and the recommendation is cited on docs/resilience.md rather than silently applied |
| Cache storing rules: only a whole, non-injected 2xx | RFC 9111 §3 (Storing Responses in Caches), §4.1 (cache keys and Vary) |
primary, not read here | An HTTP cache may store far more than this — heuristic freshness, stale serving with validators, partial content, and keys negotiated by Vary. Docket implements a strict subset: the key is an exact hash of the request as sent (no coalescing, no field negotiation), only a whole 2xx response is eligible, and anything truncated, injected or streaming is refused. The RFC defines the space; this cell is the boundary of what is used from it |
| Percentiles, not averages, for latency | Tene, How NOT to Measure Latency (talk); the HdrHistogram project (github.com/HdrHistogram/HdrHistogram) | practitioner canon | Why the mean hides the tail, why you cannot average percentiles, and why the measurement instrument must be stated. Docket reports nearest-rank p50/p95/p99 over raw client samples and records the host and the shape of the run beside them (docs/measurements/2026-10-15-2core/) |
| Tail latency as a first-class number | Dean & Barroso, The Tail at Scale, CACM 56(2), Feb 2013 | primary, not read here (abstract and multiple summaries) | The fan-out arithmetic that makes the tail the user's problem: a 1-in-100 slow server slows 63% of requests that fan out to 100, and hedged requests cut p99.9 in their BigTable benchmark by an order of magnitude for ~2% more load. Docket measures the tail and does not hedge — it is a boundary in front of one agent, not a fan-out service, and a recorder that silently duplicated calls would be lying about the spend |
| Consistency over availability when a writer conflicts | Brewer, CAP Twelve Years Later, IEEE Computer 45(2), 2012; Gilbert & Lynch, Brewer's Conjecture…, SIGACT News 33(2), 2002 | primary, not read here (the article's content via its publisher's summary and two independent descriptions); the formal proof likewise | Under a partition you choose consistency or availability; Brewer's retrospective adds that the choice is per-operation, not per-system. Docket's evidence path is a single-writer file: the second writer is refused (LedgerLockedError) rather than allowed to diverge, which is the consistency side of the trade, made explicit in ADR 0005 |
| Compensation rather than distributed transactions | Garcia-Molina & Salem, Sagas, SIGMOD Record 16(3), 1987 (DOI 10.1145/38714.38742) | primary, not read here | A long-lived action is a sequence of local steps, and a failure is answered by compensating actions — which the paper is explicit are not an undo, and can themselves fail. That is why docket restore refuses a path outside the snapshot, refuses /proc, and writes its own record instead of editing the run's records: a compensation that cannot be inspected is a compensation nobody can trust |
| Local file lock with no distributed pretension | Burrows, The Chubby lock service, OSDI 2006 (sequence numbers); Kleppmann, How to do distributed locking, 2016 (fencing tokens) | primary, not read here | The literature's answer to "how do I know a stale holder is not still writing?" is a monotonically increasing token the resource checks. Docket's lock is flock on one file, guarding one writer on one machine; there is no distributed resource to fence, so no token is issued, and the documentation claims a single-writer guard and nothing more |
| Wall clock for readers, monotonic clock for decisions | Lamport 1978 (above); Spanner: Google's Globally-Distributed Database, OSDI 2012 (TrueTime) | primary, not read here | Either you order by causality, or you pay for bounded clock uncertainty. Docket takes the first route: RFC 3339 timestamps are recorded for a human to compare, ordering never depends on them, and every cap, duration and backoff is measured on the monotonic clock, with a step backwards clamped |
| Chaos experiments from a steady-state hypothesis | Principles of Chaos Engineering, 2019 revision (principlesofchaos.org) | primary, read in full (the whole page) | Four steps — define steady state, hypothesise it continues, introduce real-world variables, try to disprove the hypothesis — plus five principles, including minimise blast radius and run experiments in production. docket game-day implements the four steps literally (each phase declares its expectation before running and is then audited against the ledger, the receipt and the disk). It departs on the fifth: nothing here experiments on production traffic, because Docket is a tool you run, not a service this project operates, and a claim to be doing Chaos Engineering "in production" would be false |
| Liveness and readiness as separate signals | Kubernetes documentation, Configure Liveness, Readiness and Startup Probes (kubernetes.io/docs/tasks/configure-pod-container) | primary, not read here | A readiness failure removes a pod from Service endpoints; a liveness failure restarts it. Docket serves /docket/healthz and /docket/readyz, where readiness reflects whether evidence can still be written (the volume above min_free_bytes), and the chart deliberately has no Service — nothing outside the pod should reach a live run — so readiness is for a sidecar or a kubelet, which the deployment page states |
Values from YAML are typed, and Helm's type is float64 |
helm/helm#11130 (Setting large int64 values in values.yaml converts them to float64, 2022); TOML v1.0.0 §Float | primary, read (the issue and the spec); reproduced here with Helm v3.16.3 | Helm parses values.yaml through YAML → JSON → map[string]any, so a template receives a float64 and Go prints a large integer as 1.048576e+07; TOML reads that token as a float. This is the defect the deploy checker now renders faithfully and refuses by name — see the post-mortem in deploy/README.md, and tools/helm_fidelity.py, which compares the checker's render against real Helm's |
| Versioning the tool and the format separately | Semantic Versioning 2.0.0 (semver.org/spec/v2.0.0.html) | primary, not read here | The tool follows SemVer; the receipt format is versioned by its tag (docket/receipt/v1) and changes only under the process in CONTRIBUTING.md. Two clocks, because a compatible field addition to the format and a breaking change to a CLI flag should not have to move together |
| Error budgets, SLOs and blameless postmortems | Beyer et al., Site Reliability Engineering, O'Reilly 2016 (sre.google/sre-book) | practitioner canon | The definitions of SLO/SLI/error budget, the overload-handling recipes, and the postmortem culture that treats a defect as a property of the system rather than of a person. Docket's postmortems (docs/testing.md, the game-day reports, docs/incident-corpus.md) follow that shape; the SLO vocabulary is not imposed on users, because a run here is a bounded unit of work with a receipt, not a service with a monthly budget |
| The risk family this project addresses | OWASP Top 10 for LLM Applications — LLM10:2025 Unbounded Consumption (genai.owasp.org) | primary, read | Names the failure mode — resource consumption driven without bound, including financial — that a cap at the boundary answers directly. docs/evidence.md and docs/incident-corpus.md carry the incident record and its confidence grades; this page does not restate it |
Where Docket departs, collected in one place¶
Departures are the interesting cells, so they are also listed together: an HTTP-date Retry-After is
ignored (clock comparison); the cache implements a much smaller subset of RFC 9111 than the RFC
allows; floats are refused in hashed structures where RFC 8785 would canonicalise them; nothing is
hedged, so tail latency is measured rather than mitigated; no fencing token is issued because there is
no distributed resource to fence; no chaos experiment runs against production traffic; and the
Retry-After-style waiting advice in the backoff literature is documented for users rather than
applied silently by the boundary. Each departure has a reason in the code or a document beside it —
that is the standard this page is held to, not agreement with every source.