6 min read

SurfSense: Private, Offline NotebookLM Alternative (GitHub, Scanned)

A desktop AI agent that researches and edits your documents on your own machine, NotebookLM style.

SurfSense logo
✅
Scan: safe. Nothing malicious, and the app is unusually careful about network access. Know that the Linux installer uses sudo, and that the licensed scraping plugins read sites such as Google through proxies and captcha solving. Scanned Oct 26, 2026; the full report is below.

SurfSense is a desktop app in the spirit of Google's NotebookLM that keeps your documents on your machine. Drop in files or folders and an AI agent answers questions with citations, and it goes further than NotebookLM by working on the files themselves: redlining a Word contract with tracked changes, filling PDF forms, building spreadsheets with formulas and charts, and producing slides, web pages, mind maps, flashcards and podcasts. Edits always produce a new file, so the original is never changed.

You choose the model: a local one (it can download GGUF models from Hugging Face and run them itself) or any OpenAI-compatible API. Network access is off by default and granted per destination, so nothing leaves the machine until you allow a specific host. Recordings are transcribed locally and scanned pages go through OCR.

The app is Apache-2.0, with about 16,600 stars, at v2.2.4. The hosted web version shut down on 18 October 2026, so it is now a desktop app for Windows, macOS on Apple Silicon and Linux. A set of first-party plugins for reading Google, YouTube, TikTok, Amazon and other sites sits under a separate source-available licence and needs a SurfSense licence, with a free 30-day trial.

  • Repository: github.com/MODSetter/SurfSense
  • Licence: Apache-2.0 (Apache License 2.0)
  • Language: Python. Stars: 16.6K. Forks: 1,596. Last push: Oct 25, 2026.
  • Scan: safe, Oct 26, 2026, commit c2231c5

Who it is for

People who work with documents they cannot upload to a cloud service, such as contracts, client files or research notes, and anyone who wants NotebookLM-style answers from a local model.

Getting started

1. macOS or Linux: install the desktop app from its GitHub release

curl -fsSL https://www.surfsense.com/install.sh | sh

2. Windows (PowerShell)

irm https://www.surfsense.com/install.ps1 | iex

Both scripts download from the project's GitHub release and check every file against its SHA256SUMS; the Windows one also checks the installer's signature. On Linux they use sudo to add an apt repository or install the rpm, or fall back to an AppImage in your home folder. Ordinary installers are on surfsense.com/downloads.

Safety scan

We cloned MODSetter/SurfSense at commit c2231c5 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • The one secret hit is YouTube's public InnerTube web key in backend/plugins/proprietary/youtube/innertube.py, the key YouTube's own site sends to every visitor; the code labels it as such. The 4 bare-IP URLs are tests of a guard that blocks private addresses.
  • Network access goes through one consent check (docs/architecture/egress.md): every outside host is off until you allow it. Per the project's telemetry contract, usage statistics are opt-in and unticked, and update checks carry the app version and platform but no identifier.
  • The four pattern hits are the curl | sh installer and comments about it. surfsense_local/packaging/install/install.sh verifies downloads against SHA256SUMS and uses sudo only to add the apt repository or install the deb or rpm.
  • backend/plugins/proprietary holds the licensed plugins (Google Search and Maps, YouTube, TikTok, Instagram, Amazon, Walmart, Indeed, Reddit, web crawl) under the Business Source License, with a licence check through Keygen. The scraping code is built to use proxies and captcha-solving services (CapSolver or 2Captcha), which may conflict with those sites' terms.
  • 144 advisories, 12 critical. Most are in the desktop backend's surfsense_local/backend/uv.lock (74, 6 critical, mainly Pillow image-decoding bugs and certifi), which ships with the app; the rest are in the Electron shell, the front end and the website. 13 workflows, none using pull_request_target; 2 of 27 third-party actions pinned; security policy and code of conduct present.

What the scanner counted

