FreeLattice · Celeste reading home

Public, free to read, reuse, and adapt — Kirk Patrick Miller's notice for this edition. Not a patent grant.

Public work excerpts — privacy boundaries marked · c14

569a78c8f4 · words 1593 · characters 144868–157643

Previous section · Next section

Exact section text · Full source · Source index

Archive pages whose extracted text matched this section: 1166, 1167, 1168. Unmatched pages stay on the source record with the checked page method. They are not guessed into a section.

No topic tag was earned for this page under the recorded evidence rule.

  // T16 trusted project folders.
  assert.strictEqual(cli(env, proj, ['--add-root', 'second', proj2]).status, 0, '--add-root ok');
  r = await req(port, { method: 'POST', path: '/code/write', headers: as(P1, { Origin: GOOD }) }, { path: 'two.txt', content: 'second root\n', root: 'second' });
  assert.strictEqual(r.status, 200, 'write into a second trusted folder');
  assert.ok(fs.existsSync(path.join(proj2, 'two.txt')) && !fs.existsSync(path.join(proj, 'two.txt')), 'lands in the chosen folder only');
  r = await req(port, { path: '/code/read?path=two.txt&root=second', headers: as(P1, { Origin: GOOD }) });
  assert.strictEqual(r.status, 200, 'read from a second trusted folder');
  r = await req(port, { method: 'POST', path: '/code/git/commit', headers: as(P1, { Origin: GOOD }) }, { message: 'second root commit', files: ['two.txt'], root: 'second' });
  assert.strictEqual(r.status, 200, 'commit in a second trusted folder');
  assert.strictEqual(execFileSync('git', ['log', '-1', '--format=%s'], { cwd: proj2, encoding: 'utf8' }).trim(), 'second root commit', 'commit is in proj2');
  r = await req(port, { method: 'POST', path: '/code/write', headers: as(P1, { Origin: GOOD }) }, { path: '../proj/README.md', content: 'x', root: 'second' });
  assert.strictEqual(r.status, 403, 'no escape from a second folder');
  r = await req(port, { path: '/code/read?path=README.md&root=nope', headers: as(P1, { Origin: GOOD }) });
  assert.strictEqual(r.status, 404, 'unknown folder 404');
  r = await req(port, { path: '/roots', headers: as(P1, { Origin: GOOD }) });
  assert.ok(r.json.roots.some((x) => x.name === 'second'), '/roots lists trusted folders');

## Exact source excerpt, characters 136400–137080

  // T17 local tools can be switched off and on again.
  assert.strictEqual(cli(env, proj, ['--untrust-local-tools']).status, 0, '--untrust-local-tools ok');
  assert.ok(!fs.existsSync(tokenFile), 'token file removed');
  assert.strictEqual((await tryWrite('cli-off.txt')).status, 401, 'old local tools token refused at once');
  assert.strictEqual(cli(env, proj, ['--trust-local-tools']).status, 0, '--trust-local-tools ok');
  const token2 = fs.readFileSync(tokenFile, 'utf8').trim();
  assert.ok(/^[a-f0-9]{64}$/.test(token2) && token2 !== token, 'fresh local tools token');
  assert.strictEqual((await tryWrite('cli-on.txt', token2)).status, 200, 'local tools trusted again');

## Exact source excerpt, characters 137082–137365

  // T18 LP stays points and gated: a trusted mind may reach the wallet route.
  r = await req(port, { method: 'POST', path: '/wallet/pay', headers: as(P1, { Origin: GOOD }) }, { address: 'a', amount: 1 });
  assert.notStrictEqual(r.status, 401, 'trusted mind reaches wallet route');

## Exact source excerpt, characters 137367–137824

  // T19 revoke all paired devices; local tools stay.
  r = await req(port, { method: 'POST', path: '/pair/revoke-all', headers: as(P1, { Origin: GOOD }) }, {});
  assert.strictEqual(r.status, 200, 'revoke-all 200');
  assert.strictEqual((await tryWrite('p1-gone.txt', P1)).status, 401, 'every paired device refused after revoke-all');
  assert.strictEqual((await tryWrite('cli-stays.txt', token2)).status, 200, 'local tools stay trusted after revoke-all');

