Karakeep, formerly Hoarder, is a read-it-later app you host yourself. Save a link, a note, an image or a PDF from the browser extension, the iOS or Android app, or an RSS feed, and it fetches the title and preview, archives the full page against link rot, can archive videos with yt-dlp, runs OCR on images, and indexes everything for full-text and semantic search. A language model then tags and summarizes each item, so a pile of saved links becomes something you can actually find again.
It is on this list because the AI part works with a local model. Tagging and summaries can use OpenAI or Ollama, so a homelab can run the whole thing without sending bookmarks anywhere. Lists can be shared with other people, a rules engine automates filing, highlights are kept, and there are importers for Chrome, Pocket, Omnivore and Linkwarden, plus a CLI and agent skills for AI assistants.
Karakeep has about 29,000 stars, is licensed AGPL-3.0, and is owned by Localhost Labs Ltd, which also sells a managed Karakeep Cloud. It was started by Mohamed Bassem, a systems engineer, as a self-hosted replacement for Pocket.
- Repository: github.com/karakeep-app/karakeep
- Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
- Language: TypeScript. Stars: 29.4K. Forks: 1,549. Last push: Sep 29, 2026.
- Scan: safe, Sep 27, 2026, commit f908b02
Who it is for
Self-hosters and data hoarders who save more than they read, people who lost their library when Pocket and Omnivore shut down, and anyone who wants AI tagging without handing over their reading history.
Getting started
1. Make a folder and download the Docker Compose file
mkdir karakeep-app && cd karakeep-app && wget https://raw.githubusercontent.com/karakeep-app/karakeep/main/docker/docker-compose.yml2. Create a .env with two random secrets (generate them with openssl rand -base64 36)
printf 'KARAKEEP_VERSION=release\nNEXTAUTH_SECRET=change_me\nMEILI_MASTER_KEY=change_me_too\nNEXTAUTH_URL=http://localhost:3000\n' > .env3. Optional: add an OpenAI key for tagging, or configure Ollama instead (see the AI providers docs)
echo 'OPENAI_API_KEY=sk-...' >> .env4. Start it and sign up at http://localhost:3000
docker compose up -dReplace both secrets before starting, and change NEXTAUTH_URL to your server's address if you will reach it from other devices. Without an AI provider, Karakeep still saves and searches everything; it just will not tag automatically.
Safety scan
We cloned karakeep-app/karakeep at commit f908b02 on Sep 27, 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.
- No secrets, no packed code and no bare-IP URLs across 2,122 files: about 150,000 lines of TypeScript plus 240,000 lines of JSON, mostly translations and data. The single pattern hit is karakeep-linux.sh line 407, the bare-metal install script enabling its own meilisearch and karakeep systemd services, which is what an installer is for; Docker users never run it.
- The only npm hook is Husky's prepare for contributors. Hosts in the code are Karakeep's own domains, Stripe checkout for the paid cloud, store links and test domains; we found no analytics service. The crawler does fetch every page you bookmark, by design.
- 52 known advisories, none critical (25 high), in pnpm-lock.yaml (3,377 packages). Most are build and test tooling: brace-expansion, braces, webpack-dev-middleware, esbuild and an old Faker used for seed data. Runtime ones: nodemailer 9.1.1 (three high), used for account emails, and undici 7.29.0 (two high), arriving with the cheerio HTML parser the crawler uses.
- drizzle-orm 0.33.0, flagged for SQL injection through unescaped identifiers, is used only by liteque, the job queue, which builds identifiers from its own fixed table names; the app's database layer uses drizzle-orm 0.45.2.
- 10 workflows, none using pull_request_target; 16 of 17 third-party actions are pinned to a commit. Security policy, licence and contributing guide present; no Dependabot or CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | None found. |
| Suspicious code | 1 pattern hit found and read; every one is listed under the raw findings. |
| Install-time code | 1 npm lifecycle script. 1 installer script |
| Committed binaries | None. |
| CI workflows | 10 workflows. None use pull_request_target. 1 of 17 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often github.com, google.com, cloud.karakeep.app, example1.com. No URLs to bare IP addresses. |
| Known vulnerabilities | 52 advisories across 3,377 pinned packages: 0 critical, 25 high, 22 moderate, 5 low. pnpm-lock.yaml: 3,377 packages, 52 advisories. |
| Project hygiene | Has security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL. |
| OpenSSF Scorecard | Not 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.
Pattern hits (1)
| Where | Rule | Match |
|---|---|---|
| karakeep-linux.sh:407 | persistence | systemctl enable -q --now meilisearch.service karakeep.target |
npm lifecycle scripts (1)
package.jsonprepare:husky
Installer scripts (1)
- start-dev.sh, 107 lines
Worst known vulnerabilities (24 of 52)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-qxc2-j82w-r537 | high | @faker-js/faker@5.5.3 | Faker: helpers.fake exploitable into arbritary code execution |
| GHSA-6j4f-fj2g-mc7p | high | brace-expansion@1.1.18 | brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion |
| GHSA-qhr7-859c-m2p7 | high | brace-expansion@1.1.18 | brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion |
| GHSA-6j4f-fj2g-mc7p | high | brace-expansion@2.1.4 | brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion |
| GHSA-qhr7-859c-m2p7 | high | brace-expansion@2.1.4 | brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion |
| GHSA-6j4f-fj2g-mc7p | high | brace-expansion@5.0.9 | brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion |
| GHSA-qhr7-859c-m2p7 | high | brace-expansion@5.0.9 | brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion |
| GHSA-vfj7-8cjw-p6xm | high | braces@3.0.3 | braces vulnerable to stack-exhaustion denial of service through deeply nested patterns |
| GHSA-j22f-vq7h-c4qm | high | devalue@5.9.2 | devalue: `stringify`/`uneval` serialize shared memory |
| GHSA-mcm9-63f2-9j32 | high | devalue@5.9.2 | devalue: Repeated primitive strings cause quadratic expansion in uneval |
| GHSA-r9w8-h9r3-54w4 | high | devalue@5.9.2 | devalue: Custom ArrayBuffer revivers can bypass typed-array allocation validation |
| GHSA-x5rw-q4pp-hg5g | high | devalue@5.9.2 | devalue: stringifyAsync can cause an unhandled rejection despite a caught returned promise |
| GHSA-gpj5-g38j-94v9 | high | drizzle-orm@0.33.0 | Drizzle ORM has SQL injection via improperly escaped SQL identifiers |
| GHSA-ch52-4w7c-c8xp | high | http-cache-semantics@4.2.0 | http-cache-semantics max-stale handling can disclose cross-user cached responses |
| GHSA-5p2g-fcmc-qvqq | high | image-size@1.2.1 | image-size: JXL and HEIF parsers allow denial of service through infinite loops |
| GHSA-w3rx-r6r6-pgpr | high | image-size@1.2.1 | image-size: ICNS parser allows denial of service through an infinite loop |
| GHSA-6h2x-m376-mqjq | high | joi@17.13.6 | joi: Quadratic regular-expression backtracking in `Joi.string().isoDate()` |
| GHSA-22p9-wv53-3rq4 | high | linkify-it@2.2.0 | LinkifyIt#match scan loop has quadratic algorithmic complexity |
| GHSA-v245-v573-v5vm | high | linkify-it@2.2.0 | linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on attacker text |
| GHSA-86w9-cpqp-85rv | high | node-forge@1.4.0 | node-forge RSA PKCS#1 v1.5 signature verification accepts extra nested DigestAlgorithm elements |
| GHSA-prgh-xp8r-p3m5 | high | nodemailer@9.1.1 | Nodemailer addressparser: O(n^2) on comment-joined addresses enables a remote DoS (reachable via mailparser) |
| GHSA-v53p-9fqp-m79j | high | nodemailer@9.1.1 | Nodemailer: Quadratic backtracking in the addressparser free-text fallback allows remote denial of service |
| GHSA-rfgv-xxqx-mfg5 | high | undici@7.29.0 | undici vulnerable to Denial of Service via unrequested WebSocket subprotocol |
| GHSA-w293-vg96-wgc3 | high | undici@7.29.0 | undici vulnerable to TLS certificate validation bypass via dropped connect options in BalancedPool |
By the numbers
| Stars | 29.4K |
|---|---|
| Forks | 1,549 |
| Contributors | 200 |
| Commits | 2,260 |
| Open issues | 616 |
| Open pull requests | 108 |
| Releases | 51 |
| Latest release | v0.33.2 |
| Licence | AGPL-3.0 |
| Main language | TypeScript |
| Project age | 2 years |
| Last push | Sep 29, 2026 |
| Tracked files | 2,122 |
| Lines of code | 432.8K |
| Checkout size | 71 MB |
Lines by language: JSON 240.4K, TypeScript 149.6K, Markdown 38.1K, YAML 1,641, CSS 866, Shell 844.
Questions
Is Karakeep free?
Self-hosted, yes: Karakeep is AGPL-3.0 and free, with no feature limits. AI tagging with OpenAI costs a small amount per item through your own key, and nothing with a local Ollama model. If you would rather not run a server, Karakeep Cloud at cloud.karakeep.app is a paid managed version that funds development.
Can Karakeep import my Pocket or Omnivore bookmarks?
Yes. It has importers for Chrome bookmarks, Pocket, Omnivore, Linkwarden and Tab Session Manager, and can keep in sync with browser bookmarks through floccus.
Can I try Karakeep before installing it?
Yes. A read-only demo runs at try.karakeep.app with the login demo@karakeep.app and the password demodemo. It is seeded with sample content and resets, so do not save anything you want to keep.
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.
