Otter is a self-hosted bookmark manager made with Next.js and Supabase with Mastodon integration.
Features • Getting started • Docs • Ecosystem
- Private bookmarking app with search, tagging and filtering
- Dark/light colour modes
- Mastodon integration - backup of your own toots as well as your favourite toots
- Raycast extension to search your bookmarks, view recent bookmarks and create new ones
- Chrome extension for easy bookmarking
- Bookmarklet
Feed (dark mode) |
Feed (light mode) |
---|---|
New bookmark |
Search |
Feed (showing tags sidebar) |
Toots feed |
- Install dependencies with pnpm:
pnpm install
- Setup env vars (see below)
- Run the app locally using
pnpm dev
- Seed your database with
pnpm supabase:seed
Set up the following env vars. For local development you can set them in a .env.local
file. See an example here).
# Find these in your Supabase project settings > API
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
PERSONAL_MASTODON_ACCESS_TOKEN=your-personal-app-mastodon-access-token
BOT_MASTODON_ACCESS_TOKEN=your-bot-app-mastodon-access-token
OTTER_API_TOKEN=your-otter-api-token
Interactive API docs can be found in the various *.rest
files in the /app/api
directory.
POST /api/new
- create new item in OtterGET /api/new?url=https://example.com
- quick create new item in Otter. Pass in aurl
query param and it will create a new item with that URL and includes its metadata tooGET /api/bookmarks
- returns all bookmarks
GET /api/search?searchTerm=zander
- search bookmarkPOST /api/toot
- A PostgreSQL trigger function calls this endpoint anytime a bookmark is created or edited which then creates a new toot on two of my Mastodon accounts (@otterbot@botsin.space & @zander@toot.cafe). It only sends a toot if the bookmark is tagged aspublic
.
Otter has the ability to auto-toot to 2 Mastodon accounts when a new bookmark is created or edited. This is done via a PostgreSQL trigger function that calls the /api/toot
endpoint.
The trigger function below uses an environment variable in the Authorization
header to ensure only the owner of the Otter instance can call the endpoint.
create trigger "toot-otter-items"
after insert
or
update on bookmarks for each row
execute function supabase_functions.http_request (
'https://{your-otter-instance}/api/toot',
'POST',
-- replace {OTTER_API_TOKEN} with your own token
'{"Content-type":"application/json","Authorization":"{OTTER_API_TOKEN}"}',
'{}',
'1000'
);
TODO:
- document the PostgreSQL trigger function that calls the
/api/toot
endpoint
- Add the new type to the types enum
ALTER TYPE type ADD VALUE '???';
- Run
pnpm run supabase:types
to update the TypeScript types - Add a new
case
to theTypeToIcon
component - Add a new
TypeRadio
component to theBookmarkForm
component
I use various other tools to make Otter even better:
- Raycast extension (not currently on the Raycast extension marketplace)
- Chrome extension (not currently on the Chrome webstore)
- Apple Shortcut - download this shortcut and update your Otter instance URL within it. Then you can add it to your iOS share sheet and quickly add new bookmarks to Otter
- Page scraprer Cloudflare worker used to scrape the metadata of a URL. This is used when adding new bookmarks to Otter
- Mastodon to Supabase Cloudflare worker used to backup my Mastodon toots to Supabase
Made by Zander • zander.wtf • GitHub • Mastodon