Skip to content

mlashcorp/stash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stash

A self-hosted read-it-later app built on Cloudflare's free tier. Save bookmarks from a Chrome extension or Android share sheet, search them semantically or by tag, and browse them from a web dashboard.

Quick start

git clone https://github.com/mlashcorp/stash.git
cd stash
./scripts/setup.sh

The script walks you through every step: creates the D1 database, Vectorize index, Cloudflare Access application (via Terraform), sets all secrets, and deploys.

Prerequisites: Node.js 18+, Wrangler authenticated (wrangler login), OpenRouter API key. Terraform ≥ 1.7 is optional but automates the Cloudflare Access configuration.

For full control over each step, see Manual Setup.


Stack

  • WorkerHono on Cloudflare Workers (TypeScript)
  • Database — Cloudflare D1 (SQLite)
  • Vector search — Cloudflare Vectorize
  • AI tagging + embeddingsOpenRouter (Gemini Flash Lite)
  • Auth — Cloudflare Access (browser + extension)
  • Extension — Chrome MV3

Cloudflare Access (required)

Without this, your dashboard is publicly accessible. Cloudflare Access puts an identity gate in front of it — only you can log in.

The setup script handles the Terraform-managed part automatically. There is one step that must be done manually from the Cloudflare dashboard — it cannot be done via API or Terraform:

  1. Go to Cloudflare DashboardWorkers & Pages → select your stash Worker
  2. Go to SettingsDomains & Routes
  3. Click Enable Cloudflare Access on the workers.dev row
  4. Click Manage Cloudflare Access → create a policy (e.g. allow your email + One-Time PIN)

The setup script will pause and prompt you to complete this step before continuing.

See Manual Setup for the full breakdown including the Terraform steps and how to configure everything without the script.


Chrome Extension

The extension authenticates using the same Cloudflare Access session as the dashboard — no API token needed.

  1. Open Chrome → chrome://extensions
  2. Enable Developer mode
  3. Click Load unpacked → select the extension/ folder
  4. Click the extension icon → open Settings → enter your Worker URL → Save
  5. Make sure you're logged into your Stash dashboard in the same browser

After that, click the extension icon on any page to save it to your Stash in one click.


Android PWA

On Android, Stash can appear in the native share sheet — letting you save any link from any app with one tap.

  1. Open your Stash URL in Chrome for Android
  2. Tap the three-dot menu → Add to Home screenInstall
  3. Stash will appear on your home screen and in the share sheet

If saving fails, open the dashboard directly to refresh your session, then try again.


MCP Server

Stash ships with an MCP server so AI assistants (Claude, etc.) can save, search, and manage your bookmarks directly.

Setup

  1. Create an API key in the Stash dashboard (Settings → API Keys)
  2. Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
  "mcpServers": {
    "stash": {
      "command": "npx",
      "args": ["-y", "stash-mcp-server"],
      "env": {
        "STASH_URL": "https://stash.<your-subdomain>.workers.dev",
        "STASH_API_KEY": "stash_your_key_here"
      }
    }
  }
}

Restart Claude Desktop — the stash tools will appear automatically.

Available tools

Tool Description
save_bookmark Save a URL with title; returns ID and AI-generated tags
delete_bookmark Delete a bookmark by ID
add_tag / remove_tag Manage tags on a bookmark
list_tags List all tags with counts
search_bookmarks Fuzzy, semantic, or tag-filter search
suggest_titles Autocomplete bookmark titles

The MCP server source lives in mcp-server/. See mcp-server/README.md for more details.


API Keys

For programmatic access (scripts, the MCP server), you can create API keys from the settings page (gear icon in the header). Keys are shown once on creation — store them somewhere safe.

All API requests with a key use the X-API-Token header:

curl https://stash.<your-subdomain>.workers.dev/api/save \
  -H "X-API-Token: stash_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "title": "Example"}'

API Reference

Method Path Description
POST /api/save Save a bookmark { url, title }
DELETE /api/save/:id Delete a bookmark
GET /api/tags List all tags with counts
GET /api/search?mode=fuzzy&q=...&page=N Fuzzy search (paginated)
GET /api/search?mode=tag&tag=...&page=N Filter by tag (paginated)
GET /api/search?mode=semantic&q=... Semantic vector search
GET /api/keys List API keys
POST /api/keys Create an API key { name }
DELETE /api/keys/:id Revoke an API key

Testing

npm test                  # Worker unit tests (Vitest)
npm run test:scripts      # Bash script tests (no dependencies)
npm run test:terraform    # Terraform tests using mock_provider (requires Terraform >= 1.7)

The Worker tests run against a local Cloudflare Workers runtime via Vitest. The script tests cover wrangler::patch_wrangler_toml and the terraform.tfvars placeholder detection — both operate on temp files with no external calls. The Terraform tests use mock_provider "cloudflare" {} so no credentials or real resources are needed.


Local development

Copy .dev.vars.example to .dev.vars and fill in your values — Wrangler loads this automatically during wrangler dev.

npm run dev

D1 and Vectorize work locally via Wrangler's local simulation. Run wrangler d1 migrations apply stash-db --local first to set up the local database.


License

MIT

About

Pocket/Instapaper alternative for personal use

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors