# Iris — the decision record Written 2026-08-20, the day the plugin was designed in conversation and first laid down. The decisions below are the conversation's residue; where one reverses later, record it here rather than deleting it. ## Not a fork Upstream (`iris-screenshot` on crates.io, github.com/brijr/iris) is used as-is. The obvious reason to fork was authentication — iris has no `--cookie`/`--header` flags — and the decision was that flags are the wrong mechanism anyway: credentials on command lines end up in transcripts and process lists. The session lives in a browser profile the *user* signs into through the desktop plugin, and the capture borrows a copy. The human authenticates; the agent only ever captures. ## Build with cargo, in the sidecar The owner's call: cargo is a declared dependency, installed before iris, and iris compiles from source on the machine. No prebuilt releases to host, no artefacts to cross-compile, and the plugin has no binary of its own to distribute. The platform's installer deliberately has no build step (`osDependencies` installs packages and fetches release binaries, nothing more), so the build lives in the one process the platform starts for a plugin: the sidecar. First start compiles; later starts probe and skip. `--locked` honours upstream's lockfile — the platform's frozen-lockfile principle, one language over. Known consequence, accepted: **distro cargo is usually too old.** Upstream declares `rust-version = 1.88`; Ubuntu's apt cargo is ~1.75. The manifest still declares distro packages (right on Arch/Fedora, and the honest default), and the sidecar checks the version *before* building and prints the rustup one-liner when it refuses. Auto-running rustup's curl-pipe-sh was considered and rejected — that is a decision the owner makes at a terminal, not one a sidecar makes silently. ## No browser in `osDependencies` Three reasons. Ubuntu's apt `chromium` is a snap stub, so a truthful package map does not exist across distros. The user prefers Brave, which is Chromium underneath — same CDP, same profile format — so the right declaration is "any of a family", which `osDependencies` cannot express. And the browser this plugin *wants* is the desktop plugin's, because a browser nobody can see is a browser nobody can sign in to. `bin/iris-browser` resolves brave → chromium → chrome at run time and fails with a sentence when none exists. ## The profile contract `~/.config/officer/browser-profile` is the persistent, per-user profile. The desktop plugin's browser should launch with `--user-data-dir` pointing there and `--password-store=basic` (portable cookie encryption — keyring-backed cookies do not survive copying). Captures never open that profile directly: the wrapper copies `Local State`, `Default/Cookies` and `Default/Login Data` into a throwaway profile, which avoids Chrome's profile lock entirely and makes the persistent profile read-only to captures. This contract predates the desktop plugin. When that plugin lands, this path is what it must honour. ## Open items, in order of bite 1. **Nothing has run.** No live install, no capture, no build. The whole verification table is owed. 2. **How chromiumoxide launches** decides which wrapper branch runs. If it passes its own `--user-data-dir`, the wrapper seeds cookies into that directory; if not, the wrapper appends its own ephemeral one. Both are written; neither is proven. Also unproven: that a seeded cookie copy is accepted (Chrome ties cookie encryption to `Local State`, which is copied for exactly that reason). 3. **Members.** The wrapper isolates per-user by `$HOME`, but the binary lands in the *owner's* `~/.local/bin`, and a confined member's agent cannot read it there. If members should capture, the binary needs a shared home. Deferred until the desktop plugin defines the member story. 4. **Cookie copy under a live browser.** Copying a SQLite file mid-write can catch a torn state; in practice the cookie DB is WAL and short-lived reads are fine, but "in practice" is not verified. 5. **`permissions: []`** is honest today (nothing platform-gated to protect) but means the owner cannot dock-gate or role-gate the plugin's existence. Revisit if an api/ or web/ arrives.