Getting Started
What do I need to run Trove?
Trove runs as a single Docker container with no external dependencies. You need:
- Docker and Docker Compose installed on your server, or
- Bun v1.0+ for local development
The tech stack is Bun, Hono, React, and SQLite — all bundled into one container.
How do I install Trove?
Follow these steps to get Trove running:
- 1. Clone the repository:
git clone https://github.com/jaydenk/trove.gitthencd trove - 2. Create your environment file:
cp env.example .env - 3. Edit
.envand setTROVE_ADMIN_PASSWORDto a secure password. - 4. Start the container:
docker compose up -d - 5. Seed the admin user:
docker compose exec trove bun run seed— this prints the admin’s auto-generated API token. Save this token — you will need it for the browser extension and API access. - 6. Open
http://localhost:3737and sign in with usernameadminand the password you set.
What does the interface look like on first login?
A three-column layout:
- Left sidebar — collections and tags for filtering.
- Centre — the link list (empty to start).
- Right — the link detail panel (appears when you select a link).
Five default collections are created for each new user. You can rename, recolour, or delete them from Settings > Collections.
How do I add my first link?
Click the Add button in the top bar, paste a URL, and click Save. Trove will:
- Create the link immediately.
- Fetch the page in the background.
- Extract the title, description, content, and favicon automatically.
The link will appear in your Inbox collection within a few seconds.
Where do I find my API token?
Go to Settings > Account in the Trove web UI. Your API token is displayed in the API Token section. Click Show to reveal it, then Copy to copy it. Click Regenerate Token if you need a new one.
The API token is used by the browser extension, iOS Shortcut, MCP server, and any other API clients to authenticate without a password.
Core Features
Multi-source Capture
Save links from the web UI, browser extension, context menu, iOS Share Sheet, bookmarklet, API, webhooks, or MCP-enabled AI assistants. Every entry point feeds into your central inbox.
Rapid Triage
Process links one at a time in a focused card-deck flow. Keyboard shortcuts let you archive, delete, skip, or send to any plugin without lifting your hands. Works on mobile with touch buttons too.
Plugin Routing
Route links outward to Readwise Reader, Things, or any HTTP API via declarative JSON plugins. Receive links inward from n8n, RSS feeds, and other webhook sources. No code required.
Full-text Search
SQLite FTS5-powered search across titles, descriptions, and extracted page content. Prefix matching, highlighted snippets, and combined filters for collections, tags, and domains.
Import/Export
Import from HTML bookmarks, JSON, CSV/TSV, or plain text. Export to JSON, CSV, or HTML bookmarks. All formats support round-trip — export and re-import preserves your data.
Dark Mode
Choose from Light, Dark, or System themes. Your preference is stored on the server and syncs across all your devices automatically.
Saving Links
What are all the ways I can save links?
- Web UI — click the Add button in the top bar and paste a URL.
- Browser extension — click the toolbar icon or press
Cmd+Shift+L(macOS) /Ctrl+Shift+L(Windows/Linux). - Context menu — right-click any page or link and select “Save to Trove” (desktop browsers only).
- iOS Share Sheet — via the Safari extension or an Apple Shortcut.
- Bookmarklet — one-click save from the bookmarks bar.
- API —
POST /api/linkswith{ “url”: ”…” } - Webhooks — pipe links from automation workflows via ingest plugins.
- MCP — AI assistants can save links via the MCP server.
What happens when I save a link?
Trove automatically fetches the page and extracts the title, description, readable content (via Mozilla Readability), Open Graph image, and favicon.
If the link is saved via the browser extension, content is extracted from the rendered DOM on the client side — this captures JavaScript-rendered content and pages behind authentication. Content is truncated at 50,000 characters by default (configurable via TROVE_MAX_CONTENT_LENGTH_CHARS).
What if content extraction fails?
A yellow warning icon appears next to the link. You can retry extraction from the link detail panel via POST /api/links/:id/extract.
Common causes: the site blocks server-side fetching, the page requires authentication, or the extraction timed out (default 10 seconds, configurable via TROVE_EXTRACTION_TIMEOUT_MS).
How do I set up a bookmarklet?
Create a new bookmark in your browser and set the URL to:
javascript:void(window.open(‘https://YOUR_TROVE_URL/?url=‘+encodeURIComponent(location.href)+‘&title=‘+encodeURIComponent(document.title),‘trove’,‘width=600,height=500’))
Replace YOUR_TROVE_URL with your Trove instance URL. When clicked, it opens a popup with the Add Link modal pre-filled with the current page’s URL and title.
How do I save links from the iOS Share Sheet?
There are two options:
Option A — Safari Extension: Build the Safari extension Xcode project for iOS (see the Browser Extension section). The extension also works as a share extension, accessible from the Share Sheet.
Option B — Apple Shortcut: Create a Shortcut that calls POST /api/links with the shared URL:
- Open the Shortcuts app and create a new shortcut.
- Enable Show in Share Sheet and restrict to URLs only.
- Add a Get Contents of URL action with method
POSTtohttps://your-trove-url/api/links, withAuthorization: Bearer <token>andContent-Type: application/jsonheaders, and a JSON body with“url”set to the Shortcut Input. - Add a Show Notification action to confirm the save.
- Name the shortcut “Save to Trove”.
Optional enhancements: add a “tags”: [“shortcut”] field to auto-tag, add a “collectionId” field to target a specific collection, or add an Ask for Input action to prompt for tags each time.
Organising
What are collections?
Collections are groups for organising your links. Each collection has a name, icon, and colour. Every link belongs to exactly one collection.
What is the Inbox?
The Inbox is the default collection. All new links land here unless a different collection is specified. The sidebar defaults to the Inbox view on login.
What is the Archive?
Archive is a virtual entry in the sidebar that shows all archived links across every collection. Archived links are preserved and searchable — unlike deleted links, which are permanently removed.
What is All Links?
A sidebar entry below Archive that shows every link regardless of collection or archived status.
How do I create, edit, or delete collections?
Go to Settings > Collections. You can:
- Create — add a new collection with a name, icon, and colour.
- Edit — change the name, icon, or colour.
- Delete — removes the collection and moves its links back to the Inbox.
How do tags work?
Tags provide flexible cross-collection categorisation. A link can have any number of tags. Add tags when saving a link (extension popup, Add Link modal) or edit them in the link detail panel — type a tag name and press Enter.
Tags with zero links are automatically hidden from the sidebar but still exist and can be managed in Settings > Tags. You can create, rename, or delete tags there, and bulk-delete all empty tags in one click.
How does search work?
Trove uses SQLite FTS5 for full-text search across link titles, descriptions, and extracted content.
- Basic search — type in the search bar or press
/to focus it. - Prefix matching — partial words match automatically (e.g. “prog” matches “programming”).
- Highlighted snippets — search results show matching text excerpts with bold highlighting below the link card.
- Combined filters — search can be combined with collection, tag, domain, or status filters.
Press Cmd+K or / to focus the search bar from anywhere in the app.
What is triage mode?
Triage mode is a focused flow for rapidly processing links one at a time — like flipping through a card deck. It is ideal for clearing your inbox after links accumulate.
How to enter:
- Desktop: Click the Triage button (lightning bolt icon) in the header.
- Mobile: Tap the lightning bolt icon in the navigation bar.
- Keyboard: Press
Twhen not in an input field.
How it works: The first link is displayed as a large card showing the title, URL, domain, description, tags, collection, and extraction status. An action bar at the bottom shows all available actions with keyboard shortcuts. Press a key or tap a button to perform an action, and the next link automatically appears. A progress indicator shows how many links remain.
Keyboard shortcuts in triage mode:
1–9— send to the corresponding configured export pluginA— archive the current linkD— delete the current linkSorRight Arrow— skip (move to the next link without taking action)KorLeft Arrow— go back to the previous linkEscape— exit triage mode
Triage mode is fully usable on mobile — all actions are presented as touch buttons below the focused card.
How do bulk actions work?
Entering bulk mode:
- Desktop: Click the Select button in the header.
- Mobile: Tap the checkbox icon in the navigation bar, or long-press a link card (500 ms) to enter bulk mode and select that card.
- Keyboard: Press
xon a focused link to toggle its selection.
Available bulk actions:
- Select All / Deselect All — selects or clears all links in the current filtered view.
- Move to Collection — move selected links to a different collection.
- Archive — archive all selected links.
- Delete — delete all selected links (with confirmation).
Click Cancel in the header or press Escape to exit bulk mode.
What swipe actions are available on mobile?
On mobile devices, you can swipe link cards left or right to perform quick actions:
- Swipe left — Delete (red background).
- Swipe right — Archive (green background).
You can customise swipe actions in Settings > Appearance > Swipe Actions. Options for each direction include Archive, Delete, None (disable), or any configured export plugin (e.g. “Send to Reader”, “Send to Things”) — shown with a blue background.
What does the link detail panel show?
Click a link (or press o / Enter on a focused link) to open the detail panel on the right side. It shows:
- Title and URL (editable).
- Extracted description and readable content.
- Collection assignment (changeable via dropdown).
- Tags (editable — type and press Enter to add).
- Plugin actions (e.g. Send to Reader, Send to Things).
- Action history (log of plugin executions and their outcomes).
What keyboard shortcuts are available?
These shortcuts work when no input field is focused:
/orCmd+K— focus the search barEscape— clear selection / close detail panel / exit triagej— move focus down in the link listk— move focus up in the link listoorEnter— open the focused link’s detail panelx— toggle bulk selection on the focused linka— archive or unarchive the focused linkd— delete the focused link1–9— send the focused link to the corresponding plugint— enter triage mode
When a link is focused, a hint bar appears at the bottom of the link list showing available plugin shortcuts (e.g. “1 Send to Things”, “2 Send to Reader”). A brief feedback toast confirms the action.
Plugins & Integrations
What are plugins?
Trove uses a declarative JSON plugin system. Plugins can do two things:
- Export — perform an action on a saved link (e.g. send to a read-later service, create a task). These appear as action buttons in the link detail panel, context menu, triage mode, and as swipe action options.
- Ingest — receive links from external tools via a webhook endpoint, automatically saving them to Trove.
Each plugin has a direction field: export, ingest, or both.
What plugins ship with Trove?
Three system plugins are built in (cannot be deleted):
- Readwise Reader — sends links to Readwise Reader for reading later. Tags from Trove are forwarded automatically. Requires a
READWISE_TOKEN(find it at readwise.io/access_token). - Things — creates a task in Things from a link. The link title becomes the task name, the URL goes in the notes, and the task is tagged with
trove. Uses the Things URL scheme — works on macOS and iOS where Things is installed. No configuration required. - n8n Webhook — receives links from n8n automation workflows. Useful for piping RSS feeds, email newsletters, or other data sources into Trove. Webhook endpoint:
POST /api/plugins/n8n/webhook. No configuration required.
How do I enable and configure a plugin?
Go to Settings > Plugins. Toggle the switch next to a plugin to enable or disable it. Expand the plugin to fill in required configuration fields (e.g. API tokens), then click Save.
How do I create a custom plugin?
Plugins are declarative JSON manifests — no TypeScript code required. You define:
- An
executeblock for export actions (typeapi-callfor HTTP requests orurl-redirectfor URL schemes). - An
ingestblock for webhooks (withitemMappingto map incoming payload fields to Trove link fields). - A
configschema for user-configurable settings.
A built-in template engine interpolates variables like {{link.url}}, {{link.title}}, {{config.API_TOKEN}}, with filters like |urlencode and |json.
How do I upload a custom plugin?
Admins can upload custom plugins from Settings > Plugins:
- Click Upload Plugin.
- Paste the JSON manifest.
- Enable the plugin for your account.
- Fill in any required configuration fields.
Admins can also delete non-system plugins. The three system plugins (Readwise Reader, Things, n8n Webhook) cannot be deleted.
What is the MCP server?
Trove includes a Model Context Protocol (MCP) server that lets AI assistants interact with your link library. It runs as a standalone process communicating over stdio.
Setup: Add the server configuration to your Claude Desktop or Claude Code MCP settings with command: “bun”, args: [“run”, “mcp”], and set TROVE_API_TOKEN and TROVE_DB_PATH in the environment block. The MCP server requires direct access to the SQLite database file.
Available tools:
search_links— full-text search across all saved links (with optional collection/tag filters)get_link— get a single link by ID with full content and metadatalist_links— browse saved links with optional filters and paginationlist_collections— list all collections with their link countslist_tags— list all tags with their link countsadd_link— save a new link to Trove (content extracted automatically)execute_action— run a plugin action on a saved link (e.g. send to Reader or Things)
Browser Extension
What browsers are supported?
Chrome and Safari (macOS and iOS). The extension uses Manifest V3 with vanilla HTML/CSS/JS — no build step required for Chrome.
How do I install the Chrome extension?
- Open
chrome://extensions/and enable Developer mode (top-right toggle). - Click Load unpacked and select the
extension/shared/directory from the Trove repository. - Click the extension icon in the toolbar to open the popup.
- Click Settings to configure your server URL and API token.
How do I install the Safari extension on macOS?
- Run the converter from the repository root:
xcrun safari-web-extension-converter extension/shared —project-location extension/safari —app-name “Save to Trove” —bundle-identifier com.trove.save-extension —swift - Open the generated Xcode project and select the macOS target.
- Configure signing with your development team.
- Build and run (
Cmd+R). - Enable the extension in Safari > Settings > Extensions.
How do I install the Safari extension on iOS?
- Use the same Xcode project generated by the macOS converter step.
- Select the iOS target and configure signing.
- Connect your iPhone via USB or select a simulator.
- Build and run.
- On your device, go to Settings > Apps > Safari > Extensions and enable Save to Trove.
- In Safari, tap the puzzle piece icon in the address bar to access the popup.
Free developer account note: You may need to re-enable Developer > Allow Unsigned Extensions in Safari after each launch. First enable Show features for web developers in Safari > Settings > Advanced.
How do I configure the extension?
- Open the extension’s Settings page.
- Enter your Trove server URL (e.g.
https://trove.example.com) — no trailing slash. - Enter your API token (find it in Settings > Account in the web UI).
- Click Test Connection to verify.
- Save your settings.
How do I use the extension popup?
Click the extension icon (or press Cmd+Shift+L) to open the save popup:
- The current page’s URL and title are pre-filled.
- Select a collection from the dropdown.
- Add tags by typing and pressing Enter.
- Click Save.
A green “OK” badge confirms the save was successful.
How does the context menu work?
Right-click on any page or link and select Save to Trove. The link is saved immediately to your inbox using your default settings. A badge indicates success or failure. Context menus are not available on iOS Safari.
How does content extraction work in the extension?
When saving via the extension, the rendered DOM content is captured from the active tab using the scripting permission. This captures:
- JavaScript-rendered content (SPAs, dynamic pages)
- Pages behind authentication (paywalled articles, logged-in dashboards)
- Visible text, meta descriptions, and raw HTML
The server skips its own extraction when pre-extracted content is provided by the extension.
What permissions does the browser extension need?
activeTab— access the current tab to extract content.contextMenus— add “Save to Trove” to the right-click menu.storage— store your server URL and API token locally in the browser.scripting— inject a script to capture the rendered DOM for content extraction.<all_urls>host permission — required to extract content from any page you visit.
The extension only communicates with your self-hosted Trove server. No data is sent to third parties.
Self-Hosting
What environment variables are available?
TROVE_DB_PATH(required, default./data/trove.db) — path to the SQLite database file.PORT(optional, default3737) — server listening port.TROVE_ADMIN_PASSWORD(required for seed) — password for the admin user, used bybun run seed.TROVE_API_TOKEN(required for MCP) — user API token for the MCP server process.TROVE_EXTRACTION_TIMEOUT_MS(optional, default10000) — content extraction fetch timeout in milliseconds.TROVE_MAX_CONTENT_LENGTH_CHARS(optional, default50000) — maximum character length for stored page content.
How do I deploy behind Traefik?
Copy the override template and edit it:
cp docker-compose.override.example.yml docker-compose.override.yml
The override file adds Traefik labels and connects the container to the external proxy network. Set HOSTNAME in your .env or replace the host rule directly. Then start as normal with docker compose up -d.
How do I back up the database?
The database is a single SQLite file at the path specified by TROVE_DB_PATH (default ./data/trove.db). SQLite WAL mode supports concurrent reads, so you can copy it while the container is running:
cp ./data/trove.db ./data/trove-backup-$(date +%Y%m%d).db
For more reliable backups:
docker compose exec trove sqlite3 /app/data/trove.db “.backup /app/data/backup.db”
Store backups off-server. Test restoring from a backup periodically.
How do I update Trove?
Run docker compose pull then docker compose up -d. Database migrations are applied automatically on startup. Back up your database before updating.
Is there a health check endpoint?
GET /health returns { “status”: “ok”, “links”: <count> }. The container includes a built-in health check that polls this endpoint every 30 seconds.
What does the CI/CD pipeline do?
CI: Every push and pull request triggers type-checking (backend and frontend) and tests.
Release: Pushes to main build and push a Docker image to the GitHub Container Registry at ghcr.io/jaydenk/trovelinkmanager:latest and ghcr.io/jaydenk/trovelinkmanager:<short-sha>.
Does Trove support multiple users?
Yes. Trove supports multi-user accounts with username/password authentication. Admin users can manage other users from Settings > Users:
- Create user — set a name, username, and password. The new user’s API token is shown once upon creation.
- Delete user — permanently removes the user and all their data (links, collections, tags, plugin configs).
To change your password, go to Settings > Account > Change Password. Minimum password length is 8 characters.
How does API authentication work?
All API routes under /api/* require a Bearer token in the Authorization header: Authorization: Bearer <your-api-token>
Exceptions: POST /api/auth/login and GET /health are public.
Login is rate limited to 10 attempts per minute per IP. Write operations (POST, PATCH, DELETE, PUT) are rate limited to 60 requests per minute per user.
Does Trove support real-time updates?
Yes. GET /api/events?token=<apiToken> provides a Server-Sent Events (SSE) stream. Events include link:created, link:updated, link:deleted, and link:archived. A heartbeat is sent every 30 seconds. Authentication is via query parameter (EventSource does not support custom headers).
Privacy & Security
Where is my data stored?
All data is stored locally in a single SQLite database file on your server. No data is sent to external services unless you explicitly configure a plugin to do so (e.g. Readwise Reader, Things).
Is Trove self-hosted only?
Yes. There is no cloud-hosted version. You run Trove on your own infrastructure — a home server, VPS, or any Docker-capable machine. You own and control all of your data.
Does Trove collect telemetry or analytics?
No. Trove collects no telemetry, no analytics, and makes no external API calls from the server. The only outbound requests the server makes are to the URLs you save (for content extraction) and to any export plugins you explicitly configure.
How does authentication work?
Users sign in with a username and password. The server issues a Bearer token for all subsequent API requests.
- Login is rate limited to 10 attempts per minute per IP.
- Write operations are rate limited to 60 requests per minute per user.
- API tokens can be regenerated at any time from Settings > Account. Regenerating a token immediately invalidates the old one.
- Tokens are stored hashed in the database.
What data do plugins access?
Export plugins send only the link data specified in their manifest template (typically URL, title, description, domain, and tags). You can inspect exactly what a plugin sends by reading its JSON manifest. Custom plugins must be uploaded by an admin.
Troubleshooting
Extension icon missing in Chrome
Check that extension/shared/ is loaded and enabled in chrome://extensions/.
Extension not appearing in Safari
Run the container app at least once — Safari only registers extensions from launched apps.
'Allow Unsigned Extensions' not available in Safari
Enable Show features for web developers in Safari > Settings > Advanced first.
Extension connection test fails
Verify the server URL has no trailing slash and the API token is correct.
Badge shows red '!' on save
Check the browser console for error details. Common causes: incorrect URL, expired token, or server unreachable.
iOS extension not visible
Go to Settings > Apps > Safari > Extensions on your device and toggle the extension on.
Extension stops working after Safari restart
With a free developer account, re-enable Developer > Allow Unsigned Extensions after each Safari launch.
iOS Shortcut shows 'Could not connect to the server'
Ensure Trove is reachable from your device. If using Tailscale, confirm the VPN is active on your iPhone/iPad.
iOS Shortcut creates duplicate links
Trove returns a 409 for duplicate URLs. The shortcut will still complete, but the notification may show an error. Add an If action to check the response status to handle duplicates gracefully.
MCP server: 'TROVE_API_TOKEN environment variable is not set'
Ensure the env block in your MCP config includes TROVE_API_TOKEN with a valid token.
MCP server: 'TROVE_API_TOKEN is invalid'
The token does not match any user. Check the token value or regenerate it from Settings in the web UI.
MCP server not connecting
Verify that bun is available in your PATH and that the cwd path is correct. The MCP server requires direct access to the SQLite database file specified by TROVE_DB_PATH.
Content extraction stuck on 'pending'
The server may be fetching the page. Wait a few seconds. If it does not resolve, the server process may have restarted during extraction. Retry via the link detail panel.
Content extraction shows 'failed'
The site may block server-side fetching, require authentication, or the extraction may have timed out. Try saving the link via the browser extension instead (client-side extraction captures authenticated and JS-rendered content). You can also increase TROVE_EXTRACTION_TIMEOUT_MS.
Extracted content is truncated
Content is limited to 50,000 characters by default. Increase TROVE_MAX_CONTENT_LENGTH_CHARS in your .env if needed.
Favicon not loading
Favicons are fetched from Google’s favicon service. If the site’s favicon is not indexed by Google, a placeholder will appear.
Cannot sign in
Verify your username and password. Check that the server is running (docker compose ps). Login is rate limited to 10 attempts per minute — wait and try again if you have exceeded the limit.
'TROVE_DB_PATH environment variable is not set'
Ensure your .env file exists and contains TROVE_DB_PATH=./data/trove.db.
Links not appearing in real-time
The app uses Server-Sent Events for real-time updates. Check the browser console for SSE connection errors. The SSE endpoint uses query-parameter authentication.
Import skipping all items
Duplicate URLs are silently skipped. If you are re-importing the same file, all items may already exist.
Rate limited (429 error)
Write operations are limited to 60 per minute per user. Login is limited to 10 attempts per minute per IP. Wait one minute and try again.
Data directory permissions error
Ensure the ./data/ directory is writable by the container. On Linux, you may need to set ownership: chown -R 1000:1000 ./data/.
Container health check failing
Verify port 3737 is not in use by another process. Check container logs with docker compose logs trove.
Plugin actions failing in MCP or triage mode
Ensure the plugin is enabled for your user account and all required configuration fields are filled in (Settings > Plugins).
Contact
Trove is free and open source under the MIT Licence.
- Source code: github.com/jaydenk/trove
- Documentation: github.com/jaydenk/trove/tree/main/docs
- Issues & feature requests: github.com/jaydenk/trove/issues
- Docker image:
ghcr.io/jaydenk/trovelinkmanager:latest - Email: [email protected]