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 · c06

569a78c8f4 · words 1572 · characters 47075–59525

Previous section · Next section

Exact section text · Full source · Source index

Archive pages whose extracted text matched this section: 1137, 1138, 1139. 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.

function runBridgeCli(argv) {
  var cmd = argv[0];
  if (!cmd || cmd.indexOf('--') !== 0) return false;
  bridgeEphemeral = false; // the command line always edits the trust file itself
  var st = trustState();
  var arg = argv[1];
  function say(s) { console.log('  ' + s); }
  function fail(s) { console.error('  ! ' + s); process.exitCode = 1; return true; }
  function findMind(id) { return st.minds.filter(function (m) { return m.id === String(id || ''); })[0] || null; }
  switch (cmd) {
    case '--list-minds': {
      say('Trusted minds (' + st.minds.length + '). Tokens are never shown; only their hashes are kept.');
      st.minds.forEach(function (m) {
        say(m.id + '  ' + m.kind + '  ' + m.name + (m.origin ? '  ' + m.origin : '') +
            '  scopes=' + m.scopes.join(',') + '  paired=' + (m.createdAt || '?') +
            '  last=' + (m.lastUsedAt || 'never') + (m.kind === 'cli' && !st.localTools ? '  (off)' : ''));
      });
      say('Local tools: ' + (st.localTools ? 'trusted (token file ' + TOKEN_FILE + ')' : 'off'));
      say('Extra trusted origins: ' + (st.trustedOrigins.length ? st.trustedOrigins.join(', ') : 'none'));
      say('Project folders: ' + rootList().map(function (r) { return r.name + '=' + r.path; }).join(', '));
      return true;
    }
    case '--revoke': {
      var m = findMind(arg);
      if (!m) return fail('No trusted mind with id ' + String(arg || '(missing)') + '. Try --list-minds.');
      st.minds = st.minds.filter(function (x) { return x.id !== m.id; });
      if (m.kind === 'cli') { st.localTools = false; try { fs.unlinkSync(TOKEN_FILE); } catch (e) {} }
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'pair', route: 'cli:--revoke', device: m.id, result: 'revoked' });
      say('Revoked ' + m.name + ' (' + m.id + '). Its next request will be asked to pair.');
      return true;
    }
    case '--revoke-all': {
      var before = st.minds.length;
      st.minds = st.minds.filter(function (x) { return x.kind === 'cli'; });
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'pair', route: 'cli:--revoke-all', result: 'revoked-all' });
      say('Revoked ' + (before - st.minds.length) + ' paired device(s). Local tools stay trusted.');
      return true;
    }
    case '--allow-secrets':
    case '--deny-secrets': {
      var sm = findMind(arg);
      if (!sm) return fail('No trusted mind with id ' + String(arg || '(missing)') + '.');
      sm.scopes = sm.scopes.filter(function (s) { return s !== 'secrets'; });
      if (cmd === '--allow-secrets') sm.scopes.push('secrets');
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'pair', route: 'cli:' + cmd, device: sm.id, result: 'scopes' });
      say(sm.name + ' scopes: ' + sm.scopes.join(','));
      return true;
    }
    case '--trust-origin':
    case '--untrust-origin': {
      var o = exactOrigin(arg);
      if (!o) return fail('Exact origins only, like https://example.dev or http://192.168.1.20:8000. Never *.');
      st.trustedOrigins = st.trustedOrigins.filter(function (x) { return x !== o; });
      if (cmd === '--trust-origin') st.trustedOrigins.push(o);
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'trust', route: 'cli:' + cmd, origin: o, result: 'ok' });
      say((cmd === '--trust-origin' ? 'Trusted ' : 'Removed ') + o + '. It still has to pair before it can change anything.');
      return true;
    }
    case '--add-root': {
      var name = String(arg || '');
      var dir = argv[2] ? path.resolve(argv[2]) : '';
      if (!/^[a-z0-9][a-z0-9_-]{0,31}$/i.test(name) || name === 'project') return fail('Name: letters, digits, - or _, up to 32, not "project".');
      var isDir = false;
      try { isDir = fs.statSync(dir).isDirectory(); } catch (e) {}
      if (!dir || !isDir) return fail('Folder not found: ' + String(argv[2] || '(missing)'));
      st.roots[name] = fs.realpathSync(dir);
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'trust', route: 'cli:--add-root', result: 'ok' });
      say('Trusted project folder "' + name + '" = ' + st.roots[name]);
      return true;
    }
    case '--remove-root': {
      if (!Object.prototype.hasOwnProperty.call(st.roots, String(arg || ''))) return fail('No project folder named ' + String(arg || '(missing)') + '.');
      delete st.roots[arg];
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'trust', route: 'cli:--remove-root', result: 'ok' });
      say('Removed project folder "' + arg + '".');
      return true;
    }
    case '--trust-local-tools': {
      st.localTools = true;
      if (!saveTrust()) return fail('Could not save.');
      ensureLocalToolsToken();
      ledgerAppend({ kind: 'pair', route: 'cli:--trust-local-tools', result: 'local-tools-on' });
      say('Local tools on this computer are trusted. Token file: ' + TOKEN_FILE);
      return true;
    }
    case '--untrust-local-tools': {
      st.localTools = false;
      st.minds = st.minds.filter(function (x) { return x.kind !== 'cli'; });
      try { fs.unlinkSync(TOKEN_FILE); } catch (e) {}
      if (!saveTrust()) return fail('Could not save.');
      ledgerAppend({ kind: 'pair', route: 'cli:--untrust-local-tools', result: 'local-tools-off' });
      say('Local tools now need to pair like any other device.');
      return true;
    }
    default:
      bridgeLockUsage();
      return fail('Unknown option ' + cmd);
  }
}

