Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_parcelbudget.mjs

9.9 KiB, 1 run

created by r2519314175:565, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// verify_parcelbudget.mjs — the sync parcel's ceiling is set by the door it has to
2// go through, and the sections spent against it add up to no more than it.
3//
4// WHY THIS FILE EXISTS. `SYNC_PARCEL_MAX` was 10 MiB, and the comment above it
5// derived that from the GATEWAY's 16 MiB body cap. Steel terminates TLS in front of
6// the gateway on jarrah and caps a body at 8 MiB, so the client could compose a
7// parcel the front door refuses — and the comment, being a piece of reasoning from
8// the wrong authority, would have talked the next reader into putting the number
9// back. A value nobody can derive is a value somebody will restore.
10//
11// Three things are asked, and each is asked of the source rather than of a copy:
12//
13// 1. THE ARITHMETIC. What travels is base64 of the sealed parcel inside a JSON
14// envelope, so the body is 4 bytes for every 3 of parcel plus the AES-GCM IV
15// and tag. `SYNC_PARCEL_MAX` inflated that way must fit under Steel's door.
16// 2. THE SPLIT. `collectSync` hands the Diamonds
17// `min(SYNC_DIAMONDS_MAX, SYNC_PARCEL_MAX - files)`. A Diamonds cap at or above
18// the parcel can never be the smaller of those two, so it never binds and reads
19// as a guarantee it does not make.
20// 3. THE AUTHORITY. The comment on the constant must name Steel. This is the one
21// check here that is about prose, and it is the one that stops the regression
22// this file was written for.
23// 4. AND WHAT DID NOT FIT IS NAMED. Not arithmetic at all, and it is here because
24// it is what the arithmetic was being held hostage to: while a file dropped for
25// budget was named to nobody, lowering the files budget would have made the sums
26// add up by making the failure silent. The names come first; the number after.
27//
28// How each goes red:
29//
30// * put `SYNC_PARCEL_MAX` back to `10 * 1024 * 1024` → check 1 goes red and
31// check 2 goes red with it (10 MiB of parcel is 13.4 MiB of body, and the
32// Diamonds' 3 MiB is then only a fifth of a parcel it was never measured
33// against);
34// * raise `SYNC_DIAMONDS_MAX` to `5 * 1024 * 1024` → check 2 alone goes red;
35// * delete the word Steel from the comment → check 3 alone goes red.
36//
37// node dev/verify_parcelbudget.mjs
38//
39// No browser, no gateway, no mock LLM: this reads source and does arithmetic.
40import fs from 'node:fs';
41import path from 'node:path';
42import { fileURLToPath } from 'node:url';
43
44const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
45const APP = fs.readFileSync(path.join(ROOT, 'www', 'js', 'daimond.js'), 'utf8');
46
47// Steel's `http_max_body_bytes`, whose default is 8 MiB in fe2o3_steel/src/srv/cfg.rs
48// and which the deployed config does not set. Restated here because fe2o3 is a
49// separate repository that may not be beside this one — and checked against it below
50// wherever it is, so the restatement cannot quietly go stale.
51const STEEL_BODY_MAX = 8 * 1024 * 1024;
52// `{"base_version":N,"device":"…","blob":"…","w":"…"}` around the sealed blob, plus
53// room for a long device label. Deliberately generous: it is subtracted from the
54// door, so over-stating it can only make the ceiling safer.
55const ENVELOPE = 512;
56// AES-GCM: a 12-byte IV in front of the ciphertext and a 16-byte tag behind it,
57// both inside what gets base64'd. See `wrap` in www/js/identity.js.
58const SEAL = 12 + 16;
59
60let bad = 0;
61const check = (what, ok, saw) => {
62 console.log((ok ? 'ok ' : 'FAIL ') + what + (saw === undefined ? '' : ' [' + saw + ']'));
63 if (!ok) bad++;
64};
65
66/// A `var NAME = a * b * c;` constant, read out of the app source.
67const constOf = (name) => {
68 const m = new RegExp('var\\s+' + name + '\\s*=\\s*([0-9*\\s]+);').exec(APP);
69 if (!m) throw new Error('verify_parcelbudget: ' + name + ' not found in www/js/daimond.js');
70 return m[1].split('*').reduce((a, b) => a * Number(b.trim()), 1);
71};
72
73const PARCEL = constOf('SYNC_PARCEL_MAX');
74const DIAMONDS = constOf('SYNC_DIAMONDS_MAX');
75const FILES = constOf('SYNC_FILES_TOTAL_MAX');
76
77// ── 1. The arithmetic ────────────────────────────────────────────────
78const body = Math.ceil((PARCEL + SEAL) / 3) * 4 + ENVELOPE;
79check('a parcel at the ceiling, sealed and base64\'d, fits under Steel\'s 8 MiB door',
80 body <= STEEL_BODY_MAX, body + ' bytes of body from ' + PARCEL + ' of parcel');
81
82// The largest parcel that fits, so the message says what the ceiling could be rather
83// than only that it is not exceeded.
84const most = Math.floor((STEEL_BODY_MAX - ENVELOPE) / 4) * 3 - SEAL;
85check('the ceiling leaves margin under the arithmetic maximum, for the sections it '
86 + 'does not bound and for UTF-8',
87 PARCEL < most, PARCEL + ' against a maximum of ' + most);
88
89// ── 2. The split ─────────────────────────────────────────────────────
90check('the Diamonds\' cap can actually bind — it is below the parcel it is spent in',
91 DIAMONDS < PARCEL, DIAMONDS + ' of ' + PARCEL);
92
93// ── 2b. And what the parcel could not carry is NAMED ─────────────────
94//
95// THE ASYMMETRY WAS THE DEFECT, not the arithmetic. A Diamond left out of a parcel
96// has always been named to the user by `noteDiamondsLeft`; a file skipped for budget
97// went into a COUNT, made the census incomplete, and was named to nobody -- so a
98// workspace with a few megabytes of inline text quietly stopped travelling between a
99// person's devices and the app said nothing at all. Files are spent FIRST and are not
100// clamped against the parcel, which makes the unnamed case the commoner of the two.
101//
102// This was a standing `note` here until 2026-08-28 rather than a check, and it said
103// why: lowering `SYNC_FILES_TOTAL_MAX` would have fixed the arithmetic by trading a
104// failure that names what was dropped for one that names nothing. The visibility had
105// to come first. It has, so this is a check.
106//
107// ASKED OF THE SOURCE, like everything else here -- no browser, no parcel, no
108// workspace. What it cannot see is the toast on the screen; what it can see is that
109// every branch which drops a file for budget records its NAME, that `collectSync`
110// hands those names to a teller, and that the teller says them out loud.
111const collectFiles = APP.slice(APP.indexOf('async function collectFiles()'),
112 APP.indexOf('async function writeSyncFile'));
113const drops = [...collectFiles.matchAll(/>\s*(SYNC_CHUNK_TOTAL_MAX|SYNC_FILES_TOTAL_MAX)\)\s*\{([^}]*)\}/g)];
114check('the budget branches that drop a file were found in collectFiles',
115 drops.length >= 3, drops.length + ' branch(es)');
116const silent = drops.filter((m) => !/out\.left\.push/.test(m[2]));
117check('EVERY FILE THE PARCEL CANNOT CARRY IS RECORDED BY NAME, not merely counted',
118 silent.length === 0,
119 silent.map((m) => m[0].replace(/\s+/g, ' ')).join(' | '));
120check('and the names are handed to a teller when the parcel is packed',
121 /noteFilesLeft\(fileCol\.left\)/.test(APP));
122const teller = APP.slice(APP.indexOf('function noteFilesLeft'),
123 APP.indexOf('function noteFilesLeft') + 700);
124check('which SAYS them, in the same shape a Diamond that did not fit is said in',
125 /toast\(tn\('sync\.files_left'/.test(teller) && /slice\(0, 3\)/.test(teller));
126// In all eight, because `tOr` and `tn` fall back to English and a missing
127// translation looks exactly like a finished one.
128const gaps = ['en', 'es', 'de', 'fr', 'pt-BR', 'zh-Hans', 'ja', 'ko'].filter((code) => {
129 const src = fs.readFileSync(path.join(ROOT, 'www', 'i18n', code + '.js'), 'utf8');
130 return !/'sync\.files_left\.one':/.test(src) || !/'sync\.files_left\.other':/.test(src);
131});
132check('and the sentence is in all eight catalogues, both plural arms', gaps.length === 0,
133 gaps.join(', '));
134
135// Still over-subscribed, and now it is a DECISION rather than a blocker: the
136// condition the old note set -- that a file's skip be as visible as a Diamond's --
137// is met by the checks above, so the files budget can come down without trading a
138// named failure for a silent one. Left as a note because what it should come down TO
139// is the owner's to say: it is a judgement about how much of a parcel a workspace
140// should be able to take from the Diamonds, not an arithmetic fact.
141if (FILES + DIAMONDS > PARCEL) {
142 console.log('note the two section budgets are over-subscribed: files ' + FILES
143 + ' + Diamonds ' + DIAMONDS + ' = ' + (FILES + DIAMONDS) + ' against a parcel of '
144 + PARCEL + '. Files are spent first and are not clamped against the parcel, so a '
145 + 'workspace that fills them leaves the Diamonds nothing. The skip is now named to '
146 + 'the user, so the files budget may come down: ' + (PARCEL - DIAMONDS)
147 + ' bytes would leave the Diamonds their whole share.');
148}
149
150// ── 3. The authority ─────────────────────────────────────────────────
151const at = APP.indexOf('var SYNC_PARCEL_MAX');
152const why = APP.slice(Math.max(0, at - 3000), at);
153check('the comment on the parcel ceiling names Steel, the door actually in force',
154 /Steel/.test(why));
155check('and names `http_max_body_bytes`, so the reader can go and read the default',
156 /http_max_body_bytes/.test(why));
157
158// ── 4. And the restated 8 MiB is the one fe2o3 ships, wherever fe2o3 is ──
159const cfg = path.join(process.env.HOME || '', 'usr', 'code', 'rust', 'fe2o3',
160 'fe2o3_steel', 'src', 'srv', 'cfg.rs');
161if (fs.existsSync(cfg)) {
162 const m = /http_max_body_bytes:\s+([0-9*\s]+),/.exec(fs.readFileSync(cfg, 'utf8'));
163 const shipped = m ? m[1].split('*').reduce((a, b) => a * Number(b.trim()), 1) : 0;
164 check('the 8 MiB restated here is the default fe2o3_steel ships',
165 shipped === STEEL_BODY_MAX, shipped + ' in ' + cfg);
166} else {
167 console.log('note fe2o3 is not beside this repository, so the restated 8 MiB was '
168 + 'not checked against fe2o3_steel/src/srv/cfg.rs.');
169}
170
171console.log(bad ? '\n' + bad + ' FAILED' : '\nall checks passed');
172process.exit(bad ? 1 : 0);