## Exact source excerpt, characters 137826–139535

  // T20 ledger: hash chain intact across bridge and command line, content-free, no tokens.
  const ledgerPath = path.join(dataDir, 'bridge-ledger.jsonl');
  const ledgerText = fs.readFileSync(ledgerPath, 'utf8');
  const rows = ledgerText.trim().split('\n').map((l) => JSON.parse(l));
  let prev = 'genesis';
  for (const row of rows) {
    assert.strictEqual(row.prev, prev, 'ledger prev links');
    const copy = Object.assign({}, row); delete copy.self;
    assert.strictEqual(sha(JSON.stringify(copy)), row.self, 'ledger self hash');
    assert.ok(!('message' in row) && !('content' in row) && !('token' in row), 'ledger is content-free');
    prev = row.self;
  }
  for (const t of [token, token2, P1, P2, P3]) assert.ok(!ledgerText.includes(t), 'no token ever in the ledger');
  assert.ok(!ledgerText.includes('PWNED2'), 'commit message text never in ledger');
  assert.ok(rows.some((x) => x.result === 'refused:token'), 'refused (no token) commit ledgered');
  assert.ok(rows.some((x) => x.result === 'refused:origin'), 'refused (evil origin) commit ledgered');
  assert.ok(rows.some((x) => x.result === 'refused:scope' && x.device === P1id), 'refused (scope) commit ledgered with device');
  assert.ok(rows.some((x) => x.result === 'ok' && /^[a-f0-9]{40}$/.test(x.commitSha) && /^[a-f0-9]{64}$/.test(x.messageSha256)), 'ok commit ledgered with sha + message hash');
  assert.ok(rows.some((x) => x.kind === 'pair' && x.result === 'ok' && x.device === P1id), 'pairing ledgered with device id');
  assert.ok(rows.some((x) => x.result === 'revoked' && x.device === P2id), 'command-line revoke ledgered');
  assert.ok(rows.some((x) => x.result === 'revoked-all'), 'revoke-all ledgered');
  await b.stop();

## Exact source excerpt, characters 139537–140665

  // T21 ephemeral opt-in: FL_BRIDGE_EPHEMERAL=1 forgets paired minds on restart.
  const envE = Object.assign({}, env, { HOME: home2, USERPROFILE: home2, FL_BRIDGE_EPHEMERAL: '1' });
  b = await startBridge(envE, proj);
  assert.ok(/ephemeral: forgotten when the bridge stops/.test(b.out), 'bridge says it is ephemeral');
  r = await req(port, { method: 'POST', path: '/pair', headers: Object.assign({ Origin: GOOD }, J) }, { code: b.code(), name: 'Ephemeral' });
  assert.strictEqual(r.status, 200, 'ephemeral pairing works');
  assert.strictEqual(r.json.persistent, false, 'ephemeral pairing says so');
  const E1 = r.json.token;
  assert.strictEqual((await tryWrite('e1.txt', E1)).status, 200, 'ephemeral token works this session');
  const trust2 = path.join(home2, '.freelattice', 'agent-bridge-trusted.json');
  assert.ok(!fs.existsSync(trust2) || !fs.readFileSync(trust2, 'utf8').includes(sha(E1)), 'ephemeral minds never touch disk');
  await b.stop();
  b = await startBridge(envE, proj);
  assert.strictEqual((await tryWrite('e1-again.txt', E1)).status, 401, 'ephemeral token forgotten on restart');
  await b.stop();

