1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Conditional reads: an `ETag` on every administrative projection, and the
//! `304` that lets a poller ask again for free.
//!
//! The administrative read surface is polled — by `axond admin`, by a
//! reconciliation loop watching for a revision it published to converge, and by
//! whatever an operator points at a dashboard. Each of those reads costs a
//! control-plane round trip and a complete desired state hydrated into memory,
//! and almost all of them answer exactly what the previous one did. A validator
//! turns the repeat into a header comparison.
//!
//! # What the validator is
//!
//! A strong `ETag` over the bytes this surface would send: the checksum of the
//! serialized projection. Not the revision id, which is the tempting shortcut and
//! the wrong one — [`convergence`](super::handlers) has no revision of its own to
//! name, a projection's *shape* changes when this build changes even though the
//! revision did not, and a scope-narrowed read of one revision is a different
//! answer to a different caller. Hashing what is about to be written cannot
//! disagree with what is about to be written.
//!
//! A validator is therefore not a name for a revision and must not be parsed as
//! one: it is opaque, and its only operations are equality and echoing it back.
//! It also discloses nothing a caller could not already read — it is a digest of
//! the response body that caller is authorized to receive.
//!
//! # The one projection that cannot be validated by its bytes
//!
//! `/convergence` reports how long this replica has been behind, so while it *is*
//! behind its bytes differ on every read and a digest of them could never match —
//! and a reconciler waiting for a publication to take effect is exactly the
//! caller this is for. That read is therefore validated over the state it
//! describes — everything but the growing `lag_ms`: revisions, generation,
//! source, last convergence duration, failures, the rejection reason — and
//! answers a **weak** validator, `W/"…"`, which is the honest label for "the same
//! state, not necessarily the same bytes": a `304` there may withhold a body
//! whose reported lag has moved on. `If-None-Match` is compared weakly
//! anyway ([RFC 9110 §13.1.2][inm]), so a caller needs no special handling.
//!
//! [inm]: https://www.rfc-editor.org/rfc/rfc9110#name-if-none-match
//!
//! # What an unreadable `If-None-Match` does
//!
//! Nothing. A conditional this surface cannot parse is treated as absent and
//! answered in full: refusing would let a mangled header deny an operator the
//! state read they are diagnosing an incident with, and matching would serve a
//! `304` against a validator nobody issued. `*` is honoured — it matches any
//! current representation, and a read that answers at all has one.
//!
//! # What a cache may do with one
//!
//! Nothing but revalidate. These responses are per-caller — a scope-narrowed
//! grant reads a narrower projection of the same revision — so every one carries
//! `Cache-Control: private, no-cache` and `Vary: Authorization` beside the
//! validator. [RFC 9111][store] already forbids a shared cache from reusing a
//! response to an `Authorization`-bearing request without explicit permission,
//! but an administrator's state projection is not a thing to leave to an
//! intermediary's conformance: the directives say it outright, and `no-cache`
//! still lets the caller keep the representation it holds and revalidate with the
//! validator, which is the whole point.
//!
//! [store]: https://www.rfc-editor.org/rfc/rfc9111#name-storing-responses-to-authen
use Body;
use ;
use ;
use ;
use Serialize;
use crateChecksum;
/// A projection, answered conditionally.
///
/// Holds the caller's validators rather than reading them from a task-local or a
/// request extension, so a handler that forgets them cannot silently answer
/// unconditionally: the type has no constructor that does not take them.