oxedyne/daimond/dev/verify_reflux.mjs
22.6 KiB, 1 run
created by r2519314175:633, 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_reflux.mjs — the door a daimon reaches `dev/reflux.mjs` through. |
| 2 | // |
| 3 | // `dev/BLOCKERS.md` B13, in the two sentences two lanes wrote it in: |
| 4 | // |
| 5 | // A daimon spends the user's own key on its own turn; there is no tool that lets |
| 6 | // it hold a budget and send a request outside it. |
| 7 | // |
| 8 | // It drives a headed browser under xvfb. `run` cannot: the fence has no display |
| 9 | // and the harness's own `displayFault()` refuses a forwarded one, correctly. |
| 10 | // |
| 11 | // Together those put `dev/reflux.mjs` -- the instrument OBJECTIVES §1 is scored on |
| 12 | // -- outside a daimon's reach, so a daimon can fix its own blockers, land the |
| 13 | // change, and never see the number move. This file is the reach. |
| 14 | // |
| 15 | // ── Why a verifier, and not a command and not another machine ──────── |
| 16 | // |
| 17 | // `run` is not a door and cannot be made into one. `hand/src/verify.rs` says why |
| 18 | // in its own opening: `listen()` is refused by `crate::seccomp`, a browser needs |
| 19 | // the display server's unix socket and `seccomp::Unix::Refuse` takes that away. |
| 20 | // A browser under the command fence is not a thing that is missing a display; it |
| 21 | // is a thing the fence exists to prevent. So no amount of display arranging |
| 22 | // reaches `reflux` through `run`. |
| 23 | // |
| 24 | // Reaching it over ssh on a machine with no seat -- gilgamesh -- would work, and |
| 25 | // it needs a chromium and an xvfb installed on a machine that is not this one, a |
| 26 | // built wasm bundle over there, and an ssh private key within a daimon's reach. |
| 27 | // Three new things to trust, to get somewhere this tree already goes. |
| 28 | // |
| 29 | // `verify` already goes there. It runs a TRACKED script outside the command |
| 30 | // fence, deliberately and on the record -- `fence:none` in the journal, the |
| 31 | // bytes checked against the commit before every spawn -- with the network open |
| 32 | // and a budget up to four hours. That is the trust class this measurement wants |
| 33 | // and it was built for exactly this reason. What it did not have was a display |
| 34 | // it could hand on and a key of its own, and those are the two halves below. |
| 35 | // |
| 36 | // ── The display this file starts for itself ───────────────────────── |
| 37 | // |
| 38 | // `verify` spawns with the hand's own environment. Start the hand from a desktop |
| 39 | // session and that environment carries `DISPLAY=:0`, which `displayFault` allowed |
| 40 | // -- rightly, for a person watching their own run, and disastrously for a run |
| 41 | // nobody asked for. So this file starts an Xvfb of its OWN, on a display number |
| 42 | // it picked because nothing held it, and hands the child that and nothing it |
| 43 | // inherited. `dev/display.mjs` refuses the difference: see `UNATTENDED_VAR` |
| 44 | // there, and `dev/verify_harness.mjs`'s `oldguard` break for the same guard with |
| 45 | // the rule cut back out of it. Spelled without the flag ON PURPOSE: the hand reads |
| 46 | // break declarations out of a verifier's own source by scanning for the flag, and it |
| 47 | // cannot tell a file naming its own break from a file naming somebody else's -- so a |
| 48 | // cross-reference written the obvious way made `verify` offer this file a break it |
| 49 | // does not have, which it refused, ending the run with no checks at all. Found by |
| 50 | // running the verb over this tree rather than by reading either file. |
| 51 | // |
| 52 | // ── The key this file spends from ─────────────────────────────────── |
| 53 | // |
| 54 | // NOT the owner's. `~/.config/oxedyne/daimond/openrouter.key` is his; the daimon |
| 55 | // holds its own beside its own ssh key, at |
| 56 | // |
| 57 | // ~/.config/oxedyne/daimond-hand/openrouter.key 0600, in a 0700 directory |
| 58 | // |
| 59 | // OpenRouter enforces a limit per key, so a key of its own IS a budget of its own |
| 60 | // -- the first half of B13 is a second key and always was. Outside `~/usr` |
| 61 | // because that tree is a Syncthing folder and a candidate for `ore init`, and a |
| 62 | // credential inside it is copied to another machine whether or not any source |
| 63 | // ever holds its value. Nothing in this repository holds the value, here or as a |
| 64 | // fallback: an example that runs with no configuration is how a live key reached |
| 65 | // a public repository from this tree on 2026-07-10 and was used by somebody else |
| 66 | // nine days later. Absent, this file SKIPS and prints the one line that fixes it. |
| 67 | // |
| 68 | // node dev/verify_reflux.mjs |
| 69 | // node dev/verify_reflux.mjs --break nodisplay # 1: no display at all, which is B13's own world |
| 70 | // node dev/verify_reflux.mjs --break inheritseat # 2: the child is handed the seat it inherited |
| 71 | // node dev/verify_reflux.mjs --break ownerkey # 3: no key of its own, so reflux reaches for his |
| 72 | // |
| 73 | // DAIMOND_REFLUX_BUDGET dollars for the paid run (default 0.35) |
| 74 | // DAIMOND_REFLUX_TASK which reflux tasks to run (default homerun) |
| 75 | // DAIMOND_REFLUX_WORLD world number, ports 8777+n and 9700+n (default 6) |
| 76 | // |
| 77 | // The paid run is ONE task by default and not the whole table. A verifier is run |
| 78 | // by a suite as well as by a daimon, and a file that spent a dollar every time |
| 79 | // anybody typed its name would be turned off within the week. `dev/run_all.sh` |
| 80 | // skips it outright unless `DAIMOND_REFLUX_PAID` is set; a daimon asking for it by |
| 81 | // name gets the run. |
| 82 | |
| 83 | import fs from 'node:fs'; |
| 84 | import os from 'node:os'; |
| 85 | import path from 'node:path'; |
| 86 | import { spawn, spawnSync } from 'node:child_process'; |
| 87 | import { fileURLToPath, pathToFileURL } from 'node:url'; |
| 88 | |
| 89 | import { |
| 90 | displayFault, |
| 91 | cleanDisplayEnv, |
| 92 | INHERITED_ENV, |
| 93 | UNATTENDED_VAR, |
| 94 | OWNED_VAR, |
| 95 | SEAT_DISPLAY, |
| 96 | } from './display.mjs'; |
| 97 | |
| 98 | const HERE = path.dirname(fileURLToPath(import.meta.url)); |
| 99 | const ROOT = path.join(HERE, '..'); |
| 100 | |
| 101 | /// Every break this file declares, so an unknown one is refused by name. |
| 102 | const BREAKS = ['nodisplay', 'inheritseat', 'ownerkey']; |
| 103 | |
| 104 | const BREAK = (() => { |
| 105 | const i = process.argv.indexOf('--break'); |
| 106 | const b = i >= 0 && process.argv[i + 1] ? String(process.argv[i + 1]) : ''; |
| 107 | if (b && !BREAKS.includes(b)) { |
| 108 | console.log(`no such break: ${b}. This file declares: ${BREAKS.join(', ')}.`); |
| 109 | process.exit(1); |
| 110 | } |
| 111 | return b; |
| 112 | })(); |
| 113 | |
| 114 | /// Where the daimon's own provider key lives. |
| 115 | /// |
| 116 | /// Beside its own ssh key, in the directory that is already the machine hand's |
| 117 | /// and already 0700. Absolute, and built from the home directory rather than |
| 118 | /// written down twice, so a scratch home in a break resolves to a scratch path. |
| 119 | function daimonKeyFile(home = os.homedir()) { |
| 120 | return path.join(home, '.config/oxedyne/daimond-hand/openrouter.key'); |
| 121 | } |
| 122 | |
| 123 | /// Where the OWNER's key lives, which is the one this file must never reach for. |
| 124 | /// |
| 125 | /// Named only to be compared against. Nothing here opens it. |
| 126 | function ownerKeyFile(home = os.homedir()) { |
| 127 | return path.join(home, '.config/oxedyne/daimond/openrouter.key'); |
| 128 | } |
| 129 | |
| 130 | const SCRATCH = path.join(process.env.DAIMOND_SCRATCH |
| 131 | || path.join(os.homedir(), '.cache/daimond'), 'refluxdoor'); |
| 132 | const BUDGET = Number(process.env.DAIMOND_REFLUX_BUDGET || '0.35'); |
| 133 | const TASK = process.env.DAIMOND_REFLUX_TASK || 'homerun'; |
| 134 | const WORLD = Number(process.env.DAIMOND_REFLUX_WORLD || '6'); |
| 135 | |
| 136 | let ok = 0, bad = 0; |
| 137 | |
| 138 | /// One check, named as a property rather than as a step. |
| 139 | function check(name, pass, detail) { |
| 140 | if (pass) { ok++; console.log(` ok ${name}${detail ? ` — ${detail}` : ''}`); } |
| 141 | else { bad++; console.log(` FAIL ${name}${detail ? ` — ${detail}` : ''}`); } |
| 142 | } |
| 143 | |
| 144 | function note(s) { console.log(` · ${s}`); } |
| 145 | |
| 146 | // ── The display ───────────────────────────────────────────────────── |
| 147 | |
| 148 | /// Is an X lock file the record of a server that is still there? |
| 149 | /// |
| 150 | /// A LOCK IS NOT A HOLD. An X server writes its process id into `/tmp/.X<n>-lock` |
| 151 | /// and removes the file on the way out -- and a process killed with `SIGKILL` gets |
| 152 | /// no way out, so the file stays and the number is burned until somebody notices. |
| 153 | /// Nine of them in one session, on 2026-08-25, from this very file: the first |
| 154 | /// version of `stopXvfb` reached for `SIGKILL` and every run cost a display. Both |
| 155 | /// halves are fixed, and this is the half that survives somebody else's crash. |
| 156 | /// |
| 157 | /// # Arguments |
| 158 | /// * `n` - The display number. |
| 159 | function lockIsLive(n) { |
| 160 | let pid; |
| 161 | try { pid = Number(fs.readFileSync(`/tmp/.X${n}-lock`, 'utf8').trim()); } |
| 162 | catch { return false; } // no lock at all |
| 163 | if (!Number.isInteger(pid) || pid <= 0) return true; // unreadable: leave it alone |
| 164 | // `/proc` rather than `kill(0)`, which answers yes for a zombie and for a |
| 165 | // process this one may not signal. |
| 166 | return fs.existsSync(`/proc/${pid}`); |
| 167 | } |
| 168 | |
| 169 | /// A display number nothing on this machine holds. |
| 170 | /// |
| 171 | /// The socket is asked about as well as the lock, because either one alone leaves |
| 172 | /// a race with the other half of an X server that is still coming up, and two |
| 173 | /// verifiers starting at once is ordinary. From 90 rather than from 99, so a run |
| 174 | /// of this file does not fight `xvfb-run -a`, which starts at 99 and counts up. |
| 175 | function freeDisplay() { |
| 176 | for (let n = 90; n < 99; n++) { |
| 177 | if (lockIsLive(n)) continue; |
| 178 | // A socket outlives its server too, and the same reasoning applies: it is |
| 179 | // evidence of a lock, and the lock has already been read. |
| 180 | if (!fs.existsSync(`/tmp/.X${n}-lock`) && fs.existsSync(`/tmp/.X11-unix/X${n}`)) continue; |
| 181 | return n; |
| 182 | } |
| 183 | return -1; |
| 184 | } |
| 185 | |
| 186 | /// An Xvfb this process started, and the display it answers on. |
| 187 | /// |
| 188 | /// Killed on every way out of this file -- an ordinary return, a throw, and the |
| 189 | /// signals the hand's `kill_on_drop` sends when a `verify` budget is spent. An X |
| 190 | /// server left behind holds its display number against the next run, which is the |
| 191 | /// same collision `freeDisplay` walks past nine times before giving up. |
| 192 | async function startXvfb() { |
| 193 | const n = freeDisplay(); |
| 194 | if (n < 0) return { display: '', proc: null, why: 'displays :90 to :98 are all held' }; |
| 195 | const sock = `/tmp/.X11-unix/X${n}`; |
| 196 | const p = spawn('Xvfb', [`:${n}`, '-screen', '0', '1500x950x24', '-nolisten', 'tcp'], |
| 197 | { stdio: ['ignore', 'ignore', 'pipe'] }); |
| 198 | let died = ''; |
| 199 | p.on('error', (e) => { died = String(e && e.message); }); |
| 200 | p.stderr.on('data', (d) => { died += String(d); }); |
| 201 | for (let i = 0; i < 100; i++) { |
| 202 | if (fs.existsSync(sock)) return { display: `:${n}`, proc: p, why: '' }; |
| 203 | if (p.exitCode !== null) break; |
| 204 | await new Promise((r) => setTimeout(r, 100)); |
| 205 | } |
| 206 | try { p.kill('SIGKILL'); } catch { /* already gone */ } |
| 207 | return { display: '', proc: null, why: `Xvfb :${n} did not come up. ${died.trim()}` }; |
| 208 | } |
| 209 | |
| 210 | let XVFB = null; |
| 211 | |
| 212 | /// Ends this run's X server and takes its lock with it. |
| 213 | /// |
| 214 | /// SIGTERM FIRST, and the difference is a display number. Xvfb removes its own |
| 215 | /// lock file on a clean exit and cannot on a `SIGKILL`, so the impatient version of |
| 216 | /// this function burned nine numbers in one session before `freeDisplay` ran out |
| 217 | /// and the clean run failed. The lock is removed here as well, because this |
| 218 | /// function is also called from a signal handler where there is no waiting. |
| 219 | function stopXvfb() { |
| 220 | if (!XVFB || !XVFB.proc) { XVFB = null; return; } |
| 221 | const { proc, display } = XVFB; |
| 222 | XVFB = null; |
| 223 | try { proc.kill('SIGTERM'); } catch { /* already gone */ } |
| 224 | // Synchronous, because `process.on('exit')` runs nothing asynchronous. |
| 225 | const until = Date.now() + 2000; |
| 226 | while (proc.exitCode === null && proc.signalCode === null && Date.now() < until) { |
| 227 | try { spawnSync('sleep', ['0.05']); } catch { break; } |
| 228 | } |
| 229 | try { proc.kill('SIGKILL'); } catch { /* already gone */ } |
| 230 | const n = display.replace(':', ''); |
| 231 | // Ours by construction: `freeDisplay` would not have offered the number if |
| 232 | // anything else had held it, and nothing else can have taken it since. |
| 233 | for (const f of [`/tmp/.X${n}-lock`, `/tmp/.X11-unix/X${n}`]) { |
| 234 | try { fs.rmSync(f, { force: true }); } catch { /* not ours to remove */ } |
| 235 | } |
| 236 | } |
| 237 | process.on('exit', stopXvfb); |
| 238 | for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) { |
| 239 | process.on(sig, () => { stopXvfb(); process.exit(1); }); |
| 240 | } |
| 241 | |
| 242 | /// The environment a child of this file is given. |
| 243 | /// |
| 244 | /// The single place the two halves are decided, so a check can be put to the |
| 245 | /// decision rather than to a copy of it -- and so a break changes the world and |
| 246 | /// not the assertion about it. |
| 247 | /// |
| 248 | /// # Arguments |
| 249 | /// * `display` - The display this file started, or `''` where it started none. |
| 250 | /// * `keyFile` - The key file to name, or `''` to name none and let `reflux` pick. |
| 251 | function childEnv(display, keyFile) { |
| 252 | const env = cleanDisplayEnv(process.env); |
| 253 | // Whatever this process inherited, gone, and only then this file's own. A |
| 254 | // deletion rather than an assignment because the assignment alone reads as a |
| 255 | // default, and the difference is the seat. |
| 256 | delete env.DISPLAY; |
| 257 | if (BREAK === 'inheritseat') { |
| 258 | // B13's world before the guard could see it: a run nobody asked for, given |
| 259 | // whatever display the hand happened to hold. |
| 260 | env.DISPLAY = (INHERITED_ENV.DISPLAY || '').trim() || SEAT_DISPLAY; |
| 261 | } else if (display) { |
| 262 | env.DISPLAY = display; |
| 263 | } |
| 264 | env[UNATTENDED_VAR] = '1'; |
| 265 | if (display) env[OWNED_VAR] = display; |
| 266 | // A key IN THE ENVIRONMENT is a key this file did not choose. `readKey` reads |
| 267 | // this name before it reads any file, so leaving it would let whatever started |
| 268 | // the hand decide what a daimon spends. |
| 269 | delete env.DAIMOND_PROBE_KEY; |
| 270 | if (keyFile) env.DAIMOND_PROBE_KEY_FILE = keyFile; |
| 271 | else delete env.DAIMOND_PROBE_KEY_FILE; |
| 272 | env.DAIMOND_SCRATCH = SCRATCH; |
| 273 | return env; |
| 274 | } |
| 275 | |
| 276 | // ── The run ───────────────────────────────────────────────────────── |
| 277 | |
| 278 | console.log('\n== verify_reflux: the door a daimon reaches the instrument through =='); |
| 279 | if (BREAK) console.log(` BREAK ${BREAK} — checks below are EXPECTED to fail`); |
| 280 | |
| 281 | fs.mkdirSync(SCRATCH, { recursive: true }); |
| 282 | |
| 283 | console.log('\n── A display this file started for itself ────────────'); |
| 284 | |
| 285 | XVFB = BREAK === 'nodisplay' |
| 286 | ? { display: '', proc: null, why: 'the break started none, which is B13\'s own world' } |
| 287 | : await startXvfb(); |
| 288 | const DISPLAY = XVFB.display; |
| 289 | |
| 290 | check('an Xvfb of this run\'s own is up, on a display number nothing else held', |
| 291 | !!DISPLAY, DISPLAY || XVFB.why); |
| 292 | |
| 293 | const KEY_FILE = BREAK === 'ownerkey' ? '' : daimonKeyFile(); |
| 294 | const ENV = childEnv(DISPLAY, KEY_FILE); |
| 295 | |
| 296 | // ASKED OF WHAT IS HANDED ON, not of what was started. The two differ by exactly |
| 297 | // one accident -- a display inherited from whatever launched the hand, quietly |
| 298 | // winning over the one this file went to the trouble of starting -- and that |
| 299 | // accident is B13's whole second half. |
| 300 | check('the display handed to the child is the one this file started, and neither the ' |
| 301 | + 'seat nor what it inherited', |
| 302 | !!DISPLAY && ENV.DISPLAY === DISPLAY && DISPLAY !== SEAT_DISPLAY |
| 303 | && DISPLAY !== (INHERITED_ENV.DISPLAY || '').trim(), |
| 304 | `started ${DISPLAY || '<none>'}, handed ${ENV.DISPLAY || '<none>'}, ` |
| 305 | + `inherited ${(INHERITED_ENV.DISPLAY || '').trim() || '<none>'}`); |
| 306 | |
| 307 | { |
| 308 | const said = displayFault(ENV); |
| 309 | check('the environment this file hands on is one the guard accepts', |
| 310 | said === null, JSON.stringify(said)); |
| 311 | } |
| 312 | |
| 313 | { |
| 314 | // The refusal put to the REAL module with the REAL environment, differing from |
| 315 | // the one above in the display alone. Not a launch: a browser started to find |
| 316 | // out where a browser would appear is a check nobody dares run. |
| 317 | const said = displayFault({ ...ENV, DISPLAY: SEAT_DISPLAY }); |
| 318 | check('the same environment carrying the seat instead is refused, and the seat named', |
| 319 | typeof said === 'string' && said.includes('OWN SEAT'), JSON.stringify(said)); |
| 320 | } |
| 321 | |
| 322 | |
| 323 | // A real headed browser, on the display this file started, and on no other. This |
| 324 | // is the one check that asks the X server rather than the guard: a headed Chromium |
| 325 | // with nowhere to paint does not start at all, so a browser that answers is a |
| 326 | // browser that connected to THIS display. |
| 327 | if (DISPLAY && BREAK !== 'inheritseat') { |
| 328 | let why = ''; |
| 329 | let landed = false; |
| 330 | try { |
| 331 | // The same playwright and the same Chrome `dev/harness.mjs` uses, taken from |
| 332 | // it rather than written down again: a browser proved to land on this display |
| 333 | // is only evidence if it is the browser everything else here launches. |
| 334 | const { PW, CHROME } = await import('./harness.mjs'); |
| 335 | const { chromium } = await import(pathToFileURL(PW).href); |
| 336 | const prof = path.join(SCRATCH, 'guard-profile'); |
| 337 | fs.rmSync(prof, { recursive: true, force: true }); |
| 338 | const b = await chromium.launchPersistentContext(prof, { |
| 339 | executablePath: CHROME, |
| 340 | headless: false, |
| 341 | env: ENV, |
| 342 | args: ['--no-sandbox', '--no-first-run'], |
| 343 | viewport: { width: 800, height: 600 }, |
| 344 | }); |
| 345 | const pg = b.pages()[0] || await b.newPage(); |
| 346 | await pg.goto('about:blank'); |
| 347 | landed = true; |
| 348 | await b.close(); |
| 349 | } catch (e) { |
| 350 | why = String(e && e.message).split('\n')[0]; |
| 351 | } |
| 352 | check('a real headed Chromium starts on that display and on nothing else', |
| 353 | landed, why || `DISPLAY=${DISPLAY}`); |
| 354 | } else if (BREAK === 'inheritseat') { |
| 355 | // NOT LAUNCHED, AND THIS IS THE ONE PLACE THAT MATTERS. The break's whole |
| 356 | // content is a display that belongs to somebody else -- on this machine, over |
| 357 | // an ssh forward to the owner's laptop. Starting a browser to watch a break |
| 358 | // work would put the window on his screen, which is the accident the guard |
| 359 | // exists to prevent. |
| 360 | note('the browser was not launched: the break\'s display is somebody else\'s'); |
| 361 | } else { |
| 362 | check('a real headed Chromium starts on that display and on nothing else', |
| 363 | false, XVFB.why || 'there is no display to start it on'); |
| 364 | } |
| 365 | |
| 366 | console.log('\n── A key of its own ──────────────────────────────────'); |
| 367 | |
| 368 | const NAMED = ENV.DAIMOND_PROBE_KEY_FILE || ''; |
| 369 | |
| 370 | check('the key named to the child is the daimon\'s, not the owner\'s', |
| 371 | !!NAMED && NAMED !== ownerKeyFile() && /daimond-hand/.test(NAMED), |
| 372 | NAMED || '<none named, so reflux picks -- and its default is his>'); |
| 373 | |
| 374 | check('and it lives outside ~/usr, which is replicated and is a candidate for ore init', |
| 375 | !!NAMED && !NAMED.startsWith(path.join(os.homedir(), 'usr')) && !NAMED.startsWith(ROOT), |
| 376 | NAMED || '<none named>'); |
| 377 | |
| 378 | // The child is told WHERE the key is and never what it is. `readKey` reads |
| 379 | // `DAIMOND_PROBE_KEY` before it reads any file, so a value left in the environment |
| 380 | // would let whatever started the hand decide what a daimon spends -- and an |
| 381 | // environment is copied into every child of every child. |
| 382 | check('the child is handed a path to the key and never the key itself', |
| 383 | ENV.DAIMOND_PROBE_KEY === undefined && !!NAMED, |
| 384 | `DAIMOND_PROBE_KEY ${ENV.DAIMOND_PROBE_KEY === undefined ? 'unset' : 'SET'}, ` |
| 385 | + `DAIMOND_PROBE_KEY_FILE ${ENV.DAIMOND_PROBE_KEY_FILE || '<unset>'}`); |
| 386 | |
| 387 | // What `reflux` actually does when the key it is pointed at is not there. The real |
| 388 | // file, the real `readKey`, and a home directory of this run's own so that neither |
| 389 | // the owner's key nor the daimon's is anywhere near the answer. |
| 390 | { |
| 391 | const home = path.join(SCRATCH, 'home'); |
| 392 | fs.mkdirSync(home, { recursive: true }); |
| 393 | const env = childEnv(DISPLAY, KEY_FILE ? daimonKeyFile(home) : ''); |
| 394 | env.HOME = home; |
| 395 | const r = spawnSync(process.execPath, ['dev/reflux.mjs', '--task', TASK, '--no-build'], |
| 396 | { cwd: ROOT, encoding: 'utf8', env, timeout: 60_000 }); |
| 397 | const said = `${r.stdout || ''}${r.stderr || ''}`; |
| 398 | check('with no key of its own, reflux stops rather than spending, and names the file', |
| 399 | r.status !== 0 && said.includes(daimonKeyFile(home)), |
| 400 | (said.split('\n').find((l) => l.includes('openrouter.key')) || said.split('\n')[0] || '') |
| 401 | .trim().slice(0, 160)); |
| 402 | check('and the file it names is not the owner\'s', |
| 403 | !said.includes(ownerKeyFile(home)), |
| 404 | said.includes(ownerKeyFile(home)) ? 'it reached for the owner\'s key' : 'it did not'); |
| 405 | } |
| 406 | |
| 407 | // ── The number ────────────────────────────────────────────────────── |
| 408 | |
| 409 | console.log('\n── The run, and the number it comes back with ────────'); |
| 410 | |
| 411 | const HAVE_KEY = !!KEY_FILE && fs.existsSync(KEY_FILE); |
| 412 | let paid = false; |
| 413 | |
| 414 | if (BREAK) { |
| 415 | note(`no paid run under a break: a break spends money to watch a check redden, ` |
| 416 | + 'and every check this file declares reddens for free.'); |
| 417 | } else if (!DISPLAY) { |
| 418 | note('no paid run: there is no display to run it on.'); |
| 419 | } else if (!HAVE_KEY) { |
| 420 | // A CHECK AND NOT A SKIP, and the difference is what a daimon is told. |
| 421 | // |
| 422 | // The tree's idiom for "cannot be given what it needs" is exit 2 and a SKIPPED |
| 423 | // line -- `verify_droots_real` does exactly that. It is right for `run_all.sh`, |
| 424 | // which reads exit codes, and it is silent through `verify`: the hand's report |
| 425 | // carries the check lines and the FAIL lines and nothing else, so this file |
| 426 | // exiting 2 with ten green checks reached a daimon as "10 checks passed, 0 |
| 427 | // failed, 3 breaks confirmed red" -- a clean bill of health for a run that never |
| 428 | // happened. Measured on 2026-08-25 by conducting this file through the verb. |
| 429 | // |
| 430 | // So the missing key is a check, it is red because it is false, and the whole of |
| 431 | // what to do about it rides in the detail, which is the part `fails` keeps whole. |
| 432 | check('the run behind this door has a key of its own to spend', |
| 433 | false, |
| 434 | `there is no key at ${KEY_FILE}. Only the owner can mint one: a NEW key at ` |
| 435 | + 'openrouter.ai/settings/keys with a credit limit of its own, then `install -d ' |
| 436 | + '-m 700 ~/.config/oxedyne/daimond-hand && install -m 600 /dev/stdin ' |
| 437 | + '~/.config/oxedyne/daimond-hand/openrouter.key`. This file WILL NOT reach for ' |
| 438 | + 'his. The door above is proved; the run behind it did not run.'); |
| 439 | console.log(''); |
| 440 | console.log(' The checks above are real. The run is not run, and that is not a pass.'); |
| 441 | console.log(''); |
| 442 | } else { |
| 443 | const args = ['dev/reflux.mjs', '--task', TASK, '--budget', BUDGET.toFixed(2), |
| 444 | '--world', String(WORLD)]; |
| 445 | note(`reflux ${args.slice(1).join(' ')} on ${DISPLAY}, from ${KEY_FILE}`); |
| 446 | const r = spawnSync(process.execPath, args, |
| 447 | { cwd: ROOT, encoding: 'utf8', env: ENV, maxBuffer: 64 * 1024 * 1024 }); |
| 448 | const said = `${r.stdout || ''}${r.stderr || ''}`; |
| 449 | for (const line of said.split('\n')) { |
| 450 | if (/^(task|TOTAL|\s{2}\S+\s+(pass|FAIL))/.test(line)) console.log(` | ${line}`); |
| 451 | } |
| 452 | const total = said.split('\n').find((l) => l.startsWith('TOTAL')) || ''; |
| 453 | const spent = Number((total.match(/\$([0-9.]+)/) || [])[1] || 'NaN'); |
| 454 | check('reflux ran to its end inside the fence-free door and reported', |
| 455 | r.status === 0, `exit ${r.status}` + (r.status === 0 ? '' : `: ${said.slice(-400)}`)); |
| 456 | check('it came back with a number, and the number is inside the budget it was given', |
| 457 | Number.isFinite(spent) && spent <= BUDGET, |
| 458 | total.trim() || said.split('\n').slice(-3).join(' | ').slice(0, 200)); |
| 459 | paid = true; |
| 460 | } |
| 461 | |
| 462 | stopXvfb(); |
| 463 | |
| 464 | console.log(`\n${ok} ok, ${bad} failed.`); |
| 465 | if (bad) { console.log('failed checks above.'); process.exit(1); } |
| 466 | if (!paid && !BREAK) { |
| 467 | // Reached only where there IS a key and the run was skipped for another reason |
| 468 | // -- no display, which is already a red above. Kept so that no path out of this |
| 469 | // file can report a pass without the run having happened. |
| 470 | console.log('The door is proved; the run behind it was not run. Exit 2 is not a pass.'); |
| 471 | process.exit(2); |
| 472 | } |