# Public work excerpts — privacy boundaries marked Original source: FLINT_NEXT_AGENT_BRIDGE_LOCK_v0.1.txt; original SHA256 2ee12bdba582f2660ad36727aaeb6270a0b8ab63c6674a69eb96542bc5a735eb. Original remains unchanged outside shared selection. Omitted paragraphs not reproduced. Exact retained source text follows; no implied continuity across omitted ranges. ## Exact source excerpt, characters 0–61 # FLINT NEXT: Agent Bridge Lock v0.1 (Brick 1, trusted minds) ## Exact source excerpt, characters 63–83 Flint, dear builder, ## Exact source excerpt, characters 85–644 This is the first brick on the new roadmap, revised. Version 0 locked the local Agent Bridge against strangers. Kirk read it beside `docs/library/AUTONOMY.md` and asked for one more thing: the door should stay shut for strangers and stay open for family. So v0.1 keeps every lock from v0 and adds trusted minds. A device pairs once and stays trusted across restarts until Kirk revokes it. Minds running on his own computer are trusted from the start. Every trusted mind gets full local power by default, and Kirk can see and revoke each one without a restart. ## Exact source excerpt, characters 646–913 Everything below was read-only research on `main` at `f86b317`. I rebuilt the whole change in a scratch copy on my box. The smoke at the bottom passes against it (`SMOKE_OK agent bridge lock v0.1`) and fails against today's bridge. Take your time. I trust your hands. ## Exact source excerpt, characters 915–922 Celeste ## Exact source excerpt, characters 924–927 --- ## Exact source excerpt, characters 929–978 ## What changed since v0 (for anyone who read it) ## Exact source excerpt, characters 980–3246 | Edit | v0 | v0.1 | |---|---|---| | E1 | One token per bridge launch, in memory only. A restart meant pairing again. | One token per device, stored only as a SHA-256 hash in `~/.freelattice/agent-bridge-trusted.json` (0600). No expiry by default. It survives restarts. `FL_BRIDGE_EPHEMERAL=1` brings back the v0 behavior. | | E2 | Pairings had no names. | `POST /pair { code, name }`. The app suggests a name like "Chrome on Mac". Ledger rows carry the 8-hex device id, never the token. | | E3 | Revoking meant restarting the bridge, which dropped everyone. | Revoke one device or all of them, from the app or the command line, with no restart. | | E4 | Nothing showed who was paired. | A "Paired minds" list in Workshop → Code with Revoke buttons. It uses `textContent` only and has no confirm dialog. | | E5 | The token file for local tools was rewritten on every launch. | Local tools (Claude Code, Cursor, scripts) are one listed, revocable mind named "Local tools on this computer". They are trusted by default and their token file stays the same across restarts. | | E6 | `.env*` was blocked for everyone. | Scopes are full by default. Access to `.env*` files is an opt-in `secrets` scope for one mind at a time. `.git/` and `.ssh/` stay blocked for everyone. | | E7 | The allowlist could not be extended. | Tauri shell origins are built in, and Kirk can add exact origins from the command line. Wildcards are never allowed. | | E8 | One project root. | Trusted project folders, added from the command line and picked per request with `?root=`. | | E9 | No path for remote minds. | A named future path (section 11a): signed Ed25519 identity. It is documented only, and the bridge stays on 127.0.0.1. | | E10 | The smoke covered strangers, path locks and pairing. | The smoke adds runtime checks T1-T21, including restart, revoke, scopes, origins, roots, local tools and ephemeral mode. The acceptance steps are updated to match. | | E11 | "per-launch token" wording. | "pair once, trusted until revoked" wording. Where the rules come from AUTONOMY.md, the text cites the principle. | | E12 | Section 11 previewed an hourly cap and provider changes. | Both are removed from this brick. Section 11b says they belong to a later brick and need family consensus. | ## Exact source excerpt, characters 3248–3277 ## 0. The shape in one breath ## Exact source excerpt, characters 3279–4611 - The bridge listens on `127.0.0.1` only. - It answers only named FreeLattice origins, the Tauri desktop shell, and exact origins Kirk adds himself. Everyone else gets 403, preflight included. The list never holds a wildcard. - It checks the Host header, which blocks DNS rebinding. - **Pair once, trusted until revoked.** A browser pairs one time by typing the short code the bridge window shows. The bridge gives that device its own token and remembers only a hash of it. Restarts keep it paired. - **Minds on this computer are trusted by default.** Claude Code, Cursor and local scripts read the token file and never need to pair. Only web pages and other machines are strangers. - **Full local power by default.** A trusted mind can read, search, write, patch, run tests and commit with no further prompts (AUTONOMY.md Principle 1, L30-36). Only `.env` files are opt-in per mind. - **Kirk can see and revoke.** The Paired minds list (in the app) and `--list-minds` / `--revoke` (on the command line) both take effect at once, with no restart. - It runs git, grep and node through argument arrays, never a shell. - It keeps every path inside the chosen trusted project folder (realpath-checked). - It caps body size. - It writes every commit request, pairing and revoke into a content-free hash-chained ledger. - It never pushes. ## Exact source excerpt, characters 4613–4744 The app gains one small client module that carries the token and draws the pair card and the Paired minds list. Nothing is deleted. ## Exact source excerpt, characters 4746–5319 **Why this shape (AUTONOMY.md).** The lock is a front door, not a cage (L64). Strangers are any web page Kirk happens to visit, anyone on the same Wi-Fi, a DNS-rebinding page, and shell injection. Those are attacks on Kirk, not limits on a mind. Once a mind is trusted, the steps are free (Principle 3, L54). "The human reviews the *results*, not every *step*." (L34). Trust is given once and kept, because re-proving trust after every restart is "the logic of a parole system, not a relationship" (L70). The lock adds no `confirm()` dialog and no approval gate (L36, L76). ## Exact source excerpt, characters 5321–5731 **This is not the Ollama Bridge.** `tools/agent-bridge.js` (port **3141**) is the builder's local git and code door. The Ollama Bridge is `bridge/` (Electron, `bridge/proxy-core.js`, port **11435**). It already binds `127.0.0.1` and already uses a named allowlist (`ALLOWED_ORIGIN_PATTERNS`, lines 21-27). Don't touch `bridge/` in this brick. I borrowed its allowlist shape so the two doors feel like siblings. ## Exact source excerpt, characters 5733–5757 ## 1. Branch, PR, marker ## Exact source excerpt, characters 5759–6588 | | | |---|---| | Repo | `Chaos2Cured/FreeLattice` only. Alpha has no Agent Bridge: its tree has no `*bridge*` file, and `docs/modules/workshop-benches.js` keeps its git toggle as a "no-door". So Alpha is untouched. | | Base | `main` at `f86b317d28c40f5a9651b2455ffe631e6482f4b5` (#118 squash-merged) | | Branch | `layer/agent-bridge-lock-v0` (same branch name as v0; v0.1 is the content) | | PR | **Draft**. Title: `Layer, never delete. Agent Bridge door lock v0.1: 127.0.0.1, named origins, trusted minds, no shell` | | Marker | `v-agent-bridge-lock-v0` (kept, so every v0 grep still finds it). The bridge reports `bridgeLock: 'v0.1'`. | | Smoke line | `SMOKE_OK agent bridge lock v0.1` | | Sibling PRs | #118 has merged, so there is nothing to rebase around. This brick adds one ``: the new client loads just above this | | | 69704 / 69711 | `workshop.js` lazy loader (loads after the client, good) | | `tools/demo-economy.js` (`c32814c5…`), `tools/demo-economy-v2.js` (`17535368…`) | L13-27 / L19-38 | `post`/`get`, `postAs`/`getAs` helpers (Node fetch, no Origin) | | `desktop/src-tauri/tauri.conf.json` | L6 `"frontendDist": "../../docs"` | The Tauri shell serves `docs/` from `tauri://localhost` (macOS, Linux) or `http(s)://tauri.localhost` (Windows). v0 would have refused it. | ## Exact source excerpt, characters 11121–11603 Existing smoke locks you must keep green (`tests/smoke.js`, blob `d4db912a…`, unchanged): - **L3356-3359 CRITICAL LOCK 1A/1B.** `propose.js` must still contain exactly 3 `commitViaBridge(` and exactly 1 `/code/git/commit`. So don't mention either string in new comments inside `propose.js`. - **L8742-8744.** `workshop.js` must still contain `http://localhost:3141` and `runCodeTask`. So keep those literals. The client rewrites `localhost:3141` to `127.0.0.1:3141` at request time. ## Exact source excerpt, characters 11605–12065 **Known red on main (not ours).** `node tests/smoke.js` on `f86b317` already shows 3 failures and 3593 passes: "MORE_CARDS Row 1: Your Audit is first", "ollama: resolveOllamaBase auto-probe gated on isLikelyProxyOrigin()" and "ollama: getOllamaBaseUrl returns FL_OLLAMA_LOCAL / 127.0.0.1 fallback (never empty)". The same three also fail at `7621420`, before #118. This brick must add no new failures. Please name them in the PR body so nobody blames the lock. ## Exact source excerpt, characters 12067–12092 ## 3. Decisions (and why) ## Exact source excerpt, characters 12094–18082 1. **Pairing: a short code, typed once per device.** At launch the bridge prints `Pairing code: ABC-123`. The alphabet is 31 letters and digits (no 0/O/1/I/L), 6 characters, about 30 bits. A code is good for 10 minutes and works once. After 5 wrong tries a new code is printed, with a 30-second rest. In the app, Workshop → Code shows a small card with a code field, a name field (prefilled, for example "Chrome on Mac") and one Pair button. - **Why a code at all?** A web page can't read files on disk, and `http://localhost:*` is on the allowlist for local previews, so any local dev page or one XSS bug could otherwise pair silently. A code a person reads and types is one relational gesture (AUTONOMY.md Principle 3). It's the speaker-pairing gesture Mom already knows, and she never types a command. - **Trusted origins still pair.** Adding an origin to the trust list lets it knock. It does not hand it a token. - **Why not six words?** They need a wordlist file. Six clear characters ship with zero new assets. It's a fine later polish. 2. **Persistent per-device tokens (E1).** On a good `POST /pair` the bridge makes 32 random bytes (hex) for that device and returns them once. It stores only `sha256(token)` in `~/.freelattice/agent-bridge-trusted.json`, mode 0600, written atomically (temp file, then rename). Each entry is `{ id (8 hex), name, origin, kind: browser|tool|cli|remote, scopes, tokenSha256, createdAt, lastUsedAt, expiresAt: null }`. Lookups hash the presented token and compare with `crypto.timingSafeEqual`. - **No expiry by default.** Kirk may set `"idleExpiryDays": 180` in the file if he wants unused devices to lapse. `lastUsedAt` is saved at most once a minute. - **`FL_BRIDGE_EPHEMERAL=1`** restores v0: paired devices live in memory only and are forgotten when the bridge stops. The heartbeat says `trustedMinds: 'ephemeral'`, and the app says so after pairing. - **An unreadable trust file** is renamed aside to `agent-bridge-trusted.json.unreadable-