├── .dockerignore ├── .env.sample ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── feature_request.yml │ └── question.yml └── workflows │ ├── android.yml │ ├── ci.yml │ ├── cli.yml │ ├── docker.yml │ ├── extension.yml │ ├── ios.yml │ ├── mcp.yml │ └── sdk.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── apps ├── browser-extension │ ├── .gitignore │ ├── components.json │ ├── index.html │ ├── manifest.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── logo-128-darkmode.png │ │ ├── logo-128.png │ │ ├── logo-16-darkmode.png │ │ ├── logo-16.png │ │ ├── logo-48-darkmode.png │ │ ├── logo-48.png │ │ ├── logo-full-white.png │ │ ├── logo-full.png │ │ └── logo.png │ ├── src │ │ ├── BookmarkDeletedPage.tsx │ │ ├── BookmarkSavedPage.tsx │ │ ├── Layout.tsx │ │ ├── Logo.tsx │ │ ├── NotConfiguredPage.tsx │ │ ├── OptionsPage.tsx │ │ ├── SavePage.tsx │ │ ├── SignInPage.tsx │ │ ├── Spinner.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── background │ │ │ ├── background.ts │ │ │ └── protocol.ts │ │ ├── components │ │ │ ├── BookmarkLists.tsx │ │ │ ├── ListsSelector.tsx │ │ │ ├── TagList.tsx │ │ │ ├── TagsSelector.tsx │ │ │ └── ui │ │ │ │ ├── badge.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── command.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── input.tsx │ │ │ │ └── popover.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ ├── utils │ │ │ ├── ThemeProvider.tsx │ │ │ ├── css.ts │ │ │ ├── providers.tsx │ │ │ ├── settings.ts │ │ │ └── trpc.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── vite.config.ts ├── cli │ ├── .gitignore │ ├── .npmignore │ ├── package.json │ ├── src │ │ ├── commands │ │ │ ├── bookmarks.ts │ │ │ ├── lists.ts │ │ │ ├── tags.ts │ │ │ └── whoami.ts │ │ ├── index.ts │ │ ├── lib │ │ │ ├── globals.ts │ │ │ ├── output.ts │ │ │ └── trpc.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ └── vite.config.mts ├── landing │ ├── README.md │ ├── components.json │ ├── components │ │ └── ui │ │ │ └── button.tsx │ ├── index.html │ ├── lib │ │ └── utils.ts │ ├── package.json │ ├── postcss.config.cjs │ ├── public │ │ ├── app-store-badge.png │ │ ├── chrome-extension-badge.png │ │ ├── favicon.ico │ │ ├── firefox-addon.png │ │ ├── google-play-badge.webp │ │ ├── hero.webp │ │ ├── icons │ │ │ ├── apple-icon.png │ │ │ ├── icon.png │ │ │ ├── karakeep-full.svg │ │ │ ├── karakeep-text.svg │ │ │ ├── logo-full.svg │ │ │ ├── logo-icon.svg │ │ │ └── logo-text.svg │ │ ├── opengraph-image.png │ │ ├── screenshot.png │ │ └── twitter-image.png │ ├── src │ │ ├── App.tsx │ │ ├── Homepage.tsx │ │ ├── Privacy.tsx │ │ └── main.tsx │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── mcp │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bookmarks.ts │ │ ├── index.ts │ │ ├── lists.ts │ │ ├── shared.ts │ │ ├── tags.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── vite.config.mts ├── mobile │ ├── .gitignore │ ├── .npmrc │ ├── app.json │ ├── app │ │ ├── +not-found.tsx │ │ ├── _layout.tsx │ │ ├── dashboard │ │ │ ├── (tabs) │ │ │ │ ├── _layout.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── lists.tsx │ │ │ │ └── settings.tsx │ │ │ ├── _layout.tsx │ │ │ ├── archive.tsx │ │ │ ├── bookmarks │ │ │ │ ├── [slug] │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── info.tsx │ │ │ │ │ ├── manage_lists.tsx │ │ │ │ │ └── manage_tags.tsx │ │ │ │ └── new.tsx │ │ │ ├── favourites.tsx │ │ │ ├── lists │ │ │ │ ├── [slug].tsx │ │ │ │ └── new.tsx │ │ │ ├── search.tsx │ │ │ ├── settings │ │ │ │ └── theme.tsx │ │ │ └── tags │ │ │ │ └── [slug].tsx │ │ ├── error.tsx │ │ ├── index.tsx │ │ ├── sharing.tsx │ │ ├── signin.tsx │ │ └── test-connection.tsx │ ├── assets │ │ ├── adaptive-icon.png │ │ ├── blur.jpeg │ │ ├── icon.png │ │ ├── splash-white.png │ │ └── splash.png │ ├── babel.config.js │ ├── components │ │ ├── FullPageError.tsx │ │ ├── Logo.tsx │ │ ├── TailwindResolver.tsx │ │ ├── bookmarks │ │ │ ├── BookmarkAssetImage.tsx │ │ │ ├── BookmarkCard.tsx │ │ │ ├── BookmarkList.tsx │ │ │ ├── BookmarkTextMarkdown.tsx │ │ │ ├── TagPill.tsx │ │ │ └── UpdatingBookmarkList.tsx │ │ ├── navigation │ │ │ ├── stack.tsx │ │ │ └── tabs.tsx │ │ └── ui │ │ │ ├── ActionButton.tsx │ │ │ ├── Button.tsx │ │ │ ├── CustomSafeAreaView.tsx │ │ │ ├── Divider.tsx │ │ │ ├── FullPageSpinner.tsx │ │ │ ├── Input.tsx │ │ │ ├── PageTitle.tsx │ │ │ ├── Skeleton.tsx │ │ │ └── Toast.tsx │ ├── eas.json │ ├── globals.css │ ├── index.ts │ ├── lib │ │ ├── hooks.ts │ │ ├── providers.tsx │ │ ├── session.ts │ │ ├── settings.ts │ │ ├── trpc.ts │ │ ├── upload.ts │ │ └── utils.ts │ ├── metro.config.js │ ├── nativewind-env.d.ts │ ├── package.json │ ├── plugins │ │ ├── camera-not-required.js │ │ ├── network_security_config.xml │ │ └── trust-local-certs.js │ ├── tailwind.config.ts │ └── tsconfig.json ├── web │ ├── @types │ │ └── i18next.d.ts │ ├── README.md │ ├── app │ │ ├── admin │ │ │ ├── background_jobs │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── overview │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── users │ │ │ │ └── page.tsx │ │ ├── api │ │ │ ├── [[...route]] │ │ │ │ └── route.ts │ │ │ ├── auth │ │ │ │ └── [...nextauth] │ │ │ │ │ └── route.tsx │ │ │ ├── bookmarks │ │ │ │ └── export │ │ │ │ │ └── route.tsx │ │ │ ├── health │ │ │ │ └── route.ts │ │ │ └── trpc │ │ │ │ └── [trpc] │ │ │ │ └── route.ts │ │ ├── apple-icon.png │ │ ├── dashboard │ │ │ ├── @modal │ │ │ │ ├── (.)preview │ │ │ │ │ └── [bookmarkId] │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── [...catchAll] │ │ │ │ │ └── page.tsx │ │ │ │ └── default.tsx │ │ │ ├── archive │ │ │ │ └── page.tsx │ │ │ ├── bookmarks │ │ │ │ └── page.tsx │ │ │ ├── cleanups │ │ │ │ └── page.tsx │ │ │ ├── error.tsx │ │ │ ├── favourites │ │ │ │ └── page.tsx │ │ │ ├── feeds │ │ │ │ └── [feedId] │ │ │ │ │ └── page.tsx │ │ │ ├── highlights │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── lists │ │ │ │ ├── [listId] │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── not-found.tsx │ │ │ ├── preview │ │ │ │ └── [bookmarkId] │ │ │ │ │ └── page.tsx │ │ │ ├── search │ │ │ │ └── page.tsx │ │ │ └── tags │ │ │ │ ├── [tagId] │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ ├── favicon.ico │ │ ├── icon.png │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── public │ │ │ ├── layout.tsx │ │ │ └── lists │ │ │ │ └── [listId] │ │ │ │ ├── not-found.tsx │ │ │ │ └── page.tsx │ │ ├── settings │ │ │ ├── ai │ │ │ │ └── page.tsx │ │ │ ├── api-keys │ │ │ │ └── page.tsx │ │ │ ├── assets │ │ │ │ └── page.tsx │ │ │ ├── broken-links │ │ │ │ └── page.tsx │ │ │ ├── feeds │ │ │ │ └── page.tsx │ │ │ ├── import │ │ │ │ └── page.tsx │ │ │ ├── info │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ ├── rules │ │ │ │ └── page.tsx │ │ │ └── webhooks │ │ │ │ └── page.tsx │ │ └── signin │ │ │ └── page.tsx │ ├── components.json │ ├── components │ │ ├── DemoModeBanner.tsx │ │ ├── KarakeepIcon.tsx │ │ ├── admin │ │ │ ├── AddUserDialog.tsx │ │ │ ├── AdminCard.tsx │ │ │ ├── AdminNotices.tsx │ │ │ ├── BackgroundJobs.tsx │ │ │ ├── ChangeRoleDialog.tsx │ │ │ ├── ResetPasswordDialog.tsx │ │ │ ├── ServerStats.tsx │ │ │ └── UserList.tsx │ │ ├── dashboard │ │ │ ├── BulkBookmarksAction.tsx │ │ │ ├── ChangeLayout.tsx │ │ │ ├── EditableText.tsx │ │ │ ├── GlobalActions.tsx │ │ │ ├── SortOrderToggle.tsx │ │ │ ├── UploadDropzone.tsx │ │ │ ├── bookmarks │ │ │ │ ├── AssetCard.tsx │ │ │ │ ├── BookmarkActionBar.tsx │ │ │ │ ├── BookmarkCard.tsx │ │ │ │ ├── BookmarkFormattedCreatedAt.tsx │ │ │ │ ├── BookmarkLayoutAdaptingCard.tsx │ │ │ │ ├── BookmarkMarkdownComponent.tsx │ │ │ │ ├── BookmarkOptions.tsx │ │ │ │ ├── BookmarkTagsEditor.tsx │ │ │ │ ├── BookmarkedTextEditor.tsx │ │ │ │ ├── Bookmarks.tsx │ │ │ │ ├── BookmarksGrid.tsx │ │ │ │ ├── BulkManageListsModal.tsx │ │ │ │ ├── BulkTagModal.tsx │ │ │ │ ├── DeleteBookmarkConfirmationDialog.tsx │ │ │ │ ├── EditBookmarkDialog.tsx │ │ │ │ ├── EditorCard.tsx │ │ │ │ ├── FooterLinkURL.tsx │ │ │ │ ├── LinkCard.tsx │ │ │ │ ├── ManageListsModal.tsx │ │ │ │ ├── NoBookmarksBanner.tsx │ │ │ │ ├── SummarizeBookmarkArea.tsx │ │ │ │ ├── TagList.tsx │ │ │ │ ├── TagModal.tsx │ │ │ │ ├── TagsEditor.tsx │ │ │ │ ├── TextCard.tsx │ │ │ │ ├── UnknownCard.tsx │ │ │ │ ├── UpdatableBookmarksGrid.tsx │ │ │ │ ├── action-buttons │ │ │ │ │ └── ArchiveBookmarkButton.tsx │ │ │ │ └── icons.tsx │ │ │ ├── cleanups │ │ │ │ └── TagDuplicationDetention.tsx │ │ │ ├── feeds │ │ │ │ └── FeedSelector.tsx │ │ │ ├── header │ │ │ │ ├── Header.tsx │ │ │ │ └── ProfileOptions.tsx │ │ │ ├── highlights │ │ │ │ ├── AllHighlights.tsx │ │ │ │ └── HighlightCard.tsx │ │ │ ├── lists │ │ │ │ ├── AllListsView.tsx │ │ │ │ ├── BookmarkListSelector.tsx │ │ │ │ ├── CollapsibleBookmarkLists.tsx │ │ │ │ ├── DeleteListConfirmationDialog.tsx │ │ │ │ ├── EditListModal.tsx │ │ │ │ ├── ListHeader.tsx │ │ │ │ ├── ListOptions.tsx │ │ │ │ ├── MergeListModal.tsx │ │ │ │ ├── PublicListLink.tsx │ │ │ │ ├── RssLink.tsx │ │ │ │ └── ShareListModal.tsx │ │ │ ├── preview │ │ │ │ ├── ActionBar.tsx │ │ │ │ ├── AssetContentSection.tsx │ │ │ │ ├── AttachmentBox.tsx │ │ │ │ ├── BookmarkHtmlHighlighter.tsx │ │ │ │ ├── BookmarkPreview.tsx │ │ │ │ ├── HighlightsBox.tsx │ │ │ │ ├── LinkContentSection.tsx │ │ │ │ ├── NoteEditor.tsx │ │ │ │ ├── TextContentSection.tsx │ │ │ │ └── highlights.ts │ │ │ ├── rules │ │ │ │ ├── RuleEngineActionBuilder.tsx │ │ │ │ ├── RuleEngineConditionBuilder.tsx │ │ │ │ ├── RuleEngineEventSelector.tsx │ │ │ │ ├── RuleEngineRuleEditor.tsx │ │ │ │ └── RuleEngineRuleList.tsx │ │ │ ├── search │ │ │ │ ├── QueryExplainerTooltip.tsx │ │ │ │ └── SearchInput.tsx │ │ │ ├── sidebar │ │ │ │ └── AllLists.tsx │ │ │ └── tags │ │ │ │ ├── AllTagsView.tsx │ │ │ │ ├── BulkTagAction.tsx │ │ │ │ ├── DeleteTagConfirmationDialog.tsx │ │ │ │ ├── EditableTagName.tsx │ │ │ │ ├── MergeTagModal.tsx │ │ │ │ ├── MultiTagSelector.tsx │ │ │ │ ├── TagAutocomplete.tsx │ │ │ │ ├── TagOptions.tsx │ │ │ │ ├── TagPill.tsx │ │ │ │ └── TagSelector.tsx │ │ ├── public │ │ │ └── lists │ │ │ │ ├── PublicBookmarkGrid.tsx │ │ │ │ └── PublicListHeader.tsx │ │ ├── settings │ │ │ ├── AISettings.tsx │ │ │ ├── AddApiKey.tsx │ │ │ ├── ApiKeySettings.tsx │ │ │ ├── ChangePassword.tsx │ │ │ ├── DeleteApiKey.tsx │ │ │ ├── FeedSettings.tsx │ │ │ ├── ImportExport.tsx │ │ │ ├── UserDetails.tsx │ │ │ ├── UserOptions.tsx │ │ │ ├── WebhookEventSelector.tsx │ │ │ └── WebhookSettings.tsx │ │ ├── shared │ │ │ └── sidebar │ │ │ │ ├── MobileSidebar.tsx │ │ │ │ ├── ModileSidebarItem.tsx │ │ │ │ ├── Sidebar.tsx │ │ │ │ ├── SidebarItem.tsx │ │ │ │ ├── SidebarLayout.tsx │ │ │ │ └── TSidebarItem.ts │ │ ├── signin │ │ │ ├── CredentialsForm.tsx │ │ │ ├── SignInForm.tsx │ │ │ └── SignInProviderButton.tsx │ │ ├── theme-provider.tsx │ │ ├── ui │ │ │ ├── action-button.tsx │ │ │ ├── action-confirming-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── back-button.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── copy-button.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── file-picker-button.tsx │ │ │ ├── form.tsx │ │ │ ├── full-page-spinner.tsx │ │ │ ├── info-tooltip.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── markdown │ │ │ │ ├── markdown-editor.tsx │ │ │ │ ├── markdown-readonly.tsx │ │ │ │ ├── plugins │ │ │ │ │ └── toolbar-plugin.tsx │ │ │ │ └── theme.ts │ │ │ ├── multiple-choice-dialog.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── spinner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── toggle.tsx │ │ │ ├── tooltip.tsx │ │ │ └── use-toast.ts │ │ └── utils │ │ │ ├── BookmarkAlreadyExistsToast.tsx │ │ │ ├── ValidAccountCheck.tsx │ │ │ └── useShowArchived.tsx │ ├── lib │ │ ├── attachments.tsx │ │ ├── bulkActions.ts │ │ ├── bulkTagActions.ts │ │ ├── clientConfig.tsx │ │ ├── drag-and-drop.ts │ │ ├── exportBookmarks.ts │ │ ├── hooks │ │ │ ├── bookmark-search.ts │ │ │ ├── relative-time.ts │ │ │ └── upload-file.ts │ │ ├── i18n │ │ │ ├── client.ts │ │ │ ├── locales │ │ │ │ ├── ar │ │ │ │ │ └── translation.json │ │ │ │ ├── da │ │ │ │ │ └── translation.json │ │ │ │ ├── de │ │ │ │ │ └── translation.json │ │ │ │ ├── en │ │ │ │ │ └── translation.json │ │ │ │ ├── en_US │ │ │ │ │ └── translation.json │ │ │ │ ├── es │ │ │ │ │ └── translation.json │ │ │ │ ├── fi │ │ │ │ │ └── translation.json │ │ │ │ ├── fr │ │ │ │ │ └── translation.json │ │ │ │ ├── gl │ │ │ │ │ └── translation.json │ │ │ │ ├── hr │ │ │ │ │ └── translation.json │ │ │ │ ├── hu │ │ │ │ │ └── translation.json │ │ │ │ ├── it │ │ │ │ │ └── translation.json │ │ │ │ ├── ja │ │ │ │ │ └── translation.json │ │ │ │ ├── ko │ │ │ │ │ └── translation.json │ │ │ │ ├── nb_NO │ │ │ │ │ └── translation.json │ │ │ │ ├── nl │ │ │ │ │ └── translation.json │ │ │ │ ├── pl │ │ │ │ │ └── translation.json │ │ │ │ ├── pt │ │ │ │ │ └── translation.json │ │ │ │ ├── pt_BR │ │ │ │ │ └── translation.json │ │ │ │ ├── ru │ │ │ │ │ └── translation.json │ │ │ │ ├── sk │ │ │ │ │ └── translation.json │ │ │ │ ├── sl │ │ │ │ │ └── translation.json │ │ │ │ ├── sv │ │ │ │ │ └── translation.json │ │ │ │ ├── tr │ │ │ │ │ └── translation.json │ │ │ │ ├── uk │ │ │ │ │ └── translation.json │ │ │ │ ├── vi │ │ │ │ │ └── translation.json │ │ │ │ ├── zh │ │ │ │ │ └── translation.json │ │ │ │ └── zhtw │ │ │ │ │ └── translation.json │ │ │ ├── provider.tsx │ │ │ ├── server.ts │ │ │ └── settings.ts │ │ ├── importBookmarkParser.ts │ │ ├── providers.tsx │ │ ├── store │ │ │ └── useSortOrderStore.ts │ │ ├── trpc.tsx │ │ ├── userLocalSettings │ │ │ ├── bookmarksLayout.tsx │ │ │ ├── types.ts │ │ │ └── userLocalSettings.ts │ │ ├── userSettings.tsx │ │ └── utils.ts │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.cjs │ ├── public │ │ ├── blur.avif │ │ ├── icons │ │ │ ├── karakeep-full.svg │ │ │ ├── karakeep-text.svg │ │ │ ├── logo-128.png │ │ │ ├── logo-16.png │ │ │ ├── logo-48.png │ │ │ ├── logo-full.svg │ │ │ ├── logo-icon.svg │ │ │ └── logo-text.svg │ │ └── manifest.json │ ├── server │ │ ├── api │ │ │ └── client.ts │ │ └── auth.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vitest.config.ts └── workers │ ├── exit.ts │ ├── index.ts │ ├── package.json │ ├── trpc.ts │ ├── tsconfig.json │ ├── utils.ts │ ├── workerUtils.ts │ └── workers │ ├── assetPreprocessingWorker.ts │ ├── crawlerWorker.ts │ ├── feedWorker.ts │ ├── inference │ ├── inferenceWorker.ts │ ├── summarize.ts │ └── tagging.ts │ ├── ruleEngineWorker.ts │ ├── searchWorker.ts │ ├── tidyAssetsWorker.ts │ ├── videoWorker.ts │ └── webhookWorker.ts ├── docker ├── .env.sample ├── Dockerfile ├── Dockerfile.dev ├── docker-compose.build.yml ├── docker-compose.dev.yml ├── docker-compose.yml └── root │ └── etc │ └── s6-overlay │ └── s6-rc.d │ ├── init-db-migration │ ├── run │ ├── type │ └── up │ ├── svc-web │ ├── dependencies.d │ │ └── init-db-migration │ ├── run │ └── type │ ├── svc-workers │ ├── dependencies.d │ │ └── init-db-migration │ ├── run │ └── type │ └── user │ └── contents.d │ └── .gitkeep ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── 01-intro.md │ ├── 02-Installation │ │ ├── 01-docker.md │ │ ├── 02-unraid.md │ │ ├── 03-archlinux.md │ │ ├── 04-kubernetes.md │ │ ├── 05-pikapods.md │ │ ├── 06-debuntu.md │ │ ├── 07-minimal-install.md │ │ └── 08-truenas.md │ ├── 03-configuration.md │ ├── 04-screenshots.md │ ├── 05-quick-sharing.md │ ├── 06-openai.md │ ├── 07-Development │ │ ├── 01-setup.md │ │ ├── 02-directories.md │ │ ├── 03-database.md │ │ └── 04-architecture.md │ ├── 08-security-considerations.md │ ├── 09-command-line.md │ ├── 09-mcp.md │ ├── 10-import.md │ ├── 11-FAQ.md │ ├── 12-troubleshooting.md │ ├── 13-community-projects.md │ ├── 14-Guides │ │ ├── 01-legacy-container-upgrade.md │ │ ├── 02-search-query-language.md │ │ ├── 03-singlefile.md │ │ ├── 04-hoarder-to-karakeep-migration.md │ │ └── 05-different-ai-providers.md │ └── API │ │ ├── add-a-bookmark-to-a-list.api.mdx │ │ ├── attach-asset.api.mdx │ │ ├── attach-tags-to-a-bookmark.api.mdx │ │ ├── create-a-new-bookmark.api.mdx │ │ ├── create-a-new-highlight.api.mdx │ │ ├── create-a-new-list.api.mdx │ │ ├── delete-a-bookmark.api.mdx │ │ ├── delete-a-highlight.api.mdx │ │ ├── delete-a-list.api.mdx │ │ ├── delete-a-tag.api.mdx │ │ ├── detach-asset.api.mdx │ │ ├── detach-tags-from-a-bookmark.api.mdx │ │ ├── get-a-bookmarks-in-a-list.api.mdx │ │ ├── get-a-bookmarks-with-the-tag.api.mdx │ │ ├── get-a-single-bookmark.api.mdx │ │ ├── get-a-single-highlight.api.mdx │ │ ├── get-a-single-list.api.mdx │ │ ├── get-a-single-tag.api.mdx │ │ ├── get-all-bookmarks.api.mdx │ │ ├── get-all-highlights.api.mdx │ │ ├── get-all-lists.api.mdx │ │ ├── get-all-tags.api.mdx │ │ ├── get-current-user-info.api.mdx │ │ ├── get-current-user-stats.api.mdx │ │ ├── get-highlights-of-a-bookmark.api.mdx │ │ ├── karakeep-api.info.mdx │ │ ├── remove-a-bookmark-from-a-list.api.mdx │ │ ├── replace-asset.api.mdx │ │ ├── search-bookmarks.api.mdx │ │ ├── sidebar.ts │ │ ├── summarize-a-bookmark.api.mdx │ │ ├── update-a-bookmark.api.mdx │ │ ├── update-a-highlight.api.mdx │ │ ├── update-a-list.api.mdx │ │ └── update-a-tag.api.mdx ├── docusaurus.config.ts ├── package.json ├── sidebars.ts ├── src │ └── css │ │ └── custom.css ├── static │ ├── .nojekyll │ ├── img │ │ ├── architecture │ │ │ └── arch.png │ │ ├── docusaurus-social-card.jpg │ │ ├── favicon.ico │ │ ├── logo-full-white.svg │ │ ├── logo-full.svg │ │ ├── logo.png │ │ ├── mcp-1.gif │ │ ├── mcp-2.gif │ │ ├── mcp-3.gif │ │ ├── opengraph-image.png │ │ ├── quick-sharing │ │ │ ├── extension.png │ │ │ └── mobile.png │ │ └── screenshots │ │ │ ├── admin.png │ │ │ ├── all-lists.png │ │ │ ├── all-tags.png │ │ │ ├── bookmark-preview.png │ │ │ ├── homepage-dark.png │ │ │ ├── homepage.png │ │ │ ├── settings.png │ │ │ └── share-sheet.png │ └── robots.txt ├── tsconfig.json ├── versioned_docs │ ├── version-v0.15.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ └── 03-archlinux.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ └── 10-import.md │ ├── version-v0.16.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ └── 04-kubernetes.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ └── 10-import.md │ ├── version-v0.17.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ └── 04-kubernetes.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ └── 10-import.md │ ├── version-v0.18.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ └── 04-kubernetes.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ └── 10-import.md │ ├── version-v0.19.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ └── 04-kubernetes.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ └── 11-FAQ.md │ ├── version-v0.20.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ ├── 04-kubernetes.md │ │ │ ├── 05-pikapods.md │ │ │ ├── 06-debuntu.md │ │ │ └── 07-minimal-install.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-community-projects.md │ │ └── 13-Guides │ │ │ └── 01-legacy-container-upgrade.md │ ├── version-v0.21.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ ├── 04-kubernetes.md │ │ │ ├── 05-pikapods.md │ │ │ ├── 06-debuntu.md │ │ │ └── 07-minimal-install.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-troubleshooting.md │ │ ├── 13-community-projects.md │ │ └── 14-Guides │ │ │ ├── 01-legacy-container-upgrade.md │ │ │ └── 02-search-query-language.md │ ├── version-v0.22.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ ├── 04-kubernetes.md │ │ │ ├── 05-pikapods.md │ │ │ ├── 06-debuntu.md │ │ │ └── 07-minimal-install.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-troubleshooting.md │ │ ├── 13-community-projects.md │ │ └── 14-Guides │ │ │ ├── 01-legacy-container-upgrade.md │ │ │ ├── 02-search-query-language.md │ │ │ └── 03-singlefile.md │ ├── version-v0.23.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ ├── 04-kubernetes.md │ │ │ ├── 05-pikapods.md │ │ │ ├── 06-debuntu.md │ │ │ └── 07-minimal-install.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-troubleshooting.md │ │ ├── 13-community-projects.md │ │ └── 14-Guides │ │ │ ├── 01-legacy-container-upgrade.md │ │ │ ├── 02-search-query-language.md │ │ │ └── 03-singlefile.md │ ├── version-v0.23.1 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ │ ├── 01-docker.md │ │ │ ├── 02-unraid.md │ │ │ ├── 03-archlinux.md │ │ │ ├── 04-kubernetes.md │ │ │ ├── 05-pikapods.md │ │ │ ├── 06-debuntu.md │ │ │ └── 07-minimal-install.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ │ ├── 01-setup.md │ │ │ ├── 02-directories.md │ │ │ ├── 03-database.md │ │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-troubleshooting.md │ │ ├── 13-community-projects.md │ │ ├── 14-Guides │ │ │ ├── 01-legacy-container-upgrade.md │ │ │ ├── 02-search-query-language.md │ │ │ ├── 03-singlefile.md │ │ │ └── 04-hoarder-to-karakeep-migration.md │ │ └── API │ │ │ ├── add-a-bookmark-to-a-list.api.mdx │ │ │ ├── attach-asset.api.mdx │ │ │ ├── attach-tags-to-a-bookmark.api.mdx │ │ │ ├── create-a-new-bookmark.api.mdx │ │ │ ├── create-a-new-highlight.api.mdx │ │ │ ├── create-a-new-list.api.mdx │ │ │ ├── delete-a-bookmark.api.mdx │ │ │ ├── delete-a-highlight.api.mdx │ │ │ ├── delete-a-list.api.mdx │ │ │ ├── delete-a-tag.api.mdx │ │ │ ├── detach-asset.api.mdx │ │ │ ├── detach-tags-from-a-bookmark.api.mdx │ │ │ ├── get-a-bookmarks-in-a-list.api.mdx │ │ │ ├── get-a-bookmarks-with-the-tag.api.mdx │ │ │ ├── get-a-single-bookmark.api.mdx │ │ │ ├── get-a-single-highlight.api.mdx │ │ │ ├── get-a-single-list.api.mdx │ │ │ ├── get-a-single-tag.api.mdx │ │ │ ├── get-all-bookmarks.api.mdx │ │ │ ├── get-all-highlights.api.mdx │ │ │ ├── get-all-lists.api.mdx │ │ │ ├── get-all-tags.api.mdx │ │ │ ├── get-current-user-info.api.mdx │ │ │ ├── get-current-user-stats.api.mdx │ │ │ ├── get-highlights-of-a-bookmark.api.mdx │ │ │ ├── hoarder-api.info.mdx │ │ │ ├── remove-a-bookmark-from-a-list.api.mdx │ │ │ ├── replace-asset.api.mdx │ │ │ ├── search-bookmarks.api.mdx │ │ │ ├── sidebar.ts │ │ │ ├── summarize-a-bookmark.api.mdx │ │ │ ├── update-a-bookmark.api.mdx │ │ │ ├── update-a-highlight.api.mdx │ │ │ ├── update-a-list.api.mdx │ │ │ └── update-a-tag.api.mdx │ └── version-v0.24.0 │ │ ├── 01-intro.md │ │ ├── 02-Installation │ │ ├── 01-docker.md │ │ ├── 02-unraid.md │ │ ├── 03-archlinux.md │ │ ├── 04-kubernetes.md │ │ ├── 05-pikapods.md │ │ ├── 06-debuntu.md │ │ ├── 07-minimal-install.md │ │ └── 08-truenas.md │ │ ├── 03-configuration.md │ │ ├── 04-screenshots.md │ │ ├── 05-quick-sharing.md │ │ ├── 06-openai.md │ │ ├── 07-Development │ │ ├── 01-setup.md │ │ ├── 02-directories.md │ │ ├── 03-database.md │ │ └── 04-architecture.md │ │ ├── 08-security-considerations.md │ │ ├── 09-command-line.md │ │ ├── 09-mcp.md │ │ ├── 10-import.md │ │ ├── 11-FAQ.md │ │ ├── 12-troubleshooting.md │ │ ├── 13-community-projects.md │ │ ├── 14-Guides │ │ ├── 01-legacy-container-upgrade.md │ │ ├── 02-search-query-language.md │ │ ├── 03-singlefile.md │ │ ├── 04-hoarder-to-karakeep-migration.md │ │ └── 05-different-ai-providers.md │ │ └── API │ │ ├── add-a-bookmark-to-a-list.api.mdx │ │ ├── attach-asset.api.mdx │ │ ├── attach-tags-to-a-bookmark.api.mdx │ │ ├── create-a-new-bookmark.api.mdx │ │ ├── create-a-new-highlight.api.mdx │ │ ├── create-a-new-list.api.mdx │ │ ├── delete-a-bookmark.api.mdx │ │ ├── delete-a-highlight.api.mdx │ │ ├── delete-a-list.api.mdx │ │ ├── delete-a-tag.api.mdx │ │ ├── detach-asset.api.mdx │ │ ├── detach-tags-from-a-bookmark.api.mdx │ │ ├── get-a-bookmarks-in-a-list.api.mdx │ │ ├── get-a-bookmarks-with-the-tag.api.mdx │ │ ├── get-a-single-bookmark.api.mdx │ │ ├── get-a-single-highlight.api.mdx │ │ ├── get-a-single-list.api.mdx │ │ ├── get-a-single-tag.api.mdx │ │ ├── get-all-bookmarks.api.mdx │ │ ├── get-all-highlights.api.mdx │ │ ├── get-all-lists.api.mdx │ │ ├── get-all-tags.api.mdx │ │ ├── get-current-user-info.api.mdx │ │ ├── get-current-user-stats.api.mdx │ │ ├── get-highlights-of-a-bookmark.api.mdx │ │ ├── karakeep-api.info.mdx │ │ ├── remove-a-bookmark-from-a-list.api.mdx │ │ ├── replace-asset.api.mdx │ │ ├── search-bookmarks.api.mdx │ │ ├── sidebar.ts │ │ ├── summarize-a-bookmark.api.mdx │ │ ├── update-a-bookmark.api.mdx │ │ ├── update-a-highlight.api.mdx │ │ ├── update-a-list.api.mdx │ │ └── update-a-tag.api.mdx ├── versioned_sidebars │ ├── version-v0.15.0-sidebars.json │ ├── version-v0.16.0-sidebars.json │ ├── version-v0.17.0-sidebars.json │ ├── version-v0.18.0-sidebars.json │ ├── version-v0.19.0-sidebars.json │ ├── version-v0.20.0-sidebars.json │ ├── version-v0.21.0-sidebars.json │ ├── version-v0.22.0-sidebars.json │ ├── version-v0.23.0-sidebars.json │ ├── version-v0.23.1-sidebars.json │ └── version-v0.24.0-sidebars.json └── versions.json ├── karakeep-linux.sh ├── kubernetes ├── .env_sample ├── .gitignore ├── .secrets_sample ├── Makefile ├── README.md ├── chrome-deployment.yaml ├── chrome-service.yaml ├── data-pvc.yaml ├── ingress_sample.yaml ├── kustomization.yaml ├── meilisearch-deployment.yaml ├── meilisearch-pvc.yaml ├── meilisearch-service.yaml ├── namespace.yaml ├── web-deployment.yaml └── web-service.yaml ├── package.json ├── packages ├── api │ ├── index.ts │ ├── middlewares │ │ ├── auth.ts │ │ └── trpcAdapter.ts │ ├── package.json │ ├── routes │ │ ├── assets.ts │ │ ├── bookmarks.ts │ │ ├── highlights.ts │ │ ├── lists.ts │ │ ├── public.ts │ │ ├── rss.ts │ │ ├── tags.ts │ │ └── users.ts │ ├── tsconfig.json │ └── utils │ │ ├── assets.ts │ │ ├── pagination.ts │ │ ├── rss.ts │ │ ├── types.ts │ │ └── upload.ts ├── db │ ├── drizzle.config.ts │ ├── drizzle.ts │ ├── drizzle │ │ ├── 0000_luxuriant_johnny_blaze.sql │ │ ├── 0001_dapper_trauma.sql │ │ ├── 0002_worried_beyonder.sql │ │ ├── 0003_parallel_supernaut.sql │ │ ├── 0004_skinny_vengeance.sql │ │ ├── 0005_quiet_gunslinger.sql │ │ ├── 0006_funny_mac_gargan.sql │ │ ├── 0007_messy_raza.sql │ │ ├── 0008_cloudy_skin.sql │ │ ├── 0009_cuddly_cammi.sql │ │ ├── 0010_curved_sharon_ventura.sql │ │ ├── 0011_ordinary_phalanx.sql │ │ ├── 0012_noisy_grim_reaper.sql │ │ ├── 0013_square_lady_ursula.sql │ │ ├── 0014_lonely_thaddeus_ross.sql │ │ ├── 0015_first_reavers.sql │ │ ├── 0016_shallow_rawhide_kid.sql │ │ ├── 0017_slippery_senator_kelly.sql │ │ ├── 0018_bright_infant_terrible.sql │ │ ├── 0019_many_vertigo.sql │ │ ├── 0020_sudden_dagger.sql │ │ ├── 0021_magical_firebrand.sql │ │ ├── 0022_tough_nextwave.sql │ │ ├── 0023_late_night_nurse.sql │ │ ├── 0024_premium_hammerhead.sql │ │ ├── 0025_aspiring_skaar.sql │ │ ├── 0026_silky_imperial_guard.sql │ │ ├── 0027_cute_talon.sql │ │ ├── 0028_melodic_norrin_radd.sql │ │ ├── 0029_short_gunslinger.sql │ │ ├── 0030_blue_synch.sql │ │ ├── 0031_yummy_famine.sql │ │ ├── 0032_futuristic_shiva.sql │ │ ├── 0033_nappy_molten_man.sql │ │ ├── 0034_wet_the_stranger.sql │ │ ├── 0035_gorgeous_may_parker.sql │ │ ├── 0036_luxuriant_white_queen.sql │ │ ├── 0037_daily_smiling_tiger.sql │ │ ├── 0038_calm_clint_barton.sql │ │ ├── 0039_purple_albert_cleary.sql │ │ ├── 0040_long_mindworm.sql │ │ ├── 0041_fat_bloodstrike.sql │ │ ├── 0042_square_gamma_corps.sql │ │ ├── 0043_puzzling_blonde_phantom.sql │ │ ├── 0044_add_password_salt.sql │ │ ├── 0045_add_rule_engine.sql │ │ ├── 0046_add_rss_feed_enabled_col.sql │ │ ├── 0047_add_summarization_status.sql │ │ ├── 0048_add_user_settings.sql │ │ ├── 0049_add_rss_token.sql │ │ ├── 0050_add_user_settings_archive_display_behaviour.sql │ │ ├── 0051_public_lists.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ ├── 0004_snapshot.json │ │ │ ├── 0005_snapshot.json │ │ │ ├── 0006_snapshot.json │ │ │ ├── 0007_snapshot.json │ │ │ ├── 0008_snapshot.json │ │ │ ├── 0009_snapshot.json │ │ │ ├── 0010_snapshot.json │ │ │ ├── 0011_snapshot.json │ │ │ ├── 0012_snapshot.json │ │ │ ├── 0013_snapshot.json │ │ │ ├── 0014_snapshot.json │ │ │ ├── 0015_snapshot.json │ │ │ ├── 0016_snapshot.json │ │ │ ├── 0017_snapshot.json │ │ │ ├── 0018_snapshot.json │ │ │ ├── 0019_snapshot.json │ │ │ ├── 0020_snapshot.json │ │ │ ├── 0021_snapshot.json │ │ │ ├── 0022_snapshot.json │ │ │ ├── 0023_snapshot.json │ │ │ ├── 0024_snapshot.json │ │ │ ├── 0025_snapshot.json │ │ │ ├── 0026_snapshot.json │ │ │ ├── 0027_snapshot.json │ │ │ ├── 0028_snapshot.json │ │ │ ├── 0029_snapshot.json │ │ │ ├── 0030_snapshot.json │ │ │ ├── 0031_snapshot.json │ │ │ ├── 0032_snapshot.json │ │ │ ├── 0033_snapshot.json │ │ │ ├── 0034_snapshot.json │ │ │ ├── 0035_snapshot.json │ │ │ ├── 0036_snapshot.json │ │ │ ├── 0037_snapshot.json │ │ │ ├── 0038_snapshot.json │ │ │ ├── 0039_snapshot.json │ │ │ ├── 0040_snapshot.json │ │ │ ├── 0041_snapshot.json │ │ │ ├── 0042_snapshot.json │ │ │ ├── 0043_snapshot.json │ │ │ ├── 0044_snapshot.json │ │ │ ├── 0045_snapshot.json │ │ │ ├── 0046_snapshot.json │ │ │ ├── 0047_snapshot.json │ │ │ ├── 0048_snapshot.json │ │ │ ├── 0049_snapshot.json │ │ │ ├── 0050_snapshot.json │ │ │ ├── 0051_snapshot.json │ │ │ └── _journal.json │ ├── index.ts │ ├── migrate.ts │ ├── package.json │ ├── schema.ts │ └── tsconfig.json ├── e2e_tests │ ├── docker-compose.yml │ ├── package.json │ ├── setup │ │ ├── html │ │ │ ├── hello.html │ │ │ └── image.png │ │ ├── seed.ts │ │ └── startContainers.ts │ ├── tests │ │ ├── api │ │ │ ├── assets.test.ts │ │ │ ├── bookmarks.test.ts │ │ │ ├── highlights.test.ts │ │ │ ├── lists.test.ts │ │ │ ├── public.test.ts │ │ │ ├── rss.test.ts │ │ │ ├── tags.test.ts │ │ │ └── users.test.ts │ │ └── workers │ │ │ └── crawler.test.ts │ ├── tsconfig.json │ ├── utils │ │ ├── api.ts │ │ ├── general.ts │ │ └── trpc.ts │ └── vitest.config.ts ├── open-api │ ├── index.ts │ ├── karakeep-openapi-spec.json │ ├── lib │ │ ├── bookmarks.ts │ │ ├── common.ts │ │ ├── errors.ts │ │ ├── highlights.ts │ │ ├── lists.ts │ │ ├── pagination.ts │ │ ├── tags.ts │ │ └── users.ts │ ├── package.json │ └── tsconfig.json ├── sdk │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── karakeep-api.d.ts │ ├── tsconfig.json │ └── vite.config.mts ├── shared-react │ ├── hooks │ │ ├── assets.ts │ │ ├── bookmark-grid-context.tsx │ │ ├── bookmark-list-context.tsx │ │ ├── bookmarks.ts │ │ ├── highlights.ts │ │ ├── lists.ts │ │ ├── rules.ts │ │ ├── tags.ts │ │ └── users.ts │ ├── package.json │ ├── providers │ │ └── trpc-provider.tsx │ ├── trpc.ts │ ├── tsconfig.json │ └── utils │ │ └── bookmarkUtils.ts ├── shared │ ├── assetdb.ts │ ├── concurrency.test.ts │ ├── concurrency.ts │ ├── config.ts │ ├── customFetch.ts │ ├── index.ts │ ├── inference.ts │ ├── langs.ts │ ├── logger.ts │ ├── package.json │ ├── prompts.ts │ ├── queues.ts │ ├── search.ts │ ├── searchQueryParser.test.ts │ ├── searchQueryParser.ts │ ├── signedTokens.ts │ ├── tsconfig.json │ ├── types │ │ ├── admin.ts │ │ ├── assets.ts │ │ ├── bookmarks.ts │ │ ├── feeds.ts │ │ ├── highlights.ts │ │ ├── lists.ts │ │ ├── pagination.ts │ │ ├── prompts.ts │ │ ├── rules.ts │ │ ├── search.ts │ │ ├── tags.ts │ │ ├── uploads.ts │ │ ├── users.ts │ │ └── webhooks.ts │ ├── utils │ │ ├── assetUtils.ts │ │ ├── bookmarkUtils.ts │ │ ├── listUtils.ts │ │ └── relativeDateUtils.ts │ └── vitest.config.ts └── trpc │ ├── auth.ts │ ├── index.ts │ ├── lib │ ├── __tests__ │ │ ├── ruleEngine.test.ts │ │ └── search.test.ts │ ├── attachments.ts │ ├── impersonate.ts │ ├── ruleEngine.ts │ └── search.ts │ ├── models │ ├── bookmarks.ts │ ├── lists.ts │ ├── privacy.ts │ └── rules.ts │ ├── package.json │ ├── routers │ ├── _app.ts │ ├── admin.ts │ ├── apiKeys.ts │ ├── assets.test.ts │ ├── assets.ts │ ├── bookmarks.test.ts │ ├── bookmarks.ts │ ├── feeds.ts │ ├── highlights.test.ts │ ├── highlights.ts │ ├── lists.test.ts │ ├── lists.ts │ ├── prompts.test.ts │ ├── prompts.ts │ ├── publicBookmarks.ts │ ├── rules.test.ts │ ├── rules.ts │ ├── tags.test.ts │ ├── tags.ts │ ├── users.test.ts │ ├── users.ts │ ├── webhooks.test.ts │ └── webhooks.ts │ ├── testUtils.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── patches └── xcode@3.0.1.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── screenshots ├── homepage.png └── logo.png ├── tooling ├── eslint │ ├── base.js │ ├── nextjs.js │ ├── package.json │ ├── react.js │ └── tsconfig.json ├── github │ ├── package.json │ └── setup │ │ └── action.yml ├── prettier │ ├── index.js │ ├── package.json │ └── tsconfig.json ├── tailwind │ ├── base.ts │ ├── globals.css │ ├── native.ts │ ├── package.json │ ├── tsconfig.json │ └── web.ts └── typescript │ ├── base.json │ ├── node.json │ └── package.json └── turbo.json /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | **/.next 7 | **/*.db 8 | **/.env* 9 | .git 10 | ./data 11 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | # See https://docs.karakeep.app/configuration for more information 2 | DATA_DIR= 3 | NEXTAUTH_SECRET= -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | buy_me_a_coffee: mbassem 4 | github: MohamedBassem 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: Question / Support Request 2 | description: Get help with anything related to Karakeep 3 | labels: ["question"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | We use Github discussions for anything that's not a bug report or a feature request. Please ask your question in the [Q&A section](https://github.com/karakeep-app/karakeep/discussions/categories/q-a) and someone will answer it soon! 9 | -------------------------------------------------------------------------------- /.github/workflows/cli.yml: -------------------------------------------------------------------------------- 1 | name: Publish CLI Package to npm 2 | on: 3 | push: 4 | tags: 5 | # This is a glob pattern not a regex 6 | - 'cli/v[0-9]+.[0-9]+.[0-9]+' 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Setup 14 | uses: ./tooling/github/setup 15 | 16 | - name: Build CLI 17 | run: pnpm build 18 | working-directory: apps/cli 19 | 20 | - run: pnpm publish --access public --no-git-checks 21 | working-directory: apps/cli 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/mcp.yml: -------------------------------------------------------------------------------- 1 | name: Publish MCP Package to npm 2 | on: 3 | push: 4 | tags: 5 | # This is a glob pattern not a regex 6 | - 'mcp/v[0-9]+.[0-9]+.[0-9]+' 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Setup 14 | uses: ./tooling/github/setup 15 | 16 | - name: Build MCP 17 | run: pnpm build 18 | working-directory: apps/mcp 19 | 20 | - run: pnpm publish --access public --no-git-checks 21 | working-directory: apps/mcp 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/sdk.yml: -------------------------------------------------------------------------------- 1 | name: Publish CLI Package to npm 2 | on: 3 | push: 4 | tags: 5 | # This is a glob pattern not a regex 6 | - 'sdk/v[0-9]+.[0-9]+.[0-9]+' 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Setup 14 | uses: ./tooling/github/setup 15 | 16 | - name: Build SDK 17 | run: pnpm build 18 | working-directory: packages/sdk 19 | 20 | - run: pnpm publish --access public --no-git-checks 21 | working-directory: packages/sdk 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnpm-store 7 | .pnp.js 8 | .yarn/install-state.gz 9 | 10 | # testing 11 | coverage 12 | 13 | # next.js 14 | .next/ 15 | out/ 16 | 17 | # production 18 | build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | .env 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | 39 | # The sqlite database 40 | **/*.db 41 | data 42 | 43 | # PWA Files 44 | **/public/sw.js 45 | **/public/workbox-*.js 46 | **/public/worker-*.js 47 | **/public/sw.js.map 48 | **/public/workbox-*.js.map 49 | **/public/worker-*.js.map 50 | 51 | # Turbo 52 | .turbo 53 | 54 | # Idea 55 | .idea 56 | *.iml 57 | 58 | # Aider 59 | .aider* 60 | 61 | # VS-Code 62 | .vscode 63 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm typecheck 2 | pnpm format --check 3 | pnpm lint 4 | pnpm exec sherif 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | .next 3 | build 4 | coverage 5 | .vscode* 6 | node_modules 7 | dist 8 | *.md 9 | *.json 10 | .env 11 | .env.* 12 | **/migrations/** 13 | packages/open-api/karakeep-openapi-spec.json 14 | 15 | # Ignore files for PNPM, NPM and YARN 16 | pnpm-lock.yaml 17 | package-lock.json 18 | yarn.lock 19 | 20 | # Expo build 21 | **/.expo/** 22 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security issues to `security@karakeep.app` 6 | -------------------------------------------------------------------------------- /apps/browser-extension/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/browser-extension/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "src/components", 15 | "utils": "src/utils/css" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/browser-extension/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Karakeep 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/browser-extension/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-128-darkmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-128-darkmode.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-128.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-16-darkmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-16-darkmode.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-16.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-48-darkmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-48-darkmode.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-48.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-full-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-full-white.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo-full.png -------------------------------------------------------------------------------- /apps/browser-extension/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/browser-extension/public/logo.png -------------------------------------------------------------------------------- /apps/browser-extension/src/BookmarkDeletedPage.tsx: -------------------------------------------------------------------------------- 1 | export default function BookmarkDeletedPage() { 2 | return