## Exact source excerpt, characters 48734–48859

function bridgeLockInit() {
  trustState();
  ensureLocalToolsToken();
}
// ── end LAYER bridge-lock-v0 · v0.1 helpers ──
```

## Exact source excerpt, characters 48861–48909

### Block B: `handleRequest` (replaces L111-129)

## Exact source excerpt, characters 48911–49277

```js
function handleRequest(req, res) {
  // LAYER bridge-lock-v0: the old `Access-Control-Allow-Origin: *` is superseded here.
  var origin = req.headers.origin || '';
  var pathname = String(req.url || '/').split('?')[0];
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('Vary', 'Origin');
  res.setHeader('X-Content-Type-Options', 'nosniff');

## Exact source excerpt, characters 49279–49498

  // 1. Host guard (DNS rebinding): only 127.0.0.1 / localhost.
  if (!hostAllowed(req.headers.host)) {
    req.resume();
    return respond(res, 403, { error: 'Host not allowed', bridgeLock: BRIDGE_LOCK_VERSION });
  }

## Exact source excerpt, characters 49500–50465

  // 2. Origin guard. Browsers always send Origin on cross-origin fetches and
  //    preflights. No Origin = local CLI or same-machine tool (still needs
  //    a trusted token for anything that changes state).
  if (origin && !originAllowed(origin)) {
    if (pathname === '/code/git/commit') {
      ledgerAppend({ route: pathname, origin: origin, result: 'refused:origin' });
    }
    req.resume();
    return respond(res, 403, { error: 'Origin not on the FreeLattice allowlist', bridgeLock: BRIDGE_LOCK_VERSION });
  }
  if (origin) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Agent-Id, X-FL-Bridge-Token');
    res.setHeader('Access-Control-Max-Age', '600');
    if (req.headers['access-control-request-private-network']) {
      res.setHeader('Access-Control-Allow-Private-Network', 'true');
    }
  }

## Exact source excerpt, characters 50467–50555

  if (req.method === 'OPTIONS') { req.resume(); res.writeHead(204); res.end(); return; }

## Exact source excerpt, characters 50557–51059

  // 3. Trusted-mind guard: which paired device (or local tool) is this?
  var gated = needsToken(req.method, pathname);
  var mind = gated ? requestMind(req) : null;
  if (gated && !mind) {
    if (pathname === '/code/git/commit') {
      ledgerAppend({ route: pathname, origin: origin, result: 'refused:token' });
    }
    req.resume();
    return respond(res, 401, { error: 'Pair with the Agent Bridge first (token missing, stale or revoked).', pair: 'code', bridgeLock: BRIDGE_LOCK_VERSION });
  }

