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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! **The appliance's wall clock**: glue between PID 1 and edda `estate-clock`.
//!
//! Every rule — the sources, the quorum, the sanity window, the signed-peer
//! protocol, step-at-boot and slew-while-running, the first fed syncs and the
//! running triggers — lives in edda's `estate-clock` crate (where Gunnar's own
//! `gunnar-clock` moved, lane L10 2026-10-21), which the front's gunnar-front
//! links too. This file decides only *whether* this process owns the clock,
//! *where* its feed is, and *what it says* about the answer.
//!
//! **Both wires during the move**: a box asks in `ESTATE-TIME/1` and asks again
//! in `GUNNAR-TIME/1` when an old front answers in Gunnar's dialect
//! (`estate_clock::peer::query`); `GUNNAR_CLOCK*` (the kit's
//! `korp.env.GUNNAR_CLOCK=`) is read as an alias of `ESTATE_CLOCK*`
//! (`estate_clock::config::var`).
//!
//! # What went wrong with the first cut (gunnar `5792dda`), measured
//!
//! The appliance booted two hours fast; `5792dda` asked three internet HTTPS
//! hosts for a `Date` at boot and was re-imaged on 2026-09-14 — and the box
//! still refused credentials `7199529 ms away`. Through the control plane, with
//! a credential deliberately signed 7 199 500 ms fast so the gate let it
//! through, `gunnar control mirror add … https://github.com/…` answered
//! `github.com could not be resolved: failed to lookup address information: Try
//! again`. UpCloud's firewall catch-all `drop` drops the replies to outbound
//! UDP (from the front: `dig +notcp` times out, `dig +tcp` answers), musl's
//! resolver asks over UDP only, so every source was unreachable, the verdict was
//! "no quorum", and the clock was left alone. The only record was a log line on a
//! VGA console. See the `estate-clock` crate note for the full measurement.
//!
//! # What it does now
//!
//! * **The source is configuration** (`korp.env.GUNNAR_CLOCK=` on the kernel
//! command line). On gunnar.rs that is `peer://10.13.8.99:50123` — the front,
//! by IP literal over the utility network, answering a nonce with a signed
//! timestamp (`GUNNAR_CLOCK_KEY` names its public key). No internet, no DNS.
//! Unset means **not configured**, and nothing is asked: an appliance in an
//! airgap has no business dialling Stripe for the time.
//! * **At boot**, after the network and before any listener: one sync, steps.
//! * **While running**, only when a credential is refused for skew (rate-limited
//! to one sync a minute) and on a 6-hour safety timer — `estate_clock::trigger`.
//! Small offsets slew, large ones step — `estate_clock::adjust`.
//! * **Every attempt is recorded** (`estate_clock::report`) and served on the
//! control plane: `Admin.Health` carries the boot and the last report, and
//! `Bootstrap.Status` — the one call that needs no credential — carries the
//! last one and this server's own clock. When the clock is wrong a credential
//! is exactly what gets refused, so the credential-free call is the one that
//! makes a wrong clock diagnosable from the front.
//!
//! # Why it can never stop the boot or the server
//!
//! Every function here returns `()` and swallows everything. A forge that
//! refuses to come up because a clock fetch failed is strictly worse than a
//! forge with a wrong clock: the wrong clock refuses credentials, the dead
//! forge refuses everything — including the call an operator uses to find out
//! why. The periodic sync runs on the blocking pool, never on a request path.
use ;
use FedRun;
use ;
pub use ;
/// Whether this process may set the machine's clock at all.
///
/// Gated exactly as `crate::disktest` and `crate::dashboard` are — PID 1 **and**
/// `korp.entry=` on the kernel command line. A container entrypoint is PID 1 of
/// its namespace too, and a privileged container shares the host's clock:
/// stepping it there would move somebody's real machine. Naming sources in
/// [`CLOCK_ENV`] (or its Gunnar alias `GUNNAR_CLOCK`) explicitly turns it on
/// regardless, which is what the installtest rig and a developer reproducing a
/// refusal need; the syscall still needs `CAP_SYS_TIME` and a refusal is still
/// only a report.
/// Say what an attempt concluded, at the level it deserves.
/// One attempt on the blocking pool; the report is recorded by `sync` itself.
async
/// **The boot call.** Sync once, stepping if the source says so.
pub async
/// Whether a clock is configured at all (the medium's or the feed's).
/// **The running triggers**: a credential refused for skew, and the 6-hour
/// safety timer (`estate_clock::trigger::arm` has the rules and why there are
/// only these, and arms them once per process). Called after the boot sync, and
/// by a fed run before its first pause. Does nothing on a box that does not
/// own its clock, or whose clock is not configured — a clock the installer's
/// door feeds later arms them then ([`fed_arrived`]).
///
/// Both run on plain threads, never on the runtime: the skew handler is started
/// by the credential gate and must not borrow a worker for a TCP exchange, and a
/// sync is blocking by nature.
/// One sync of the clock in force (the feed read now), logged.
/// This process's fed clock runs (`estate_clock::offer::FedRun`): the
/// triggers armed first, then syncs backing off while the front does not
/// answer; one run at a time, off the caller's thread; the tunnel coming up
/// cuts a pause short.
static FED: FedRun = new;
/// **The installer's door fed a clock source or key while this box runs**
/// (the pairing loop's first answer, `crate::feed`). Every sync already reads
/// the feed ([`crate::feed::clock_config`]), so this only makes the first one
/// happen NOW — a first boot whose medium carries no clock would otherwise
/// wait for the 6-hour timer, which was never armed — and arms the triggers.
///
/// ★ **Returns at once: the syncs run on their own thread** (cov-E r3
/// 2026-10-06, runs 2 and 4): called from the installer's FEED, a twin fed
/// before its tunnel was up once ran the whole backoff on the feed's thread
/// and for 219 s answered nobody. A clock re-fed meanwhile gets one more run
/// of its own when the one under way ends.
/// **This box's tunnel just came up** (the pairing loop's `TunnelUp`).
///
/// The fed clock source is the front's utility address, reached through
/// this tunnel on a box whose fed syncs ran before it was up (i54a run23:
/// the twin's three syncs timed out, then it paused 20 s while its tunnel
/// came up and the front's `BecomeTwin` was refused for skew seven times).
/// So: a clock not yet set right is asked NOW — the pause under way is cut
/// short, and a run is started if none is. Once per process; nothing when
/// the clock is already right.