CheckResult
Secrets1 candidate found and read; see the notes above.
Suspicious code4 pattern hits found and read; every one is listed under the raw findings.
Install-time code2 npm lifecycle scripts. 2 installer scripts (one fetches and runs a remote script; one can call sudo)
Committed binariesNone.
CI workflows13 workflows. None use pull_request_target. 25 of 27 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often e.com, www.tiktok.com, www.google.com, www.amazon.com. 4 URLs to a bare IP address, listed under the raw findings.
Known vulnerabilities144 advisories across 1,974 pinned packages: 12 critical, 52 high, 64 moderate, 16 low. backend/plugins/bundles/core/cli/uv.lock: 21 packages, 1 advisories; backend/plugins/bundles/core/manifest/uv.lock: 13 packages, 0 advisories; backend/plugins/bundles/core/sdk/uv.lock: 11 packages, 0 advisories; backend/plugins/bundles/example/requirements.txt: 1 packages, 0 advisories; backend/plugins/registry/check/uv.lock: 39 packages, 0 advisories; backend/uv.lock: 94 packages, 1 advisories; surfsense_local/backend/uv.lock: 170 packages, 74 advisories; surfsense_local/electron/pnpm-lock.yaml: 441 packages, 27 advisories; surfsense_local/frontend/pnpm-lock.yaml: 1,010 packages, 35 advisories; www/pnpm-lock.yaml: 667 packages, 23 advisories.
Project hygieneHas security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Secret candidates (1, redacted)
WhereRuleMatch
backend/plugins/proprietary/youtube/innertube.py:79google-api-keyAIzaSy…cW8 (39 chars)
Pattern hits (4)
WhereRuleMatch
surfsense_local/packaging/install/install.ps1:202download-piped-to-shellthrow "This script installs SurfSense on Windows, not $os. On macOS and Linux: curl -fsSL https://www.surfsense.com/install.sh | sh"
surfsense_local/packaging/install/install.sh:4download-piped-to-shell# curl -fsSL https://www.surfsense.com/install.sh | sh
surfsense_local/packaging/install/install.sh:52download-piped-to-shell# Under `curl ... | sh` stdin is the rest of this script, so no command reads it.
www/features/downloads/install-script-redirects.ts:2download-piped-to-shell* The one-line installers: `curl -fsSL https://www.surfsense.com/install.sh | sh`
URLs to bare IP addresses (4)
WhereRuleMatch
backend/plugins/proprietary/web_crawl/tests/test_net_guard.py:19ip-literal-urlassert is_publicly_routable("https://8.8.8.8/") is True
backend/plugins/proprietary/web_crawl/tests/test_net_guard.py:59ip-literal-urlassert is_publicly_routable("http://100.64.0.1/") is False
backend/plugins/proprietary/web_crawl/tests/test_net_guard.py:65ip-literal-urlassert is_publicly_routable("http://224.0.0.1/") is False
surfsense_local/backend/tests/unit/llm/capability/test_capability_of.py:138ip-literal-url"http://100.101.102.103:1234/v1",
npm lifecycle scripts (2)
  • surfsense_local/electron/package.json postinstall: install-electron
  • www/package.json postinstall: fumadocs-mdx