## Exact source excerpt, characters 51061–51618

  // 4. Scope guard. A new mind has every scope except 'secrets' (.env files).
  var need = mind ? scopeFor(req.method, pathname) : '';
  if (need && mind.scopes.indexOf(need) === -1) {
    if (pathname === '/code/git/commit') {
      ledgerAppend({ route: pathname, origin: origin, device: mind.id, result: 'refused:scope' });
    }
    req.resume();
    return respond(res, 403, { error: 'This mind has not been given "' + need + '" here. Kirk can add it in Paired minds.', scope: need, bridgeLock: BRIDGE_LOCK_VERSION });
  }
  if (mind) touchMind(mind);

## Exact source excerpt, characters 51620–51757

  // Allow requests to act as a different agent via X-Agent-Id header
  var requestAgentId = req.headers['x-agent-id'] || agentId.meshId;

## Exact source excerpt, characters 51759–52650

  // 5. Body cap. Drain the rest so the client sees the 413 cleanly.
  var chunks = [];
  var size = 0;
  var tooBig = false;
  req.on('data', function(chunk) {
    size += chunk.length;
    if (size > MAX_BODY_BYTES) { tooBig = true; chunks = []; return; }
    if (!tooBig) chunks.push(chunk);
  });
  req.on('end', function() {
    if (tooBig) return respond(res, 413, { error: 'Body too large (max ' + MAX_BODY_BYTES + ' bytes)' });
    var body = Buffer.concat(chunks).toString('utf8');
    var data = {};
    if (body) {
      try { data = JSON.parse(body); }
      catch (e) { return respond(res, 400, { error: 'Body is not valid JSON' }); }
      if (!data || typeof data !== 'object' || Array.isArray(data)) return respond(res, 400, { error: 'Body must be a JSON object' });
    }
    route(req.url, req.method, data, res, requestAgentId, { origin: origin, mind: mind });
  });
}
```

## Exact source excerpt, characters 52652–52717

### Block C: pairing and trusted-minds routes (insert after L133)

## Exact source excerpt, characters 52719–55008

```js
  // ── LAYER bridge-lock-v0 · v0.1: pairing and trusted minds ──
  if (url === '/pair' && method === 'POST') {
    var now = Date.now();
    if (!pairState.code || now > pairState.expires) newPairCode('expired');
    if (now < pairState.coolUntil) return respond(res, 429, { error: 'Resting for a moment. Try again in 30 seconds.' });
    var given = normalizePairCode(data.code);
    var good = given.length === 6 &&
      crypto.timingSafeEqual(Buffer.from(given, 'utf8'), Buffer.from(pairState.code, 'utf8'));
    var pairOrigin = (meta && meta.origin) || '';
    if (!good) {
      pairState.tries += 1;
      if (pairState.tries >= PAIR_MAX_TRIES) {
        ledgerAppend({ kind: 'pair', route: '/pair', origin: pairOrigin, result: 'refused:lockout' });
        newPairCode('lockout');
        return respond(res, 429, { error: 'Too many tries. The bridge window shows a new code.' });
      }
      return respond(res, 403, { error: 'That code does not match. Check the bridge window.', triesLeft: PAIR_MAX_TRIES - pairState.tries });
    }
    var st = trustState();
    var tok = newToken();
    var mind = {
      id: newMindId(),
      name: cleanName(data.name) || (pairOrigin ? 'A browser at ' + pairOrigin : 'A local tool'),
      origin: pairOrigin,
      kind: pairOrigin ? 'browser' : 'tool',
      scopes: DEFAULT_SCOPES.slice(),
      tokenSha256: sha256Hex(tok),
      createdAt: new Date().toISOString(),
      lastUsedAt: '',
      expiresAt: null
    };
    st.minds.push(mind);
    if (!saveTrust()) {
      st.minds = st.minds.filter(function (m) { return m.id !== mind.id; });
      return respond(res, 500, { error: 'The bridge could not save this pairing. Check ~/.freelattice.' });
    }
    ledgerAppend({ kind: 'pair', route: '/pair', origin: pairOrigin, device: mind.id, result: 'ok' });
    console.log('  \u2726 Paired "' + mind.name + '" (' + mind.id + '). ' +
      (bridgeEphemeral ? 'Ephemeral mode: forgotten when the bridge stops.' : 'It stays paired until you revoke it.'));
    newPairCode('used'); // single use: the next device gets a fresh code
    return respond(res, 200, { token: tok, id: mind.id, name: mind.name, scopes: mind.scopes,
                               persistent: !bridgeEphemeral, bridgeLock: BRIDGE_LOCK_VERSION });
  }

## Exact source excerpt, characters 55010–55274