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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//! **What a cap on each meter actually DOES on this box** — one table, and the
//! only place that answer is written down.
//!
//! # The defect this table exists to close
//!
//! `Usage.Read` has reported seven caps to the tenant since 2026-09-05 and three
//! of them are a door. The other four were reported in the same message, in the
//! same shape, under a field called `Allowance`:
//!
//! > *"If a signed fact carries `caps["repos"]=5`, the console shows the tenant
//! > a limit of 5 and nothing on the box refuses the sixth."*
//!
//! Two surfaces were wrong at once, and they are the two halves of one bug:
//!
//! 1. `Usage.Read` showed a MEASURED number as if it were a wall. Fixed by
//! [`enforcement`], which puts monetize's own word beside every cap.
//! 2. `Entitlement.Set` accepted a fact carrying a cap on a meter gunnar does
//! not even REPORT, and then carried it silently forever. Fixed by
//! [`caps_gunnar_cannot_honour`], which refuses the fact by name.
//!
//! # Why the vocabulary is monetize's and not a new one
//!
//! `monetize_product::Enforcement` already models exactly this distinction —
//! `Enforced` / `Measured` / `Provisioned`, with `Enforcement::is_a_wall()` and a
//! console that renders the three differently — and `products/gunnar`'s `METERS`
//! already carries a row per meter. Coining a second spelling on this side
//! (`is_a_wall`, `hard`/`soft`, a list of enforced names) would have put two
//! vocabularies on the one seam that reports gunnar's numbers to monetize's
//! console, and the day they disagreed there would have been no way to tell
//! which one was stale. The rule: reuse the sibling crates, including their words.
//!
//! # Why gunnar keeps its own copy of the table at all, then
//!
//! Because this one is about the CODE IN THIS BINARY and monetize's is about
//! what may be SOLD, and they are allowed to disagree — in fact they do, and
//! finding that is what this table is for (see [`caps_gunnar_cannot_honour`]).
//! gunnar cannot link `monetize-product`: `gunnar-grpc` compiles on the
//! community appliance, which contains no monetize code at all. A table that
//! says "this build refuses on these three" must live in the build it describes.
use Enforcement;
/// **Every meter `Allowance` has a field for, and what a cap on it does here.**
///
/// The order is `Allowance`'s field order, which is `NamespaceUsage`'s, which is
/// the order a console renders. Each `Enforced` row names the door in the
/// comment above it; a row may only claim `Enforced` when a REFUSAL exists on
/// this box, and `tests/allowance_is_labelled.rs` pins the enforced set by name
/// so that promoting a row is a deliberate act with a test to change.
pub const METERS: & = &;
/// **The FLOW meters, and what a cap on one of them would do here: nothing at
/// all, because there is no field to put one in.**
///
/// `Usage.Read` has reported these seven since the flow ledger landed
/// (`gunnar_core::flow`) and every one of them is a count of something that
/// HAPPENED — bytes off the wire, sessions, refusals, a timestamp. They are the
/// answer to *is this tenant using the product*, which no meter in [`METERS`]
/// can give: every one of those is a STOCK meter and reads the same for a
/// tenant who pushed this morning and a tenant who last touched this box in
/// March.
///
/// # Why they are a second table and not seven more rows above
///
/// [`METERS`] is defined as *"every meter `Allowance` has a field for"*, and
/// these have none. That is not an oversight to be corrected later: a cap on a
/// count of pushes is a promise nothing on this box could keep, `Allowance`
/// mirrors `NamespaceUsage`'s field numbers 2–8 by construction, and adding an
/// eighth cap field for a meter no door reads would be exactly the defect
/// [`METERS`]'s own doc comment exists to describe — a MEASURED number reported
/// in a message called `Allowance`.
///
/// The consequence is deliberate and is the correct one: because they are in
/// neither [`METERS`] nor [`PROVISIONED_ELSEWHERE`],
/// [`caps_gunnar_cannot_honour`] REFUSES a signed fact that tries to cap one,
/// by name, at the moment of the bad sale.
///
/// # `Measured` is not a stage on the way to `Enforced`
///
/// Every row here is `Enforcement::Measured` and is expected to stay that way.
/// A row may only claim `Enforced` when a REFUSAL exists on this box; there is
/// none for any of these, and building one would mean deciding to cut a tenant
/// off mid-month for having fetched too often — a door nobody has asked for and
/// which this table is not the place to invent.
pub const ACTIVITY_METERS: & = &;
/// **Cap keys gunnar neither enforces nor reports, but which a plan may legally
/// sell** — the provider's iron.
///
/// `monetize_product::Enforcement::Provisioned` is the honest name for these:
/// CPU and RAM are bought with real money at the cloud provider, they change
/// what the whole appliance can do, and no git server polices them per
/// namespace. They are `Scope::Product` on monetize's side — the box owns the
/// cores, not the tenant — so they have no `Allowance` field and never will.
///
/// They are listed here for one reason: [`caps_gunnar_cannot_honour`] must not
/// refuse a fact for carrying them. A cap gunnar deliberately leaves to the
/// provider's invoice is not a broken promise; a cap gunnar has simply never
/// heard of is.
pub const PROVISIONED_ELSEWHERE: & = &;
/// The word for one meter, or `None` for a name that is not one of
/// [`METERS`].
/// **The whole table, as `Allowance.enforcement` carries it**: all seven
/// meters, every read, capped or not.
///
/// Built rather than kept as a constant because prost's map field is a
/// `HashMap<String, i32>` and a `const` cannot be one. Seven entries of a short
/// `&'static str` and an `i32`, once per `Usage.Read` — the read walks every
/// repository in the namespace off the disk immediately afterwards, so this is
/// not the cost on that path.
/// **Cap keys in a signed fact that this appliance can neither refuse on nor
/// report** — the reason `Entitlement.Set` turns a fact away, sorted, or empty
/// when the fact is honourable.
///
/// # What is NOT here, deliberately
///
/// A cap on a `Measured` meter of [`METERS`] is fine and is accepted. `repos`,
/// `cold_bytes`, `tombstoned_bytes` and `objects` are real numbers that
/// `Usage.Read` reports and monetize bills from; selling one is selling a
/// measurement, which is honest as long as it is LABELLED as one — and since
/// `Allowance.enforcement` it is. Refusing them would refuse an honest sale.
///
/// So is a cap on [`PROVISIONED_ELSEWHERE`]: iron bought at the provider is not
/// gunnar's to police and never was.
///
/// # What IS here, and why refusing is the right answer
///
/// A key that is in neither list is a number that reaches NO surface on this
/// box. It is not enforced, it is not reported by `Usage.Read`, it does not
/// appear in `Admin.StorageInfo`, and the only trace it leaves is the
/// attestation line that recorded the sale. The operator finds out three months
/// later from a tenant who noticed, or never. `INVALID_ARGUMENT` naming the key
/// puts that discovery at the moment of the bad sale, which is the only moment
/// anyone can act on it.
///
/// **Two real keys land here today**, and both are a disagreement worth being
/// loud about rather than a hypothetical typo:
///
/// * `concurrent_transfers` — `products/gunnar`'s `METERS` declares it
/// `Enforcement::Enforced`. Nothing in gunnar reads it.
/// `SshConfig::max_concurrent_transfers` is an OPERATOR flag, one number for
/// the whole box, and it has never been per namespace or read off a fact.
/// monetize claims a wall gunnar does not have.
/// * `open_store_ram_bytes` — monetize's name for the quantity gunnar reports
/// as `objects` (`products/gunnar`'s own conversion is
/// `meters["open_store_ram_bytes"] / RAM_BYTES_PER_OBJECT`). Two names for one
/// number across one seam, so a cap written under monetize's name is invisible
/// to `allowance_of`, which looks up `caps["objects"]` — a key monetize never
/// writes. The `objects` allowance is unreachable from any fact monetize can
/// issue, and this refusal is what makes that say so out loud.
///
/// # The blast radius, measured before this landed
///
/// A refusal here CANNOT demote a tenant that is already paid up. It is reached
/// only from the `Entitlement.Set` handler; the boot path
/// (`gunnar_server::entitlement::EntitlementSlot::open`) pushes stored facts
/// straight into the cache and runs no shape check at all, so a restart re-loads
/// every stored fact exactly as before. And `INVALID_ARGUMENT` means the fact is
/// not stored, which leaves the PREVIOUS fact in force — the tenant keeps the
/// plan they have until it expires, and the operator has until then to correct
/// the order. Nothing in gunnar re-pushes a stored fact through `Set`, so no
/// internal loop can be caught by this either.
/// Every cap key this appliance accepts, for the refusal's own message: an
/// operator told "`concurrent_transfers` is not a cap this appliance can
/// honour" and nothing else has to go and read a proto file to find out what is.