Otter
A self-hosted bookmark manager and media tracker built for people who value privacy and ownership.
Project README

Otter
Otter is a self-hosted bookmark manager and media tracker built with React, Hono, Postgres, and Cloudflare Workers
Features
- Private bookmarking app with search, tagging, collections, and filtering
- Starred items and public/private visibility per bookmark
- Dark/light colour modes
- Media tracking — kanban-style board for tracking movies, TV shows, games, and more
- AI-powered title and description rewriting via Cloudflare Workers AI
- RSS feed parsing and URL scraping
- Mastodon integration — backup your own toots and favourite toots, plus auto-posting public bookmarks
- Bluesky and Twitter/X import
- MCP server — let MCP clients (Claude, Cursor, etc.) search and manage your bookmarks
- REST API with API-key auth, plus an OAuth provider for first-party clients
- Cross-browser web extension (Chrome & Firefox)
- Raycast extension to search, view, and create bookmarks
- Terminal UI (
otter-tui) - Native macOS/iOS app
- Bookmarklet
Screenshots
Feed (dark mode) ![]() |
Feed (light mode) ![]() |
|---|---|
New bookmark ![]() |
Search ![]() |
Feed (showing tags sidebar) ![]() |
Toots feed ![]() |
Packages
This is a pnpm monorepo containing the following packages:
| Package | Description |
|---|---|
packages/web |
Web app, Hono API, OAuth + MCP provider on Cloudflare Workers |
packages/app |
Native macOS/iOS app (Safari web extension + share sheet) |
packages/web-extension |
Cross-browser extension (Chrome & Firefox) |
packages/raycast-extension |
Raycast extension |
packages/tui |
Terminal UI — browse, search, star and save bookmarks from the shell |
packages/chrome-extension |
Legacy Chrome extension (superseded by web-extension) |
Getting started
Prerequisites
- pnpm v11 (pinned via
packageManager) — install withcorepack enable && corepack prepare pnpm@latest --activate - A Postgres database, e.g. Neon
- Cloudflare account — used for hosting, Workers AI, Hyperdrive, and the API
For a full walkthrough — including database setup, Cloudflare configuration, and deployment — see the Setup Instructions.
Quick start
pnpm install
pnpm web:dev
Releasing
Otter uses semantic-release with the semantic-release-monorepo plugin to version each package independently based on Conventional Commits. Packages are not published to npm — releases are GitHub releases only.
Commit message format
| Prefix | Release type |
|---|---|
fix: |
Patch (1.0.x) |
feat: |
Minor (1.x.0) |
feat!: or BREAKING CHANGE: |
Major (x.0.0) |
Per-package versioning
Each releasable package has its own release.config.mjs that extends semantic-release-monorepo. The plugin filters commits to those that touch files inside the package's directory, so a commit changing only packages/web will only bump and release @mrmartineau/otter-web.
Releasable packages:
@mrmartineau/otter-web— tags as@mrmartineau/otter-web@vX.Y.Z@mrmartineau/otter-chrome-extension— tags as@mrmartineau/otter-chrome-extension@vX.Y.Z@mrmartineau/otter-web-extension— tags as@mrmartineau/otter-web-extension@vX.Y.Z@mrmartineau/otter-tui— tags as@mrmartineau/otter-tui@vX.Y.Z
The app and raycast-extension packages are released through their own platforms (App Store / Raycast Store) and are not part of this workflow.
CI / automated releases
Releases are triggered manually via the "Release" workflow in GitHub Actions (.github/workflows/release.yml). The workflow:
- Installs dependencies
- Runs
semantic-releaseinside each releasable package viapnpm --filter ... exec semantic-release - For each package with relevant new commits: bumps
package.json, updates that package'sCHANGELOG.md, commits the bump back tomain, and creates a GitHub release with package-scoped tag and notes
GITHUB_TOKEN is provided automatically by GitHub Actions — no additional secrets required.
Scoping commits
To target a specific package, use a Conventional Commits scope, e.g. feat(web): ... or fix(chrome-extension): .... The plugin uses changed file paths (not the scope) to decide which package releases, but scopes make the changelog clearer.
Tech stack
- Frontend: React 19, TanStack Router, TanStack Query, Tailwind CSS v4, Radix UI
- API: Hono on Cloudflare Workers with AI bindings
- Database: Postgres (e.g. Neon) via Drizzle ORM and Cloudflare Hyperdrive
- Auth: Better Auth with OAuth provider + JWT plugins
- Hosting: Cloudflare
- Tooling: pnpm workspaces, Biome (formatting & linting), Vite, Vitest
License
Made by Zander • zander.wtf • GitHub • Mastodon