Bookmark Deleted!

; 3 | } 4 | -------------------------------------------------------------------------------- /apps/browser-extension/src/Logo.tsx: -------------------------------------------------------------------------------- 1 | import logoImgWhite from "../public/logo-full-white.png"; 2 | import logoImg from "../public/logo-full.png"; 3 | 4 | export default function Logo() { 5 | return ( 6 | 7 | karakeep logo 8 | karakeep logo 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/browser-extension/src/Spinner.tsx: -------------------------------------------------------------------------------- 1 | export default function Spinner() { 2 | return ( 3 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/browser-extension/src/background/protocol.ts: -------------------------------------------------------------------------------- 1 | export const NEW_BOOKMARK_REQUEST_KEY_NAME = "karakeep-new-bookmark"; 2 | -------------------------------------------------------------------------------- /apps/browser-extension/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../../utils/css"; 4 | 5 | export type InputProps = React.InputHTMLAttributes; 6 | 7 | const Input = React.forwardRef( 8 | ({ className, type, ...props }, ref) => { 9 | return ( 10 | 19 | ); 20 | }, 21 | ); 22 | Input.displayName = "Input"; 23 | 24 | export { Input }; 25 | -------------------------------------------------------------------------------- /apps/browser-extension/src/index.css: -------------------------------------------------------------------------------- 1 | @import "@karakeep/tailwind-config/globals.css"; 2 | -------------------------------------------------------------------------------- /apps/browser-extension/src/utils/css.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)); 7 | } 8 | -------------------------------------------------------------------------------- /apps/browser-extension/src/utils/providers.tsx: -------------------------------------------------------------------------------- 1 | import { TRPCProvider } from "@karakeep/shared-react/providers/trpc-provider"; 2 | 3 | import usePluginSettings from "./settings"; 4 | import { ThemeProvider } from "./ThemeProvider"; 5 | 6 | export function Providers({ children }: { children: React.ReactNode }) { 7 | const { settings } = usePluginSettings(); 8 | 9 | return ( 10 | 11 | 12 | {children} 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/browser-extension/src/utils/trpc.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from "@trpc/react-query"; 2 | 3 | import type { AppRouter } from "@karakeep/trpc/routers/_app"; 4 | 5 | export const api = createTRPCReact(); 6 | -------------------------------------------------------------------------------- /apps/browser-extension/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/browser-extension/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import web from "@karakeep/tailwind-config/web"; 2 | 3 | const config = { 4 | darkMode: "media", 5 | content: web.content, 6 | presets: [web], 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /apps/browser-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | "types": ["chrome"], 10 | 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | "strict": true, 19 | "noUnusedLocals": false, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src", "vite.config.ts"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/browser-extension/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { crx } from "@crxjs/vite-plugin"; 2 | import react from "@vitejs/plugin-react-swc"; 3 | import { defineConfig } from "vite"; 4 | 5 | import manifest from "./manifest.json"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | react(), 11 | crx({ 12 | manifest, 13 | browser: process.env.VITE_BUILD_FIREFOX ? "firefox" : "chrome", 14 | }), 15 | ], 16 | }); 17 | -------------------------------------------------------------------------------- /apps/cli/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /apps/cli/.npmignore: -------------------------------------------------------------------------------- 1 | .turbo/** 2 | src/** 3 | vite.config.mts 4 | tsconfig.json 5 | -------------------------------------------------------------------------------- /apps/cli/src/commands/whoami.ts: -------------------------------------------------------------------------------- 1 | import { printError, printObject } from "@/lib/output"; 2 | import { getAPIClient } from "@/lib/trpc"; 3 | import { Command } from "@commander-js/extra-typings"; 4 | 5 | export const whoamiCmd = new Command() 6 | .name("whoami") 7 | .description("returns info about the owner of this API key") 8 | .action(async () => { 9 | await getAPIClient() 10 | .users.whoami.query() 11 | .then(printObject) 12 | .catch( 13 | printError( 14 | `Unable to fetch information about the owner of this API key`, 15 | ), 16 | ); 17 | }); 18 | -------------------------------------------------------------------------------- /apps/cli/src/lib/globals.ts: -------------------------------------------------------------------------------- 1 | export interface GlobalOptions { 2 | apiKey: string; 3 | serverAddr: string; 4 | json?: true; 5 | } 6 | 7 | export let globalOpts: GlobalOptions | undefined = undefined; 8 | 9 | export function setGlobalOptions(opts: GlobalOptions) { 10 | globalOpts = opts; 11 | } 12 | 13 | export function getGlobalOptions() { 14 | if (!globalOpts) { 15 | throw new Error("Global options are not initalized yet"); 16 | } 17 | return globalOpts; 18 | } 19 | -------------------------------------------------------------------------------- /apps/cli/src/lib/trpc.ts: -------------------------------------------------------------------------------- 1 | import { getGlobalOptions } from "@/lib/globals"; 2 | import { createTRPCClient, httpBatchLink } from "@trpc/client"; 3 | import superjson from "superjson"; 4 | 5 | import type { AppRouter } from "@karakeep/trpc/routers/_app"; 6 | 7 | export function getAPIClient() { 8 | const globals = getGlobalOptions(); 9 | return createTRPCClient({ 10 | links: [ 11 | httpBatchLink({ 12 | url: `${globals.serverAddr}/api/trpc`, 13 | maxURLLength: 14000, 14 | transformer: superjson, 15 | headers() { 16 | return { 17 | authorization: `Bearer ${globals.apiKey}`, 18 | }; 19 | }, 20 | }), 21 | ], 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /apps/cli/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly CLI_VERSION: string; 5 | } 6 | 7 | interface ImportMeta { 8 | readonly env: ImportMetaEnv; 9 | } 10 | -------------------------------------------------------------------------------- /apps/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@karakeep/tsconfig/node.json", 4 | "include": ["src", "vite.config.mts"], 5 | "exclude": ["node_modules", "dist"], 6 | "compilerOptions": { 7 | "baseUrl": ".", 8 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", 9 | "strictNullChecks": true, 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | }, 13 | "types": ["vite/client"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/cli/vite.config.mts: -------------------------------------------------------------------------------- 1 | // This file is shamelessly copied from immich's CLI vite config 2 | // https://github.com/immich-app/immich/blob/main/cli/vite.config.ts 3 | import { defineConfig } from "vite"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | export default defineConfig({ 7 | build: { 8 | rollupOptions: { 9 | input: "src/index.ts", 10 | output: { 11 | dir: "dist", 12 | }, 13 | }, 14 | ssr: true, 15 | }, 16 | ssr: { 17 | // bundle everything except for Node built-ins 18 | noExternal: /^(?!node:).*$/, 19 | }, 20 | plugins: [tsconfigPaths()], 21 | define: { 22 | "import.meta.env.CLI_VERSION": JSON.stringify( 23 | process.env.npm_package_version, 24 | ), 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /apps/landing/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/styles.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/landing/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)); 7 | } 8 | -------------------------------------------------------------------------------- /apps/landing/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/landing/public/app-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/app-store-badge.png -------------------------------------------------------------------------------- /apps/landing/public/chrome-extension-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/chrome-extension-badge.png -------------------------------------------------------------------------------- /apps/landing/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/favicon.ico -------------------------------------------------------------------------------- /apps/landing/public/firefox-addon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/firefox-addon.png -------------------------------------------------------------------------------- /apps/landing/public/google-play-badge.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/google-play-badge.webp -------------------------------------------------------------------------------- /apps/landing/public/hero.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/hero.webp -------------------------------------------------------------------------------- /apps/landing/public/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/icons/apple-icon.png -------------------------------------------------------------------------------- /apps/landing/public/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/icons/icon.png -------------------------------------------------------------------------------- /apps/landing/public/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/opengraph-image.png -------------------------------------------------------------------------------- /apps/landing/public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/screenshot.png -------------------------------------------------------------------------------- /apps/landing/public/twitter-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/landing/public/twitter-image.png -------------------------------------------------------------------------------- /apps/landing/src/App.tsx: -------------------------------------------------------------------------------- 1 | import Homepage from "@/src/Homepage"; 2 | import Privacy from "@/src/Privacy"; 3 | 4 | import "@karakeep/tailwind-config/globals.css"; 5 | 6 | export default function App() { 7 | // Poor man router 8 | if (window.location.pathname === "/privacy") { 9 | return ; 10 | } 11 | 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /apps/landing/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import App from "@/src/App"; 3 | import { createRoot } from "react-dom/client"; 4 | 5 | createRoot(document.getElementById("root")!).render( 6 | 7 | 8 | , 9 | ); 10 | -------------------------------------------------------------------------------- /apps/landing/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | import web from "@karakeep/tailwind-config/web"; 4 | 5 | const config = { 6 | content: web.content, 7 | presets: [web], 8 | } satisfies Config; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /apps/landing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@karakeep/tsconfig/base.json", 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./*"] 8 | } 9 | }, 10 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "vite.config.ts"], 11 | "exclude": ["node_modules"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/landing/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /apps/landing/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from "node:url"; 2 | import react from "@vitejs/plugin-react"; 3 | import { defineConfig } from "vite"; 4 | import svgr from "vite-plugin-svgr"; 5 | 6 | // https://vite.dev/config/ 7 | export default defineConfig({ 8 | resolve: { 9 | alias: { 10 | "@/": fileURLToPath(new URL("./", import.meta.url)), 11 | }, 12 | }, 13 | plugins: [react(), svgr()], 14 | }); 15 | -------------------------------------------------------------------------------- /apps/mcp/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /apps/mcp/.npmignore: -------------------------------------------------------------------------------- 1 | .turbo/** 2 | src/** 3 | vite.config.mts 4 | tsconfig.json 5 | -------------------------------------------------------------------------------- /apps/mcp/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | 4 | import { mcpServer } from "./shared"; 5 | 6 | import "./bookmarks.ts"; 7 | import "./lists.ts"; 8 | import "./tags.ts"; 9 | 10 | async function run() { 11 | // Start receiving messages on stdin and sending messages on stdout 12 | const transport = new StdioServerTransport(); 13 | await mcpServer.connect(transport); 14 | } 15 | 16 | run(); 17 | -------------------------------------------------------------------------------- /apps/mcp/src/shared.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import TurndownService from "turndown"; 3 | 4 | import { createKarakeepClient } from "@karakeep/sdk"; 5 | 6 | const addr = process.env.KARAKEEP_API_ADDR; 7 | const apiKey = process.env.KARAKEEP_API_KEY; 8 | 9 | export const karakeepClient = createKarakeepClient({ 10 | baseUrl: `${addr}/api/v1`, 11 | headers: { 12 | "Content-Type": "application/json", 13 | authorization: `Bearer ${apiKey}`, 14 | }, 15 | }); 16 | 17 | export const mcpServer = new McpServer({ 18 | name: "Karakeep", 19 | version: "0.23.0", 20 | }); 21 | 22 | export const turndownService = new TurndownService(); 23 | -------------------------------------------------------------------------------- /apps/mcp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@karakeep/tsconfig/node.json", 4 | "include": ["src", "vite.config.mts"], 5 | "exclude": ["node_modules", "dist"], 6 | "compilerOptions": { 7 | "baseUrl": ".", 8 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", 9 | "strictNullChecks": true, 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | }, 13 | "types": ["vite/client"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/mcp/vite.config.mts: -------------------------------------------------------------------------------- 1 | // This file is shamelessly copied from immich's CLI vite config 2 | // https://github.com/immich-app/immich/blob/main/cli/vite.config.ts 3 | import { defineConfig } from "vite"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | export default defineConfig({ 7 | build: { 8 | rollupOptions: { 9 | input: "src/index.ts", 10 | output: { 11 | dir: "dist", 12 | }, 13 | }, 14 | commonjsOptions: { 15 | transformMixedEsModules: true, 16 | }, 17 | ssr: true, 18 | }, 19 | ssr: { 20 | // bundle everything except for Node built-ins 21 | noExternal: /^(?!node:).*$/, 22 | }, 23 | plugins: [tsconfigPaths()], 24 | }); 25 | -------------------------------------------------------------------------------- /apps/mobile/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | 11 | # Native 12 | *.orig.* 13 | *.jks 14 | *.p8 15 | *.p12 16 | *.key 17 | *.mobileprovision 18 | 19 | # Metro 20 | .metro-health-check* 21 | 22 | # debug 23 | npm-debug.* 24 | yarn-debug.* 25 | yarn-error.* 26 | 27 | # macOS 28 | .DS_Store 29 | *.pem 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | 37 | #build files 38 | ios/ 39 | android/ 40 | -------------------------------------------------------------------------------- /apps/mobile/.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /apps/mobile/app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | 3 | // This is kinda important given that the sharing modal always resolve to an unknown route 4 | export default function NotFound() { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /apps/mobile/app/dashboard/archive.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect } from "react"; 2 | import { useNavigation } from "expo-router"; 3 | import UpdatingBookmarkList from "@/components/bookmarks/UpdatingBookmarkList"; 4 | import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; 5 | 6 | export default function Archive() { 7 | const navigator = useNavigation(); 8 | useLayoutEffect(() => { 9 | navigator.setOptions({ 10 | headerTitle: "🗄️ Archive", 11 | headerLargeTitle: true, 12 | }); 13 | }, [navigator]); 14 | return ( 15 | 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/mobile/app/dashboard/favourites.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect } from "react"; 2 | import { useNavigation } from "expo-router"; 3 | import UpdatingBookmarkList from "@/components/bookmarks/UpdatingBookmarkList"; 4 | import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; 5 | 6 | export default function Favourites() { 7 | const navigator = useNavigation(); 8 | useLayoutEffect(() => { 9 | navigator.setOptions({ 10 | headerTitle: "⭐️ Favourites", 11 | headerLargeTitle: true, 12 | }); 13 | }, [navigator]); 14 | return ( 15 | 16 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/mobile/app/error.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from "react-native"; 2 | 3 | export default function ErrorPage() { 4 | return ( 5 | 6 | Error! 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/mobile/app/index.tsx: -------------------------------------------------------------------------------- 1 | import { Redirect } from "expo-router"; 2 | import FullPageSpinner from "@/components/ui/FullPageSpinner"; 3 | import { useIsLoggedIn } from "@/lib/session"; 4 | 5 | export default function App() { 6 | const isLoggedIn = useIsLoggedIn(); 7 | 8 | if (isLoggedIn === undefined) { 9 | // Wait until it's loaded 10 | return ; 11 | } else if (isLoggedIn) { 12 | return ; 13 | } else { 14 | return ; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/mobile/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/mobile/assets/adaptive-icon.png -------------------------------------------------------------------------------- /apps/mobile/assets/blur.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/mobile/assets/blur.jpeg -------------------------------------------------------------------------------- /apps/mobile/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/mobile/assets/icon.png -------------------------------------------------------------------------------- /apps/mobile/assets/splash-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/mobile/assets/splash-white.png -------------------------------------------------------------------------------- /apps/mobile/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karakeep-app/karakeep/224608368b7328c2ed7a64e1330e49e6ef44e14a/apps/mobile/assets/splash.png -------------------------------------------------------------------------------- /apps/mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | ["babel-preset-expo", { jsxImportSource: "nativewind" }], 6 | "nativewind/babel", 7 | ], 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/mobile/components/FullPageError.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from "react-native"; 2 | 3 | import { Button } from "./ui/Button"; 4 | 5 | export default function FullPageError({ 6 | error, 7 | onRetry, 8 | }: { 9 | error: string; 10 | onRetry: () => void; 11 | }) { 12 | return ( 13 | 14 | 15 | 16 | Something Went Wrong 17 | 18 | {error} 19 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/web/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { ThemeProviderProps } from "next-themes/dist/types"; 4 | import * as React from "react"; 5 | import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"; 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children}; 9 | } 10 | 11 | export function useToggleTheme() { 12 | const { theme, setTheme } = useTheme(); 13 | if (theme == "dark") { 14 | return () => setTheme("light"); 15 | } else { 16 | return () => setTheme("dark"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/components/ui/back-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useRouter } from "next/navigation"; 4 | 5 | import type { ButtonProps } from "./button"; 6 | import { Button } from "./button"; 7 | 8 | export function BackButton({ ...props }: ButtonProps) { 9 | const router = useRouter(); 10 | return