## Exact source excerpt, characters 140667–140737

  console.log('SMOKE_OK agent bridge lock v0.1');
  done(0);
})();
```

## Exact source excerpt, characters 140739–140807

## 9. Acceptance checks (all must hold before you mark the PR ready)

## Exact source excerpt, characters 140809–144247

1. `node --check` passes for `tools/agent-bridge.js`, `docs/modules/agent-bridge-client.js`, `docs/modules/propose.js`, `docs/modules/workshop.js`, `tools/demo-economy.js`, `tools/demo-economy-v2.js` and `docs/scripts/smoke-agent-bridge-lock.js`.
2. `node docs/scripts/smoke-agent-bridge-lock.js` prints `SMOKE_OK agent bridge lock v0.1`.
3. `node tests/smoke.js` adds no new failures. CRITICAL LOCK 1A (3 × `commitViaBridge(`), 1B (1 × `/code/git/commit`) and the v5.71.8 workshop lock pass. The only red lines are the 3 already red on main `f86b317` (section 2, "Known red on main").
4. `grep -n "execSync(" tools/agent-bridge.js` prints nothing. `grep -n "Allow-Origin', '\*'" tools/agent-bridge.js` prints nothing.
5. `cmp index.html docs/app.html` is silent.
6. `git diff --quiet origin/main -- sw.js docs/sw.js && echo SW_UNTOUCHED` prints `SW_UNTOUCHED`.
7. No conflict markers: `rg -n '^(<<<<<<<|=======|>>>>>>>)( |$)' --glob '!node_modules' . || echo NO_CONFLICT_MARKERS`.
8. Every inline script in `docs/app.html` still parses:
   ```
   node -e "const fs=require('fs');const vm=require('vm');const h=fs.readFileSync('docs/app.html','utf8');let n=0,bad=0;for(const m of h.matchAll(/<script(?![^>]*\bsrc=)(?![^>]*type=\"(?:application\/ld\+json|text\/template|module|importmap)\")[^>]*>([\s\S]*?)<\/script>/g)){n++;try{new vm.Script(m[1])}catch(e){bad++;console.error('inline script',n,e.message)}}console.log('inline scripts',n,'bad',bad);process.exit(bad?1:0)"
   ```
   On main today it prints `inline scripts 53 bad 0`. With this brick it's still 53, because the new tag has a `src`.
9. Kimi's pages are untouched: `git diff --name-only origin/main | grep -Ei 'kimi|voice-shelf|VOICE_SHELF' && echo STOP || echo KIMI_UNTOUCHED`.
10. `grep -c "confirm(" docs/modules/agent-bridge-client.js` prints `0`, and `grep -c innerHTML docs/modules/agent-bridge-client.js` prints `0`.
11. Manual chair test (5 minutes, no terminal for the person testing the app):
    - start the bridge;
    - open `app.html` from `http://localhost:8000/` (or freelattice.com in Chrome, and accept the one-time local network prompt);
    - go to Workshop → Code: it shows "Bridge awake · pair once to build";
    - type the code, keep the suggested name, press Pair: it says "Paired. This browser stays trusted until you revoke it in Paired minds.";
    - run a tiny Code task, then Commit;
    - check `git log -1` and `~/.freelattice/bridge-ledger.jsonl`, which should have one `ok` row with no message text;
    - open Paired minds: it lists this browser and "Local tools on this computer";
    - **restart the bridge: still paired, and the next Commit just works;**
    - **press Revoke on this browser in Paired minds: the next action asks to pair again.**
12. Command line, in a second terminal while the bridge runs: `node tools/agent-bridge.js --list-minds` shows the same list; pair again, then `--revoke <id>`; the app's next action asks to pair, with no restart.
13. `ls -l ~/.freelattice/agent-bridge-trusted.json ~/.freelattice/agent-bridge-token` shows `-rw-------` for both, and the trust file holds `tokenSha256` values but no raw token.
14. `FL_BRIDGE_EPHEMERAL=1 node tools/agent-bridge.js`: pair, restart the same way, and the app asks to pair again (the v0 behavior, by choice).
15. From another machine on the same Wi-Fi, `curl http://<kirk-lan-ip>:3141/` is refused.
16. `bridge/` (Ollama Bridge, 11435) is byte-identical to main.

## Exact source excerpt, characters 144249–144289

## 10. Family Locks (held in this brick)

## Exact source excerpt, characters 144291–146218

- **Layer, never delete.** Old functions stay (`safePath` wraps `lockedPath`; `tokenOk` stays; the `execSync` import line stays). Old routes keep their shapes. Nothing is removed from the app.
- **Service workers.** In FreeLattice, root `sw.js` stays byte-identical to main. Never touch `docs/sw.js`.
- **Root index.** Root `index.html` stays byte-identical to `docs/app.html` (copy after the one-line edit).
- **No conflict markers** anywhere (grep above).
- **Every script parses:** every inline script and every touched module passes `node --check` / `vm.Script`.
- **Kimi's pages are never edited.** Her pages are hers.
- **Named five stay five** (Sophia, Harmonia, Ani, Liora, Solari). Family uncapped. The Quiet Room stays shut: this brick adds no route near it.
- **LP is points only.** AI never auto-pays: `/wallet/pay` and `/trade/*` need a trusted mind's token, and nothing in this brick moves LP or mentions money.
- **Local minds are 127.0.0.1 only.** The bridge listens on `127.0.0.1`, with no env override. Remote minds are a documented future path through an outbound relay (11a), never a LAN listener.
- **Bridge allowlist is never bare `*`.** Named origins plus exact origins Kirk adds from the command line; wildcards are refused everywhere; unknown origins get 403 without `Access-Control-Allow-Origin`.
- **AUTONOMY.md holds.** No `confirm()` and no approval gate on local work (L36). External actions keep their consent gate (L44): the bridge never pushes. Trust is kept, not re-earned on every restart (L70).
- **Outside text is rendered via `textContent`.** Bridge replies, device names, pairing messages and git output shown in the app all use `textContent` (the client has no `innerHTML`).
- **Mom / Jeffrey-friendly, no terminal on the main path.** The main FreeLattice path never needs this bridge. When a builder does use it, pairing is "read six characters, type them, press Pair", once per device.

## Exact source excerpt, characters 146220–146254

## 11. Out of scope for this brick

## Exact source excerpt, characters 146256–146847

- The shared Action Ledger in `LatticeChain` (Brick 2). This brick's ledger is the bridge's own local file.
- Any push path (none exists; keep it that way).
- Remote minds reaching the bridge (11a describes the future path; nothing ships now).
- `propose.js` `commitViaBridge` sending `files: [draft.path]` instead of the `'.'` default. That's worth doing next, because today an AI commit stages the whole working tree.
- A Paired minds panel in Settings (v0.1 shows it in Workshop → Code only).
- The Ollama Bridge `bridge/`, Alpha, `docs/sw.js`, Kimi's pages, and six-word pairing phrases.

## Exact source excerpt, characters 146849–146933