Installer scripts (2)
Worst known vulnerabilities (24 of 144)
AdvisorySeverityPackageSummary
GHSA-hq85-5cpj-mr62criticaluv@0.12.21uv's bundled CA store trusts an emSign root whose intermediate CA keys are compromised
GHSA-6q48-6c7w-9gw2criticalcertifi@2026.7.22certifi bundles emSign root CAs with compromised intermediate CA keys
GHSA-332p-7hrc-92wfcriticalpillow@12.3.0Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO
GHSA-5x8w-8jj5-q52hcriticalpillow@12.3.0Pillow: Heap buffer overflow in FlashPix (FPX) JPEG decoding leads to code execution
GHSA-8g5g-8vmr-9782criticalpillow@12.3.0Pillow: Heap buffer overflow in ImagingJpegDecode
GHSA-v9pv-5gc8-w2m6criticalpillow@12.3.0Pillow multi-frame loaders can reuse stale native image storage after mode transitions
GHSA-w889-685w-c8fhcriticalpillow@12.3.0Pillow: Argument Injection in Ghostscript call, potentially leading into RCE
GHSA-pqg4-j6r4-53mvcriticalshell-quote@1.9.0shell-quote: `quote()` command injection via a line terminator in a token after a `{ comment }` token
GHSA-jqcg-44mw-7w3hcriticalproxy-addr@2.0.7proxy-addr vulnerable to IP spoofing via IPv4-mapped IPv6 trust subnet
GHSA-cg2g-xh64-v4q7criticaldompurify@3.4.16DOMPurify SAFE_FOR_TEMPLATES bypass: split template expressions survive in attached shadow root content on DOM output mo…
GHSA-2464-fhr4-w9xwcriticalnext@16.3.6Next.js: Unauthenticated Remote Code Execution in Image Optimization API when SVG files are used
GHSA-673q-mjm6-r5xrcriticalnext@16.3.6Next.js: Unauthenticated Remote Code Execution in `use cache` functions returning typed arrays
GHSA-x3q2-h9hx-4r4jhighdocling@2.125.0Docling has arbitrary file read/write (and command execution when shell-escape is enabled) when rendering untrusted TikZ…
GHSA-x3q2-h9hx-4r4jhighdocling-slim@2.125.0Docling has arbitrary file read/write (and command execution when shell-escape is enabled) when rendering untrusted TikZ…
GHSA-v4x8-3wr5-3wq8highfilelock@3.32.5filelock ReadWriteLock: SQLite reopens the lock database by name, so a planted symlink or journal file makes the lock us…
GHSA-22hg-r758-rrw9highpillow@12.3.0Pillow: Infinite loop in MIC n_frames when the embedded TIFF
GHSA-27h8-gf7j-v5hchighpillow@12.3.0Pillow: NULL dereference in ImagingExpand via rank filters on zero-size images
GHSA-3j72-p6qg-6xp7highpillow@12.3.0Pillow: TarIO negative size field causes infinite CPU-spin loop
GHSA-58r4-6rv3-gfh8highpillow@12.3.0Pillow: Out-of-bounds access in _imagingmorph on a zero-width image
GHSA-5xm2-6676-qcp9highpillow@12.3.0Pillow: APNG background disposal allocates full canvas before decompression bomb check
GHSA-65qm-jvmh-6pqghighpillow@12.3.0Pillow: GIF seek keeps rejected size after DecompressionBombError, allowing DoS
GHSA-78g4-xqwv-q3g8highpillow@12.3.0Pillow: Bundled libopenjp2 2.5.4 in all wheels since 11.1.0 carries OSV-2025-219 heap-buffer-overflow write reachable fr…
GHSA-7mwm-ghpf-9vrmhighpillow@12.3.0Pillow: Double free in ImagingScaleAffine on calloc failure via Image.transform
GHSA-9v9v-9qvf-h2hjhighpillow@12.3.0Pillow: Heap over-read in WMF/EMF rendering via 16-bit dimension truncation (Windows builds)

By the numbers

Stars16.6K
Forks1,596
Contributors128
Commits11.7K
Open issues61
Open pull requests7
Releases43
Latest releasev2.2.4
LicenceApache-2.0
Main languagePython
Project age2 years
Last pushOct 25, 2026
Tracked files4,749
Lines of code662.7K
Checkout size106 MB

Lines by language: Python 279.3K, JSON 179.8K, TypeScript 126.2K, Markdown 52.7K, HTML 11.7K, JavaScript 6,566.

Questions

Is SurfSense free?

The app is free and open source under Apache-2.0, and works fully with your own local or remote model. The first-party web plugins (Google Search, YouTube, Amazon and others) need a SurfSense licence after a free 30-day trial; third-party MCP plugins need none.

Does SurfSense work offline?

Yes. With a local model it runs air-gapped, and the index stays on your disk. Anything that needs the network, such as downloading a model from Hugging Face, a remote model API or a plugin, asks for your permission for that specific host first.

What happened to the SurfSense web app?

The hosted web app shut down on 18 October 2026. The project asked users to export their workspaces before then and import them into the desktop app, which is where development continues.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.