Browser clipper

Capture a visible selection or Reader-style page into local GNO with previewed Markdown and exact provenance.

The GNO Browser Clipper is a local, unpacked Chromium Manifest V3 extension shipped with the GNO npm package. It captures only after you open the popup and request a selection or Reader extraction from the active top-level tab. You preview the normalized Markdown, destination, tags, collision result, warnings, and provenance before confirming a write.

This is an unpacked local extension, not a Chrome Web Store listing. Firefox is not supported.

Install the unpacked extension

  1. Install or update GNO with bun install -g @gmickel/gno.
  2. Open chrome://extensions in Chromium or Chrome and enable Developer mode.
  3. Choose Load unpacked and select ~/.bun/install/global/node_modules/@gmickel/gno/browser-extension/dist. If you installed with npm, use $(npm root -g)/@gmickel/gno/browser-extension/dist.
  4. Pin the GNO Browser Clipper if you want it on the toolbar.

The manifest version follows the installed GNO package version. The package also contains browser-extension/artifacts/gno-browser-clipper-v<version>.zip and the adjacent .zip.sha256 checksum. You can verify and unzip that reproducible archive into a stable local directory instead of loading browser-extension/dist directly. Keeping the same unpacked directory preserves the extension identity. Moving it to a different path can produce a different Chromium extension ID; pair again if that happens.

cd ~/.bun/install/global/node_modules/@gmickel/gno/browser-extension/artifacts
shasum -a 256 -c gno-browser-clipper-v<version>.zip.sha256

Update

bun install -g @gmickel/gno
# Then reload the extension from browser-extension/dist

Return to chrome://extensions and click Reload on GNO Browser Clipper. If the package manager or a manual copy changed the unpacked directory, remove the old extension, load the new directory, and complete pairing again.

Pair with local GNO

gno serve
  1. Open the clipper popup and pair with http://127.0.0.1:3000.
  2. Chrome 142 and newer may first ask whether the extension can access the local network. Approve that browser prompt.
  3. The clipper opens the local GNO approval page. Type the eight-digit code shown in the popup into that page and approve. A pairing request expires after five minutes.
  4. The extension receives a grant that only allows captures from this extension. The approval page never sees or shows that grant.

Pairing is separate from the REST API and MCP credentials. A REST or MCP bearer token cannot authorize clipping, and enabling gateway writes does not create a clipper grant.

Preview and capture

  1. Select rendered text on the active page, or leave no selection to request the visible Reader-style extraction.
  2. Open the clipper. Review or edit the Markdown, select a collection and path or folder, add tags, and choose the collision policy.
  3. Request a preview. Any content, source metadata, destination, tag, extraction mode, or authenticated-content change makes that preview stale and requires a fresh one.
  4. Confirm the unchanged preview to write the note.

Selection mode preserves the exact selected text in provenance. Reader mode sends a constrained visible-content AST: paragraphs, headings, quotes, lists, code, horizontal rules, text, and validated HTTP(S) links. GNO renders canonical Markdown on the server. Raw HTML is never a trusted wire format.

Duplicate planning reports one of created, opened_existing, created_with_suffix, overwritten, or conflict. GNO opens an existing note only when its stored clip identity matches. Changed provenance or a changed final body conflicts instead of silently merging unrelated evidence.

Provenance and warnings

Browser-clip provenance has four hashes with distinct ownership:

Warning codes are authenticated_visible_content, canonical_url_differs, edited_content, line_endings_normalized, reader_partial, selection_truncated, spa_snapshot, and unicode_normalized. Authenticated-visible means you chose to capture content already rendered for your signed-in browser; GNO does not transfer the browser session or bypass access controls.

Interrupted writes and recovery

If a write is interrupted, the extension keeps it, one at most, so you can finish it. Reopening the popup shows its destination and source. Choose Retry saved write to reuse the same payload, digest, and key, or Stop recovery to discard it. While that write is pending, the popup hides normal extraction controls so a different capture cannot replace the recovery state.

If a restart removed the server preview, GNO refreshes the preview only for that same payload and resumes with the same idempotency key. Receipt replay is safe. A changed payload, path, file, plan, or recovery key fails closed; recovery does not choose another suffix or destination.

The clipper uses its idempotency key, not the request ID other capture surfaces accept; a clip body with requestId is rejected with 400 CLIPPER_INVALID_REQUEST.

Revoke access

Use Revoke in the popup. Revocation persists in GNO, and the extension clears its local grant and pending write. Expired, revoked, or server-restarted pairing state requires a new visible pairing when no usable grant remains.

Privacy and storage

The extension does not read browser history, cookies, session tokens, background tabs, arbitrary iframe documents, images, media, raw HTML, or hidden page content. It does not use OAuth, sync captured content to a cloud service, send telemetry, fetch the source URL from GNO, or capture autonomously.

Local gateway boundary

The clipper routes exist only on gno serve’s loopback listener, and they accept requests only from this machine and from the paired chrome-extension:// origin. When gno serve listens on a LAN or public address, the clipper routes are not available there.

Chrome Local Network Access is a separate browser permission. Chrome asks for it only from the open popup, so start pairing from the popup.

Request sizes, rates, and concurrency are limited. Unfinished pairings and previews live in memory and are cleared when gno serve restarts.

Routes and response contract

Versioned success bodies use schemaVersion: "1.0" and reject unknown fields. A valid opened_existing receipt uses HTTP 200. created, created_with_suffix, and overwritten use HTTP 202. A valid provenance conflict receipt uses HTTP 409. Other failures use the closed clipper-error@1.0 code/status matrix; unknown versions, fields, codes, non-JSON bodies, and impossible status/body combinations fail closed.

CLIPPER_OFFLINE, CLIPPER_INVALID_RESPONSE, and CLIPPER_CLIENT are local client classifications, not wire error codes.