Integrations
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 packaging and parity are not claimed.
bun install -g @gmickel/gno.chrome://extensions in Chromium or Chrome and enable Developer mode.~/.bun/install/global/node_modules/@gmickel/gno/browser-extension/dist. If you installed with npm, use $(npm root -g)/@gmickel/gno/browser-extension/dist.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.sha256bun install -g @gmickel/gno
# Then reload the extension from browser-extension/distReturn 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.
gno servehttp://127.0.0.1:3000.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.
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.
Browser-clip provenance has four hashes with distinct ownership:
extractionHash — canonical extracted selection or Reader content before user editsfinalBodyHash — final Markdown after allowed editsclipIdentity — stable identity used for safe duplicate handlingpreviewDigest — server-owned digest binding the exact preview to the writeWarning 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.
Before committing, the service worker stores at most one pending {payload, previewDigest, idempotencyKey} logical write. 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.
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.
http://127.0.0.1 GNO listener.chrome.storage.session holds the transient pair ID, code, expiry, exact extension origin, and approval route.chrome.storage.local holds the loopback origin; grant ID, plaintext grant token, and expiry; and at most one pending payload, preview digest, and idempotency key. A pending selection or Reader extract can therefore remain locally stored until you retry or discard it.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.
The clipper routes exist only on gno serve’s loopback listener. Every extension request must come from an actual loopback peer, use the exact listener Host, and carry the exact paired chrome-extension:// Origin. CORS and Local/Private Network Access preflights allow only that origin, with no wildcard or credentials mode.
Chrome Local Network Access is an additional browser permission. Service workers cannot trigger its prompt, so only the user-activated popup performs the exact-origin, credentials: "omit" pairing-start request. Grant polling, preview, write, and revoke remain service-worker-only.
The safe CSRF-token GET accepts a browser-omitted Origin only whenSec-Fetch-Site: same-origin proves the request came from the local approval page. Approval POST still requires the exact same-origin Origin plus X-GNO-CSRF. Request bodies, rates, and concurrency are bounded. Unfinished pairings, one-time plaintext grant delivery, CSRF state, and preview tickets stay in memory and disappear when gno serve restarts. Non-loopback and public binds do not expose clipper routes.
POST /api/clipper/pair/start — begin pairingGET /api/clipper/pair/csrf — obtain the same-origin approval tokenPOST /api/clipper/pair/approve — approve the displayed codePOST /api/clipper/pair/:pairId — poll once for the grantPOST /api/capture/clip/preview — validate and plan without writingPOST /api/capture/clip — commit the unchanged previewPOST /api/clipper/revoke — revoke the scoped grantVersioned 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.