The browser bar is full, and you cannot find last month’s docs link. You’ve tried scrolling through 200+ unsorted bookmarks, typed every partial keyword you can remember, and still come up empty. This lightweight local setup runs entirely on your machine, no cloud sync, no paid tools, no database installation required. It works for any operating system with basic command line access, and takes less than 15 minutes to set up from start to finish.
JSON fields you actually query later
You don’t need 15 metadata fields for a local bookmark tool—only the values you’ll actually search against or use to open links. Below is the full production schema for your `bookmarks.json` file, plus the pre-written grep-style search cheat sheet you can save as a text file on your desktop for quick access:

| Field Name | Data Type | Required | Query Use Case |
|---|---|---|---|
| `id` | Integer | Yes | Auto-incrementing unique identifier for deletions/edits |
| `url` | String | Yes | Full hyperlink, guaranteed to start with http/https per validation |
| `title` | String | Yes | Human-readable label, 3-100 characters |
| `tags` | Array of strings | Yes | 1-5 comma-separated labels you assign, no spaces allowed |
| `added_at` | ISO 8601 string | Yes | Auto-generated timestamp for date-range filtering |
| `notes` | String | No | 1-200 character context for edge-case searches |
Grep-Style Search Cheat Sheet (save as `bookmark-search-card.txt`)
- Basic title match: `grep -i “search term” bookmarks.json | jq ‘.[] | select(.title | test(“search term”; “i”)) | .url, .title’`
- Tag exact match: `jq ‘.[] | select(.tags[] == “react”) | .url, .title’ bookmarks.json`
- Combined title + tag match: `jq ‘.[] | select((.title | test(“docker”; “i”)) and (.tags[] == “devops”)) | .url’ bookmarks.json`
- Date range filter (last 30 days): `jq –argjson cutoff “$(date -d “-30 days” +%s)” ‘.[] | select(.added_at | fromdateiso8601 > $cutoff) | .title, .url’ bookmarks.json`
- Full text search across all fields: `grep -i “kubernetes” bookmarks.json | jq ‘.url, .title’`
Initialize your `bookmarks.json` file with an empty array `[]` to avoid parsing errors when adding your first entry.
Add command that refuses a missing URL
You can add a new bookmark in 10 seconds or less with a tiny shell function added to your shell config file (`.zshrc` for Zsh users, `.bashrc` for Bash users). The function first validates that a valid URL is provided, and exits with an error if no URL is passed, or if the URL does not start with `http://` or `https://` to avoid broken entries.
Paste this into your shell config, then run `source ~/.zshrc` or `source ~/.bashrc` to activate it:

“`bash
add-bookmark() {
if [ -z “$1” ] || [[ ! $1 =~ ^https?:// ]]; then
echo “Error: Please provide a valid HTTP/HTTPS URL as the first argument”
return 1
fi
# Get next auto-increment ID
NEXT_ID=$(jq ‘length + 1’ ~/bookmarks.json)
# Prompt for required fields
echo “Enter bookmark title:”
read TITLE
echo “Enter comma-separated tags (no spaces, e.g. react,devops,tutorial):”
read TAGS
echo “Enter optional notes (press enter to skip):”
read NOTES
# Convert tags to array
TAGS_ARRAY=$(echo “$TAGS” | jq -R ‘split(“,”)’)
# Add to bookmarks file
jq –argjson id “$NEXT_ID” –arg url “$1” –arg title “$TITLE” –argjson tags “$TAGS_ARRAY” –arg added_at “$(date -Iseconds)” –arg notes “$NOTES” ‘. += [{“id”: $id, “url”: $url, “title”: $title, “tags”: $tags, “added_at”: $added_at, “notes”: $notes}]’ ~/bookmarks.json > temp.json && mv temp.json ~/bookmarks.json
echo “Bookmark added successfully!”
}
“`
To use it, run `add-bookmark https://example.com/docs` from your terminal, and follow the prompts. The function will never add an incomplete or broken bookmark to your JSON file, so you don’t have to clean up bad entries later. Example measurement: This function takes less than 7 seconds to run for most standard bookmark entries.
Title-and-tag match without a server
All search operations run entirely on your local machine using pre-installed or easily accessible command line tools, with no backend server, port binding, or background process required. The `jq` tool is a lightweight JSON parser that runs queries directly against your static `bookmarks.json` file, with zero network calls or third-party data sharing.
Illustrative example: A library of 2000 bookmarks returns search results in under 200ms on a 3-year-old entry-level laptop. You can extend the base search commands to fit your workflow, like adding `| xargs open` to the end of any jq query to automatically open the first matching URL in your default browser, or creating shell aliases for common searches to cut down on typing. For example, adding `alias bsearch=’jq –arg t “$1” ‘”‘”‘.[] | select(.tags[] == $t) | .url, .title'”‘”‘ ~/bookmarks.json’` to your shell config lets you run `bsearch react` to pull up all React-related bookmarks in one step. No external services, no login walls, no latency from cloud requests.
Import from a browser HTML dump, once
You don’t have to manually add hundreds of existing bookmarks to your new setup: all major desktop browsers let you export your full bookmark library as a standardized HTML file, and you can run a one-time short Python script to parse that file and import all entries directly into your `bookmarks.json` format. You only need to run this import once, then use the `add-bookmark` shell function for all new bookmarks going forward.
No-server rule until you need a second device
This single-file setup works perfectly for single-device use, and there is no need to add a database, server, or cloud sync layer until you explicitly need access to your bookmarks on a second laptop, phone, or shared device. When you do reach that point, you have two low-lift upgrade paths that require zero changes to your existing core workflow: sync your `bookmarks.json` file via a standard cloud storage tool like Dropbox or Google Drive, and point your shell function to the synced file location, or spin up a 20-line Node.js server that exposes the same jq search queries as API endpoints for mobile access.
You can extend the base setup with optional features like fuzzy search via `fzf` or a simple Tkinter GUI for point-and-click access later, but the core functionality works with zero extra dependencies beyond `jq`, which is pre-installed on most Linux distributions and available via Homebrew on Mac in 30 seconds. The entire stack weighs less than 1MB even with 1000+ bookmarks, so it will never take up meaningful storage space on your machine.
Right now, create an empty `bookmarks.json` file in your home directory with the content `[]`, install `jq` if you don’t already have it, and add the `add-bookmark` function to your shell config to test the workflow with one of your most frequently accessed missing docs links.
Written by the Build Next Stack editors.